HDFS-Connector für Object Storage

Mit dem Connector für Hadoop Distributed File System-(HDFS-)Connector können die Apache Hadoop-Anwendung Daten im und aus dem Oracle Cloud Infrastructure Object Storage-Service lesen und schreiben.

Dieses SDK und das Beispiel sind im Rahmen der Universal Permissive License 1.0 und der Apache License 2.0 doppelt lizenziert. Drittanbieterinhalte werden separat lizenziert, wie im Code beschrieben.

Anforderungen

Zur Verwendung des HDFS-Connectors ist Folgendes erforderlich:

  • Ein Oracle Cloud Infrastructure-Account.
  • Ein in diesem Account erstellter Benutzer in einer Gruppe mit einer Policy, die die gewünschten Berechtigungen für jeden zu verwendenden Bucket erteilt. Dies kann ein Benutzer für Sie selbst oder eine andere Person/ein anderes System sein, die/das die API aufrufen muss. Ein Beispiel für die Einrichtung eines neuen Benutzers, einer neuen Gruppe, eines neuen Compartments und einer neuen Policy finden Sie unter Benutzer hinzufügen. Informationen zu einer allgemeinen Object Storage-Policy finden Sie unter Verwalten von Buckets und Objekten durch Object Storage-Administratoren zulässig.
  • Java 8
  • Ein TTL-Wert von 60. Weitere Informationen finden Sie unter JVM-TTL für DNS-Namen-Lookups konfigurieren.

Zugangsdaten und Kennwörter

Wenn Sie eine verschlüsselte PEM-Datei für Zugangsdaten verwenden, wird die Passphrase mit der Hadoop-Konfigurationsmethode getPassword aus der Konfiguration gelesen. Die Option getPassword sucht nach einem Kennwort in einem registrierten Sicherheitsprovider. Wenn der Sicherheitsprovider nicht den angeforderten Schlüssel enthält, wird die Klartext-Passphrase direkt aus der Konfigurationsdatei gelesen.

JVM-TTL für DNS-Namen-Lookups konfigurieren

Java Virtual Machine (JVM) cacht DNS-Antworten aus Lookups für einen festgelegten Zeitraum, der als Gültigkeitsdauer (Time to Live; TTL) bezeichnet wird. Dadurch wird eine kürzere Antwortzeit in Code gewährleistet, der häufige Namensauflösungen erfordert.

Die JVM verwendet die Eigenschaft networkaddress.cache.ttl zur Angabe der Caching-Policy für DNS-Namen-Lookups. Der Wert ist eine Ganzzahl, die angibt, wie viele Sekunden der erfolgreiche Lookup im Cache gespeichert wird. Der Standardwert für viele JVMs, -1, gibt an, dass der Lookup unbegrenzt gecacht werden soll.

Da Ressourcen in Oracle Cloud Infrastructure DNS-Namen verwenden, die sich ändern können, wird empfohlen, den TTL-Wert in 60 Sekunden zu ändern. Dadurch wird sichergestellt, dass die neue IP-Adresse für die Ressource bei der nächsten DNS-Abfrage zurückgegeben wird. Sie können diesen Wert global oder speziell für Ihre Anwendung ändern:

  • Um TTL für alle Anwendungen global mit der JVM festzulegen, fügen Sie Folgendes in der Datei $JAVA_HOME/jre/lib/security/java.security hinzu:

    networkaddress.cache.ttl=60
  • Um TTL nur für Ihre Anwendung festzulegen, legen Sie Folgendes im Initialisierungscode Ihrer Anwendung fest:

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

Installation

Kopieren Sie die gebündelten JAR-Dateien aus lib und third-party/lib in jeden Knoten des Hadoop-Clusters, damit sie im Classpath von Hadoop enthalten sind.

SDK für Java und Maven-Artefakte

Das Erstellen eines HDFS-Connectors basiert auf Maven-Artefakten, die vom Oracle Cloud Infrastructure-SDK für Java bereitgestellt werden. Um die Artefakte zu beziehen, müssen Sie das SDK für Java herunterladen und lokal erstellen. Dann können Sie den HDFS-Connector erstellen.

Wichtig

