Connecteur HDFS pour le stockage d'objets

Le connecteur HDFS (Hadoop Distributed File System) permet à votre application Apache Hadoop de lire et d'écrire des données vers et depuis le service Oracle Cloud Infrastructure Object Storage.

Cette trousse SDK et cet exemple sont des licences doubles fournies dans le cadre des licences Universal Permissive License 1.0 et Apache License 2.0. Le contenu de tierce partie est fourni sous une licence distincte, comme décrit dans le code.

Conditions requises

Pour utiliser le connecteur HDFS, vous devez avoir :

Données d'identification et mots de passe

Si vous utilisez un fichier PEM chiffré pour les données d'identification, la phrase secrète est lue à partir de la configuration à l'aide de la méthode de configuration Hadoop getPassword. L'option getPassword vérifie la présence d'un mot de passe dans un fournisseur de sécurité inscrit. Si le fournisseur de sécurité ne contient pas la clé demandée, la phrase secrète en texte brut est lue directement à partir du fichier de configuration.

Configuration de la durée de vie de la JVM pour les consultations de nom DNS

La machine virtuelle Java (JVM) met en mémoire cache les réponses DNS à partir des consultations pendant un certain temps, appelé Durée de vie. Cela garantit un temps de réponse plus rapide pour le code qui nécessite une résolution fréquente des noms.

La JVM utilise la propriété networkaddress.cache.ttl pour spécifier la politique de mise en mémoire cache des consultations de nom DNS. La valeur est un nombre entier qui représente le nombre de secondes pour mettre en mémoire cache la consultation réussie. La valeur par défaut pour de nombreuses machines virtuelles Java, -1, indique que la consultation doit toujours être mise en mémoire cache.

Comme les ressources dans Oracle Cloud Infrastructure utilisent des noms DNS pouvant être modifiés, nous vous recommandons de remplacer la valeur de durée de vie par 60 secondes. Cela garantit que la nouvelle adresse IP de la ressource est retournée lors de la prochaine interrogation de DNS. Vous pouvez modifier cette valeur de façon globale ou précise pour votre application :

  • Pour définir la durée de vie globalement pour toutes les applications utilisant la JVM, ajoutez la valeur suivante dans le fichier $JAVA_HOME/jre/lib/security/java.security :

    networkaddress.cache.ttl=60
  • Pour définir la durée de vie uniquement pour votre application, définissez les éléments suivants dans le code d'initialisation de votre application :

    java.security.Security.setProperty("networkaddress.cache.ttl" , "60");

Installation

Copiez les fichiers JAR regroupés de la bibliothèque et de la bibliothèque de tierce partie dans chaque noeud de la grappe Hadoop afin qu'ils soient inclus dans la variable CLASSPATH de Hadoop.

Trousse SDK pour les artefacts Java et Maven

La création du connecteur HDFS s'appuie sur les artefacts Maven fournis par la trousse SDK Oracle Cloud Infrastructure pour Java. Pour obtenir les artefacts, vous devez télécharger la trousse SDK pour Java et la créer localement. Vous pouvez ensuite créer le connecteur HDFS.

Important

La trousse SDK pour la version du fichier Java que vous téléchargez à partir de la page des versions Oracle doit correspondre à la version du connecteur HDFS figurant dans le fichier hdfs-connector/pom.xml du bloc de balise de dépendance qui contient l'attribut groupId.

Connecteur HDFS et artefacts Maven

Le connecteur HDFS est disponible sur Maven Central et JCenter.

Pour utiliser le connecteur HDFS dans votre projet, importez la dépendance de projet suivante. Par exemple :

<dependency>
  <groupId>com.oracle.oci.sdk</groupId>
  <artifactId>oci-hdfs-connector</artifactId>
  <!-- Replace the version below with your required version -->
  <version>2.9.2.0</version>
</dependency>

Propriétés

Vous pouvez définir les propriétés de connecteur HDFS suivantes dans le fichier core-site.xml. La page BmcProperties liste les propriétés supplémentaires que vous pouvez configurer pour une connexion au service de stockage d'objets.

Propriété Description Type Obligatoire
fs.oci.client.hostname

URL du point d'extrémité de l'hôte.

Par exemple, https://www.example.com.

Chaîne Oui
fs.oci.client.auth.tenantId

OCID de votre location.

Pour obtenir la valeur, voir Clés et OCID requis.

Chaîne Oui
fs.oci.client.auth.userId

OCID de l'utilisateur appelant l'API.

Pour obtenir la valeur, voir Clés et OCID requis.

Chaîne Oui
fs.oci.client.auth.fingerprint

Empreinte pour la paire de clés utilisée.

Pour obtenir la valeur, voir Clés et OCID requis.

Chaîne

Oui, sauf si vous fournissez un authentificateur personnalisé.

