Bolt 프로토콜을 사용하여 Neptune에 openCypher 쿼리하기 - Amazon Neptune

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Bolt 프로토콜을 사용하여 Neptune에 openCypher 쿼리하기

Bolt는 Neo4j에서 처음 개발했으며 크리에이티브 커먼즈 3.0 저작자 표시 라이선스에 따라 라이선스가 부여된 성명서 중심의 클라이언트/서버 프로토콜입니다. ShareAlike 클라이언트 중심적이므로, 메시지 교환은 클라이언트가 항상 시작합니다.

Neo4j의 Bolt 드라이버를 사용하여 Neptune에 연결하려면 구성표를 사용하여 및 포트 번호를 URL 클러스터 엔드포인트로 바꾸면 됩니다. bolt URI 단일 Neptune 인스턴스가 실행 중인 경우 read_write 엔드포인트를 사용하세요. 여러 인스턴스가 실행 중인 경우 라이터용 드라이버와 모든 읽기 전용 복제본용 드라이버를 2개 사용하는 것이 좋습니다. 기본 엔드포인트가 2개만 있는 경우에는 read_write와 read_only 드라이버로도 충분하지만, 사용자 지정 엔드포인트도 있는 경우에는 각 엔드포인트에 대한 드라이버 인스턴스를 생성하는 것이 좋습니다.

참고

Bolt 사양에는 Bolt가 OR 중 하나를 TCP 사용하여 연결할 수 있다고 명시되어 있지만 WebSockets Neptune은 Bolt에 대한 연결만 지원합니다. TCP

Neptune은 최대 1,000개의 동시 Bolt 연결을 허용합니다.

Bolt 드라이버를 사용하는 다양한 언어의 openCypher 쿼리 예는 Neo4j 드라이버 및 언어 가이드 문서를 참조하십시오.

중요

파이썬을 위한 Neo4j 볼트 드라이버,. NET JavaScript, 그리고 Golang은 처음에 AWS 시그니처 v4 인증 토큰의 자동 갱신을 지원하지 않았습니다. 즉, 서명이 만료된 후(보통 5분 후) 드라이버가 인증에 실패했고 후속 요청도 실패했습니다. 파이썬,. NET아래 JavaScript,, Go 예시는 모두 이 문제의 영향을 받았습니다.

Neo4j 파이썬 드라이버 문제 #834, Neo4j를 참조하십시오. NET자세한 내용은 문제 #664, Neo4j JavaScript 드라이버 문제 #993, goLang Neo4j 드라이버 문제 #429.

드라이버 버전 5.8.0부터 Go 드라이버에 대한 새로운 미리 보기 재인증이 API 릴리스되었습니다 (v5.8.0 참조 - 재인증에 대한 피드백 필요).

Java와 함께 Bolt를 사용하여 Neptune에 연결

Maven MVN리포지토리에서 원하는 버전의 드라이버를 다운로드하거나 프로젝트에 다음과 같은 종속 항목을 추가할 수 있습니다.

<dependency> <groupId>org.neo4j.driver</groupId> <artifactId>neo4j-java-driver</artifactId> <version>4.3.3</version> </dependency>

그런 다음 이러한 Bolt 드라이버 중 하나를 사용하여 Java에서 Neptune에 연결하려면 다음과 같은 코드를 사용하여 클러스터의 기본/라이터 인스턴스용 드라이버 인스턴스를 생성하세요.

import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; final Driver driver = GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", AuthTokens.none(), Config.builder().withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build());

리더 복제본이 하나 이상 있는 경우 다음과 같은 코드를 사용하여 마찬가지로 리더 복제본에 대한 드라이버 인스턴스를 만들 수 있습니다.

final Driver read_only_driver = // (without connection timeout) GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", Config.builder().withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build());

아니면 다음과 같이 제한 시간을 사용할 수 있습니다.

final Driver read_only_timeout_driver = // (with connection timeout) GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", Config.builder().withConnectionTimeout(30, TimeUnit.SECONDS) .withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build());

사용자 지정 엔드포인트가 있는 경우 각 엔드포인트에 대한 드라이버 인스턴스를 생성하는 것도 유용할 수 있습니다.

