使用 Bolt 协议向 Neptune 进行 openCypher 查询 - Amazon Neptune

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Bolt 协议向 Neptune 进行 openCypher 查询

Bolt 是一种面向语句的客户端/服务器协议,最初由Neo4j开发,并根据知识共享3.0署名许可进行许可。ShareAlike它由客户端驱动,这意味着客户端始终发起消息交换。

要使用 Neo4j 的 Bolt 驱动程序连接到 Neptune,只需使用该方案将URL和端口号替换为集群端点即可。bolt URI如果您有单个 Neptune 实例在运行,请使用 read_write 端点。如果有多个实例在运行,则建议使用两个驱动程序,一个用于写入器,另一个用于所有只读副本。如果您只有两个默认端点,则 read_write 和 read_only 驱动程序就足够了,但是如果您还有自定义端点,可以考虑为每个端点创建一个驱动程序实例。

注意

尽管 Bolt 规范规定 Bolt 可以使用TCP或进行连接 WebSockets,但 Neptune 仅TCP支持 Bolt 的连接。

Neptune 允许多达 1000 个并发 Bolt 连接。

有关使用 Bolt 驱动程序的各种语言的 openCypher 查询示例,请参阅 Neo4j 驱动程序和语言指南文档

重要

适用于 Python 的 Neo4j Bolt 驱动程序,。 NET JavaScript,而且 Golang 最初不支持自动续订 S AWS ignature v4 身份验证令牌。这意味着在签名过期后(通常在 5 分钟内),驱动程序无法进行身份验证,随后的请求会失败。Python,。 NET、 JavaScript、和下面的 Go 示例都受此问题的影响。

参见 Neo4j Python 驱动程序问题 #834,neo4j。 NET问题 #664neo4j JavaScript 驱动程序问题 #993 和 neo4j goLang 驱动程序问题 #429 了解更多信息

从驱动程序版本 5.8.0 开始,Go 驱动程序发布API了新的预览版重新身份验证(参见 v5.8.0- 需要有关重新身份验证的反馈)。

使用 Bolt 以及 Java 连接到 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());

如果您有自定义端点,则可能还值得为每个端点创建一个驱动程序实例。

使用 Bolt 的 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 查询。 NET使用 Bolt,第一步是使用安装 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 进行IAM身份验证的 Java openCypher 查询示例

下面的 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 代码使用 for AWS SDK JavaScript 版本 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 使用 Bolt 进行IAM身份验证的查询示例

要在中启用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 查询的方法。 NET使用带IAM身份验证的 Bolt。下面的示例使用 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(); } } }

使用带身份验证的 Bolt 的 Golang openCypher 查询示例 IAM

下面的 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 连接的一些注意事项:

  • 由于 Bolt 连接是在该TCP层创建的,因此您不能像使用HTTP端点那样在它们前面使用 App lication Load Bal ancer。

  • Neptune 用于 Bolt 连接的端口是您的数据库集群的端口。

  • 根据传递给它的 Bolt 序言,Neptune 服务器会选择最合适的 Bolt 版本(1、2、3 或 4.0)。

  • 客户端在任何时间点可以打开的与 Neptune 服务器的最大连接数为 1000。

  • 如果客户端在查询后没有关闭连接,则该连接可用于执行下一个查询。

  • 但是,如果连接空闲了 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 服务器会回滚事务并关闭连接。在这种情况下,驱动程序会为下一个查询创建一个新连接。

  • 请注意,会话不是线程安全的。多个并行操作必须使用多个单独的会话。