Conector HDFS para o Serviço Object Storage

O conector Hadoop Distributed File System (HDFS) permite que seu aplicativo Apache Hadoop leia e grave dados de/para o serviço Oracle Cloud Infrastructure Object Storage.

Esse SDK e seu respectivo exemplo têm licenciamento duplo sob a Licença Permissiva Universal 1.0 e sob a Licença Apache 2.0; o conteúdo de terceiros é licenciado separadamente, conforme descrito no código.

Requisitos

Para usar o conector HDFS, você deve ter:

Credenciais e Senhas

Se você usar um arquivo PEM criptografado para credenciais, a frase-senha será lida na configuração usando o método de Configuração do Hadoop getPassword. A opção getPassword verifica se há uma senha em um provedor de segurança registrado. Se o provedor de segurança não contiver a chave solicitada, ele voltará a ler a frase-senha de texto sem formatação diretamente do arquivo de configuração.

Configurando TTL da JVM para Pesquisas de Nome do DNS

A JVM (Java Virtual Machine) armazena no cache as respostas do DNS para pesquisas por um tempo definido, chamado TTL (time-to-live). Isso garante um tempo de resposta mais rápido no código que requer uma resolução frequente de nomes.

A JVM usa a propriedade networkaddress.cache.ttl para especificar a política de cache destinada a pesquisas de nomes de DNS. O valor é um número inteiro que representa o número de segundos para armazenar no cache a pesquisa bem-sucedida. O valor padrão para muitas JVMs, -1, indica que a pesquisa deve ser armazenada no cache para sempre.

Como os recursos do Oracle Cloud Infrastructure usam nomes de DNS que podem ser alterados, recomendamos que você altere o valor do TTL para 60 segundos. Isso garante que o novo endereço IP do recurso seja retornado na próxima consulta de DNS. Você pode alterar esse valor de forma global ou específica para o seu aplicativo:

  • Para definir o TTL globalmente em relação a todos os aplicativos que usam a JVM, adicione o seguinte código no arquivo $JAVA_HOME/jre/lib/security/java.security:

    networkaddress.cache.ttl=60
  • Para definir o TTL somente em relação ao seu aplicativo, inclua o seguinte no código de inicialização do aplicativo:

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

Instalação

Copie os jars agrupados de lib e de third-party/lib para cada nó do cluster Hadoop, a fim de que eles sejam incluídos no CLASSPATH do Hadoop.

SDK para Artefatos Java e Maven

A criação de um conector HDFS depende de artefatos Maven fornecidos pelo Oracle Cloud Infrastructure SDK for Java. Para obter os artefatos, você deve fazer o download do SDK para o Java e criá-lo localmente. Depois, você poderá criar o conector HDFS.

Importante

A versão do arquivo do SDK para Java cujo download é feito na página Oracle Releases deve corresponder à versão do conector HDFS, que você encontra no arquivo hdfs-connector/pom.xml, no bloco de tags de dependência que tem o atributo groupId.

Conector HDFS e Artefatos Maven

O conector HDFS está disponível em Maven Central e JCenter.

Para usar o conector HDFS no seu projeto, importe a dependência de projeto a seguir. Por exemplo:

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

Propriedades

Você pode definir as propriedades a seguir do conector HDFS no arquivo core-site.xml. A página BmcProperties lista propriedades adicionais que você pode configurar para uma conexão com o serviço Object Storage.

Propriedade Descrição Tipo Obrigatória
fs.oci.client.hostname

O URL do ponto final do host.

Por exemplo, https://www.example.com.

String Sim
fs.oci.client.auth.tenantId

O OCID da tenancy.

Para obter o valor, consulte Chaves e OCIDs Necessários.

String Sim
fs.oci.client.auth.userId

O OCID do usuário que chama a API.

Para obter o valor, consulte Chaves e OCIDs Necessários.

String Sim
fs.oci.client.auth.fingerprint

A impressão digital do par de chaves que está sendo usado.

Para obter o valor, consulte Chaves e OCIDs Necessários.

String

Sim, a menos que você forneça um autenticador personalizado.

fs.oci.client.auth.pemfilepath O caminho completo e o nome do arquivo da chave privada usada para autenticação. O arquivo deve estar no sistema de arquivos local. String Sim, a menos que você forneça um autenticador personalizado.
fs.oci.client.auth.passphrase A frase-senha usada para a chave, se estiver criptografada. String Somente se a sua chave estiver criptografada.
fs.oci.client.regionCodeOrId O código ou o identificador de região usado para estabelecer o nome do ponto final do serviço Object Storage. String Não
Observação