Die Dateiversion des SDK für Java, die Sie von der Seite mit Oracle-Releases herunterladen, muss mit der Version des HDFS-Connectors übereinstimmen. Diese finden Sie in der Datei hdfs-connector/pom.xml im Abhängigkeitstagblock mit dem Attribut groupId.

HDFS-Connector und Maven-Artefakte

Der HDFS-Connector ist unter Maven Central und JCenter verfügbar.

Um den HDFS-Connector in Ihrem Projekt zu verwenden, importieren Sie die folgende Projektabhängigkeit. Beispiel:

<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>

Eigenschaften

Sie können die folgenden HDFS-Connector-Eigenschaften in der Datei core-site.xml festlegen. Auf der Seite BmcProperties werden zusätzliche Eigenschaften aufgeführt, die Sie für eine Verbindung zu Object Storage konfigurieren können.

Eigenschaft Beschreibung Typ Erforderlich
fs.oci.client.hostname

Die URL des Hostendpunkts.

Beispiel: https://www.example.com.

Zeichenfolge Ja
fs.oci.client.auth.tenantId

Die OCID Ihres Mandanten.

Die Werte finden Sie unter Erforderliche Schlüssel und OCIDs.

Zeichenfolge Ja
fs.oci.client.auth.userId

Die OCID des Benutzers, der die API aufruft.

Die Werte finden Sie unter Erforderliche Schlüssel und OCIDs.

Zeichenfolge Ja
fs.oci.client.auth.fingerprint

Der Fingerprint für das verwendete Schlüsselpaar.

Die Werte finden Sie unter Erforderliche Schlüssel und OCIDs.

Zeichenfolge

Ja, es sei denn, Sie stellen einen benutzerdefinierten Authentikator bereit

fs.oci.client.auth.pemfilepath Vollständiger Pfad und Dateiname des Private Keys für die Authentifizierung. Die Datei muss sich im lokalen Dateisystem befinden. Zeichenfolge Ja, es sei denn, Sie stellen einen benutzerdefinierten Authentikator bereit
fs.oci.client.auth.passphrase Die Passphrase für den Schlüssel, sofern er verschlüsselt ist. Zeichenfolge Nur wenn der Schlüssel verschlüsselt ist.
fs.oci.client.regionCodeOrId Der Regionscode oder die Regions-ID zum Einrichten des Object Storage-Endpunktnamens. Zeichenfolge Nein
Hinweis

Sie können angeben, dass ein Eigenschaftswert auf einen bestimmten Bucket angewendet wird, indem Sie .<bucket_name>.<namespace_name> an den Eigenschaftsnamen anhängen.

Regionsendpunkt festlegen

Es gibt verschiedene Methoden, mit denen Sie den Regionsendpunkt für den HDFS-Connector festlegen können:

  • Hostnamenseigenschaft in core-site.xml angeben
  • Regionscode oder Regions-ID-Eigenschaft in core-site.xml angeben
  • Zulassen, dass der ObjectStorage-Client den Endpunkt über den Instance Metadata Service abruft

Eigenschaften mit core-site.xml konfigurieren

Dieses Beispiel zeigt, wie Eigenschaften in einer core-site.xml-Datei konfiguriert werden können (OCIDs sind aus Gründen der Einfachheit gekürzt):

<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>

Instanz-Principals für Authentifizierung verwenden

Oracle stellt Instanz-Principals bereit, damit Sie keine Benutzerzugangsdaten mehr konfigurieren oder PEM-Dateien auf Services angeben müssen, die auf Instanzen ausgeführt werden. Jede dieser Instanzen hat eine eigene Identität und authentifiziert sich durch Verwendung von Zertifikaten, die der Instanz von Instanz-Principals hinzugefügt wurden.

Um die Authentifizierung durch Instanz-Principals mit dem HDFS-Connector zu verwenden, geben Sie die Eigenschaft fs.oci.client.custom.authenticator an, und setzen Sie den Wert auf com.oracle.bmc.hdfs.auth.InstancePrincipalsCustomAuthenticator.

Da die Verwendung von Instanz-Principals dem Connector einen benutzerdefinierten Authentifizierer bereitstellt, müssen die folgenden Eigenschaften nicht mehr konfiguriert werden:

  • 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