fs.oci.client.auth.pemfilepath Chemin complet et nom de fichier de la clé privée servant à l'authentification. Le fichier doit se trouver dans le système de fichiers local. Chaîne Oui, sauf si vous fournissez un authentificateur personnalisé.
fs.oci.client.auth.passphrase Phrase secrète utilisée pour la clé si elle est chiffrée. Chaîne Uniquement si votre clé est chiffrée.
fs.oci.client.regionCodeOrId Code ou identificateur de région utilisé pour établir le nom du point d'extrémité du service de stockage d'objets. Chaîne Non
Note

Vous pouvez indiquer qu'une valeur de propriété s'applique à un seau spécifique en ajoutant .<bucket_name>.<namespace_name> au nom de la propriété.

Définition du point d'extrémité de la région

Vous pouvez utiliser plusieurs méthodes pour définir le point d'extrémité de la région pour le connecteur HDFS :

  • Définir la propriété de nom d'hôte dans core-site.xml
  • Définir la propriété de code ou d'identificateur de région dans core-site.xml
  • Autoriser le client ObjectStorage à récupérer le point d'extrémité au moyen du service de métadonnées d'instance

Configuration des propriétés à l'aide de core-site.xml

Cet exemple montre comment les propriétés peuvent être configurées dans un fichier core-site.xml (les OCID sont abrégés par souci de concision) :

<configuration>
...
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-ashburn-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.hostname.myBucket.myNamespace</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value><!-- Use Phoenix for myBucket@myNamespace -->
  </property>
  <property>
    <name>fs.oci.client.auth.tenantId</name>
    <value>ocid1.tenancy.oc1..exampleuniqueID</value> 
  </property>
  <property>
    <name>fs.oci.client.auth.userId</name>
    <value>ocid1.user.oc1..exampleuniqueID</value>
  </property>
  <property>
    <name>fs.oci.client.auth.fingerprint</name>
    <value>20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34</value>
  </property>
  <property>
    <name>fs.oci.client.auth.pemfilepath</name>
    <value>~/.oci/oci_api_key.pem</value>
  </property>
...
</configuration>

Utilisation des principaux d'instance pour l'authentification

Oracle fournit des principaux d'instance afin que vous n'ayez plus à configurer de données d'identification des utilisateurs ni à fournir des fichiers PEM pour les services en cours d'exécution sur des instances. Chaque instance possède sa propre identité et s'authentifie à l'aide des certificats que les principaux d'instance lui ajoutent.

Pour utiliser l'authentification fondée sur des principaux d'instance avec le connecteur HDFS, il vous suffit de fournir la propriété fs.oci.client.custom.authenticator et de régler la valeur à com.oracle.bmc.hdfs.auth.InstancePrincipalsCustomAuthenticator.

Comme les principaux d'instance fournissent au connecteur un authentificateur personnalisé, il n'est plus nécessaire de configurer les propriétés suivantes :

  • fs.oci.client.auth.tenantId
  • fs.oci.client.auth.userId
  • fs.oci.client.auth.fingerprint
  • fs.oci.client.auth.pemfilepath
  • fs.oci.client.auth.passphrase

L'exemple de code suivant illustre l'utilisation des principaux d'instance pour l'authentification avec le connecteur HDFS :

<?xml version="1.0"?>
<configuration>
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.custom.authenticator</name>
    <value>com.oracle.bmc.hdfs.auth.InstancePrincipalsCustomAuthenticator</value>
  </property>
</configuration>

Pour plus d'informations sur les principaux d'instance, voir Annonce concernant les principaux d'instance pour la gestion des identités et des accès.

Utilisation des principaux de ressource pour l'authentification

Comme pour les principaux d'instance, Oracle fournit des principaux de ressource pour authentifier les ressources qui ne sont pas des instances (un carnet jupyter, par exemple). Chaque ressource possède sa propre identité et s'authentifie à l'aide des certificats qui y sont ajoutés.

Pour utiliser l'authentification fondée sur des principaux de ressource avec le connecteur HDFS, il vous suffit de fournir la propriété fs.oci.client.custom.authenticator et de régler la valeur à com.oracle.bmc.hdfs.auth.ResourcePrincipalsCustomAuthenticator.

Comme l'utilisation des principaux de ressource fournit au connecteur un authentificateur personnalisé, il n'est pas nécessaire de configurer les propriétés suivantes :

  • fs.oci.client.auth.tenantId
  • fs.oci.client.auth.userId
  • fs.oci.client.auth.fingerprint
  • fs.oci.client.auth.pemfilepath
  • fs.oci.client.auth.passphrase

L'exemple de code suivant illustre l'utilisation des principaux de ressource pour l'authentification avec le connecteur HDFS :

<?xml version="1.0"?>
<configuration>
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.custom.authenticator</name>
    <value>com.oracle.bmc.hdfs.auth.ResourcePrincipalsCustomAuthenticator</value>
  </property>
</configuration>

Pour plus d'informations sur les principaux de ressource, voir Utilisation des principaux de ressource dans le service de science des données.

Utilisation de l'authentification Kerberos

Oracle prend en charge l'authentification Kerberos pour la connexion au service de stockage d'objets à l'aide du connecteur HDFS.