Você pode especificar que um valor de propriedade se aplique a um bucket específico anexando .<bucket_name>.<namespace_name> ao nome da propriedade.

Definindo o Ponto Final da Região

Há vários métodos que você pode usar para definir o ponto final da região para o Conector HDFS:

  • Especificando a propriedade de nome de host em core-site.xml
  • Especificando o código de região ou a propriedade do identificador de região em core-site.xml
  • Permitindo que o cliente ObjectStorage selecione o ponto final por meio do serviço de metadados da instância

Configurando Propriedades com core-site.xml

Este exemplo mostra como as propriedades podem ser configuradas em um arquivo core-site.xml (os OCIDs são encurtados para fins de simplificação):

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

Usando 'Instance Principals' para Autenticação

O Oracle fornece 'instance principals' para que você não precise mais configurar credenciais de usuário ou forneça arquivos PEM nos serviços em execução nas instâncias. Cada uma dessas instâncias tem identidade própria. As instâncias são autenticadas por meio da utilização de certificados adicionados por 'instance principals'.

Para usar a autenticação de 'instance principals' com o conector HDFS, basta fornecer a propriedade fs.oci.client.custom.authenticator e definir o valor para com.oracle.bmc.hdfs.auth.InstancePrincipalsCustomAuthenticator.

Como o uso de controladores de instância fornece ao conector um autenticador personalizado, não é mais necessário configurar as seguintes propriedades:

  • 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

O exemplo de código a seguir ilustra como usar 'instance principals' para autenticação com o conector 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>

Para obter mais informações sobre 'instance principals', consulte Anunciando 'Instance Principals' para o Serviço IAM.

Usando Principais de Recursos para Autenticação

Semelhante aos controladores de instância, a Oracle fornece controladores de recurso para autenticar os recursos que não são instâncias (como um notebook jupyter). Cada recurso tem sua própria identidade e faz a autenticação usando os certificados que são adicionados a ele.

Para usar a autenticação de principais de recursos com o conector HDFS, basta fornecer a propriedade fs.oci.client.custom.authenticator e definir o valor como com.oracle.bmc.hdfs.auth.ResourcePrincipalsCustomAuthenticator.

Como o uso de controladores de recursos fornece ao conector um autenticador personalizado, não é necessário configurar as seguintes propriedades:

  • 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

O exemplo de código a seguir ilustra como usar principais de recursos para autenticação com o conector 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>

Para obter mais informações sobre principais de instâncias, consulte Usando Principais de Recursos no Serviço Data Science.

Usando a Autenticação do Kerberos

A Oracle suporta a autenticação Kerberos para estabelecer conexão com o Object Storage usando o Conector HDFS.

Para usar a autenticação do Kerberos com o Conector HDFS:
  1. No core-site.xml, defina a propriedade fs.oci.client.custom.authenticator como com.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator.
  2. Em core-site.xml, defina as seguintes propriedades:
    • 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

O seguinte exemplo de arquivo core-site.xml ilustra o uso do Kerberos com autenticação de token SPNEGO com o Conector 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>

Para obter mais informações sobre o Kerberos, consulte o Tutorial do Protocolo Kerberos.

Para obter mais informações sobre SPNEGO, consulte RFC 4178.

Usando o Padrão de Jersey HttpUrlConnectorProvider

A partir da versão 3.3.0.7.0.0, o HDFS suporta o uso do cliente Apache, por padrão, para fazer chamadas de serviço do OCI. Isso ocorre porque o Conector HDFS utiliza o SDK para que o Java envie solicitações ao servidor. O SDK para Java suporta o uso do Jersey ApacheConnectorProvider por padrão, em vez do Jersey HttpUrlConnectorProvider, para permitir que o Apache HttpClient faça chamadas de serviço do OCI.

Para voltar para o cliente padrão Jersey antigo, defina a propriedade fs.oci.client.jersey.default.connector.enabled no arquivo core-site.xml como true. Por padrão, esse valor é definido como false.

Otimização de desempenho com o Apache Connector para HDFS

