Skip to content

Latest commit

 

History

History
390 lines (271 loc) · 18.6 KB

File metadata and controls

390 lines (271 loc) · 18.6 KB

Managing Connections

project-docs:partial$attributes.adoc

This section describes how to connect the Java SDK to a Couchbase cluster. It contains best practices as well as information on TLS/SSL and other advanced connection options.

Connecting to a Cluster

A connection to a Couchbase Server cluster is represented by a Cluster object. A Cluster provides access to Buckets, Scopes, and Collections, as well as various Couchbase services and management interfaces. The simplest way to create a Cluster object is to call Cluster.connect() with a connection string, username, and password:

link:devguide:example$java/ManagingConnections.java[role=include]
Note
If you are connecting to a version of Couchbase Server older than 6.5, it will be more efficient if the addresses are those of data (KV) nodes. You will in any case, with 7.0 and earlier, need to open a Bucket instance before connecting to any other HTTP services (such as Query or Search).

In a production environment, your connection string should include the addresses of multiple server nodes in case some are currently unavailable. Multiple addresses may be specified in a connection string by delimiting them with commas:

link:devguide:example$java/ManagingConnections.java[role=include]
Tip
You don’t need to include the address of every node in the cluster. The client fetches the full address list from the first node it is able to contact.

Cluster Environment

A ClusterEnvironment manages shared resources like thread pools, timers, and schedulers. It also holds the client settings. You can customize the client’s behavior by providing a callback that modifies a ClusterEnvironment.Builder:

link:devguide:example$java/ManagingConnections.java[role=include]
Tip
When you customize the environment using a callback like in the above example, the client creates an environment that is managed completely by the Java SDK. This environment is automatically shut down when the associated Cluster is disconnected.

Connection Strings

A Couchbase connection string is a comma-delimited list of IP addresses and/or hostnames, optionally followed by a list of parameters.

The parameter list is just like the query component of a URI; name-value pairs have an equals sign (=) separating the name and value, with an ampersand (&) between each pair. Just as in a URI, the first parameter is prefixed by a question mark (?).

Simple connection string with one seed node
127.0.0.1
Connection string with two seed nodes
nodeA.example.com,nodeB.example.com
Connection string with two parameters
127.0.0.1?io.networkResolution=external&timeout.kvTimeout=10s

The full list of recognized parameters is documented in the client settings reference. Any client setting with a system property name may also be specified as a connection string parameter (without the com.couchbase.env. prefix).

Warning
When creating a Cluster using a custom ClusterEnvironment, connection string parameters are ignored, since client settings are frozen when the cluster environment is built.

Connection Lifecycle

Most of the high-level classes in the Java SDK are designed to be safe for concurrent use by multiple threads. You will get the best performance if you share and reuse instances of ClusterEnvironment, Cluster, Bucket, Scope, and Collection, all of which are thread-safe.

We recommend creating a single Cluster instance when your application starts up, and sharing this instance throughout your application. If you know at startup time which buckets, scopes, and CollectionsExample your application will use, we recommend obtaining them from the Cluster at startup time and sharing those instances throughout your application as well.

Before your application stops, gracefully shut down the client by calling the disconnect() method of each Cluster you created. If you created any ClusterEnvironment instances, call their shutdown() method after disconnecting the associated clusters.

Connecting to Multiple Clusters

If a single application needs to connect to multiple Couchbase Server clusters, it is possible to reduce the SDK’s resource consumption by creating a single ClusterEnvironment and sharing it between the Clusters:

link:devguide:example$java/ManagingConnections.java[role=include]
Warning

If you manually create a ClusterEnvironment, be aware of these limitations:

  • A manually created ClusterEnvironment cannot be further customized by connection string parameters or Java system properties.

  • When you manually create a ClusterEnvironment, the SDK will not shut it down when you call Cluster.disconnect(). Instead, you are responsible for shutting it down after disconnecting all clusters that share the environment.

Tip
When connecting to a single cluster, it’s usually better to customize the ClusterEnvironment by providing a configuration callback. That way, the SDK automatically shuts down the environment for you, and you can customize the environment with connection string parameters and Java system properties.

Alternate Addresses and Custom Ports

If your Couchbase Server cluster is running in a containerized, port mapped, or otherwise NAT’d environment like Docker or Kubernetes, a client running outside that environment may need additional information in order to connect the cluster. Both the client and server require special configuration in this case.

On the server side, each server node must be configured to advertise its external address as well as any custom port mapping. This is done with the setting-alternate-address CLI command introduced in Couchbase Server 6.5. A node configured in this way will advertise two addresses: one for connecting from the same network, and another for connecting from an external network.

On the client side, the externally visible ports must be used when connecting. If the external ports are not the default, you can specify custom ports in the connection string when calling Cluster.connect().

link:devguide:example$java/managing_connections.java[role=include]