Der folgende Beispielcode zeigt, wie Instanz-Principals zur Authentifizierung mit dem HDFS-Connector verwendet werden:

<?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>

Weitere Informationen zu Instanz-Principals finden Sie unter Instanz-Principals für Identity and Access Management ankündigen.

Resource Principals für die Authentifizierung verwenden

Ähnlich wie Instanz-Principals stellt Oracle Resource Principals zur Authentifizierung der Ressourcen zur Verfügung, die keine Instanzen sind (wie ein Jupyter-Notizbuch). Jede Ressource hat eine eigene Identität und authentifiziert sich mit den ihr hinzugefügten Zertifikaten.

Um die Authentifizierung durch Resource Principals mit dem HDFS-Connector zu verwenden, geben Sie die Eigenschaft fs.oci.client.custom.authenticator an, und setzen Sie den Wert auf com.oracle.bmc.hdfs.auth.ResourcePrincipalsCustomAuthenticator.

Da die Verwendung von Resource Principals dem Connector einen benutzerdefinierten Authentifizierer bereitstellt, ist es nicht lange erforderlich, die folgenden Eigenschaften zu konfigurieren:

  • 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

Der folgende Beispielcode zeigt, wie Resource Principals zur Authentifizierung mit dem HDFS-Connector verwendet werden:

<?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>

Weitere Informationen zu Instanz-Principals finden Sie unter Resource Principals im Data Science-Service verwenden.

Kerberos-Authentifizierung verwenden

Oracle unterstützt die Kerberos-Authentifizierung für die Verbindung mit Object Storage über den HDFS-Connector.

So verwenden Sie die Kerberos-Authentifizierung mit dem HDFS-Connector:
  1. Setzen Sie in core-site.xml die Eigenschaft fs.oci.client.custom.authenticator auf com.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator.
  2. Legen Sie in core-site.xml die folgenden Eigenschaften fest:
    • 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

Die folgende Beispieldatei core-site.xml veranschaulicht die Verwendung von Kerberos mit SPNEGO-Tokenauthentifizierung mit dem HDFS-Connector:

<?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>

Weitere Informationen zu Kerberos finden Sie im Kerberos-Protokoll-Tutorial.

Weitere Informationen zu SPNEGO finden Sie unter RFC 4178.

Jersey-Standardwert HttpUrlConnectorProvider verwenden

Ab Version 3.3.0.7.0.0 unterstützt HDFS standardmäßig die Verwendung des Apache-Clients zum Ausführen von OCI-Serviceaufrufen. Der Grund ist, dass sich der HDFS-Connector auf das SDK für Java verlässt, um Anforderungen an den Server zu senden. Das SDK für Java unterstützt standardmäßig die Verwendung von Jersey ApacheConnectorProvider anstelle von Jersey HttpUrlConnectorProvider, damit Apache HttpClient OCI-Serviceaufrufe ausführen kann.

Um zum alten Jersey-Standardclient zurückzukehren, setzen Sie die Eigenschaft fs.oci.client.jersey.default.connector.enabled in der Datei core-site.xml auf true. Standardmäßig ist dieser Wert auf false gesetzt.

Performanceoptimierung mit dem Apache-Connector für HDFS

Der Apache-Connector unterstützt zwei Verbindungsschließungsstrategien: ApacheConnectionClosingStrategy.GracefulClosingStrategy und ApacheConnectionClosingStrategy.ImmediateClosingStrategy.

Wenn Sie ApacheConnectionClosingStrategy.GracefulClosingStrategy verwenden, werden die von einer Antwort zurückgegebenen Streams bis zum Ende gelesen, bevor der Stream geschlossen wird. Dies kann je nach Größe des restlichen Streams beim Schließen des Streams mit einem partiellen Lesevorgang zusätzliche Zeit erfordern. Um diese Verzögerung zu vermeiden, können Sie ApacheConnectionClosingStrategy.ImmediateClosingStrategy für große Dateien mit partiellen Lesevorgängen verwenden. Mit ApacheConnectionClosingStrategy.ImmediateClosingStrategy werden Streams nicht bis zum Ende gelesen, bevor der Stream geschlossen wird, was die Performance verbessern kann. Beachten Sie, dass ApacheConnectionClosingStrategy.ImmediateClosingStrategy länger dauert, wenn partielle Lesevorgänge für kleinere Streamgrößen (unter 1 MB) verwendet werden.