O Apache Connector suporta duas estratégias de fechamento de conexão: ApacheConnectionClosingStrategy.GracefulClosingStrategy e ApacheConnectionClosingStrategy.ImmediateClosingStrategy.

Ao usar ApacheConnectionClosingStrategy.GracefulClosingStrategy, os streams retornados de uma resposta são lidos até o final do stream ao fechá-lo. Isso pode introduzir um tempo adicional ao fechar o stream com uma leitura parcial, dependendo do tamanho do stream restante. Para evitar esse atraso, considere o uso de ApacheConnectionClosingStrategy.ImmediateClosingStrategy para arquivos grandes com leituras parciais. Com ApacheConnectionClosingStrategy.ImmediateClosingStrategy, os streams não são lidos até o final ao fechá-lo, o que pode melhorar o desempenho. Observe que ApacheConnectionClosingStrategy.ImmediateClosingStrategy leva mais tempo ao usar a leitura parcial para um tamanho de stream menor (streams inferiores a 1 MB).

Definindo a estratégia para fechamento da conexão

Defina a estratégia de fechamento da conexão definindo a propriedade fs.oci.client.apache.connection.closing.strategy no arquivo core-site.xml:

  • Para usar ApacheConnectionClosingStrategy.GracefulClosingStrategy, defina fs.oci.client.apache.connection.closing.strategy como graceful.
  • Para usar ApacheConnectionClosingStrategy.ImmediateClosingStrategy, defina fs.oci.client.apache.connection.closing.strategy como immediate.
Observação

Essas estratégias de fechamento só funcionam com o Apache Connector para HDFS e são ignoradas ao usar o conector padrão Jersey.

Retornando ao conector padrão Jersey

O conector padrão Jersey lê streams até o final e, em seguida, reutiliza o stream, o que pode levar a um desempenho melhor do que o do Apache Connector para HDFS em alguns cenários. Se essas estratégias de fechamento de Conexão do Apache não derem a você os resultados ideais para seus casos de uso, você poderá voltar para Jersey Default `HttpUrlConnectorProvider` Poderá voltar para o cliente padrão Jersey antigo definindo a propriedade fs.oci.client.jersey.default.connector.enabled no arquivo core-site.xml como true. Por padrão, esse valor é definido como false.

Para obter mais informações, consulte: https://github.com/oracle/oci-java-sdk/blob/master/ApacheConnector-README.md.

Pool de Conexões no HDFS

Você pode definir o número máximo de conexões no pool de conexões do HDFS Connector.

Para fazer isso, altere a propriedade fs.oci.client.apache.max.connection.pool.size no arquivo core-site.xml para um inteiro positivo que especifique quantas conexões devem ser colocadas no pool.

Observação

Essa propriedade só é suportada ao usar ApacheConnector para HDFS; caso contrário, ela será ignorada.

Pontos Finais Dedicados

Pontos finais dedicados são os modelos de ponto final definidos por um serviço para um realm específico no nível do cliente. O Conector HDFS do OCI permite ativar o uso desses modelos de ponto final específicos do realm definindo o nome do host fs.oci.client.hostname ou a propriedade fs.oci.realmspecific.endpoint.template.enabled do flag do modelo de ponto final em core-site.xml.
Observação

Se você definir a propriedade do modelo de ponto final, também precisará definir fs.oci.client.regionCodeOrId em core-site.xml.
Observação

O valor definido por meio do nome do host em core-site.xml tem precedência sobre o valor definido usando a propriedade de modelo de ponto final em core-site.xml.
Este exemplo mostra como ativar o recurso de modelos de ponto final específicos do realm definindo a propriedade 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>
Este exemplo mostra como ativar o recurso de modelos de ponto final específicos do realm definindo a propriedade 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>

Configurando um Proxy HTTP

É possível definir as seguintes propriedades opcionais no arquivo core-site.xml para configurar um proxy HTTP:

Propriedade Descrição Tipo Obrigatória
fs.oci.client.proxy.uri

O URI do ponto final do proxy.

Por exemplo, http://proxy.mydomain.com:80.

String Não
fs.oci.client.proxy.username O nome de usuário para autenticação do proxy. String Não
fs.oci.client.proxy.password A senha para autenticação do proxy. String Não
fs.oci.client.multipart.allowed Permite que o gerenciador de upload suporte uploads em várias partes Booliano Não
fs.oci.client.multipart.minobjectsize.mb