볼트를 사용한 Python openCypher 쿼리 예제

Bolt를 사용하여 Python에서 openCypher 쿼리를 만드는 방법은 다음과 같습니다.

python -m pip install neo4j
from neo4j import GraphDatabase uri = "bolt://(your cluster endpoint URL):(your cluster port)" driver = GraphDatabase.driver(uri, auth=("username", "password"), encrypted=True)

참고로 auth 파라미터는 무시됩니다.

A. NET openCypher Bolt를 사용한 쿼리 예제

에서 openCypher 쿼리를 작성하기 위해서요. NETBolt를 사용하는 첫 번째 단계는 를 사용하여 Neo4j 드라이버를 설치하는 것입니다. NuHet 동기 호출을 하려면 다음과 같은 .Simple 버전을 사용하세요.

Install-Package Neo4j.Driver.Simple-4.3.0
using Neo4j.Driver; namespace hello { // This example creates a node and reads a node in a Neptune // Cluster where IAM Authentication is not enabled. public class HelloWorldExample : IDisposable { private bool _disposed = false; private readonly IDriver _driver; private static string url = "bolt://(your cluster endpoint URL):(your cluster port)"; private static string createNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'"; private static string readNodeQuery = "MATCH(n:Greeting) RETURN n.message"; ~HelloWorldExample() => Dispose(false); public HelloWorldExample(string uri) { _driver = GraphDatabase.Driver(uri, AuthTokens.None, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted)); } public void createNode() { // Open a session using (var session = _driver.Session()) { // Run the query in a write transaction var greeting = session.WriteTransaction(tx => { var result = tx.Run(createNodeQuery); // Consume the result return result.Consume(); }); // The output will look like this: // ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample"..... Console.WriteLine(greeting); } } public void retrieveNode() { // Open a session using (var session = _driver.Session()) { // Run the query in a read transaction var greeting = session.ReadTransaction(tx => { var result = tx.Run(readNodeQuery); // Consume the result. Read the single node // created in a previous step. return result.Single()[0].As<string>(); }); // The output will look like this: // HelloWorldExample Console.WriteLine(greeting); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { _driver?.Dispose(); } _disposed = true; } public static void Main() { using (var apiCaller = new HelloWorldExample(url)) { apiCaller.createNode(); apiCaller.retrieveNode(); } } } }

인증과 함께 Bolt를 사용하는 자바 openCypher 쿼리 예제 IAM

아래 Java 코드는 IAM 인증과 함께 Bolt를 사용하여 Java에서 openCypher 쿼리를 만드는 방법을 보여줍니다. JavaDoc 주석은 사용법을 설명합니다. 드라이버 인스턴스를 사용할 수 있게 되면 이를 활용하여 인증된 요청을 여러 번 실행할 수 있습니다.

package software.amazon.neptune.bolt; import com.amazonaws.DefaultRequest; import com.amazonaws.Request; import com.amazonaws.auth.AWS4Signer; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.http.HttpMethodName; import com.google.gson.Gson; import lombok.Builder; import lombok.Getter; import lombok.NonNull; import org.neo4j.driver.Value; import org.neo4j.driver.Values; import org.neo4j.driver.internal.security.InternalAuthToken; import org.neo4j.driver.internal.value.StringValue; import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static com.amazonaws.auth.internal.SignerConstants.AUTHORIZATION; import static com.amazonaws.auth.internal.SignerConstants.HOST; import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_DATE; import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SECURITY_TOKEN; /** * Use this class instead of `AuthTokens.basic` when working with an IAM * auth-enabled server. It works the same as `AuthTokens.basic` when using * static credentials, and avoids making requests with an expired signature * when using temporary credentials. Internally, it generates a new signature * on every invocation (this may change in a future implementation). * * Note that authentication happens only the first time for a pooled connection. * * Typical usage: * * NeptuneAuthToken authToken = NeptuneAuthToken.builder() * .credentialsProvider(credentialsProvider) * .region("aws region") * .url("cluster endpoint url") * .build(); * * Driver driver = GraphDatabase.driver( * authToken.getUrl(), * authToken, * config * ); */ public class NeptuneAuthToken extends InternalAuthToken { private static final String SCHEME = "basic"; private static final String REALM = "realm"; private static final String SERVICE_NAME = "neptune-db"; private static final String HTTP_METHOD_HDR = "HttpMethod"; private static final String DUMMY_USERNAME = "username"; @NonNull private final String region; @NonNull @Getter private final String url; @NonNull private final AWSCredentialsProvider credentialsProvider; private final Gson gson = new Gson(); @Builder private NeptuneAuthToken( @NonNull final String region, @NonNull final String url, @NonNull final AWSCredentialsProvider credentialsProvider ) { // The superclass caches the result of toMap(), which we don't want super(Collections.emptyMap()); this.region = region; this.url = url; this.credentialsProvider = credentialsProvider; } @Override public Map<String, Value> toMap() { final Map<String, Value> map = new HashMap<>(); map.put(SCHEME_KEY, Values.value(SCHEME)); map.put(PRINCIPAL_KEY, Values.value(DUMMY_USERNAME)); map.put(CREDENTIALS_KEY, new StringValue(getSignedHeader())); map.put(REALM_KEY, Values.value(REALM)); return map; } private String getSignedHeader() { final Request<Void> request = new DefaultRequest<>(SERVICE_NAME); request.setHttpMethod(HttpMethodName.GET); request.setEndpoint(URI.create(url)); // Comment out the following line if you're using an engine version older than 1.2.0.0 request.setResourcePath("/opencypher"); final AWS4Signer signer = new AWS4Signer(); signer.setRegionName(region); signer.setServiceName(request.getServiceName()); signer.sign(request, credentialsProvider.getCredentials()); return getAuthInfoJson(request); } private String getAuthInfoJson(final Request<Void> request) { final Map<String, Object> obj = new HashMap<>(); obj.put(AUTHORIZATION, request.getHeaders().get(AUTHORIZATION)); obj.put(HTTP_METHOD_HDR, request.getHttpMethod()); obj.put(X_AMZ_DATE, request.getHeaders().get(X_AMZ_DATE)); obj.put(HOST, request.getHeaders().get(HOST)); obj.put(X_AMZ_SECURITY_TOKEN, request.getHeaders().get(X_AMZ_SECURITY_TOKEN)); return gson.toJson(obj); } }

IAM인증과 함께 Bolt를 사용하는 Python openCypher 쿼리 예제

아래 Python 클래스를 사용하면 IAM 인증과 함께 Bolt를 사용하여 Python에서 openCypher 쿼리를 만들 수 있습니다.

import json from neo4j import Auth from botocore.awsrequest import AWSRequest from botocore.credentials import Credentials from botocore.auth import ( SigV4Auth, _host_from_url, ) SCHEME = "basic" REALM = "realm" SERVICE_NAME = "neptune-db" DUMMY_USERNAME = "username" HTTP_METHOD_HDR = "HttpMethod" HTTP_METHOD = "GET" AUTHORIZATION = "Authorization" X_AMZ_DATE = "X-Amz-Date" X_AMZ_SECURITY_TOKEN = "X-Amz-Security-Token" HOST = "Host" class NeptuneAuthToken(Auth): def __init__( self, credentials: Credentials, region: str, url: str, **parameters ): # Do NOT add "/opencypher" in the line below if you're using an engine version older than 1.2.0.0 request = AWSRequest(method=HTTP_METHOD, url=url + "/opencypher") request.headers.add_header("Host", _host_from_url(request.url)) sigv4 = SigV4Auth(credentials, SERVICE_NAME, region) sigv4.add_auth(request) auth_obj = { hdr: request.headers[hdr] for hdr in [AUTHORIZATION, X_AMZ_DATE, X_AMZ_SECURITY_TOKEN, HOST] } auth_obj[HTTP_METHOD_HDR] = request.method creds: str = json.dumps(auth_obj) super().__init__(SCHEME, DUMMY_USERNAME, creds, REALM, **parameters)

이 클래스를 사용하여 다음과 같이 드라이버를 생성합니다.

authToken = NeptuneAuthToken(creds, REGION, URL) driver = GraphDatabase.driver(URL, auth=authToken, encrypted=True)

IAM인증과 Bolt를 사용한 Node.js 예제

아래 Node.js 코드는 JavaScript 버전 AWS SDK 3용 및 ES6 구문을 사용하여 요청을 인증하는 드라이버를 만듭니다.

import neo4j from "neo4j-driver"; import { HttpRequest } from "@aws-sdk/protocol-http"; import { defaultProvider } from "@aws-sdk/credential-provider-node"; import { SignatureV4 } from "@aws-sdk/signature-v4"; import crypto from "@aws-crypto/sha256-js"; const { Sha256 } = crypto; import assert from "node:assert"; const region = "us-west-2"; const serviceName = "neptune-db"; const host = "(your cluster endpoint URL)"; const port = 8182; const protocol = "bolt"; const hostPort = host + ":" + port; const url = protocol + "://" + hostPort; const createQuery = "CREATE (n:Greeting {message: 'Hello'}) RETURN ID(n)"; const readQuery = "MATCH(n:Greeting) WHERE ID(n) = $id RETURN n.message"; async function signedHeader() { const req = new HttpRequest({ method: "GET", protocol: protocol, hostname: host, port: port, // Comment out the following line if you're using an engine version older than 1.2.0.0 path: "/opencypher", headers: { host: hostPort } }); const signer = new SignatureV4({ credentials: defaultProvider(), region: region, service: serviceName, sha256: Sha256 }); return signer.sign(req, { unsignableHeaders: new Set(["x-amz-content-sha256"]) }) .then((signedRequest) => { const authInfo = { "Authorization": signedRequest.headers["authorization"], "HttpMethod": signedRequest.method, "X-Amz-Date": signedRequest.headers["x-amz-date"], "Host": signedRequest.headers["host"], "X-Amz-Security-Token": signedRequest.headers["x-amz-security-token"] }; return JSON.stringify(authInfo); }); } async function createDriver() { let authToken = { scheme: "basic", realm: "realm", principal: "username", credentials: await signedHeader() }; return neo4j.driver(url, authToken, { encrypted: "ENCRYPTION_ON", trust: "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES", maxConnectionPoolSize: 1, // logging: neo4j.logging.console("debug") } ); } function unmanagedTxn(driver) { const session = driver.session(); const tx = session.beginTransaction(); tx.run(createQuery) .then((res) => { const id = res.records[0].get(0); return tx.run(readQuery, { id: id }); }) .then((res) => { // All good, the transaction will be committed const msg = res.records[0].get("n.message"); assert.equal(msg, "Hello"); }) .catch(err => { // The transaction will be rolled back, now handle the error. console.log(err); }) .then(() => session.close()); } createDriver() .then((driver) => { unmanagedTxn(driver); driver.close(); }) .catch((err) => { console.log(err); });

A. NET openCypher IAM인증과 함께 Bolt를 사용한 쿼리 예제

에서 IAM 인증을 활성화하려면 NET연결을 설정할 때 요청에 서명해야 합니다. 아래 예제는 인증 토큰을 생성하는 NeptuneAuthToken 헬퍼를 만드는 방법을 보여줍니다.

using Amazon.Runtime; using Amazon.Util; using Neo4j.Driver; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Web; namespace Hello { /* * Use this class instead of `AuthTokens.None` when working with an IAM-auth-enabled server. * * Note that authentication happens only the first time for a pooled connection. * * Typical usage: * * var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host); * _driver = GraphDatabase.Driver(Url, authToken, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted)); */ public class NeptuneAuthToken { private const string ServiceName = "neptune-db"; private const string Scheme = "basic"; private const string Realm = "realm"; private const string DummyUserName = "username"; private const string Algorithm = "AWS4-HMAC-SHA256"; private const string AWSRequest = "aws4_request"; private readonly string _accessKey; private readonly string _secretKey; private readonly string _region; private readonly string _emptyPayloadHash; private readonly SHA256 _sha256; public NeptuneAuthToken(string awsKey = null, string secretKey = null, string region = null) { var awsCredentials = awsKey == null || secretKey == null ? FallbackCredentialsFactory.GetCredentials().GetCredentials() : null; _accessKey = awsKey ?? awsCredentials.AccessKey; _secretKey = secretKey ?? awsCredentials.SecretKey; _region = region ?? FallbackRegionFactory.GetRegionEndpoint().SystemName; //ex: us-east-1 _sha256 = SHA256.Create(); _emptyPayloadHash = Hash(Array.Empty<byte>()); } public IAuthToken GetAuthToken(string url) { return AuthTokens.Custom(DummyUserName, GetCredentials(url), Realm, Scheme); } /******************** AWS SIGNING FUNCTIONS *********************/ private string Hash(byte[] bytesToHash) { return ToHexString(_sha256.ComputeHash(bytesToHash)); } private static byte[] HmacSHA256(byte[] key, string data) { return new HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(data)); } private byte[] GetSignatureKey(string dateStamp) { var kSecret = Encoding.UTF8.GetBytes($"AWS4{_secretKey}"); var kDate = HmacSHA256(kSecret, dateStamp); var kRegion = HmacSHA256(kDate, _region); var kService = HmacSHA256(kRegion, ServiceName); return HmacSHA256(kService, AWSRequest); } private static string ToHexString(byte[] array) { return Convert.ToHexString(array).ToLowerInvariant(); } private string GetCredentials(string url) { var request = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri($"https://{url}/opencypher") }; var signedrequest = Sign(request); var headers = new Dictionary<string, object> { [HeaderKeys.AuthorizationHeader] = signedrequest.Headers.GetValues(HeaderKeys.AuthorizationHeader).FirstOrDefault(), ["HttpMethod"] = HttpMethod.Get.ToString(), [HeaderKeys.XAmzDateHeader] = signedrequest.Headers.GetValues(HeaderKeys.XAmzDateHeader).FirstOrDefault(), // Host should be capitalized, not like in Amazon.Util.HeaderKeys.HostHeader ["Host"] = signedrequest.Headers.GetValues(HeaderKeys.HostHeader).FirstOrDefault(), }; return JsonSerializer.Serialize(headers); } private HttpRequestMessage Sign(HttpRequestMessage request) { var now = DateTimeOffset.UtcNow; var amzdate = now.ToString("yyyyMMddTHHmmssZ"); var datestamp = now.ToString("yyyyMMdd"); if (request.Headers.Host == null) { request.Headers.Host = $"{request.RequestUri.Host}:{request.RequestUri.Port}"; } request.Headers.Add(HeaderKeys.XAmzDateHeader, amzdate); var canonicalQueryParams = GetCanonicalQueryParams(request); var canonicalRequest = new StringBuilder(); canonicalRequest.Append(request.Method + "\n"); canonicalRequest.Append(request.RequestUri.AbsolutePath + "\n"); canonicalRequest.Append(canonicalQueryParams + "\n"); var signedHeadersList = new List<string>(); foreach (var header in request.Headers.OrderBy(a => a.Key.ToLowerInvariant())) { canonicalRequest.Append(header.Key.ToLowerInvariant()); canonicalRequest.Append(':'); canonicalRequest.Append(string.Join(",", header.Value.Select(s => s.Trim()))); canonicalRequest.Append('\n'); signedHeadersList.Add(header.Key.ToLowerInvariant()); } canonicalRequest.Append('\n'); var signedHeaders = string.Join(";", signedHeadersList); canonicalRequest.Append(signedHeaders + "\n"); canonicalRequest.Append(_emptyPayloadHash); var credentialScope = $"{datestamp}/{_region}/{ServiceName}/{AWSRequest}"; var stringToSign = $"{Algorithm}\n{amzdate}\n{credentialScope}\n" + Hash(Encoding.UTF8.GetBytes(canonicalRequest.ToString())); var signing_key = GetSignatureKey(datestamp); var signature = ToHexString(HmacSHA256(signing_key, stringToSign)); request.Headers.TryAddWithoutValidation(HeaderKeys.AuthorizationHeader, $"{Algorithm} Credential={_accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}"); return request; } private static string GetCanonicalQueryParams(HttpRequestMessage request) { var querystring = HttpUtility.ParseQueryString(request.RequestUri.Query); // Query params must be escaped in upper case (i.e. "%2C", not "%2c"). var queryParams = querystring.AllKeys.OrderBy(a => a) .Select(key => $"{key}={Uri.EscapeDataString(querystring[key])}"); return string.Join("&", queryParams); } } }

openCypher 쿼리를 만드는 방법은 다음과 같습니다. NETBolt를 IAM 인증과 함께 사용하기. 아래 예제에서는 NeptuneAuthToken 헬퍼를 사용합니다.

using Neo4j.Driver; namespace Hello { public class HelloWorldExample { private const string Host = "(your hostname):8182"; private const string Url = $"bolt://{Host}"; private const string CreateNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'"; private const string ReadNodeQuery = "MATCH(n:Greeting) RETURN n.message"; private const string AccessKey = "(your access key)"; private const string SecretKey = "(your secret key)"; private const string Region = "(your AWS region)"; // e.g. "us-west-2" private readonly IDriver _driver; public HelloWorldExample() { var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host); // Note that when the connection is reinitialized after max connection lifetime // has been reached, the signature token could have already been expired (usually 5 min) // You can face exceptions like: // `Unexpected server exception 'Signature expired: XXXX is now earlier than YYYY (ZZZZ - 5 min.)` _driver = GraphDatabase.Driver(Url, authToken, o => o.WithMaxConnectionLifetime(TimeSpan.FromMinutes(60)).WithEncryptionLevel(EncryptionLevel.Encrypted)); } public async Task CreateNode() { // Open a session using (var session = _driver.AsyncSession()) { // Run the query in a write transaction var greeting = await session.WriteTransactionAsync(async tx => { var result = await tx.RunAsync(CreateNodeQuery); // Consume the result return await result.ConsumeAsync(); }); // The output will look like this: // ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample"..... Console.WriteLine(greeting.Query); } } public async Task RetrieveNode() { // Open a session using (var session = _driver.AsyncSession()) { // Run the query in a read transaction var greeting = await session.ReadTransactionAsync(async tx => { var result = await tx.RunAsync(ReadNodeQuery); var records = await result.ToListAsync(); // Consume the result. Read the single node // created in a previous step. return records[0].Values.First().Value; }); // The output will look like this: // HelloWorldExample Console.WriteLine(greeting); } } } }

이 예제는 아래 코드를 다음 패키지와 함께 .NET 6 또는 .NET 7에서 실행하여 시작할 수 있습니다.

  • Neo4j.Driver=4.3.0

  • AWSSDK.Core=3.7.102.1

namespace Hello { class Program { static async Task Main() { var apiCaller = new HelloWorldExample(); await apiCaller.CreateNode(); await apiCaller.RetrieveNode(); } } }

인증과 함께 IAM Bolt를 사용하는 Golang openCypher 쿼리 예제

아래 Golang 패키지는 IAM 인증과 함께 Bolt를 사용하여 Go 언어로 openCypher 쿼리를 만드는 방법을 보여줍니다.

package main import ( "context" "encoding/json" "fmt" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/neo4j/neo4j-go-driver/v5/neo4j" "log" "net/http" "os" "time" ) const ( ServiceName = "neptune-db" DummyUsername = "username" ) // Find node by id using Go driver func findNode(ctx context.Context, region string, hostAndPort string, nodeId string) (string, error) { req, err := http.NewRequest(http.MethodGet, "https://"+hostAndPort+"/opencypher", nil) if err != nil { return "", fmt.Errorf("error creating request, %v", err) } // credentials must have been exported as environment variables signer := v4.NewSigner(credentials.NewEnvCredentials()) _, err = signer.Sign(req, nil, ServiceName, region, time.Now()) if err != nil { return "", fmt.Errorf("error signing request: %v", err) } hdrs := []string{"Authorization", "X-Amz-Date", "X-Amz-Security-Token"} hdrMap := make(map[string]string) for _, h := range hdrs { hdrMap[h] = req.Header.Get(h) } hdrMap["Host"] = req.Host hdrMap["HttpMethod"] = req.Method password, err := json.Marshal(hdrMap) if err != nil { return "", fmt.Errorf("error creating JSON, %v", err) } authToken := neo4j.BasicAuth(DummyUsername, string(password), "") // +s enables encryption with a full certificate check // Use +ssc to disable client side TLS verification driver, err := neo4j.NewDriverWithContext("bolt+s://"+hostAndPort+"/opencypher", authToken) if err != nil { return "", fmt.Errorf("error creating driver, %v", err) } defer driver.Close(ctx) if err := driver.VerifyConnectivity(ctx); err != nil { log.Fatalf("failed to verify connection, %v", err) } config := neo4j.SessionConfig{} session := driver.NewSession(ctx, config) defer session.Close(ctx) result, err := session.Run( ctx, fmt.Sprintf("MATCH (n) WHERE ID(n) = '%s' RETURN n", nodeId), map[string]any{}, ) if err != nil { return "", fmt.Errorf("error running query, %v", err) } if !result.Next(ctx) { return "", fmt.Errorf("node not found") } n, found := result.Record().Get("n") if !found { return "", fmt.Errorf("node not found") } return fmt.Sprintf("+%v\n", n), nil } func main() { if len(os.Args) < 3 { log.Fatal("Usage: go main.go (region) (host and port)") } region := os.Args[1] hostAndPort := os.Args[2] ctx := context.Background() res, err := findNode(ctx, region, hostAndPort, "72c2e8c1-7d5f-5f30-10ca-9d2bb8c4afbc") if err != nil { log.Fatal(err) } fmt.Println(res) }

Neptune에서의 Bolt 연결 동작

Neptune Bolt 연결에 대해 염두에 두어야 할 몇 가지 사항이 있습니다.

  • 볼트 연결은 TCP 레이어에서 생성되므로 엔드포인트와 마찬가지로 볼트 연결 앞에 Application Load Balancer를 사용할 수 없습니다. HTTP

  • Neptune이 Bolt 연결에 사용하는 포트는 DB 클러스터의 포트입니다.

  • 전달된 Bolt 서문을 기반으로 Neptune 서버는 가장 적합한 Bolt 버전(1, 2, 3 또는 4.0)을 선택합니다.

  • 클라이언트가 언제든지 열 수 있는 Neptune 서버에 대한 최대 연결 수는 1,000개입니다.

  • 클라이언트가 쿼리 후 연결을 끊지 않으면 해당 연결을 사용하여 다음 쿼리를 실행할 수 있습니다.

  • 하지만 연결이 20분 동안 유휴 상태인 경우 서버는 자동으로 연결을 닫습니다.

  • IAM인증이 활성화되지 않은 경우 더미 사용자 이름과 암호를 입력하는 AuthTokens.none() 대신 사용할 수 있습니다. 예를 들어, Java의 경우는 다음과 같습니다.

    GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", AuthTokens.none(), Config.builder().withEncryption().withTrustStrategy(TrustStrategy.trustSystemCertificates()).build());
  • IAM인증이 활성화되면 다른 이유로 Bolt 연결이 아직 닫히지 않은 경우 설정된 지 10일이 지난 후 몇 분이 지나면 항상 연결이 끊깁니다.

  • 클라이언트가 이전 쿼리의 결과를 소비하지 않고 연결을 통해 실행되도록 쿼리를 보내면 새 쿼리는 삭제됩니다. 대신 이전 결과를 삭제하려면 클라이언트가 연결을 통해 재설정 메시지를 보내야 합니다.

  • 지정된 연결에서 한 번에 트랜잭션을 하나만 생성할 수 있습니다.

  • 트랜잭션 중에 예외가 발생하면 Neptune 서버가 트랜잭션을 롤백하고 연결을 닫습니다. 이 경우 드라이버는 다음 쿼리를 위해 새 연결을 생성합니다.

  • 세션은 스레드 세이프가 아니라는 점에 유의하세요. 다중 병렬식 작업은 별도의 세션을 여러 개 사용해야 합니다.