Pour utiliser l'authentification Kerberos avec le connecteur HDFS :
  1. Dans core-site.xml, réglez la propriété fs.oci.client.custom.authenticator à com.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator.
  2. Dans core-site.xml, définissez les propriétés suivantes :
    • fs.oci.client.upst.domainUrl
    • fs.oci.client.upst.clientId

    • fs.oci.client.upst.clientSecret

    • fs.oci.client.upst.tokenExchangeServicePrincipal

    • fs.oci.client.upst.userPrincipal

    • fs.oci.client.upst.issuer
    • fs.oci.client.keytab.path
    • fs.oci.client.kinit.internal.mode

L'exemple de fichier core-site.xml suivant illustre l'utilisation de Kerberos avec l'authentification par jeton SPNEGO avec le connecteur HDFS :

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.auth.tenantId</name>
    <value></value>
  </property>
  <property>
    <name>fs.oci.client.auth.userId</name>
    <value></value>
  </property>
   <property>
    <name>fs.oci.client.custom.authenticator</name>
    <value>com.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator</value>
  </property>
  <property>
    <name>fs.oci.client.upst.tokenExchangeServicePrincipal</name>
    <value><!-- Service Principal for generating SPNEGO token --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.userPrincipal</name>
    <value><!-- User Principal for generating SPNEGO token --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.issuer</name>
    <value><!-- Issuer for generating SPNEGO token --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.domainUrl</name>
    <value><!-- Domain application client ID for IAM token exchange --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.clientId</name>
    <value><!-- Domain application client ID for IAM token exchange --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.clientSecret</name>
    <value><!-- Domain application client secret for IAM token exchange --></value>
  </property>
  <property>
    <name>fs.oci.client.keytab.path</name>
    <value><!-- File path to the keytab used for token exchange --></value>
  </property>
 </configuration>

Pour plus d'informations sur Kerberos, voir le tutoriel sur le protocole Kerberos.

Pour plus d'informations sur SPNEGO, voir le document RFC 4178.

Utilisation du connecteur Jersey par défaut HttpUrlConnectorProvider

À partir de la version 3.3.0.7.0.0, HDFS prend en charge l'utilisation du client Apache par défaut pour effectuer des appels de service OCI. En effet, le connecteur HDFS utilise la trousse SDK pour Java pour envoyer des demandes au serveur. La trousse SDK pour Java prend en charge l'utilisation du connecteur Jersey par défaut ApacheConnectorProvider au lieu du connecteur Jersey HttpUrlConnectorProvider pour permettre au client HTTP Apache d'effectuer des appels de service OCI.

Pour revenir à l'ancien client Jersey par défaut, réglez la propriété fs.oci.client.jersey.default.connector.enabled dans le fichier core-site.xml à true. Par défaut, cette valeur est réglée à false.

Optimisation de la performance avec le connecteur Apache pour HDFS

Le connecteur Apache prend en charge deux stratégies de fermeture de connexion : ApacheConnectionClosingStrategy.GracefulClosingStrategy et ApacheConnectionClosingStrategy.ImmediateClosingStrategy.

Lorsque vous utilisez ApacheConnectionClosingStrategy.GracefulClosingStrategy, les flux retournés à partir d'une réponse sont lus jusqu'à la fin du flux lors de la fermeture de celui-ci. Cela peut entraîner un délai supplémentaire lors de la fermeture du flux avec une lecture partielle, selon la taille du flux restant. Pour éviter ce délai, envisagez d'utiliser ApacheConnectionClosingStrategy.ImmediateClosingStrategy pour les fichiers volumineux avec des lectures partielles. Avec ApacheConnectionClosingStrategy.ImmediateClosingStrategy, les flux ne sont pas lus jusqu'à la fin lors de leur fermeture, ce qui peut améliorer la performance. Notez que ApacheConnectionClosingStrategy.ImmediateClosingStrategy prend plus de temps lors de l'utilisation de la lecture partielle pour les flux dont la taille est inférieure à 1 Mo.

Définition de la stratégie de fermeture de connexion

Définissez la stratégie de fermeture de connexion en définissant la propriété fs.oci.client.apache.connection.closing.strategy dans le fichier core-site.xml :

  • Pour utiliser ApacheConnectionClosingStrategy.GracefulClosingStrategy, réglez fs.oci.client.apache.connection.closing.strategy à graceful.
  • Pour utiliser ApacheConnectionClosingStrategy.ImmediateClosingStrategy, réglez fs.oci.client.apache.connection.closing.strategy à immediate.
Note

Ces stratégies de fermeture fonctionnent uniquement avec le connecteur Apache pour HDFS et sont ignorées lors de l'utilisation du connecteur Jersey par défaut.

Retour au connecteur Jersey par défaut

Le connecteur Jersey par défaut lit les flux jusqu'à la fin, puis réutilise le flux, ce qui peut améliorer la performance par rapport au connecteur Apache pour HDFS dans certains scénarios. Si ces stratégies de fermeture de connexion Apache ne produisent pas des résultats optimaux pour vos cas d'utilisation, vous pouvez revenir au connecteur Jersey par défaut HttpUrlConnectorProvider. Vous pouvez revenir à l'ancien client Jersey par défaut en réglant la propriété fs.oci.client.jersey.default.connector.enabled dans le fichier core-site.xml à true. Par défaut, cette valeur est réglée à false.