For nodes that do not have the KV service, you can connect using a custom manager port instead. Notice how =manager appears after the port number, identifying it as a manager port:

link:devguide:example$java/managing_connections.java[role=include]
Tip
In a deployment that uses multi-dimensional scaling, a custom KV port is only applicable for nodes running the KV service. A custom manager port may be specified regardless of which services are running on the node.

In many cases the client is able to automatically select the correct set of addresses to use when connecting to a cluster that advertises multiple addresses. If the detection heuristic fails in your environment, you can override it by setting the io.networkResolution client setting to default if the client and server are on the same network, or external if they’re on different networks.

Note
Any TLS certificates must be set up at the point where the connections are being made.

Secure Connections

Couchbase Server Enterprise Edition and Couchbase Capella support full encryption of client-side traffic using Transport Layer Security (TLS). That includes key-value type operations, queries, and configuration communication. Make sure you have the Enterprise Edition of Couchbase Server, or a Couchbase Capella account, before proceeding with configuring encryption on the client side.

Couchbase Capella

The Java SDK bundles Capella’s standard root certificate by default. This means you don’t need any additional configuration to enable TLS — simply use couchbases:// in your connection string.

Note
Capella’s root certificate is not signed by a well known CA (Certificate Authority). However, as the certificate is bundled with the SDK, it is trusted by default.
Couchbase Server

As of SDK 3.4, if you connect to a Couchbase Server cluster with a root certificate issued by a trusted CA (Certificate Authority), you no longer need to configure this in the securityConfig settings.

The cluster’s root certificate just needs to be issued by a CA whose certificate is in the JVM’s trust store. This includes well known CAs (e.g., GoDaddy, Verisign, etc…​), plus any other CA certificates that you wish to add.

Tip
The JVM’s trust store is represented by a file named cacerts, which can be found inside your Java installation folder.

You can still provide a certificate explicitly if necessary:

  1. Get the CA certificate from the cluster and save it in a text file.

  2. Enable encryption on the client side and point it to the file containing the certificate.

It is important to make sure you are transferring the certificate in an encrypted manner from the server to the client side, so either copy it through SSH or through a similar secure mechanism.

If you are running on localhost and just want to enable TLS for a development machine, just copying and pasting it suffices — so long as you use 127.0.0.1 rather than localhost in the connection string. This is because the certificate will not match the name localhost.

Navigate in the admin UI to menu:Settings[Cluster] and copy the input box of the TLS certificate into a file on your machine (which we will refer to as cluster.cert). It looks similar to this:

-----BEGIN CERTIFICATE-----
MIICmDCCAYKgAwIBAgIIE4FSjsc3nyIwCwYJKoZIhvcNAQEFMAwxCjAIBgNVBAMT
ASowHhcNMTMwMTAxMDAwMDAwWhcNNDkxMjMxMjM1OTU5WjAMMQowCAYDVQQDEwEq
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzz2I3Gi1XcOCNRVYwY5R
................................................................
mgDnQI8nw2arBRoseLpF6WNw22CawxHVOlMceQaGOW9gqKNBN948EvJJ55Dhl7qG
BQp8sR0J6BsSc86jItQtK9eQWRg62+/XsgVCmDjrB5owHPz+vZPYhsMWixVhLjPJ
mkzeUUj/kschgQ0BWT+N+pyKAFFafjwFYtD0e5NwFUUBfsOyQtYV9xu3fw+T2N8S
itfGtmmlEfaplVGzGPaG0Eyr53g5g2BgQbi5l5Tt2awqhd22WOVbCalABd9t2IoI
F4+FjEqAEIr1mQepDaNM0gEfVcgd2SzGhC3yhYFBAH//8W4DUot5ciEhoBs=
-----END CERTIFICATE-----

The next step is to enable encryption and pass it the path to the certificate file.

link:devguide:example$java/ManagingConnections.java[role=include]

If you want to verify it’s actually working, you can use a tool like tcpdump. For example, an unencrypted upsert request looks like this (using sudo tcpdump -i lo0 -A -s 0 port 11210):