Especifica o tamanho mínimo do objeto em mebibytes para usar o gerenciador de upload.

Inteiro Não
fs.oci.client.multipart.partsize.mb Especifica o tamanho da parte em mebibytes para o gerenciador de upload. Inteiro Não
Observação

Ao configurar um proxy, você pode usar o conector ApacheConnectorProvider durante conexões com o serviço Object Storage. Ele armazena as solicitações na memória e pode afetar a utilização da memória durante o upload de objetos grandes. É recomendável permitir uploads em várias partes e ajustar as propriedades multipartes para gerenciar o consumo de memória.

Uploads de Objetos Grandes

O upload de objetos grandes no Object Storage ocorre em várias partes. O arquivo é dividido em partes menores que são carregadas em paralelo, o que reduz o tempo de upload. Isso também permite que o conector HDFS repita o upload de partes com falha, em vez de fazer o upload inteiro. No entanto, os uploads podem falhar de forma temporária, e o conector tentará abortar arquivos parcialmente carregados. Como esses arquivos se acumulam (e você pagará pelo uso do armazenamento), liste os uploads periodicamente e, após determinado número de dias, cancele-os manualmente usando o SDK para Java.

Informações sobre o uso da API do serviço Object Storage para gerenciar uploads em várias partes podem ser encontradas em Usando Uploads em Várias Partes.

Observação

Se preferir não usar uploads em várias partes, você poderá desativá-los definindo a propriedade fs.oci.client.multipart.allowed como false.

Melhores Práticas

As seções a seguir contêm as melhores práticas para otimizar uso e desempenho.

Nomes de Diretórios

Não há diretórios reais no Object Storage. O agrupamento de diretórios é uma função de convenção de nomenclatura na qual os objetos usam delimitadores / em seus nomes. Por exemplo, um objeto chamado a/example.json implica que há um diretório chamado a. No entanto, se esse objeto for excluído, o diretório a também será implicitamente excluído. Para preservar a semântica do sistema de arquivos onde o diretório pode existir sem a presença de arquivos, o conector HDFS cria um objeto real cujo nome termina com / com um caminho que representa o diretório (ou seja, crie um objeto chamado a/). Agora, a exclusão de a/example.json não afeta a existência do diretório a, porque o objeto a/ mantém sua presença. Entretanto, é inteiramente possível que alguém exclua esse objeto a/ sem excluir os arquivos/diretórios abaixo dele. O conector HDFS só excluirá o objeto de pasta se não houver objetos abaixo desse caminho. O objeto de pasta em si tem zero byte.

Sistema de Arquivos Inconsistente

A exclusão de um diretório significa a exclusão de todos os objetos que começam com o prefixo que representa esse diretório. O HDFS permite consultar o status de um arquivo ou de um diretório. O status de um diretório é implementado quando se verifica se o objeto de pasta para esse diretório existe. Entretanto, é possível que o objeto de pasta tenha sido excluído, mas alguns dos objetos com esse prefixo ainda existam. Por exemplo, em uma situação com estes objetos:

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

O HDFS saberia que o diretório /a/b/ existe e é um diretório, e a varredura resultaria em example.json e file.json. No entanto, se o objeto a/b/ tiver sido excluído, o sistema de arquivos parecerá estar em um estado inconsistente. Você pode consultar esse objeto para todos os arquivos no diretório /a/b/ e encontrar as duas entradas, mas a consulta para o status do diretório /a/b/ real resultaria em uma exceção porque o diretório não existe. O conector HDFS não tenta reparar o estado do sistema de arquivos.

Criação de Arquivos

O Object Storage suporta objetos que podem ter muitos gigabytes de tamanho. A criação de arquivos normalmente ocorrerá por meio da gravação em um arquivo temporário e do upload do conteúdo do arquivo quando o stream estiver fechado. O espaço temporário deve ser grande o suficiente para cuidar de vários uploads. O diretório temporário usado é controlado pela propriedade de configuração hadoop.tmp.dir.

Suporte para Leitura/Busca

Quando os buffers da memória estão ativados (fs.oci.io.read.inmemory), operações de busca são totalmente suportadas porque o arquivo inteiro é armazenado temporariamente em um array de bytes. Quando o buffer na memória não está ativado (provavelmente porque os tamanhos dos objetos são grandes), a operação de busca é implementada por meio do fechamento do stream, e há uma nova solicitação de intervalo a partir do deslocamento especificado.