Pour plus d'informations, voir : https://github.com/oracle/oci-java-sdk/blob/master/ApacheConnector-README.md.

Réserve de connexions dans HDFS

Vous pouvez définir le nombre maximal de connexions dans la réserve de connexions HDFS.

Pour ce faire, remplacez la propriété fs.oci.client.apache.max.connection.pool.size dans le fichier core-site.xml par un entier positif qui spécifie le nombre de connexions dans la réserve.

Note

Cette propriété n'est prise en charge que si vous utilisez ApacheConnector pour HDFS; sinon, elle est ignorée.

Points d'extrémité dédiés

Les endpoints dédiés sont les modèles de endpoint définis par un service pour un domaine spécifique au niveau client. Le connecteur HDFS OCI vous permet d'activer l'utilisation de ces modèles de point d'extrémité propres au domaine en définissant le nom d'hôte fs.oci.client.hostname ou la propriété fs.oci.realmspecific.endpoint.template.enabled de l'indicateur de modèle de point d'extrémité dans core-site.xml.
Note

Si vous définissez la propriété du modèle de point d'extrémité, vous devez également définir fs.oci.client.regionCodeOrId dans core-site.xml.
Note

La valeur définie au moyen du nom d'hôte dans core-site.xml a préséance sur la valeur définie au moyen de la propriété de modèle de point d'extrémité dans core-site.xml.
Cet exemple montre comment activer la fonction des modèles de point d'extrémité propres au domaine en définissant la propriété fs.oci.client.hostname :
<property>
  <name>fs.oci.client.hostname</name>
  <value>https://my-namespace.objectstorage.me-dubai-1.oci.customer-oci.com</value>
</property>
Cet exemple montre comment activer la fonction des modèles de point d'extrémité propres au domaine en définissant la propriété fs.oci.realmspecific.endpoint.template.enabled :
<property>
  <name>fs.oci.client.regionCodeOrId</name>
  <value>me-dubai-1</value>
</property>
<property>
  <name>fs.oci.realmspecific.endpoint.template.enabled</name>
  <value>true</value>
</property>

Configuration d'un mandataire HTTP

Vous pouvez définir les propriétés facultatives suivantes dans le fichier core-site.xml pour configurer un mandataire HTTP :

Propriété Description Type Obligatoire
fs.oci.client.proxy.uri

URI du point d'extrémité du mandataire.

Par exemple, http://proxy.mydomain.com:80.

Chaîne Non
fs.oci.client.proxy.username Nom d'utilisateur pour l'authentification auprès du mandataire. Chaîne Non
fs.oci.client.proxy.password Mot de passe pour l'authentification auprès du mandataire. Chaîne Non
fs.oci.client.multipart.allowed Active le gestionnaire de chargement pour la prise en charge des chargements en plusieurs parties. Booléen Non
fs.oci.client.multipart.minobjectsize.mb

Spécifie la taille minimale de l'objet en mébioctets pour le gestionnaire de chargement.

Entier Non
fs.oci.client.multipart.partsize.mb Spécifie la taille de la partie en mébioctets pour le gestionnaire du chargement. Entier Non
Note

La configuration d'un mandataire permet l'utilisation de ApacheConnectorProvider lors des connexions au Stockage d'objets. Les demandes sont mises en mémoire tampon, ce qui peut avoir une incidence sur l'utilisation de la mémoire lors du chargement d'objets volumineux. Il est recommandé d'activer les chargements en plusieurs parties et d'ajuster les propriétés des parties pour gérer la consommation de la mémoire.

Chargements d'objets volumineux

Les objets volumineux sont chargés dans le stockage d'objets à l'aide de chargements en plusieurs parties. Le fichier est fractionné en parties plus petites qui sont chargées en parallèle, ce qui réduit le temps de chargement. Cela permet également au connecteur HDFS de charger à nouveau uniquement les parties en échec et d'éviter ainsi l'échec de l'ensemble du chargement. Toutefois, il arrive que les chargements échouent de façon temporaire. Dans ces cas, le connecteur tentera d'abandonner les fichiers partiellement chargés. Comme ces fichiers se cumulent (et que vous serez facturé pour le stockage), listez les chargements périodiquement, puis après un certain nombre de jours, abandonnez-les manuellement à l'aide de la trousse SDK pour Java.

Vous pouvez trouver des informations sur l'utilisation de l'API Stockage d'objets pour gérer les chargements en plusieurs parties sous Utilisation des chargements en plusieurs parties.

Note

Si vous préférez ne pas utiliser les chargements en plusieurs parties, vous pouvez les désactiver en réglant la propriété fs.oci.client.multipart.allowed à false.

Meilleures pratiques

Les sections qui suivent contiennent les meilleures pratiques pour optimiser l'utilisation et la performance.