E..e..@.@.............+......q{...#..Y.....
.E...Ey........9........................id{"key":"value"}

After enabling encryption, you cannot inspect the traffic in cleartext (same upsert request, but watched on port 11207 which is the default encrypted port):

E.....@.@.............+....Z.'yZ..#........
..... ...xuG.O=.#.........?.Q)8..D...S.W.4.-#....@7...^.Gk.4.t..C+......6..)}......N..m..o.3...d.,.	...W.....U..
.%v.....4....m*...A.2I.1.&.*,6+..#..#.5

Quarkus Java Extension

Our Couchbase Quarkus Java Extension docs cover installing and connecting with the Quarkus extension in detail, but if you already have Quarkus installed and a project ready (with quarkus-couchbase in your pom.xml or build.gradle), then your src/main/resources/application.properties file needs to contain:

quarkus.couchbase.connection-string=localhost
quarkus.couchbase.username=username
quarkus.couchbase.password=password

Cloud Native Gateway

Couchbase’s next generation connection protocol, introduced in Java SDK 3.5 and Couchbase Autonomous Operator 2.6.1, can be enabled simply by changing the connection string to couchbase2:// but there are a few differences to be aware of, described below.

The protocol implements a gRPC-style interface between the SDK and Couchbase Server (in this case, only available in the Server running on Kubernetes or OpenShift, with a recent version of Couchbase Autonomous Operator).

Limitations

The underlying protocol will not work with certain legacy features: MapReduce Views (a deprecated Service — use Query instead) and Memcached buckets (superseded by the improved Ephemeral Buckets).

The following are not currently implemented over the couchbase2:// protocol:

  • Authentication by client certificate.

  • Multi-document ACID transactions.

  • Analytics service.

  • Health Check.

And the output from these features should be seen as volatile and subject to change:

  • Metrics and tracing, including the ThresholdLoggingTracer, LoggingMeter, and spans output.

There are some different behaviors seen with this protocol:

  • Some config options are unsupported — see the Settings page.

  • The SDK will poll the gRPC channels until they are in a good state, or return an error, or timeout while waiting — in our standard protocol there is an option of setting waitUntilReady() for just certain services to become available.

  • Some error codes are more generic — in cases where the client would not be expected to need to take specific action — but should cause no problem, unless you have written code looking at individual strings within the error messages.

  • Although documents continue to be stored compressed by Couchbase Server, they will not be transmitted in compressed form (to and from the client) over the wire, using couchbase2://.

{version-common}@sdk:shared:partial$dnssrv-pars.adoc

DNS SRV bootstrapping is enabled by default in the {name-sdk}. In order to make the SDK use the SRV records, you need to pass in the hostname from your records (here example.com):

link:devguide:example$java/ManagingConnections.java[role=include]

If the DNS SRV records could not be loaded properly you’ll get the exception logged and the given host name will be used as a A record lookup.

WARNING: DNS SRV lookup failed, proceeding with normal bootstrap.
javax.naming.NameNotFoundException: DNS name not found [response code 3];
   remaining name '_couchbase._tcp.example.com'
	at com.sun.jndi.dns.DnsClient.checkResponseCode(DnsClient.java:651)
	at com.sun.jndi.dns.DnsClient.isMatchResponse(DnsClient.java:569)

Also, if you pass in more than one node, DNS SRV bootstrap will not be initiated:

INFO: DNS SRV enabled, but less or more than one seed node given.
Proceeding with normal bootstrap.

Waiting for Bootstrap Completion

Depending on the environment and network latency, bootstrapping the SDK fully might take a little longer than the default key-value timeout of 2.5 seconds, so you may see timeouts during bootstrap. To prevent those early timeouts from happening, you can use the waitUntilReady method.

If you are working at the Cluster level, then add to the cluster() in the earlier example:

link:devguide:example$java/managing_connections.java[role=include]

Or more fully:

link:devguide:example$java/ClusterExample.java[role=include]

If you are working at the Bucket level, then the Bucket-level waitUntilReady does the same as the Cluster-level version, plus it waits for the K-V (data) sockets to be ready.

Other timeout issues may occur when using the SDK located geographically separately from the Couchbase Server cluster — this is not recommended. See the Cloud section below for some suggestions of settings adjustments.

{version-common}@sdk:shared:partial$managing-connections.adoc

Async and Reactive APIs

The Couchbase Java SDK provides first-class support for asynchronous and reactive programming. In fact, the synchronous API is just a thin wrapper around the asynchronous API.

Methods in the asynchronous API return instances of the standard CompletableFuture introduced in Java 8.

The Java SDK’s reactive API is built on Project Reactor. Reactor implements the standard Reactive Streams API introduced in Java 9, and extends it with a rich set of useful operators. Methods in the reactive API return instances of Flux or Mono.

If you wish to embrace the async or reactive programming model, there are two ways to get started:

  • Call async() or reactive() on the object to access its Async* or Reactive* counterpart. For example, if you have a Collection called myCollection, you can obtain a ReactiveCollection by calling myCollection.reactive().

  • Instantiate an AsyncCluster or ReactiveCluster in the first place. The bucket() method of an AsyncCluster returns an AsyncBucket, while the bucket() method of a ReactiveCluster turns a ReactiveBucket.

So if you are connecting to the bucket synchronously but then want to switch over to asynchronous data operations, you can do it like this:

link:devguide:example$java/ManagingConnections.java[role=include]

On the other hand, you can use the Async API right from the beginning:

link:devguide:example$java/ManagingConnections.java[role=include]

Here’s the same example, but using the Reactive API instead of the Async API:

link:devguide:example$java/ManagingConnections.java[role=include]