Verbindungsschließungsstrategie festlegen

Legen Sie die Verbindungsschließungsstrategie fest, indem Sie die Eigenschaft fs.oci.client.apache.connection.closing.strategy in der Datei core-site.xml festlegen:

  • Um ApacheConnectionClosingStrategy.GracefulClosingStrategy zu verwenden, setzen Sie fs.oci.client.apache.connection.closing.strategy auf graceful.
  • Um ApacheConnectionClosingStrategy.ImmediateClosingStrategy zu verwenden, setzen Sie fs.oci.client.apache.connection.closing.strategy auf immediate.
Hinweis

Diese Schließungsstrategien funktionieren nur mit dem Apache-Connector für HDFS und werden bei Verwendung des Jersey-Standard-Connectors ignoriert.

Zurück zum Jersey-Standard-Connector wechseln

Der Jersey-Standard-Connector liest Streams bis zum Ende und verwendet den Stream dann wieder, was in einigen Szenarios zu einer besseren Performance als beim Apache-Connector für HDFS führen kann. Wenn diese Apache-Verbindungsschließungsstrategien keine optimalen Ergebnisse für Ihre Anwendungsfälle liefern, können Sie den Jersey-Standardwert HttpUrlConnectorProvider verwenden. Um zurück zum alten Jersey-Standardclient zu wechseln, setzen Sie die Eigenschaft fs.oci.client.jersey.default.connector.enabled in der Datei core-site.xml auf true. Standardmäßig ist dieser Wert auf false gesetzt.

Weitere Informationen finden Sie unter: https://github.com/oracle/oci-java-sdk/blob/master/ApacheConnector-README.md.

Verbindungspooling in HDFS

Sie können die maximale Anzahl von Verbindungen im HDFS-Connector-Verbindungspool festlegen.

Ändern Sie dazu die Eigenschaft fs.oci.client.apache.max.connection.pool.size in der Datei core-site.xml in eine positive Ganzzahl, mit der Sie die Anzahl der Verbindungen im Pool angeben.

Hinweis

Diese Eigenschaft wird nur bei Verwendung von ApacheConnector für HDFS unterstützt. Andernfalls wird sie ignoriert.

Dedizierte Endpunkte

Dedizierte Endpunkte sind die Endpunktvorlagen, die von einem Service für eine bestimmte Realm auf Clientebene definiert werden. Mit dem OCI-HDFS-Connector können Sie die Verwendung dieser realitätsspezifischen Endpunktvorlagen aktivieren, indem Sie entweder den Hostnamen fs.oci.client.hostname oder die Eigenschaft fs.oci.realmspecific.endpoint.template.enabled des Endpunktvorlagen-Flags in core-site.xml festlegen.
Hinweis

Wenn Sie die Endpunktvorlageneigenschaft festlegen, müssen Sie auch fs.oci.client.regionCodeOrId in core-site.xml festlegen.
Hinweis

Der über den Hostnamen in core-site.xml festgelegte Wert hat Vorrang vor dem Wert, der mit der Endpunktvorlageneigenschaft in core-site.xml festgelegt wurde.
In diesem Beispiel wird gezeigt, wie Sie das Feature "Realm-spezifische Endpunktvorlagen" aktivieren, indem Sie die Eigenschaft fs.oci.client.hostname festlegen:
<property>
  <name>fs.oci.client.hostname</name>
  <value>https://my-namespace.objectstorage.me-dubai-1.oci.customer-oci.com</value>
</property>
In diesem Beispiel wird gezeigt, wie Sie das Feature für Realm-spezifische Endpunktvorlagen aktiviert haben, indem Sie die Eigenschaft fs.oci.realmspecific.endpoint.template.enabled festlegen:
<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>

HTTP-Proxy konfigurieren

Sie können die folgenden optionalen Eigenschaften in der Datei core-site.xml festlegen, um einen HTTP-Proxy zu konfigurieren:

Eigenschaft Beschreibung Typ Erforderlich
fs.oci.client.proxy.uri

Die URI des Proxyendpunkts.

Beispiel: http://proxy.mydomain.com:80.