Nom de répertoire

Il n'existe aucun répertoire réel dans le stockage d'objets. Le regroupement de répertoires est une fonction d'une convention d'attribution de nom, où les objets utilisent les délimiteurs / dans leur nom. Par exemple, un objet nommé a/example.json implique qu'il existe un répertoire nommé a. Toutefois, si cet objet est supprimé, le répertoire a est également supprimé implicitement. Pour préserver la sémantique du système de fichiers où le répertoire peut exister sans présence de fichiers, le connecteur HDFS crée un objet réel dont le nom se termine par / avec un chemin qui représente le répertoire (par exemple, il crée un objet nommé a/). Désormais, la suppression de a/example.json n'affecte pas l'existence du répertoire a, car l'objet a/ garantit sa présence. En revanche, il est tout à fait possible qu'un utilisateur puisse supprimer cet objet a/ sans supprimer les fichiers et les répertoires connexes. Le connecteur HDFS supprimera uniquement l'objet de dossier s'il n'existe aucun objet sous ce chemin. L'objet de dossier lui-même est de zéro octet.

Système de fichiers incohérent

La suppression d'un répertoire entraîne la suppression de tous les objets qui commencent par le préfixe représentant ce répertoire. HDFS vous permet d'interroger le statut d'un fichier ou d'un répertoire. Le statut de fichier d'un répertoire est mis en oeuvre en vérifiant que l'objet de dossier pour ce répertoire existe. Il est toutefois possible que l'objet de dossier ait été supprimé, mais que des objets ayant ce préfixe existent encore. Prenons, par exemple, les objets suivants :

  • a/b/example.json
  • a/b/file.json
  • a/b/

HDFS peut vérifier que le répertoire /a/b/ existe et est un répertoire, et le balayage aurait pour résultat example.json et file.json. Toutefois, si l'objet a/b/ a été supprimé, le système de fichiers pourrait avoir un état incohérent. Vous pouvez interroger le système pour tous les fichiers du répertoire /a/b/ et rechercher les deux entrées, mais l'interrogation du statut du répertoire /a/b/ réel générerait une exception, car le répertoire n'existe pas. Le connecteur HDFS ne tente pas de réparer l'état du système de fichiers.

Création de fichier

Le service de stockage d'objets prend en charge des objets d'une taille de plusieurs gigaoctets. La création de fichiers se fera normalement en écrivant dans un fichier temporaire, puis en chargeant le contenu du fichier lorsque le flux est fermé. L'espace temporaire doit être suffisant pour traiter des chargements multiples. Le répertoire temporaire utilisé est contrôlé par la propriété de configuration hadoop.tmp.dir.

Prise en charge de la lecture/recherche

Lorsque des tampons en mémoire sont activés (fs.oci.io.read.inmemory), la recherche est totalement prise en charge, car l'intégralité du fichier est mis en mémoire tampon dans un tableau d'octets. Si le tampon en mémoire n'est pas activé (probablement parce que les tailles d'objet sont volumineuses), la recherche est mise en oeuvre en fermant le flux et en effectuant une nouvelle demande d'intervalle commençant au décalage spécifié.

Liste de répertoires

La fonction permettant de lister un répertoire est, par essence, une opération qui liste les seaux avec un préfixe et un délimiteur spécifiés. Pour créer une instance de statut de fichier (FileStatus) HDFS pour chaque clé, le connecteur effectue une demande HEAD supplémentaire afin d'obtenir les métadonnées d'objet pour chaque clé. Cette opération sera requise tant que le stockage d'objets ne prendra pas en charge les données d'opération de liste enrichie.

Format d'URI pour les systèmes de fichiers et les fichiers

Les systèmes de fichiers et les fichiers HDFS sont référencés par des URI. Le schéma de l'URI spécifie le type de système de fichiers. La partie restante de l'URI peut être interprétée librement par la mise en oeuvre du système de fichiers.

Comme le stockage d'objets est un magasin d'objets, sa capacité à nommer les objets comme s'ils étaient des fichiers d'un système de données est utilisée pour reproduire un système de données réel.

Racine

La racine du système de fichiers du stockage d'objets est indiquée par un chemin où le composant d'autorité comprend le nom du seau et le nom de l'espace de noms, comme illustré ci-après :

Note

Dans les exemples, "MyBucket" et "MyNamespace" sont des paramètres fictifs et doivent être remplacés par des valeurs appropriées.

oci://MyBucket@MyNamespace/
		

Il s'agit toujours de la racine du système de fichiers. La raison qui sous-tend l'utilisation d'une autorité à la fois pour le seau et l'espace de noms réside dans le fait que HDFS n'autorise que la partie autorité pour déterminer où le système de fichiers est; la partie du chemin indique seulement le chemin d'accès à la ressource (par conséquent, "oci/MyNamespace/MyBucket" ne fonctionnera pas, par exemple). Notez que le caractère @ n'est pas valide pour les seaux ou les espaces de noms et qu'il doit pouvoir être analysé correctement par l'autorité.

