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.
- Serviços suportados: Object Storage
- Download: GitHub ou Maven
- Documentação da API: Referência da API do Conector HDFS
Requisitos
Para usar o conector HDFS, você deve ter:
- Uma conta no Oracle Cloud Infrastructure.
- Um usuário criado nessa conta, em um grupo com uma política que concede as permissões desejadas para qualquer bucket que você deseja usar. Pode ser um usuário para você mesmo ou para outra pessoa/sistema que precise chamar a API. Para obter um exemplo de como configurar um novo usuário, um novo grupo, um novo compartimento e uma nova política, consulte Adicionando Usuários. Para obter uma política básica do serviço Object Storage, consulte Permitir que administradores do serviço Object Storage gerenciem buckets e objetos.
- Java 8
- Um valor de TTL igual a 60. Para obter mais informações, consulte Configurando o TTL da JVM para Pesquisas de Nomes de DNS.
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.
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, |
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 |
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.
- No
core-site.xml
, defina a propriedadefs.oci.client.custom.authenticator
comocom.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator
. - 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
, definafs.oci.client.apache.connection.closing.strategy
comograceful
. - Para usar
ApacheConnectionClosingStrategy.ImmediateClosingStrategy
, definafs.oci.client.apache.connection.closing.strategy
comoimmediate
.
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.
Essa propriedade só é suportada ao usar
ApacheConnector
para HDFS; caso contrário, ela será ignorada.Pontos Finais Dedicados
fs.oci.client.hostname
ou a propriedade fs.oci.realmspecific.endpoint.template.enabled
do flag do modelo de ponto final em core-site.xml
. Se você definir a propriedade do modelo de ponto final, também precisará definir
fs.oci.client.regionCodeOrId
em core-site.xml
.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
.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>
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, |
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 |
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.
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:
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.
- Faça download do jar de binding Simples do SLF4J Binding Simples do SLF4J
- Adicione o jar ao seu classpath
- 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
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
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
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
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
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
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
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
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
.
public OCIMonitorConsumerPlugin(BmcPropertyAccessor propertyAccessor, String bucketName, String monitoringGroupingID, String namespaceName);
/**
* 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:
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;
}
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;
}
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
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:
- Use uma chave de 128 bits, como AES128.
-
Instale o Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction pelo seguinte local: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
Contribuições
Tem uma correção de bug ou uma funcionalidade que gostaria de divulgar? O SDK tem código-fonte aberto e aceita solicitações de extração no GitHub.
Notificações
Se quiser ser notificado quando uma nova versão do conector HDFS for liberada, inscreva-se no Feed Atom.
Perguntas ou Feedback
Formas de entrar em contato:
- Problemas com o GitHub: para registrar bugs e fazer solicitações de funcionalidades
- Overflow de Pilha: use as tags oracle-cloud-infrastructure e oci-hdfs-connector na sua postagem
- Seção Ferramentas do Desenvolvedor dos fóruns do Oracle Cloud
- My Oracle Support