Zeichenfolge Nein
fs.oci.client.proxy.username Der Benutzername zur Authentifizierung beim Proxy. Zeichenfolge Nein
fs.oci.client.proxy.password Das Kennwort für die Authentifizierung beim Proxy. Zeichenfolge Nein
fs.oci.client.multipart.allowed Ermöglicht, dass der Uploadmanager Multipart-Uploads unterstützt. Boolescher Wert Nein
fs.oci.client.multipart.minobjectsize.mb

Gibt die minimale Objektgröße für die Verwendung des Uploadmanagers in Mebibyte an.

Ganzzahl Nein
fs.oci.client.multipart.partsize.mb Gibt die Teilgröße für den Uploadmanager in Mebibyte an. Ganzzahl Nein
Hinweis

Die Konfiguration eines Proxys ermöglicht die Verwendung von ApacheConnectorProvider beim Herstellen von Verbindungen zu Object Storage. Anforderungen werden im Arbeitsspeicher gepuffert. Dies kann sich beim Hochladen von Large Objects auf die Speicherauslastung auswirken. Es wird empfohlen, Multipart-Uploads zu aktivieren und die Eigenschaften für mehrteilige Vorgänge anzupassen, um den Speicherverbrauch zu verwalten.

Large-Object-Uploads

Large Objects werden mithilfe von Multipart-Uploads in Object Storage hochgeladen. Die Datei wird in kleinere Teile aufgeteilt, die parallel hochgeladen werden, wodurch die Uploadzeiten reduziert werden. Dadurch kann der HDFS-Connector zudem den Upload nicht erfolgreicher Teile wiederholen, statt dass der gesamte Upload nicht erfolgreich verläuft. Uploads können jedoch vorübergehend nicht erfolgreich sein, und der Connector versucht, teilweise hochgeladene Dateien abzubrechen. Da sich diese Dateien ansammeln (und Sie für die Speicherung bezahlen müssen), listen Sie die Uploads regelmäßig und danach nach einer bestimmten Anzahl von Tagen manuell mit dem SDK für Java auf.

Informationen zur Verwendung der Object Storage-API zur Verwaltung von mehrteiligen Uploads finden Sie unter Multipart-Uploads verwenden.

Hinweis

Wenn Sie keine Multipart-Uploads verwenden möchten, können Sie sie deaktivieren, indem Sie die Eigenschaft fs.oci.client.multipart.allowed auf false setzen.

Best Practices

Die folgenden Abschnitte enthalten Best Practices zur Optimierung von Nutzung und Performance.

Verzeichnisnamen

Es sind keine eigentlichen Verzeichnisse in Object Storage. Die Verzeichnisgruppierung ist eine Funktion der Benennungskonvention, bei der Objekte /-Begrenzungszeichen in ihren Namen verwenden. Beispiel: Ein Objekt mit dem Namen a/example.json impliziert ein Verzeichnis mit dem Namen a. Wenn dieses Objekt jedoch gelöscht wird, wird das Verzeichnis a implizit ebenfalls gelöscht. Um die Dateisystemsemantik beizubehalten, bei der das Verzeichnis ohne Vorliegen von Dateien vorhanden sein kann, erstellt der HDFS-Connector ein tatsächliches Objekt, dessen Name mit / endet, mit einem Pfad, der das Verzeichnis darstellt (d.h. ein Objekt mit dem Namen a/). Wenn Sie a/example.json löschen, wirkt sich dies nicht auf das Vorhandensein des Verzeichnisses a aus, da das Objekt a/ weiterhin bestehen bleibt. Dennoch ist es möglich, dass ein Benutzer das Objekt a/ löscht, ohne die Dateien/Verzeichnisse darunter zu löschen. Der HDFS-Connector löscht das Ordnerobjekt nur, wenn unter diesem Pfad keine Objekte vorhanden sind. Das Ordnerobjekt selbst hat null Byte.

Inkonsistentes Dateisystem