Listagem de Diretórios

A listagem de um diretório é essencialmente uma operação List bucket com um prefixo e um delimitador específicos. Ao criar uma instância de FileStatus HDFS para cada chave, o conector realiza uma solicitação HEAD adicional para obter o ObjectMetadata de cada chave individual. Isso será necessário até o Object Storage suportar dados de operação de lista mais ricos.

Formato de URI para Sistemas de Arquivos e Arquivos

Sistemas de arquivos HDFS e arquivos são referenciados por meio de URIs. O esquema especifica o tipo de sistema de arquivos, e a parte restante do URI é altamente flexível para que a implementação do sistema de arquivos a interprete como quiser.

Como o Object Storage é uma área de armazenamento de objeto, sua capacidade de nomear objetos como se fossem arquivos em um sistema é usada para imitar um sistema.

Raiz

A raiz do sistema do Object Storage é denotada por um caminho em que o componente de autoridade inclui o nome do bucket e o nome do namespace, conforme mostrado:

Observação

Nos exemplos, "MyBucket" e "MyNamespace" são placeholders e devem ser substituídos por valores apropriados.

oci://MyBucket@MyNamespace/
		

Esta é sempre a raiz do sistema de arquivos. O motivo de se utilizar autoridade para o bucket e o namespace é que o HDFS só permite que a parte da autoridade determine onde está o sistema de arquivos; a parte do caminho denota apenas o caminho para o recurso (portanto, "oci//MyNamespace/MyBucket" não funcionará, por exemplo). Observe que o caractere @ não é um caractere válido para buckets ou namespaces e deve permitir que a autoridade seja corretamente analisada.

Subdiretórios

Na verdade, os subdiretórios não existem, mas podem ser imitados por meio da criação de objetos com caracteres /. Por exemplo, dois arquivos chamados a/b/c/example.json e a/b/d/path.json seriam exibidos como se estivessem em um diretório comum a/b. Isso seria conseguido usando a consulta baseada em prefixo e delimitador do serviço Object Storage. No exemplo fornecido, a referência a um subdiretório como um URI seria:


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

Objetos/Arquivos

Um objeto chamado a/b/c/example.json é referenciado como:


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

Log

O registro em logs no conector é feito por meio do SLF4J. O SLF4J é uma abstração de log que permite o uso de uma biblioteca de logs fornecida pelo usuário (por exemplo, log4j). Para obter mais informações, consulte o Manual do SLF4J.

O exemplo a seguir mostra como ativar o log básico na saída padrão.

  1. Faça download do jar de binding Simples do SLF4J Binding Simples do SLF4J
  2. Adicione o jar ao seu classpath
  3. Adicione o seguinte argumento VM para ativar o log em nível de depuração (por padrão, o nível de informações é usado): -Dorg.slf4j.simpleLogger.defaultLogLevel=debug

É possível configurar opções de log mais avançadas usando o binding log4j.

Usando o Monitoring Framework

O Conector HDFS para Armazenamento de Objetos inclui uma estrutura de monitoramento que fornece métricas sobre as operações executadas com o conector. A estrutura de monitoramento fornece uma interface que pode ser implementada para consumir/ouvir métricas geradas pelo conector. Você pode fornecer uma implementação personalizada dessa interface ou pode usar a implementação de telemetria pública do OCI incluída nessa estrutura.

Conceitos Básicos

Para começar a usar a estrutura de monitoramento do Conector HDFS, você precisará definir as propriedades a seguir. Depois que essas propriedades forem definidas para OCIMonitoring, você poderá usar a view Explorador de Métricas na Console do OCI para observar as métricas emitidas pelo conector HDFS.

fs.oci.mon.consumer.plugins

O fs.oci.mon.consumer.plugins obtém uma lista separada por vírgulas de nomes de classe totalmente qualificados das implementações da interface de monitoramento. O com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin deverá ser usado na lista se você quiser que as métricas sejam emitidas para a telemetria pública do 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

A propriedade fs.oci.mon.grouping.cluster.id especifica o identificador do cluster do HDFS ou de qualquer outro ID no qual você deseja agrupar as métricas. Essa é uma propriedade obrigatória, que também é usada pelo OCIMonitorPlugin para marcar métricas. Essa propriedade é visível como uma dimensão na interface de usuário e na API de telemetria pública do OCI.
<property>
    <name>fs.oci.mon.grouping.cluster.id</name>
    <value>hdfs-sample-cluster-id</value>
