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 sich der Client verbindet. 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 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 Erfahrung mit dem Cassandra-Adapter 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 eignen sich für Entwicklungs-, Test- und Produktions- oder Staging-Umgebungen mit geringem Volumen. 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 Erschöpfung 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 eine Anzahl von Knoten im Cluster erwarten, die dem Replikationsfaktor entspricht. 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