Sous-répertoires

Les sous-répertoires n'existent pas réellement, mais peuvent être reproduits en créant des objets avec des caractères /. Par exemple, deux fichiers nommés a/b/c/example.json et a/b/d/path.json s'afficheraient comme s'ils se trouvaient dans un répertoire a/b commun. Pour ce faire, utilisez l'interrogation basée sur les préfixes et délimiteurs du stockage d'objets. Dans l'exemple indiqué, le référencement d'un sous-répertoire en tant qu'URI se présenterait comme suit :


oci://MyBucket@MyNamespace/a/b/
		

Objets/Fichiers

Un objet nommé a/b/c/example.json est référencé en tant que :


oci://MyBucket@MyNamespace/a/b/c/example.json
		

Journalisation

La journalisation dans le connecteur s'effectue au moyen de SLF4J. SLF4J est une abstraction de journalisation qui permet l'utilisation d'une bibliothèque de journalisation fournie par l'utilisateur (par exemple, log4j). Pour plus d'informations, consultez le manuel SLF4J.

L'exemple suivant montre comment activer la journalisation de base pour une sortie standard.

  1. Téléchargez le fichier JAR de liaison simple SLF4J : SLF4J Simple Binding
  2. Ajoutez le fichier JAR à la variable classpath
  3. Ajoutez l'argument de machine virtuelle suivant pour permettre la journalisation du niveau de débogage (par défaut, le niveau d'information est utilisé) : -Dorg.slf4j.simpleLogger.defaultLogLevel=debug

Vous pouvez configurer des options de journalisation plus avancées en utilisant la liaison log4j.

Utilisation de la structure de surveillance

Le connecteur HDFS pour le stockage d'objets comprend un cadre de surveillance qui fournit des mesures sur les opérations effectuées à l'aide du connecteur. La structure de surveillance fournit une interface qui peut être mise en oeuvre pour consommer/écouter les mesures générées par le connecteur. Vous pouvez fournir une mise en oeuvre personnalisée de cette interface ou utiliser la mise en oeuvre de télémétrie publique OCI incluse dans ce cadre.

Introduction

Pour commencer à utiliser le cadre de surveillance du connecteur HDFS, vous devez définir les propriétés suivantes. Une fois ces propriétés définies pour OCIMonitoring, vous pouvez utiliser la vue Explorateur de mesures dans la console OCI pour observer les mesures émises à partir du connecteur HDFS.

fs.oci.mon.consumer.plugins

fs.oci.mon.consumer.plugins prend une liste séparée par des virgules de noms de classe complets pour les mises en oeuvre de l'interface de surveillance. com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin doit être utilisé dans la liste si vous voulez que les mesures soient émises pour la télémétrie publique OCI.
<property>
    <name>fs.oci.mon.consumer.plugins</name>
    <value>com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin,com.your.new.plugin.PluginImpl1</value>
</property>

fs.oci.mon.grouping.cluster.id

La propriété fs.oci.mon.grouping.cluster.id spécifie l'identificateur de la grappe HDFS ou de tout autre ID dans lequel vous voulez regrouper les mesures. Il s'agit d'une propriété obligatoire, qui est également utilisée par OCIMonitorPlugin pour marquer des mesures. Cette propriété est visible en tant que dimension dans l'interface utilisateur et l'API de télémétrie publique OCI.
<property>
    <name>fs.oci.mon.grouping.cluster.id</name>
    <value>hdfs-sample-cluster-id</value>
</property>

Propriétés com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin

Si la propriété com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin est activée, les propriétés suivantes sont applicables :

fs.oci.mon.telemetry.ingestion.endpoint

La propriété fs.oci.mon.telemetry.ingestion.endpoint permet de configurer le point d'extrémité d'ingestion de télémétrie de la surveillance OCI. Pour plus d'informations, consultez la liste des points disponibles.
<property>
    <name>fs.oci.mon.telemetry.ingestion.endpoint</name>
    <value>https://telemetry-ingestion.us-ashburn-1.oraclecloud.com</value>
</property>

fs.oci.mon.compartment.ocid

La propriété fs.oci.mon.compartment.ocid est utilisée pour configurer le compartiment OCI auquel les mesures seront attachées. Il s'agit généralement du compartiment auquel les seaux appartiennent.
<property>
    <name>fs.oci.mon.compartment.ocid</name>
    <value>ocid1.compartment.oc1..sample.compartment.id</value>
</property>

fs.oci.mon.bucket.level.enabled

La propriété fs.oci.mon.bucket.level.enabled détermine si le nom du seau doit être attaché en tant que dimension aux mesures émises.
<property>
    <name>fs.oci.mon.bucket.level.enabled</name>
    <value>true</value>
</property>

fs.oci.mon.ns.name

La propriété fs.oci.mon.ns.name contrôle l'espace de noms utilisé pour les mesures émises par le connecteur HDFS. Un exemple d'espace de noms pourrait être "hdfsconnector". Cela résidera le long d'autres espaces de noms prédéfinis tels que oci_objectstorage dans la télémétrie publique.
<property>
    <name>fs.oci.mon.ns.name</name>
    <value>name.of.namespace.on.oci.telemetry</value>