Wenn ein Verzeichnis gelöscht wird, werden alle Objekte gelöscht, die mit dem Präfix beginnen, das dieses Verzeichnis darstellt. Mit HDFS können Sie den Dateistatus einer Datei oder eines Verzeichnisses abfragen. Der Dateistatus eines Verzeichnisses wird implementiert, indem geprüft wird, ob das Ordnerobjekt für dieses Verzeichnis vorhanden ist. Es ist jedoch möglich, dass das Ordnerobjekt gelöscht wurde, einige Objekte mit diesem Präfix jedoch noch vorhanden sind. Beispielsweise in einer Situation mit den folgenden Objekten:

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

HDFS weiß, dass das Verzeichnis /a/b/ vorhanden und ein Verzeichnis ist. Das Scannen würde example.json und file.json zurückgeben. Wenn das Objekt a/b/ jedoch gelöscht wurde, weist das Dateisystem einen inkonsistenten Status auf. Sie können diese Abfrage für alle Dateien im Verzeichnis /a/b/ ausführen und die beiden Einträge suchen. Eine Abfrage des Status des tatsächlichen Verzeichnisses /a/b/ würde jedoch zu einer Ausnahme führen, weil das Verzeichnis nicht vorhanden ist. Der HDFS-Connector versucht nicht, den Status des Dateisystems zu reparieren.

Dateierstellung

Object Storage unterstützt Objekte mit einer Größe von vielen GB. Das Erstellen von Dateien erfolgt normalerweise, indem Sie in eine temporäre Datei schreiben und dann den Inhalt der Datei hochladen, wenn der Stream geschlossen wird. Der temporäre Speicherplatz muss ausreichend sein, um mehrere Uploads verarbeiten zu können. Das verwendete temporäre Verzeichnis wird von der Konfigurationseigenschaft hadoop.tmp.dir gesteuert.

Support für Lesen/Suchen

Wenn In-Memory-Puffer aktiviert sind (fs.oci.io.read.inmemory), wird das Suchen vollständig unterstützt, weil die gesamte Datei in einem Bytearray gepuffert wird. Wenn der In-Memory-Puffer nicht aktiviert ist (wahrscheinlich weil die Objektgrößen groß sind), wird die Suche implementiert, indem der Stream geschlossen und eine neue Bereichsanforderung ab dem angegebenen Offset gestellt wird.

Verzeichnisliste

Das Auflisten eines Verzeichnisses ist im Wesentlichen ein Listen-Bucket-Vorgang mit einem angegebenen Präfix und Begrenzungszeichen. Um eine HDFS-FileStatus-Instanz für jeden Schlüssel zu erstellen, führt der Connector eine zusätzliche HEAD-Anforderung aus, um ObjectMetadata für jeden einzelnen Schlüssel abzurufen. Dies ist erforderlich, bis Object Storage umfassendere Listenvorgangsdaten unterstützt.

URI-Format für Dateisysteme und Dateien

HDFS-Dateisysteme und -Dateien werden über URIs referenziert. Das Schema gibt den Dateisystemtyp an, und der verbleibende Teil der URI ist größtenteils frei, sodass die Dateisystemimplementierung ihn je nach Bedarf interpretieren kann.

Da Object Storage ein Objektspeicher ist, wird dessen Fähigkeit, Objekte so zu benennen, als wären es Dateien in einem Dateisystem, verwendet, um ein tatsächliches Dateisystem nachgeahmt.

Root

Die Root des Object Storage-Dateisystems wird durch einen Pfad angegeben, in dem die Berechtigungskomponente den Bucket-Namen und den Namespace-Namen enthält, wie dargestellt:

Hinweis

In den Beispielen sind "MyBucket" und "MyNamespace" Platzhalter und müssen durch die entsprechenden Werte ersetzt werden.

oci://MyBucket@MyNamespace/
		

Dies ist immer der Root des Dateisystems. Der Grund für die Verwendung der Berechtigung für Bucket und Namespace besteht darin, dass bei HDFS nur anhand des Berechtigungsteils der Ort des Dateisystems bestimmt werden kann. Der Pfadteil bezeichnet lediglich den Pfad zur Ressource (daher würde z.B. "oci//MyNamespace/MyBucket" nicht funktionieren). Beachten Sie, dass das Zeichen @ kein gültiges Zeichen für Buckets oder Namespaces ist und die Berechtigung korrekt geparst werden muss.

Unterverzeichnisse

