{"id":5432,"date":"2018-06-29T01:23:36","date_gmt":"2018-06-29T08:23:36","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=5432"},"modified":"2018-06-29T01:23:36","modified_gmt":"2018-06-29T08:23:36","slug":"inside-the-java-sdk-connection-management","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/inside-the-java-sdk-connection-management\/","title":{"rendered":"Por dentro do Java SDK: Gerenciamento de conex\u00f5es"},"content":{"rendered":"<p>Nesta segunda parte de \"Por dentro do Java SDK\", daremos uma olhada detalhada em como o SDK gerencia e agrupa soquetes para os v\u00e1rios n\u00f3s e servi\u00e7os. Embora n\u00e3o seja necess\u00e1rio seguir o exemplo, recomendo que voc\u00ea d\u00ea uma olhada na primeira postagem sobre <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/inside-the-java-sdk-bootstrap\/\">bootstrapping\u00a0<\/a>tamb\u00e9m.<\/p>\n<p>Observe que esta postagem foi escrita tendo em mente as vers\u00f5es 2.5.9 \/ 2.6.0 do Java SDK. As coisas podem mudar com o tempo, mas a abordagem geral deve permanecer praticamente a mesma.<\/p>\n<p>No esp\u00edrito dos modelos OSI e TCP, proponho um modelo de tr\u00eas camadas que representa a pilha de conex\u00f5es dos SDKs:<\/p>\n<pre class=\"lang:default decode:true\">+-----------------+\r\n| Service Layer   |\r\n+-----------------+\r\n| Endpoint Layer  |\r\n+-----------------+\r\n| Channel Layer   |\r\n+-----------------+<\/pre>\n<p>Os n\u00edveis mais altos s\u00e3o constru\u00eddos sobre os n\u00edveis mais baixos, portanto, come\u00e7aremos com a camada Channel (Canal) e subiremos na pilha.<\/p>\n<h2>A camada de canal<\/h2>\n<p>A camada de canal \u00e9 o n\u00edvel mais baixo em que o SDK lida com a rede e \u00e9 constru\u00edda sobre a excelente biblioteca de E\/S totalmente ass\u00edncrona chamada <a href=\"https:\/\/netty.io\/\">Netty<\/a>\u00a0Somos usu\u00e1rios extensivos do Netty h\u00e1 anos e tamb\u00e9m contribu\u00edmos com patches, bem como com o <a href=\"https:\/\/github.com\/netty\/netty\/tree\/4.1\/codec-memcache\">codec de memcache<\/a> de volta ao projeto.<\/p>\n<p>Cada Netty <a href=\"https:\/\/netty.io\/4.0\/api\/io\/netty\/channel\/Channel.html\">Canal<\/a> corresponde a um soquete e \u00e9 multiplexado em cima de loops de eventos. Abordaremos o modelo de threading em uma postagem posterior do blog, mas, por enquanto, \u00e9 importante saber que, em vez do modelo \"um thread por soquete\" da E\/S de bloqueio tradicional, o Netty pega todos os soquetes abertos e os distribui em um punhado de loops de eventos. Ele faz isso de forma muito eficiente, portanto, n\u00e3o \u00e9 de se admirar que o Netty seja usado <a href=\"https:\/\/netty.io\/wiki\/adopters.html\">em todo o setor<\/a>\u00a0para componentes de rede de alto desempenho e baixa lat\u00eancia.<\/p>\n<p>Como um canal se preocupa apenas com a entrada e sa\u00edda de bytes, precisamos de uma maneira de codificar e decodificar solicita\u00e7\u00f5es em n\u00edvel de aplicativo (como uma consulta N1QL ou uma solicita\u00e7\u00e3o de obten\u00e7\u00e3o de chave\/valor) em sua representa\u00e7\u00e3o bin\u00e1ria adequada. No Netty, isso \u00e9 feito adicionando <a href=\"https:\/\/netty.io\/4.0\/api\/io\/netty\/channel\/ChannelHandler.html\">manipuladores<\/a> para o <a href=\"https:\/\/netty.io\/4.0\/api\/io\/netty\/channel\/ChannelPipeline.html\">canal de distribui\u00e7\u00e3o<\/a>. Todas as opera\u00e7\u00f5es de grava\u00e7\u00e3o na rede descem pelo pipeline e as respostas do servidor voltam pelo pipeline (tamb\u00e9m chamadas de entrada e sa\u00edda na terminologia Netty).<\/p>\n<p>Alguns manipuladores s\u00e3o adicionados independentemente do servi\u00e7o usado (como registro ou criptografia) e outros dependem do tipo de servi\u00e7o (por exemplo, para uma resposta N1QL, temos analisadores de fluxo JSON personalizados para a estrutura da resposta).<\/p>\n<p>Se voc\u00ea j\u00e1 se perguntou como obter uma sa\u00edda de registro em n\u00edvel de pacote durante o desenvolvimento ou a depura\u00e7\u00e3o (para produ\u00e7\u00e3o, use tcpdump, wireshark ou similar), tudo o que precisa fazer \u00e9 ativar o n\u00edvel de registro TRACE em sua biblioteca de registro favorita e ver\u00e1 uma sa\u00edda como esta:<\/p>\n<pre class=\"lang:default decode:true\">[cb-io-1-1] 2018-06-28 14:03:34 TRACE LoggingHandler:94 - [id: 0x41407638, L:\/127.0.0.1:60923 - R:localhost\/127.0.0.1:11210] WRITE: 243B\r\n+-------------------------------------------------+\r\n| 0 1 2 3 4 5 6 7 8 9 a b c d e f |\r\n+--------+-------------------------------------------------+----------------+\r\n|00000000| 80 1f 00 db 00 00 00 00 00 00 00 e5 00 00 00 00 |................|\r\n|00000010| 00 00 00 00 00 00 00 00 7b 22 61 22 3a 22 63 6f |........{\"a\":\"co|\r\n|00000020| 75 63 68 62 61 73 65 2d 6a 61 76 61 2d 63 6c 69 |uchbase-java-cli|\r\n|00000030| 65 6e 74 2f 32 2e 36 2e 30 2d 53 4e 41 50 53 48 |ent\/2.6.0-SNAPSH|\r\n|00000040| 4f 54 20 28 67 69 74 3a 20 32 2e 36 2e 30 2d 62 |OT (git: 2.6.0-b|\r\n|00000050| 65 74 61 2d 31 36 2d 67 35 63 65 30 38 62 30 2c |eta-16-g5ce08b0,|\r\n|00000060| 20 63 6f 72 65 3a 20 31 2e 36 2e 30 2d 62 65 74 | core: 1.6.0-bet|\r\n|00000070| 61 2d 33 33 2d 67 31 62 33 65 36 66 62 29 20 28 |a-33-g1b3e6fb) (|\r\n|00000080| 4d 61 63 20 4f 53 20 58 2f 31 30 2e 31 33 2e 34 |Mac OS X\/10.13.4|\r\n|00000090| 20 78 38 36 5f 36 34 3b 20 4a 61 76 61 20 48 6f | x86_64; Java Ho|\r\n|000000a0| 74 53 70 6f 74 28 54 4d 29 20 36 34 2d 42 69 74 |tSpot(TM) 64-Bit|\r\n|000000b0| 20 53 65 72 76 65 72 20 56 4d 20 31 2e 38 2e 30 | Server VM 1.8.0|\r\n|000000c0| 5f 31 30 31 2d 62 31 33 29 22 2c 22 69 22 3a 22 |_101-b13)\",\"i\":\"|\r\n|000000d0| 30 43 34 37 35 41 43 41 35 46 33 38 30 41 32 31 |0C475ACA5F380A21|\r\n|000000e0| 2f 30 30 30 30 30 30 30 30 34 31 34 30 37 36 33 |\/000000004140763|\r\n|000000f0| 38 22 7d |8\"} |\r\n+--------+-------------------------------------------------+----------------+<\/pre>\n<p>Observe o pequeno <span class=\"lang:default decode:true crayon-inline\">Gerenciador de registro<\/span>\u00a0 l\u00e1 em cima? Isso ocorre porque s\u00f3 adicionamos o manipulador de registro se o rastreamento estiver ativado no pipeline, para que voc\u00ea n\u00e3o pague a sobrecarga se n\u00e3o o estiver usando (o que ocorre na maioria das vezes):<\/p>\n<pre class=\"lang:java decode:true\">bootstrap = new BootstrapAdapter(new Bootstrap()\r\n  \/\/ *snip*\r\n  .option(ChannelOption.ALLOCATOR, allocator)\r\n  .option(ChannelOption.TCP_NODELAY, tcpNodelay)\r\n  .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, env.socketConnectTimeout())\r\n  .handler(new ChannelInitializer&lt;Channel&gt;() {\r\n    @Override\r\n    protected void initChannel(Channel channel) throws Exception {\r\n      ChannelPipeline pipeline = channel.pipeline();\r\n      if (env.sslEnabled()) {\r\n        pipeline.addLast(new SslHandler(sslEngineFactory.get()));\r\n      }\r\n      if (LOGGER.isTraceEnabled()) {\r\n        pipeline.addLast(LOGGING_HANDLER_INSTANCE);\r\n      }\r\n    customEndpointHandlers(pipeline);\r\n  }\r\n}));<\/pre>\n<p>Voc\u00ea tamb\u00e9m pode ver que, dependendo da configura\u00e7\u00e3o do ambiente, fazemos outros ajustes, como adicionar um manipulador de SSL\/TLS ao pipeline ou configurar o nodelay do TCP e os tempos limite do soquete.<\/p>\n<p>O <span class=\"lang:default decode:true crayon-inline\">customEndpointHandlers<\/span>\u00a0 \u00e9 substitu\u00eddo para cada servi\u00e7o, aqui est\u00e1 o pipeline para a camada KV (ligeiramente simplificado):<\/p>\n<pre class=\"lang:java decode:true\">if (environment().keepAliveInterval() &gt; 0) {\r\n    pipeline.addLast(new IdleStateHandler(environment().keepAliveInterval(), 0, 0, TimeUnit.MILLISECONDS));\r\n}\r\n\r\npipeline\r\n    .addLast(new BinaryMemcacheClientCodec())\r\n    .addLast(new BinaryMemcacheObjectAggregator(Integer.MAX_VALUE));\r\n\r\npipeline\r\n    .addLast(new KeyValueFeatureHandler(context()))\r\n    .addLast(new KeyValueErrorMapHandler());\r\n\r\nif (!environment().certAuthEnabled()) {\r\n    pipeline.addLast(new KeyValueAuthHandler(username(), password(), environment().forceSaslPlain()));\r\n}\r\n\r\npipeline\r\n    .addLast(new KeyValueSelectBucketHandler(bucket()))\r\n    .addLast(new KeyValueHandler(this, responseBuffer(), false, true));<\/pre>\n<p>H\u00e1 muita coisa acontecendo aqui! Vamos analisar um por um:<\/p>\n<ul>\n<li>O <span class=\"lang:default decode:true crayon-inline\">IdleStateHandler<\/span>\u00a0 \u00e9 usado para acionar keepalives no n\u00edvel do aplicativo.<\/li>\n<li>Os pr\u00f3ximos dois manipuladores <span class=\"lang:default decode:true crayon-inline\">BinaryMemcacheClientCodec<\/span>\u00a0 e <span class=\"lang:default decode:true crayon-inline\">BinaryMemcacheObjectAggregator<\/span>\u00a0 lidam com a codifica\u00e7\u00e3o de objetos de solicita\u00e7\u00e3o e resposta do memcache em suas representa\u00e7\u00f5es de bytes e vice-versa.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">Manipulador KeyValueFeatureHandler<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline\">Manipulador de KeyValueErrorMap<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline\">KeyValueAuthHandler<\/span>\u00a0 e <span class=\"lang:default decode:true crayon-inline\">KeyValueSelectBucketHandler<\/span>\u00a0 todos realizam handshaking, autentica\u00e7\u00e3o, sele\u00e7\u00e3o de bucket e assim por diante durante a fase de conex\u00e3o e se retiram do pipeline ap\u00f3s a conclus\u00e3o.<\/li>\n<li>Por fim, o <span class=\"lang:default decode:true crayon-inline\">Manipulador de valor-chave<\/span>\u00a0 faz a maior parte do trabalho e \"conhece\" todos os diferentes tipos de solicita\u00e7\u00e3o que entram e saem do sistema.<\/li>\n<\/ul>\n<p>Se quiser dar uma olhada em um diferente, <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/endpoint\/query\/QueryEndpoint.java#L48\">aqui<\/a>\u00a0\u00e9 o pipeline N1QL, por exemplo.<\/p>\n<p>Antes de subirmos uma camada, h\u00e1 uma parte importante. O observ\u00e1vel RxJava <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/endpoint\/AbstractGenericHandler.java#L507\">conclus\u00e3o<\/a> tamb\u00e9m acontece nessa camada. Depois que uma resposta \u00e9 decodificada, ela \u00e9 conclu\u00edda diretamente no loop de eventos ou em um pool de threads (configurado por padr\u00e3o).<\/p>\n<p>\u00c9 importante saber que, quando um canal \u00e9 desativado (porque o soquete subjacente \u00e9 fechado), todo o estado nesse n\u00edvel desaparece. Em uma tentativa de reconex\u00e3o, um novo canal \u00e9 criado. Ent\u00e3o, quem gerencia um canal? Vamos subir uma camada.<\/p>\n<h2>A camada de endpoint<\/h2>\n<p>O <span class=\"lang:default decode:true crayon-inline\">Ponto final<\/span>\u00a0 \u00e9 respons\u00e1vel por gerenciar o ciclo de vida de um canal, incluindo a inicializa\u00e7\u00e3o, a reconex\u00e3o e a desconex\u00e3o. Voc\u00ea pode encontrar o c\u00f3digo <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/endpoint\/AbstractEndpoint.java\">aqui<\/a>.<\/p>\n<p>H\u00e1 sempre uma rela\u00e7\u00e3o de 1:1 entre o endpoint e o canal que ele gerencia, mas se um canal for removido e um soquete precisar ser reconectado, o endpoint permanecer\u00e1 o mesmo e receber\u00e1 um novo internamente. O endpoint tamb\u00e9m \u00e9 o local em que a solicita\u00e7\u00e3o \u00e9 entregue aos loops de eventos (simplificado):<\/p>\n<pre class=\"lang:java decode:true\">@Override\r\npublic void send(final CouchbaseRequest request) {\r\n\tif (channel.isActive() &amp;&amp; channel.isWritable()) {\r\n\t    channel.write(request, channel.voidPromise());\r\n\t} else {\r\n\t    responseBuffer.publishEvent(ResponseHandler.RESPONSE_TRANSLATOR, request, request.observable());\r\n\t}\r\n}<\/pre>\n<p>Se o nosso canal estiver ativo e for grav\u00e1vel, escreveremos a solicita\u00e7\u00e3o no pipeline; caso contr\u00e1rio, ela ser\u00e1 enviada de volta e enfileirada novamente para outra tentativa.<\/p>\n<p>Aqui est\u00e1 um aspecto muito importante do ponto de extremidade que deve ser lembrado: se um canal for fechado, o ponto de extremidade tentar\u00e1 se reconectar (com o backoff configurado), desde que seja explicitamente instru\u00eddo a parar. Ele para quando o gerente do canal <span class=\"lang:default decode:true crayon-inline\">Ponto final<\/span>\u00a0 chamadas <span class=\"lang:default decode:true crayon-inline\">desconectar<\/span>\u00a0 nele, o que ocorrer\u00e1 quando o respectivo servi\u00e7o\/n\u00f3 n\u00e3o fizer mais parte da configura\u00e7\u00e3o. Portanto, ao final de um rebalanceamento ou durante um failover, o cliente receber\u00e1 uma nova configura\u00e7\u00e3o de cluster a partir da qual ele infere que esse ponto de extremidade pode ser encerrado e o faz de acordo. Se, por qualquer motivo, houver um atraso entre a desconex\u00e3o de um soquete e a propaga\u00e7\u00e3o dessas informa\u00e7\u00f5es, voc\u00ea poder\u00e1 ver algumas tentativas de reconex\u00e3o que acabar\u00e3o parando.<\/p>\n<p>Um endpoint \u00e9 muito bom, mas mais \u00e9 sempre melhor, certo? Portanto, vamos subir mais uma camada para descobrir como os endpoints s\u00e3o agrupados para criar pools de conex\u00e3o sofisticados por n\u00f3 e servi\u00e7o.<\/p>\n<h2>A camada de servi\u00e7o<\/h2>\n<p>O <span class=\"lang:default decode:true crayon-inline\">Servi\u00e7o<\/span>\u00a0 gerencia um ou mais endpoints por n\u00f3. Cada servi\u00e7o \u00e9 respons\u00e1vel apenas por um n\u00f3, portanto, por exemplo, se voc\u00ea tiver um cluster do Couchbase de 5 n\u00f3s com apenas o servi\u00e7o KV ativado em cada um deles, se inspecionar um heap dump, encontrar\u00e1 5 inst\u00e2ncias do servi\u00e7o <span class=\"lang:default decode:true crayon-inline\">Servi\u00e7o KeyValue<\/span>\u00a0.<\/p>\n<p>Em vers\u00f5es anteriores do cliente, voc\u00ea s\u00f3 podia configurar um n\u00famero fixo de endpoints por servi\u00e7o por meio de m\u00e9todos como <span class=\"lang:default decode:true crayon-inline\">kvEndpoints<\/span>\u00a0, <span class=\"lang:default decode:true crayon-inline\">pontos de consulta<\/span>\u00a0 e assim por diante. Devido a requisitos mais complexos, descontinuamos essa abordagem \"fixa\" com uma implementa\u00e7\u00e3o avan\u00e7ada de pool de conex\u00f5es. \u00c9 por isso que, em vez de i.e. <span class=\"lang:default decode:true crayon-inline\">pontos de consulta<\/span>\u00a0 agora voc\u00ea deve usar <span class=\"lang:default decode:true crayon-inline\">queryServiceConfig<\/span>\u00a0 e equivalentes.<\/p>\n<p>Aqui est\u00e3o os pools padr\u00e3o atuais por servi\u00e7o nas vers\u00f5es 2.5.9 e 2.6.0:<\/p>\n<ul>\n<li><span class=\"lang:default decode:true crayon-inline\">Servi\u00e7o KeyValue<\/span>\u00a0: 1 ponto de extremidade por n\u00f3, fixo.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">Servi\u00e7o de consulta<\/span>\u00a0: de 0 a 12 endpoints por n\u00f3, din\u00e2mico.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">ViewService<\/span>\u00a0: de 0 a 12 endpoints por n\u00f3, din\u00e2mico.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">AnalyticsService<\/span>\u00a0: de 0 a 12 endpoints por n\u00f3, din\u00e2mico.<\/li>\n<li><span class=\"lang:default decode:true crayon-inline\">Servi\u00e7o de busca<\/span>\u00a0: de 0 a 12 endpoints por n\u00f3, din\u00e2mico.<\/li>\n<\/ul>\n<p>O motivo pelo qual o KV n\u00e3o \u00e9 agrupado por padr\u00e3o \u00e9 que o handshaking da conex\u00e3o \u00e9 muito mais caro (lembre-se de todos os manipuladores no pipeline) e o padr\u00e3o de tr\u00e1fego geralmente \u00e9 muito diferente dos servi\u00e7os mais pesados baseados em consultas. A experi\u00eancia de campo mostrou que o aumento do n\u00famero de endpoints KV s\u00f3 faz sentido em cen\u00e1rios de \"carga em massa\" e em tr\u00e1fego muito irregular, em que o \"tubo\" de um soquete \u00e9 muito pequeno. Se isso n\u00e3o for avaliado adequadamente, tamb\u00e9m pode ser que a adi\u00e7\u00e3o de mais soquetes \u00e0 camada de KV possa degradar seu desempenho em vez de melhor\u00e1-lo - acho que mais nem sempre \u00e9 melhor.<\/p>\n<p>A l\u00f3gica de agrupamento pode ser encontrada <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/blob\/master\/src\/main\/java\/com\/couchbase\/client\/core\/service\/PooledService.java\">aqui<\/a> se voc\u00ea estiver curioso, mas vale a pena examinar certas sem\u00e2nticas ali.<\/p>\n<p>Durante a fase de conex\u00e3o do servi\u00e7o, ele garante que o n\u00famero m\u00ednimo de pontos de extremidade seja estabelecido antecipadamente. Se o m\u00ednimo for igual ao m\u00e1ximo, o pooling din\u00e2mico ser\u00e1 efetivamente desativado e o c\u00f3digo escolher\u00e1 um dos endpoints para cada solicita\u00e7\u00e3o:<\/p>\n<pre class=\"lang:java decode:true\">synchronized (epMutex) {\r\n    int numToConnect = minEndpoints - endpoints.size();\r\n    if (numToConnect == 0) {\r\n        LOGGER.debug(\"No endpoints needed to connect, skipping.\");\r\n        return Observable.just(state());\r\n    }\r\n    for (int i = 0; i &lt; numToConnect; i++) {\r\n        Endpoint endpoint = endpointFactory.create(hostname, bucket, username, password, port, ctx);\r\n        endpoints.add(endpoint);\r\n        endpointStates.register(endpoint, endpoint);\r\n    }\r\n\r\n    LOGGER.debug(logIdent(hostname, PooledService.this)\r\n            + \"New number of endpoints is {}\", endpoints.size());\r\n}<\/pre>\n<p>Isso pode ser observado nos registros imediatamente durante o bootstrap:<\/p>\n<pre class=\"lang:default decode:true\">[cb-computations-5] 2018-06-28 14:03:34 DEBUG Service:257 - [localhost][KeyValueService]: New number of endpoints is 1\r\n[cb-computations-8] 2018-06-28 14:03:35 DEBUG Service:248 - [localhost][QueryService]: No endpoints needed to connect, skipping.<\/pre>\n<p>Quando uma solicita\u00e7\u00e3o chega, ela \u00e9 despachada ou, se outro endpoint precisar ser criado (ainda h\u00e1 espa\u00e7o no pool), isso tamb\u00e9m \u00e9 tratado (de forma ligeiramente simplificada):<\/p>\n<pre class=\"lang:java decode:true\">@Override\r\npublic void send(final CouchbaseRequest request) {\r\n    Endpoint endpoint = endpoints.size() &gt; 0 ? selectionStrategy.select(request, endpoints) : null;\r\n\r\n    if (endpoint == null) {\r\n        if (fixedEndpoints || (endpoints.size() &gt;= maxEndpoints)) {\r\n            RetryHelper.retryOrCancel(env, request, responseBuffer);\r\n        } else {\r\n            maybeOpenAndSend(request);\r\n        }\r\n    } else {\r\n        endpoint.send(request);\r\n    }\r\n}<\/pre>\n<p>Observe que, se n\u00e3o conseguirmos encontrar um endpoint adequado e o pool for fixo ou se tivermos atingido nosso limite m\u00e1ximo, a opera\u00e7\u00e3o ser\u00e1 agendada para nova tentativa, de forma muito semelhante \u00e0 l\u00f3gica do endpoint quando ele n\u00e3o estiver ativo ou grav\u00e1vel.<\/p>\n<p>Nos servi\u00e7os baseados em HTTP em pool, n\u00e3o queremos manter esses soquetes para sempre, portanto, voc\u00ea pode configurar um tempo ocioso (que \u00e9 de 300s por padr\u00e3o). Cada pool executa um cron\u00f4metro de inatividade que examina regularmente os pontos de extremidade para ver se eles ficaram ociosos por mais tempo do que o intervalo configurado e, se for o caso, desconecta-os. Observe que a l\u00f3gica sempre garante que n\u00e3o fiquemos abaixo do n\u00famero m\u00ednimo.<\/p>\n<h2>Erros comuns relacionados \u00e0 conex\u00e3o<\/h2>\n<p>Agora que voc\u00ea tem uma boa ideia de como o SDK lida com soquetes e os agrupa, vamos falar sobre alguns cen\u00e1rios de erro que podem ocorrer.<\/p>\n<h3>Solicita\u00e7\u00e3o de cancelamento<\/h3>\n<p>Vamos falar sobre o <span class=\"lang:default decode:true crayon-inline\">RequestCancelledException<\/span>\u00a0 Primeiro.<\/p>\n<p>Se voc\u00ea estiver executando uma opera\u00e7\u00e3o e ela falhar com um <span class=\"lang:default decode:true crayon-inline\">RequestCancelledException<\/span>\u00a0 geralmente h\u00e1 duas causas diferentes:<\/p>\n<ul>\n<li>A opera\u00e7\u00e3o circulou dentro do cliente (sem ser enviada pela rede) por mais tempo do que o configurado. <span class=\"lang:default decode:true crayon-inline\">maxRequestLifetime<\/span>\u00a0.<\/li>\n<li>Uma solicita\u00e7\u00e3o foi gravada na rede, mas antes de recebermos uma resposta, o canal subjacente foi fechado.<\/li>\n<\/ul>\n<p>H\u00e1 outros motivos menos comuns (por exemplo, problemas durante a codifica\u00e7\u00e3o de uma solicita\u00e7\u00e3o), mas, para fins deste blog, vamos nos concentrar na segunda causa.<\/p>\n<p>Ent\u00e3o, por que temos que cancelar a solicita\u00e7\u00e3o e n\u00e3o tentar novamente em outro soquete que ainda esteja ativo? O motivo \u00e9 que n\u00e3o sabemos se a opera\u00e7\u00e3o j\u00e1 causou um efeito colateral no servidor (por exemplo, uma muta\u00e7\u00e3o aplicada). Se tent\u00e1ssemos novamente opera\u00e7\u00f5es n\u00e3o idempotentes, haveria efeitos estranhos que seriam dif\u00edceis de diagnosticar na pr\u00e1tica. Em vez disso, informamos ao chamador que a solicita\u00e7\u00e3o falhou e, em seguida, cabe \u00e0 l\u00f3gica do aplicativo descobrir o que fazer em seguida. Se foi uma simples solicita\u00e7\u00e3o de obten\u00e7\u00e3o e voc\u00ea ainda estiver no tempo limite, poder\u00e1 tentar novamente por conta pr\u00f3pria. Se for uma muta\u00e7\u00e3o, voc\u00ea precisar\u00e1 implementar mais alguma l\u00f3gica para ler o documento e descobrir se ele foi aplicado ou se ele pode ser enviado novamente imediatamente. E sempre h\u00e1 a op\u00e7\u00e3o de propagar o erro de volta para o chamador de sua API. De qualquer forma, isso \u00e9 previs\u00edvel do lado do SDK e n\u00e3o causar\u00e1 mais nenhum dano em segundo plano.<\/p>\n<h3>Problemas com o Bootstrap<\/h3>\n<p>A outra fonte de erros que vale a pena conhecer s\u00e3o os problemas durante a fase de conex\u00e3o do soquete. Normalmente, voc\u00ea encontrar\u00e1 erros descritivos nos logs que informam o que est\u00e1 acontecendo (por exemplo, credenciais erradas), mas h\u00e1 dois que podem ser um pouco mais dif\u00edceis de decifrar: O tempo limite de prote\u00e7\u00e3o da conex\u00e3o e os erros de sele\u00e7\u00e3o de bucket durante o rebalanceamento.<\/p>\n<p>Como voc\u00ea viu anteriormente, o pipeline do KV cont\u00e9m muitos manipuladores que trabalham com o servidor durante a inicializa\u00e7\u00e3o para descobrir todos os tipos de configura\u00e7\u00f5es e negociar os recursos suportados. No momento em que escrevo, cada opera\u00e7\u00e3o individual n\u00e3o tem um tempo limite individual, mas, em vez disso, o tempo limite de prote\u00e7\u00e3o da conex\u00e3o \u00e9 acionado se demorar mais do que o permitido para a fase de conex\u00e3o em termos de or\u00e7amento total.<\/p>\n<p>Portanto, se voc\u00ea vir o <span class=\"lang:default decode:true crayon-inline\">ConnectTimeoutException<\/span>\u00a0 nos registros com a mensagem <span class=\"lang:default decode:true crayon-inline\">Connect callback did not return, hit safeguarding timeout.<\/span>\u00a0 O que isso significa \u00e9 que uma opera\u00e7\u00e3o ou a soma de todas elas demorou mais do que o or\u00e7amento previsto e outra tentativa de reconex\u00e3o ser\u00e1 executada. Em geral, isso n\u00e3o \u00e9 prejudicial, pois vamos nos reconectar, mas \u00e9 uma boa indica\u00e7\u00e3o de que pode haver alguma lentid\u00e3o na rede ou em algum outro ponto da pilha que deve ser analisado com mais cuidado. Uma boa pr\u00f3xima etapa seria iniciar <a href=\"https:\/\/www.wireshark.org\/\">wireshark<\/a> \/ <a href=\"https:\/\/www.tcpdump.org\/\">tcpdump<\/a> e registre as fases de bootstrap para descobrir onde o tempo \u00e9 gasto e, em seguida, passe para o lado do cliente ou do servidor, dependendo dos tempos registrados. Por padr\u00e3o, o tempo limite de prote\u00e7\u00e3o \u00e9 configurado como o valor <span class=\"lang:default decode:true crayon-inline\">socketConnectTimeout<\/span>\u00a0 mais o <span class=\"lang:default decode:true crayon-inline\">connectCallbackGracePeriod<\/span>\u00a0 que est\u00e1 definido para 2 segundos e pode ser ajustado por meio do <span class=\"lang:default decode:true crayon-inline\">com.couchbase.connectCallbackGracePeriod<\/span>\u00a0 propriedade do sistema.<\/p>\n<p>Uma das etapas durante a inicializa\u00e7\u00e3o, desde que adicionamos suporte ao RBAC (controle de acesso baseado em fun\u00e7\u00e3o), \u00e9 chamada de \"select bucket\" por meio do <span class=\"lang:default decode:true crayon-inline\">KeyValueSelectBucketHandler<\/span>\u00a0. Como h\u00e1 uma desconex\u00e3o entre a autentica\u00e7\u00e3o e o acesso a um bucket, \u00e9 poss\u00edvel que o cliente se conecte a um servi\u00e7o de KV, mas o mecanismo de KV em si ainda n\u00e3o esteja pronto para atend\u00ea-lo. O cliente lidar\u00e1 com a situa\u00e7\u00e3o e tentar\u00e1 novamente - e nenhum impacto em uma carga de trabalho real ser\u00e1 observado - mas, como a higiene do registro tamb\u00e9m \u00e9 uma preocupa\u00e7\u00e3o, estamos atualmente aprimorando o algoritmo do SDK aqui. Se quiser, voc\u00ea pode acompanhar o progresso em<a href=\"https:\/\/issues.couchbase.com\/browse\/JVMCBC-553\"> JVMCBC-553<\/a>.<\/p>\n<h2>Considera\u00e7\u00f5es finais<\/h2>\n<p>A esta altura, voc\u00ea j\u00e1 deve ter uma s\u00f3lida compreens\u00e3o de como o SDK gerencia seus soquetes subjacentes e os agrupa na camada de servi\u00e7o. Se voc\u00ea quiser se aprofundar na base de c\u00f3digo, comece com <a href=\"https:\/\/github.com\/couchbase\/couchbase-jvm-core\/tree\/master\/src\/main\/java\/com\/couchbase\/client\/core\">aqui<\/a>\u00a0e, em seguida, examine os respectivos namespaces para <span class=\"lang:default decode:true crayon-inline\">servi\u00e7o<\/span>\u00a0 e <span class=\"lang:default decode:true crayon-inline\">ponto final<\/span>\u00a0. Todos os manipuladores de canal do Netty est\u00e3o abaixo do <span class=\"lang:default decode:true crayon-inline\">ponto final<\/span>\u00a0 tamb\u00e9m.<\/p>\n<p>Se voc\u00ea tiver outras d\u00favidas, comente abaixo! A pr\u00f3xima postagem discutir\u00e1 o modelo geral de threading do SDK.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you [&hellip;]<\/p>","protected":false},"author":19,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1821,1818,2201],"tags":[],"ppma_author":[8987],"class_list":["post-5432","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-couchbase-architecture","category-java","category-tools-sdks"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Inside the Java SDK: Connection Management - The Couchbase Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/inside-the-java-sdk-connection-management\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Inside the Java SDK: Connection Management\" \/>\n<meta property=\"og:description\" content=\"In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/inside-the-java-sdk-connection-management\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-06-29T08:23:36+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/11\/couchbase-nosql-dbaas.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1800\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Michael Nitschinger\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daschl\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Michael Nitschinger\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\"},\"author\":{\"name\":\"Michael Nitschinger\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d\"},\"headline\":\"Inside the Java SDK: Connection Management\",\"datePublished\":\"2018-06-29T08:23:36+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\"},\"wordCount\":2235,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"articleSection\":[\"Couchbase Architecture\",\"Java\",\"Tools &amp; SDKs\"],\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\",\"name\":\"Inside the Java SDK: Connection Management - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2018-06-29T08:23:36+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Inside the Java SDK: Connection Management\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d\",\"name\":\"Michael Nitschinger\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/95e178617974d46e3b02dd1754a3f60b\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g\",\"caption\":\"Michael Nitschinger\"},\"description\":\"Michael Nitschinger works as a Principal Software Engineer at Couchbase. He is the architect and maintainer of the Couchbase Java SDK, one of the first completely reactive database drivers on the JVM. He also authored and maintains the Couchbase Spark Connector. Michael is active in the open source community, a contributor to various other projects like RxJava and Netty.\",\"sameAs\":[\"https:\/\/nitschinger.at\",\"https:\/\/x.com\/daschl\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/pt\/author\/michael-nitschinger\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Inside the Java SDK: Connection Management - The Couchbase Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/pt\/inside-the-java-sdk-connection-management\/","og_locale":"pt_BR","og_type":"article","og_title":"Inside the Java SDK: Connection Management","og_description":"In this second instalment of &#8220;Inside the Java SDK&#8221; we are going to take an in-depth look at how the SDK manages and pools sockets to the various nodes and services. While not ultimately necessary to follow, I recommend you [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/inside-the-java-sdk-connection-management\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-06-29T08:23:36+00:00","og_image":[{"width":1800,"height":630,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/11\/couchbase-nosql-dbaas.png","type":"image\/png"}],"author":"Michael Nitschinger","twitter_card":"summary_large_image","twitter_creator":"@daschl","twitter_misc":{"Written by":"Michael Nitschinger","Est. reading time":"13 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/"},"author":{"name":"Michael Nitschinger","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d"},"headline":"Inside the Java SDK: Connection Management","datePublished":"2018-06-29T08:23:36+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/"},"wordCount":2235,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","articleSection":["Couchbase Architecture","Java","Tools &amp; SDKs"],"inLanguage":"pt-BR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","url":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/","name":"Inside the Java SDK: Connection Management - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2018-06-29T08:23:36+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/inside-the-java-sdk-connection-management\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Inside the Java SDK: Connection Management"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"Blog do Couchbase","description":"Couchbase, o banco de dados NoSQL","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"Blog do Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/e5d4d332756da6f361dd88c1576de61d","name":"Michael Nitschinger","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/95e178617974d46e3b02dd1754a3f60b","url":"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g","caption":"Michael Nitschinger"},"description":"Michael Nitschinger trabalha como engenheiro de software principal na Couchbase. Ele \u00e9 o arquiteto e mantenedor do Couchbase Java SDK, um dos primeiros drivers de banco de dados totalmente reativos na JVM. Ele tamb\u00e9m \u00e9 o autor e mantenedor do Couchbase Spark Connector. Michael participa ativamente da comunidade de c\u00f3digo aberto e contribui para v\u00e1rios outros projetos, como RxJava e Netty.","sameAs":["https:\/\/nitschinger.at","https:\/\/x.com\/daschl"],"url":"https:\/\/www.couchbase.com\/blog\/pt\/author\/michael-nitschinger\/"}]}},"authors":[{"term_id":8987,"user_id":19,"is_guest":0,"slug":"michael-nitschinger","display_name":"Michael Nitschinger","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/dad99b5e02a74ca4bec14352e9da710160647a97290814b669babb3aac0ea675?s=96&d=mm&r=g","author_category":"","last_name":"Nitschinger, Principal Software Engineer, Couchbase","first_name":"Michael","job_title":"","user_url":"https:\/\/nitschinger.at","description":"Michael Nitschinger trabalha como engenheiro de software principal na Couchbase. Ele \u00e9 o arquiteto e mantenedor do Couchbase Java SDK, um dos primeiros drivers de banco de dados totalmente reativos na JVM. Ele tamb\u00e9m \u00e9 o autor e mantenedor do Couchbase Spark Connector. Michael participa ativamente da comunidade de c\u00f3digo aberto e contribui para v\u00e1rios outros projetos, como RxJava e Netty."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/5432","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/users\/19"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=5432"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/5432\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=5432"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=5432"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=5432"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=5432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}