</property>

fs.oci.mon.rg.name

La propriété fs.oci.mon.rg.name définit le nom du groupe de ressources utilisé pour contenir les mesures. Il peut s'agir de n'importe quel nom logique pour regrouper des ressources qui seront surveillées ensemble. Ce nom de groupe de ressources apparaîtra sous l'espace de noms qui a été sélectionné précédemment dans la télémétrie publique OCI.
<property>
    <name>fs.oci.mon.rg.name</name>
    <value>name.of.resource.group.on.oci.telemetry</value>
</property>

Créer votre propre consommateur pour les mesures

Pour utiliser l'interface com.oracle.bmc.hdfs.monitoring.OCIMonitorConsumerPlugin, vous devez définir deux méthodes :
  • accept
  • shutdown

Les classes d'extension doivent avoir un constructeur avec la même signature que celle de la classe OCIMonitorConsumerPlugin.

Par exemple :
public OCIMonitorConsumerPlugin(BmcPropertyAccessor propertyAccessor, String bucketName, String monitoringGroupingID, String namespaceName);
Cet exemple illustre chaque méthode qui doit être mise en oeuvre par un plugiciel de consommateur :
/**
 * This class that has to be extended by any plugin, that wants to consume the metrics emitted by OCI HDFS connector.
 */
public abstract class OCIMonitorConsumerPlugin {
    /**
     * This method will be called on each plugin, by the OCI monitoring framework, whenever it wants to emit out a metric.
     * This method should finish as quickly as possible, so the consumer of this should ideally handover the
     * ocimetric and stage it elsewhere for processing, instead of trying to deal with it in the accept call itself.
     * @param ociMetric The metric that is being emitted by the OCI HDFS connector
     */
    public void accept(OCIMetric ociMetric);
 
    /**
     * This shutdown method will be called on the implementing plugins, whenever the JVM is shutting down.
     * It could be used to cleanup, finish pending tasks before exit.
     */
    public void shutdown();
}

La classe OCIMetric peut être mise en oeuvre de trois façons :

Objet OCIMetric simple avec les champs suivants :
public class OCIMetric {
    /**
     * The time in milliseconds (epoch) when the metric was recorded.
     */
    private final long recordedTime;
    /**
     * The overall time taken by the operation to complete/error-out in milliseconds.
     */
    private final double overallTime;
    /**
     * The operation key. This will be one of {"LIST", "HEAD", "WRITE", "READ", "DELETE", "RENAME"}
     */
    private final String key;
    /**
     * The boolean error indicates whether the operation errored out.
     */
    private final boolean error;
    /**
     * The target OCI bucket where the operation was attempted to.
     */
    private final String bucketName;
}
Mise en oeuvre de l'objet OCIMetricWithThroughput qui étend OCIMetric et comporte des champs supplémentaires pour le débit et les octets transférés. Ceci s'applique aux opérations READ et WRITE :
public class OCIMetricWithThroughput extends OCIMetric {
    /**
     * The throughput that was recorded for the operation in bytes/second
     */
    private final double throughput;
    /**
     * The total count of bytes that were transferred in or out.
     */
    private final double bytesTransferred;
}
Objet OCIMetricWithFBLatency qui étend OCIMetricWithThroughput, avec un délai supplémentaire jusqu'au champ de latence du premier octet. Cela ne s'applique qu'aux opérations READ :
public class OCIMetricWithFBLatency extends OCIMetricWithThroughput {
    /**
     * The time to first byte when a read operation was performed in milliseconds.
     */
    private final double ttfb;
}

Exemple de tâche Hadoop

hadoop_sample_hdfs :


