Cassandra 어댑터를 사용하여 Spanner에 연결

이 페이지에서는 Cassandra 어댑터를 설명하고 이를 통해 Spanner를 사용하고 연결하는 방법을 설명합니다.

Cassandra 어댑터는 애플리케이션과 동일한 머신에서 실행되도록 설계되었습니다. 어댑터는 Cassandra Query Language(CQL) 유선 프로토콜을 지원하는 localhost에 엔드포인트를 노출합니다. 이는 CQL 유선 프로토콜을 Spanner 유선 프로토콜인 gRPC로 변환합니다. 이 프록시가 로컬로 실행되면 Cassandra 클라이언트를 Spanner 데이터베이스에 연결할 수 있습니다.

다음과 같은 방법으로 Cassandra 어댑터를 시작할 수 있습니다.

  • Go 애플리케이션을 사용한 In-process
  • Java 애플리케이션으로 처리 중
  • 독립형 프로세스로
  • Docker 컨테이너에서

시작하기 전에

Cassandra 어댑터를 시작하기 전에 Cassandra 어댑터가 실행될 머신에서 사용자 계정이나 서비스 계정으로 인증했는지 확인합니다. 서비스 계정을 사용하는 경우 JSON 키 파일(사용자 인증 정보 파일) 위치를 알아야 합니다. GOOGLE_APPLICATION_CREDENTIALS 환경 변수를 설정하여 사용자 인증 정보 경로를 지정합니다.

자세한 내용은 다음을 참고하세요.

애플리케이션에 Cassandra 어댑터 연결

Cassandra 어댑터를 사용하려면 다음 정보가 필요합니다.

  • 프로젝트 이름
  • Spanner 인스턴스 이름
  • 연결할 데이터베이스

Docker를 사용하는 경우 JSON 형식의 사용자 인증 정보 파일(키 파일)의 경로가 필요합니다.

Go In-process

Go 애플리케이션의 경우 Spanner Cassandra Go 클라이언트를 통합하려면 클러스터 초기화 파일을 한 줄만 변경하면 됩니다. 그러면 Cassandra 어댑터를 애플리케이션에 직접 연결할 수 있습니다.

  1. Go 애플리케이션의 Spanner Cassandra Go 클라이언트에서 어댑터의 spanner 패키지를 가져옵니다.

    import spanner "github.com/googleapis/go-spanner-cassandra/cassandra/gocql"
    
  2. gocql.NewCluster 대신 spanner.NewCluster를 사용하도록 클러스터 생성 코드를 수정하고 Spanner 데이터베이스 URI를 제공합니다.

    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
    }

    Spanner 데이터베이스에 연결한 후에도 평소와 같이 클러스터를 계속 구성할 수 있습니다.

Java In-process

  1. 서비스 계정을 인증에 사용하는 경우 GOOGLE_APPLICATION_CREDENTIALS 환경 변수가 사용자 인증 정보 파일의 경로로 설정되었는지 확인합니다.

  2. Java 애플리케이션의 경우 google-cloud-spanner-cassandra를 종속 항목으로 프로젝트에 추가하여 Cassandra 어댑터를 애플리케이션에 직접 연결할 수 있습니다.

    Maven의 경우 <dependencies> 섹션에 다음과 같은 새 종속 항목을 추가합니다.

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

    Gradle의 경우 다음을 추가합니다.

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

  3. CqlSession 생성 코드를 수정합니다. CqlSessionBuilder 대신 SpannerCqlSessionBuilder를 사용하고 Spanner 데이터베이스 URI를 제공합니다.

    
    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();
        }
      }
    }
    

독립형

  1. 저장소를 복제합니다.

     git clone https://github.com/googleapis/go-spanner-cassandra.git
     cd go-spanner-cassandra
    
  2. 필수 -db 플래그를 사용하여 cassandra_launcher.go를 실행합니다.

     go run cassandra_launcher.go \
     -db "projects/my_project/instances/my_instance/databases/my_database"
    

    -db를 Spanner 데이터베이스 URI로 바꿉니다.

Docker

다음 명령어를 사용하여 Cassandra 어댑터를 시작합니다.

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

