Mit dem Cassandra-Adapter eine Verbindung zu Spanner herstellen

Auf dieser Seite wird der Cassandra-Adapter beschrieben und erklärt, wie Sie Spanner damit verwenden und eine Verbindung zu Spanner herstellen.

Der Cassandra-Adapter ist so konzipiert, dass er auf demselben Computer wie Ihre Anwendung ausgeführt wird. Der Adapter stellt einen Endpunkt auf „localhost“ bereit, der das CQL-Wire Protocol (Cassandra Query Language) unterstützt. Er übersetzt das CQL-Wire Protocol in gRPC, das Spanner-Wire Protocol. Wenn dieser Proxy lokal ausgeführt wird, kann ein Cassandra-Client eine Verbindung zu einer Spanner-Datenbank herstellen.

Sie können den Cassandra-Adapter auf folgende Arten starten:

  • In-process mit Ihrer Go-Anwendung
  • In-process mit Ihrer Java-Anwendung
  • Als eigenständiger Prozess
  • In einem Docker-Container

Hinweis

Bevor Sie den Cassandra-Adapter starten, müssen Sie sich auf dem Computer, auf dem der Cassandra-Adapter ausgeführt wird, mit einem Nutzerkonto oder Dienstkonto authentifiziert haben. Wenn Sie ein Dienstkonto verwenden, müssen Sie den Speicherort der JSON-Schlüsseldatei (der Datei mit den Anmeldedaten) kennen. Legen Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS fest, um den Pfad zu den Anmeldedaten anzugeben. Bevor Sie den Cassandra-Adapter starten, müssen Sie sich auf dem Computer, auf dem der Cassandra-Adapter ausgeführt wird, mit einem Nutzerkonto oder Dienstkonto authentifiziert haben. Wenn Sie ein Dienstkonto verwenden, müssen Sie den Speicherort der JSON-Schlüsseldatei (der Datei mit den Anmeldedaten) kennen. Legen Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS fest, um den Pfad zu den Anmeldedaten anzugeben.

Weitere Informationen finden Sie unter:

Cassandra-Adapter mit Ihrer Anwendung verbinden

Für den Cassandra-Adapter sind folgende Informationen erforderlich:

  • Projektname
  • Name der Spanner-Instanz
  • Datenbank, zu der eine Verbindung hergestellt werden soll

Wenn Sie Docker verwenden, benötigen Sie den Pfad für eine JSON-formatierte Datei mit Anmeldedaten (Schlüsseldatei).

Java in-process

  1. Wenn Sie ein Dienstkonto für die Authentifizierung verwenden, muss die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS auf den Pfad der Datei mit den Anmeldedaten festgelegt sein.

  2. Bei Java-Anwendungen können Sie den Cassandra-Adapter direkt mit der Anwendung verknüpfen, indem Sie google-cloud-spanner-cassandra als Abhängigkeit zu Ihrem Projekt hinzufügen.

Fügen Sie für Maven die folgende neue Abhängigkeit im <dependencies> Abschnitt hinzu:

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-cassandra</artifactId>
    <version>1.2.0</version>
</dependency>

Fügen Sie für Gradle Folgendes hinzu:

dependencies {
    implementation 'com.google.cloud:google-cloud-spanner-cassandra:1.2.0'
}

  1. Ändern Sie den Code zum Erstellen von CqlSession. Verwenden Sie anstelle von CqlSessionBuilder, verwenden Sie SpannerCqlSessionBuilder und geben Sie den Spanner-Datenbank-URI an:
    
    import com.datastax.oss.driver.api.core.CqlSession;
    import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
    import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
    import com.datastax.oss.driver.api.core.cql.ResultSet;
    import com.datastax.oss.driver.api.core.cql.Row;
    import com.google.cloud.spanner.adapter.SpannerCqlSession;
    import java.net.InetSocketAddress;
    import java.time.Duration;
    import java.util.Random;
    
    // This sample assumes your spanner database <my_db> contains a table <users>
    // with the following schema:
    
    // CREATE TABLE users (
    //  id        INT64          OPTIONS (cassandra_type = 'int'),
    //  active    BOOL           OPTIONS (cassandra_type = 'boolean'),
    //  username  STRING(MAX)    OPTIONS (cassandra_type = 'text'),
    // ) PRIMARY KEY (id);
    
    class QuickStartSample {
    
      public static void main(String[] args) {
    
        // TODO(developer): Replace these variables before running the sample.
        final String projectId = "my-gcp-project";
        final String instanceId = "my-spanner-instance";
        final String databaseId = "my_db";
    
        final String databaseUri =
            String.format("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseId);
    
        try (CqlSession session =
            SpannerCqlSession.builder() // `SpannerCqlSession` instead of `CqlSession`
                .setDatabaseUri(databaseUri) // Set spanner database URI.
                .addContactPoint(new InetSocketAddress("localhost", 9042))
                .withLocalDatacenter("datacenter1")
                .withKeyspace(databaseId) // Keyspace name should be the same as spanner database name
                .withConfigLoader(
                    DriverConfigLoader.programmaticBuilder()
                        .withString(DefaultDriverOption.PROTOCOL_VERSION, "V4")
                        .withDuration(
                            DefaultDriverOption.CONNECTION_INIT_QUERY_TIMEOUT, Duration.ofSeconds(5))
                        .build())
                .build()) {
    
          final int randomUserId = new Random().nextInt(Integer.MAX_VALUE);
    
          System.out.printf("Inserting user with ID: %d%n", randomUserId);
    
          // INSERT data
          session.execute(
              "INSERT INTO users (id, active, username) VALUES (?, ?, ?)",
              randomUserId,
              true,
              "John Doe");
    
          System.out.printf("Successfully inserted user: %d%n", randomUserId);
          System.out.printf("Querying user: %d%n", randomUserId);
    
          // SELECT data
          ResultSet rs =
              session.execute("SELECT id, active, username FROM users WHERE id = ?", randomUserId);
    
          // Get the first row from the result set
          Row row = rs.one();
    
          System.out.printf(
              "%d %b %s%n", row.getInt("id"), row.getBoolean("active"), row.getString("username"));
    
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    

Go in-process

Bei Go-Anwendungen müssen Sie eine einzeilige Änderung an der Clusterinitialisierungsdatei vornehmen, um den Spanner Cassandra Go-Client zu integrieren. Anschließend können Sie den Cassandra-Adapter direkt mit der Anwendung verknüpfen.

  1. Importieren Sie das spanner-Paket des Adapters aus dem Spanner Cassandra Go-Client in Ihre Go-Anwendung.
import spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
  1. Ändern Sie den Code zum Erstellen des Clusters, um spanner.NewCluster anstelle von gocql.NewCluster zu verwenden, und geben Sie den Spanner-Datenbank-URI an:
    import (
    	"fmt"
    	"io"
    	"math"
    	"math/rand/v2"
    	"time"
    
    	spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
    )
    
    // This sample assumes your spanner database <your_db> contains a table <users>
    // with the following schema:
    //
    // CREATE TABLE users (
    //	id   	 	INT64          OPTIONS (cassandra_type = 'int'),
    //	active    	BOOL           OPTIONS (cassandra_type = 'boolean'),
    //	username  	STRING(MAX)    OPTIONS (cassandra_type = 'text'),
    // ) PRIMARY KEY (id);
    
    func quickStart(databaseURI string, w io.Writer) error {
    	opts := &spanner.Options{
    		DatabaseUri: databaseURI,
    	}
    	cluster := spanner.NewCluster(opts)
    	if cluster == nil {
    		return fmt.Errorf("failed to create cluster")
    	}
    	defer spanner.CloseCluster(cluster)
    
    	// You can still configure your cluster as usual after connecting to your
    	// spanner database
    	cluster.Timeout = 5 * time.Second
    	cluster.Keyspace = "your_db_name"
    
    	session, err := cluster.CreateSession()
    
    	if err != nil {
    		return err
    	}
    
    	randomUserId := rand.IntN(math.MaxInt32)
    	if err = session.Query("INSERT INTO users (id, active, username) VALUES (?, ?, ?)",
    			       randomUserId, true, "John Doe").
    		Exec(); err != nil {
    		return err
    	}
    
    	var id int
    	var active bool
    	var username string
    	if err = session.Query("SELECT id, active, username FROM users WHERE id = ?",
    			       randomUserId).
    		Scan(&id, &active, &username); err != nil {
    		return err
    	}
    	fmt.Fprintf(w, "%d %v %s\n", id, active, username)
    	return nil
    }

Sie können Ihren Cluster wie gewohnt konfigurieren, nachdem Sie eine Verbindung zu Ihrer Spanner-Datenbank hergestellt haben.

Eigenständig

  1. Klonen Sie das Repository:
git clone https://github.com/googleapis/go-spanner-cassandra.git
cd go-spanner-cassandra
  1. Führen Sie cassandra_launcher.go mit dem erforderlichen Flag -db aus:
go run cassandra_launcher.go \
-db "projects/my_project/instances/my_instance/databases/my_database"
  1. Ersetzen Sie -db durch den URI Ihrer Spanner-Datenbank.

Docker

Starten Sie den Cassandra-Adapter mit dem folgenden Befehl.

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
docker run -d -p 9042:9042 \
-e GOOGLE_APPLICATION_CREDENTIALS \
-v ${GOOGLE_APPLICATION_CREDENTIALS}:${GOOGLE_APPLICATION_CREDENTIALS}:ro \
gcr.io/cloud-spanner-adapter/cassandra-adapter \
-db DATABASE_URI

Die folgende Liste enthält die am häufigsten verwendeten Startoptionen für den Spanner Cassandra-Adapter:

  • -db <DatabaseUri>

Der Spanner-Datenbank-URI (erforderlich). Hiermit wird die Spanner-Datenbank angegeben, mit der der Client eine Verbindung herstellt. Beispiel: projects/YOUR_PROJECT/instances/YOUR_INSTANCE/databases/YOUR_DATABASE.

  • -tcp <TCPEndpoint>

Die Adresse des Client-Proxy-Listeners. Hiermit wird der TCP-Endpunkt definiert, an dem der Client auf eingehende Cassandra-Clientverbindungen wartet. Standard:localhost:9042

  • -grpc-channels <NumGrpcChannels>

Die Anzahl der gRPC-Kanäle, die beim Herstellen einer Verbindung zu Spanner verwendet werden sollen. Standard: 4

Mit dem folgenden Befehl wird beispielsweise der Cassandra-Adapter auf Port 9042 mit den Anmeldedaten der Anwendung gestartet und der Adapter mit der Datenbank projects/my_project/instances/my_instance/databases/my_database verbunden:

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
docker run -d -p 9042:9042 \
-e GOOGLE_APPLICATION_CREDENTIALS \
-v ${GOOGLE_APPLICATION_CREDENTIALS}:${GOOGLE_APPLICATION_CREDENTIALS}:ro \
gcr.io/cloud-spanner-adapter/cassandra-adapter \
-db projects/my_project/instances/my_instance/databases/my_database

Empfehlungen

Die folgenden Empfehlungen helfen Ihnen, die Nutzung des Cassandra-Adapters zu verbessern. Diese Empfehlungen beziehen sich auf Java, insbesondere auf den Cassandra-Clienttreiber für Java Version 4.

Timeout für Anfragen erhöhen

Ein Timeout für Anfragen von mindestens fünf Sekunden bietet eine bessere Nutzung des Cassandra-Adapters als der Standardwert von zwei Sekunden.

# Sample application.conf: increases request timeout to five seconds
datastax-java-driver {
  basic {
    request {
      timeout = 5 seconds
    }
  }
}

Verbindungs-Pooling optimieren

Die Standardkonfigurationen für die maximale Anzahl von Verbindungen und die maximale Anzahl gleichzeitiger Anfragen pro Verbindung oder Host sind für Entwicklungs-, Test- und Produktions- oder Staging-Umgebungen mit geringem Volumen geeignet. Wir empfehlen jedoch, diese Werte zu erhöhen, da der Cassandra-Adapter im Gegensatz zu einem Pool von Knoten in einem Cassandra-Cluster als einzelner Knoten auftritt.

Wenn Sie diese Werte erhöhen, sind mehr gleichzeitige Verbindungen zwischen dem Client und der Cassandra-Schnittstelle möglich. So kann eine Überlastung des Verbindungspools bei hoher Last verhindert werden.

# Sample application.conf: increases maximum number of requests that can be
# executed concurrently on a connection
advanced.connection {
  max-requests-per-connection = 32000
  pool {
    local.size = 10
  }
}

gRPC-Kanäle optimieren

gRPC-Kanäle werden vom Spanner-Client für die Kommunikation verwendet. Ein gRPC-Kanal entspricht in etwa einer TCP-Verbindung. Ein gRPC-Kanal kann bis zu 100 gleichzeitige Anfragen verarbeiten. Das bedeutet, dass eine Anwendung mindestens so viele gRPC-Kanäle benötigt wie die Anzahl der gleichzeitigen Anfragen, die die Anwendung ausführt, geteilt durch 100.

Token-basierte Weiterleitung deaktivieren

Bei Treibern, die einen Token-basierten Lastenausgleich verwenden, wird möglicherweise eine Warnung ausgegeben oder sie funktionieren nicht, wenn der Cassandra-Adapter verwendet wird. Da der Cassandra-Adapter als einzelner Knoten auftritt, funktioniert er nicht immer gut mit Token-basierten Treibern, die mindestens so viele Knoten im Cluster erwarten, wie der Replikationsfaktor angibt. Einige Treiber geben möglicherweise eine Warnung aus (die ignoriert werden kann) und greifen auf eine Round-Robin-Lastenausgleichsrichtlinie zurück, während andere Treiber mit einem Fehler fehlschlagen. Bei den Treibern, die mit einem Fehler fehlschlagen, müssen Sie die Token-basierte Weiterleitung deaktivieren oder die Round-Robin-Lastenausgleichsrichtlinie konfigurieren.

# Sample application.conf: disables token-aware routing
metadata {
  token-map {
    enabled = false
  }
}

Protokollversion auf V4 festlegen

Der Cassandra-Adapter ist mit jedem CQL Binary v4 wire protocol kompatiblen Open-Source-Apache Cassandra-Clienttreiber kompatibel. Legen Sie PROTOCOL_VERSION auf V4 fest, da sonst möglicherweise Verbindungsfehler auftreten.

# Sample application.conf: overrides protocol version to V4
datastax-java-driver {
  advanced.protocol.version = V4
}

Nächste Schritte