Unterverzeichnisse sind zwar nicht vorhanden, können jedoch durch das Erstellen von Objekten mit dem Zeichen / imitiert werden. Beispiel: Zwei Dateien mit dem Namen a/b/c/example.json und a/b/d/path.json werden so angezeigt, als befänden sie sich in einem gemeinsamen Verzeichnis a/b. Dies wird durch die Object Storage-Abfrage mithilfe von Präfix und Begrenzungszeichen erreicht. Im angegebenen Beispiel würde das Referenzieren eines Unterverzeichnisses als URI wie folgt aussehen:


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

Objekte/Dateien

Ein Objekt mit dem Namen a/b/c/example.json wird referenziert als:


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

Logging

Das Logging im Connector erfolgt über SLF4J. SLF4J ist eine Loggingabstraktion, die die Verwendung einer vom Benutzer angegebenen Logging-Library (z.B. log4j) ermöglicht. Weitere Informationen finden Sie im SLF4J-Handbuch.

Das folgende Beispiel zeigt, wie das allgemeine Logging für die Standardausgabe aktiviert wird.

  1. Laden Sie die SLF4J-JAR-Datei für einfaches Binding herunter: SLF4J für einfaches Binding
  2. Fügen Sie die JAR-Datei dem Classpath hinzu.
  3. Fügen Sie das folgende VM-Argument hinzu, um das Logging auf Debugebene zu aktivieren (standardmäßig wird die Informationsebene verwendet): -Dorg.slf4j.simpleLogger.defaultLogLevel=debug

Mit dem log4j-Binding können Sie erweiterte Loggingoptionen konfigurieren.

Monitoring Framework verwenden

Der HDFS-Connector für Object Storage umfasst ein Monitoring-Framework, das Metriken für Vorgänge bereitstellt, die mit dem Connector ausgeführt werden. Das Monitoring-Framework bietet eine Schnittstelle, die implementiert werden kann, um vom Connector generierte Metriken zu konsumieren/zuhören. Sie können eine benutzerdefinierte Implementierung dieser Schnittstelle bereitstellen oder die öffentliche OCI-Telemetrieimplementierung verwenden, die in diesem Framework enthalten ist.

Erste Schritte

Um das HDFS Connector-Überwachungs-Framework zu verwenden, müssen Sie die folgenden Eigenschaften festlegen. Nachdem diese Eigenschaften für OCIMonitoring festgelegt wurden, können Sie die Metriken, die vom HDFS-Connector ausgegeben werden, in der Ansicht "Metrik-Explorer" in der OCI-Konsole beobachten.

fs.oci.mon.consumer.plugins

Die fs.oci.mon.consumer.plugins verwendet eine durch Komma getrennte Liste vollqualifizierter Klassennamen von Implementierungen der Monitoringschnittstelle. com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin muss in der Liste verwendet werden, wenn die Metriken an die öffentliche OCI-Telemetrie ausgegeben werden sollen.
<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

Die Eigenschaft fs.oci.mon.grouping.cluster.id gibt die ID für das HDFS-Cluster oder eine andere ID an, in der die Metriken gruppiert werden sollen. Dies ist eine obligatorische Eigenschaft, die auch von der OCIMonitorPlugin zum Taggen von Metriken verwendet wird. Diese Eigenschaft ist als Dimension in der öffentlichen OCI-Telemetriebenutzeroberfläche und -API sichtbar.
<property>
    <name>fs.oci.mon.grouping.cluster.id</name>
    <value>hdfs-sample-cluster-id</value>
</property>

com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin-Eigenschaften

Wenn die Eigenschaft com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin aktiviert ist, gelten die folgenden Eigenschaften:

fs.oci.mon.telemetry.ingestion.endpoint

Mit der Eigenschaft fs.oci.mon.telemetry.ingestion.endpoint kann der Telemetrieaufnahmeendpunkt des OCI-Monitoring konfiguriert werden. Weitere Informationen finden Sie in der Liste der verfügbaren Punkte.
<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

Mit der Eigenschaft fs.oci.mon.compartment.ocid wird das OCI-Compartment konfiguriert, an das die Metriken angehängt werden. Dies ist in der Regel das Compartment, zu dem die Buckets gehören.
<property>
    <name>fs.oci.mon.compartment.ocid</name>
    <value>ocid1.compartment.oc1..sample.compartment.id</value>