다음 목록에는 Spanner Cassandra 어댑터에서 가장 자주 사용되는 시작 옵션이 포함되어 있습니다.

  • -db <DatabaseUri>

    • Spanner 데이터베이스 URI(필수)입니다. 클라이언트가 연결되는 Spanner 데이터베이스를 지정합니다. 예를 들면 projects/YOUR_PROJECT/instances/YOUR_INSTANCE/databases/YOUR_DATABASE입니다.
  • -tcp <TCPEndpoint>

    • 클라이언트 프록시 리스너 주소입니다. 이 주소는 클라이언트가 들어오는 Cassandra 클라이언트 연결을 리슨하는 TCP 엔드포인트를 정의합니다.
    • 기본값:
      • Go 언어 애플리케이션 내에서 In-process를 실행하는 경우: localhost:9042
      • 사이드카 프록시로 실행하는 경우: :9042는 모든 네트워크 인터페이스를 바인딩하므로 Docker 전달에 적합합니다.
  • -grpc-channels <NumGrpcChannels>

    • Spanner에 연결할 때 사용할 gRPC 채널 수입니다.
    • 기본값: 4

    예를 들어 다음 명령어는 애플리케이션 사용자 인증 정보를 사용하여 포트 9042에서 Cassandra 어댑터를 시작하고 어댑터를 projects/my_project/instances/my_instance/databases/my_database 데이터베이스에 연결합니다.

    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
    

추천

다음 추천은 Cassandra 어댑터 사용 환경을 개선하는 데 도움이 됩니다. 이러한 추천은 Java, 특히 Java 버전 4용 Cassandra 클라이언트 드라이버에 중점을 둡니다.

요청 제한 시간 늘리기

요청 시간 제한을 5초 이상으로 설정하면 기본값인 2초보다 더 나은 Cassandra 어댑터 사용 환경이 제공됩니다.

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

연결 풀링 조정

최대 연결 수 기본 구성과 연결 또는 호스트당 최대 동시 요청 수 기본 구성은 개발, 테스트, 소규모 프로덕션 또는 스테이징 환경에 적합합니다. 그러나 Cassandra 클러스터 내 노드 풀과 달리 Cassandra 어댑터는 단일 노드로 가장하므로 이러한 값을 늘리는 것이 좋습니다.

이러한 값을 늘리면 클라이언트와 Cassandra 인터페이스 간에 더 많은 동시 연결을 설정할 수 있습니다. 이렇게 하면 과부하가 발생할 때 연결 풀이 소진되는 것을 방지할 수 있습니다.

# 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 채널 조정

gRPC 채널은 Spanner 클라이언트에서 통신에 사용합니다. 하나의 gRPC 채널은 TCP 연결과 거의 동일합니다. 하나의 gRPC 채널은 최대 100개의 동시 요청을 처리할 수 있습니다. 즉, 애플리케이션에는 애플리케이션이 실행되는 동시 요청 수를 100으로 나눈 값 이상의 gRPC 채널이 필요합니다.

토큰 인식 라우팅 사용 중지

토큰 인식 부하 분산을 사용하는 드라이버는 Cassandra 어댑터를 사용할 때 경고를 출력하거나 작동하지 않을 수 있습니다. Cassandra 어댑터는 단일 노드로 가장하므로 클러스터에 노드가 복제 인자 수 이상으로 있을 것으로 예상되는 토큰 인식 드라이버에서는 제대로 작동하지 않습니다. 일부 드라이버는 무시할 수 있는 경고를 출력하고 라운드 로빈 분산 정책과 같은 정책으로 대체될 수 있지만 다른 드라이버는 오류가 발생하면서 실패할 수 있습니다. 오류가 발생하면서 실패하는 드라이버의 경우 토큰 인식을 사용 중지하거나 라운드 로빈 부하 분산 정책을 구성해야 합니다.

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

V4로 프로토콜 버전 고정

Cassandra 어댑터는 CQL Binary v4 유선 프로토콜을 준수하는 모든 오픈소스 Apache Cassandra 클라이언트 드라이버와 호환됩니다. PROTOCOL_VERSIONV4에 고정해야 합니다. 그러지 않으면 연결 오류가 발생할 수 있습니다.

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

다음 단계