</property>

Propriedades com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin

Se a propriedade com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin estiver ativada, as seguintes propriedades serão aplicáveis:

fs.oci.mon.telemetry.ingestion.endpoint

A propriedade fs.oci.mon.telemetry.ingestion.endpoint ajuda a configurar o ponto final de ingestão de telemetria do monitoramento do OCI. Para obter mais informações, consulte a lista de pontos disponíveis.
<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

A propriedade fs.oci.mon.compartment.ocid é usada para configurar o compartimento do OCI ao qual as métricas serão anexadas. Em geral, esse será o compartimento ao qual os buckets pertencem.
<property>
    <name>fs.oci.mon.compartment.ocid</name>
    <value>ocid1.compartment.oc1..sample.compartment.id</value>
</property>

fs.oci.mon.bucket.level.enabled

A propriedade fs.oci.mon.bucket.level.enabled determina se o nome do bucket deve ser anexado como uma dimensão às métricas emitidas.
<property>
    <name>fs.oci.mon.bucket.level.enabled</name>
    <value>true</value>
</property>

fs.oci.mon.ns.name

A propriedade fs.oci.mon.ns.name controla o namespace usado para as métricas emitidas pelo conector HDFS. Um exemplo de namespace poderia ser "hdfsconnector". Isso residirá ao lado de outros namespaces predefinidos, como oci_objectstorage, na telemetria pública.
<property>
    <name>fs.oci.mon.ns.name</name>
    <value>name.of.namespace.on.oci.telemetry</value>
</property>

fs.oci.mon.rg.name

A propriedade fs.oci.mon.rg.name define o nome do grupo de recursos usado para conter as métricas. Pode ser qualquer nome lógico para agrupar recursos que serão monitorados juntos. Esse nome de grupo de recursos aparecerá no namespace escolhido anteriormente na telemetria pública do OCI.
<property>
    <name>fs.oci.mon.rg.name</name>
    <value>name.of.resource.group.on.oci.telemetry</value>
</property>

Criando seu próprio consumidor para métricas

Para usar a interface com.oracle.bmc.hdfs.monitoring.OCIMonitorConsumerPlugin, você precisará definir dois métodos:
  • accept
  • shutdown

A extensão das classes deve ter um construtor com a mesma assinatura da classe OCIMonitorConsumerPlugin.

Por exemplo:
public OCIMonitorConsumerPlugin(BmcPropertyAccessor propertyAccessor, String bucketName, String monitoringGroupingID, String namespaceName);
Este exemplo demonstra cada método que precisa ser implementado por um plug-in de consumidor:
/**
 * 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();
}

A classe OCIMetric pode ser implementada de três maneiras:

Um objeto simples OCIMetric com os seguintes campos:
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;
}
Uma implementação do objeto OCIMetricWithThroughput que estende OCIMetric e tem campos adicionais para throughput e bytes transferidos. Isso se aplica às operações READ e 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;
}
Um objeto OCIMetricWithFBLatency que estende OCIMetricWithThroughput, com um tempo adicional para o campo de latência do primeiro byte. Isso só se aplica a operações READ:
public class OCIMetricWithFBLatency extends OCIMetricWithThroughput {
    /**
     * The time to first byte when a read operation was performed in milliseconds.
     */
    private final double ttfb;
}

Exemplo de Job 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);
    }
}

Diagnóstico e Solução de Problemas

Esta seção contém informações sobre diagnóstico e solução de problemas do conector HDFS.

Diagnóstico e Solução de Problemas com Erros de Serviço

Qualquer operação que resulte em um erro de serviço fará com que uma exceção do tipo com.oracle.bmc.model.BmcException seja gerada pelo conector HDFS. Para obter informações sobre erros de serviço comuns retornados pelo OCI, consulte Erros da API.

Erros de Tamanho da Chave de Criptografia Java

O conector HDFS só pode tratar chaves de128 bits ou menos. Os usuários obtêm erros "Exceção de Chave Inválida" e "Tamanho de chave ilegal" quando utilizam chaves mais longas, como AES256. Use uma destas soluções alternativas para corrigir esse problema:

Notificações

Se quiser ser notificado quando uma nova versão do conector HDFS for liberada, inscreva-se no Feed Atom.