</property>

fs.oci.mon.bucket.level.enabled

Die Eigenschaft fs.oci.mon.bucket.level.enabled bestimmt, ob der Bucket-Name als Dimension an die ausgegebenen Metriken angehängt wird.
<property>
    <name>fs.oci.mon.bucket.level.enabled</name>
    <value>true</value>
</property>

fs.oci.mon.ns.name

Die Eigenschaft fs.oci.mon.ns.name steuert den Namespace, der für die Metriken verwendet wird, die vom HDFS-Connector ausgegeben werden. Ein Beispiel-Namespace könnte "hdfsconnector" sein. Dies wird neben anderen vordefinierten Namespaces wie oci_objectstorage in der öffentlichen Telemetrie gespeichert.
<property>
    <name>fs.oci.mon.ns.name</name>
    <value>name.of.namespace.on.oci.telemetry</value>
</property>

fs.oci.mon.rg.name

Die Eigenschaft fs.oci.mon.rg.name legt den Namen der Ressourcengruppe fest, der für die Metriken verwendet wird. Dies kann ein beliebiger logischer Name für die Gruppierung von Ressourcen sein, die gemeinsam überwacht werden. Dieser Ressourcengruppenname wird unter dem Namespace angezeigt, der zuvor in der öffentlichen OCI-Telemetrie ausgewählt wurde.
<property>
    <name>fs.oci.mon.rg.name</name>
    <value>name.of.resource.group.on.oci.telemetry</value>
</property>

Eigenen Consumer für Metriken erstellen

Um die Schnittstelle com.oracle.bmc.hdfs.monitoring.OCIMonitorConsumerPlugin zu verwenden, müssen Sie zwei Methoden definieren:
  • accept
  • shutdown

Erweiterungsklassen sollten einen Konstruktor mit derselben Signatur wie die Klasse OCIMonitorConsumerPlugin haben.

Beispiel:
public OCIMonitorConsumerPlugin(BmcPropertyAccessor propertyAccessor, String bucketName, String monitoringGroupingID, String namespaceName);
In diesem Beispiel wird jede Methode gezeigt, die von einem Consumer-Plug-in implementiert werden muss:
/**
 * 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();
}

Die Klasse OCIMetric kann auf drei Arten implementiert werden:

Ein einfaches OCIMetric-Objekt mit den folgenden Feldern:
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;
}
Eine Implementierung des OCIMetricWithThroughput-Objekts, das OCIMetric erweitert und zusätzliche Felder für Durchsatz und übertragene Byte enthält. Dies gilt für READ- und WRITE-Vorgänge:
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;
}
Ein OCIMetricWithFBLatency-Objekt, das OCIMetricWithThroughput um ein zusätzliches Zeitfeld für die erste Byte-Latenz erweitert. Dies gilt nur für READ-Vorgänge:
public class OCIMetricWithFBLatency extends OCIMetricWithThroughput {
    /**
     * The time to first byte when a read operation was performed in milliseconds.
     */
    private final double ttfb;
}

Hadoop-Beispieljob

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);
    }
}

Fehlerbehebung

Dieser Abschnitt enthält Informationen zur Fehlerbehebung für den HDFS-Connector.

Servicefehler beheben

Jeder Vorgang, der einen Servicefehler verursacht, führt zu einer Ausnahme vom Typ com.oracle.bmc.model.BmcException, die vom HDFS-Connector ausgelöst wird. Informationen zu häufigen Servicefehlern, die von OCI zurückgegeben werden, finden Sie unter API-Fehler.

Fehler mit Java-Verschlüsselungsschlüsselgröße

Der HDFS-Connector kann nur Schlüssel mit höchstens 128 Bit verarbeiten. Benutzer erhalten die Fehler "Ausnahme: Ungültiger Schlüssel" und "Unzulässige Schlüsselgröße", wenn sie längere Schlüssel wie AES256 verwenden. Um dieses Problem zu beheben, verwenden Sie einen der folgenden Workarounds:

Benachrichtigungen

Wenn Sie benachrichtigt werden möchten, wenn eine neue Version des HDFS-Connectors veröffentlicht wird, abonnieren Sie den Atom-Feed.