package com.oracle.oci.hadoop.example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.oracle.oci.hdfs.BmcFilesystem;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class SampleOracleBmcHadoopJob
{
    private static final String SAMPLE_JOB_PATH = "/samplehadoopjob";
    private static final String INPUT_FILE = SAMPLE_JOB_PATH + "/input.dat";
    private static final String OUTPUT_DIR = SAMPLE_JOB_PATH + "/output";

    // non-static since this is the runner class it needs to initialize after we set the properties
    private final Logger log = LoggerFactory.getLogger(SampleOracleBmcHadoopJob.class);

    /**
     * Runner for sample hadoop job. This expects 3 args: path to configuration file, Object Store namespace, Object
     * Store bucket. To run this, you must:
     *{@code 


         * 
    Create a standard hadoop configuration file

         * 
    Create the bucket ahead of time.

         *} 


     * This runner will create a test input file in a file '/samplehadoopjob/input.dat', and job results will be written
     * to '/samplehadoopjob/output'.
     * 
     * @param args
     *            1) path to configuration file, 2) namespace, 3) bucket
     * @throws Exception
     */
    public static void main(final String[] args) throws Exception
    {
        if (args.length != 3)
        {
            throw new IllegalArgumentException(
                    "Must have 3 args: 1) path to config file, 2) object storage namespace, 3) object storage bucket");
        }

        // redirect all logs to sysout
        System.setProperty("org.slf4j.simpleLogger.logFile", "System.out");
        System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");

        final SampleOracleBmcHadoopJob job = new SampleOracleBmcHadoopJob(args[0], args[1], args[2]);
        System.exit(job.execute());
    }

    private final String configurationFilePath;
    private final String namespace;
    private final String bucket;

    public int execute() throws IOException, ClassNotFoundException, InterruptedException, URISyntaxException
    {
        log.info("Creating hadoop configuration");
        final Configuration configuration = this.createConfiguration(this.configurationFilePath);

        final String authority = this.bucket + "@" + this.namespace;
        final String uri = "oci://" + authority;
        log.info("Using uri: {}", uri);

        log.info("Creating job inputs");
        this.setup(uri, configuration);

        log.info("Creating job");
        final Job job = this.createJob(configuration);

        final String in = uri + INPUT_FILE;
        final String out = uri + OUTPUT_DIR;
        log.info("Using input: {}", in);
        log.info("Using output: {}", out);

        FileInputFormat.addInputPath(job, new Path(in));
        FileOutputFormat.setOutputPath(job, new Path(out));

        log.info("Executing job...");
        final int response = job.waitForCompletion(true) ? 0 : 1;

        log.info("Attempting to read job results");
        this.tryReadResult(uri, configuration);
        return response;
    }

    private Configuration createConfiguration(final String configFilePath)
    {
        final Configuration configuration = new Configuration();
        configuration.addResource(new Path(configFilePath));
        return configuration;
    }

    private void setup(final String uri, final Configuration configuration) throws IOException, URISyntaxException
    {
        try (final BmcFilesystem fs = new BmcFilesystem())
        {
            fs.initialize(new URI(uri), configuration);
            fs.delete(new Path(SAMPLE_JOB_PATH), true);
            final FSDataOutputStream output = fs.create(new Path(INPUT_FILE));
            output.writeChars("example\npath\ngak\ntest\nexample\ngak\n\ngak");
            output.close();
        }
    }

    private Job createJob(final Configuration configuration) throws IOException
    {
        final Job job = Job.getInstance(configuration, "word count");
        job.setJarByClass(SampleOracleBmcHadoopJob.class);
        job.setMapperClass(SimpleMapper.class);
        job.setCombinerClass(SimpleReducer.class);
        job.setReducerClass(SimpleReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        return job;
    }

    private void tryReadResult(final String uri, final Configuration configuration)
            throws IOException, URISyntaxException
    {
        try (final BmcFilesystem fs = new BmcFilesystem())
        {
            fs.initialize(new URI(uri), configuration);
            // this should be the output file name, but that could change
            final FSDataInputStream input = fs.open(new Path(OUTPUT_DIR + "/part-r-00000"));

            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtils.copy(input, baos);
            log.info("\n=====\n" + baos.toString() + "=====");
            input.close();
        }
    }
}


package com.oracle.oci.hadoop.example;

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class SimpleMapper extends Mapper
{
    private final static IntWritable one = new IntWritable(1);
    private final Text word = new Text();

    @Override
    public void map(final Object key, final Text value, final Context context) throws IOException, InterruptedException
    {
        final StringTokenizer itr = new StringTokenizer(value.toString());
        while (itr.hasMoreTokens())
        {
            this.word.set(itr.nextToken());
            context.write(this.word, one);
        }
    }
}


package com.oracle.oci.hadoop.example;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class SimpleReducer extends Reducer
{
    private final IntWritable result = new IntWritable();

    @Override
    public void reduce(final Text key, final Iterable values, final Context context)
            throws IOException, InterruptedException
    {
        int sum = 0;
        for (final IntWritable val : values)
        {
            sum += val.get();
        }
        this.result.set(sum);
        context.write(key, this.result);
    }
}

Dépannage

Cette section contient des informations de dépannage pour le connecteur HDFS.

Dépannage des erreurs de service

Toute opération résultant d'une erreur de service provoquera la génération d'une exception de type com.oracle.bmc.model.BmcException par le connecteur HDFS. Pour plus d'informations sur les erreurs de service communes retournées par OCI, voir Erreurs d'API.

Erreurs de taille de clé de chiffrement Java

Le connecteur HDFS peut uniquement traiter les clés de 128 bits ou de longueur inférieure. Les utilisateurs reçoivent une exception de clé non valide et des erreurs de taille de clé non autorisée lorsqu'ils utilisent des clés plus longues, telles qu'AES256. Utilisez l'une des solutions de contournement suivantes pour résoudre ce problème :

Contributions

Vous avez une solution pour un bogue ou une nouvelle fonctionnalité que vous aimeriez partager? La trousse SDK est une solution à code source libre qui accepte les demandes d'extraction sur GitHub.

Avis

Si vous souhaitez recevoir un avis lorsqu'une nouvelle version du connecteur HDFS est publiée, abonnez-vous au flux Atom.