Utilisation du protocole Bolt pour envoyer des requêtes OpenCypher à Neptune - Amazon Neptune

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Utilisation du protocole Bolt pour envoyer des requêtes OpenCypher à Neptune

Boulonest un protocole client/serveur orienté déclaration initialement développé par Neo4j et sous licence Creative Commons 3.0Attribution-ShareAlikeLicence. Il est piloté par le client, ce qui signifie que le client initie toujours des échanges de messages.

Pour vous connecter à Neptune à l'aide des pilotes Bolt de Neo4j, remplacez simplement l'URL et le numéro de port par les points de terminaison de votre cluster à l'aide duboltSchéma d'URI Si vous avez une seule instance Neptune en cours d'exécution, utilisez le point de terminaison read_write. Si plusieurs instances sont en cours d'exécution, deux pilotes sont recommandés, l'un pour le rédacteur et l'autre pour toutes les répliques lues. Si vous ne disposez que des deux points de terminaison par défaut, un pilote read_write et un driver read_only sont suffisants, mais si vous avez également des points de terminaison personnalisés, pensez à créer une instance de pilote pour chacun d'eux.

Note

Bien que la spécification Bolt indique que Bolt peut se connecter via TCP ou WebSockets, Neptune ne prend en charge que les connexions TCP pour Bolt.

Neptune permet jusqu'à 1 000 connexions Bolt simultanées.

Neptune prend en charge la version de spécification des messages Neo4J Bolt suivante : 4.0.0.

Pour des exemples de requêtes OpenCypher dans différents langages utilisant les pilotes Bolt, consultez le Neo4jPilotes et guides linguistiques.

Utiliser Bolt avec Java pour se connecter à Neptune

Vous pouvez télécharger un pilote pour la version que vous souhaitez utiliser depuis le MavenRéférentiel MVN, ou vous pouvez ajouter cette dépendance à votre projet :

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

Ensuite, pour vous connecter à Neptune en Java à l'aide de l'un de ces pilotes Bolt, créez une instance de pilote pour l'instance principale/d'écriture de votre cluster à l'aide du code suivant :

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

Si vous possédez une ou plusieurs répliques de lecteurs, vous pouvez également créer une instance de pilote pour ces derniers à l'aide du code suivant :

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

Ou, avec un délai d'attente :

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

Si vous avez des points de terminaison personnalisés, il peut également être utile de créer une instance de pilote pour chacun d'entre eux.

Exemple de requête Python OpenCypher utilisant Bolt

Voici comment créer une requête OpenCypher en Python à l'aide de Bolt :

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)

Notez queauthles paramètres sont ignorés.

Exemple de requête .NET OpenCypher utilisant Bolt

Voici comment créer une requête OpenCypher dans .NET à l'aide de Bolt :

Install-Package Neo4j.Driver-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(); } } } }

Exemple de requête Java OpenCypher utilisant Bolt avec authentification IAM

Le code Java ci-dessous montre comment créer des requêtes OpenCypher en Java à l'aide de Bolt avec authentification IAM. Dans la JavaDoc Un commentaire décrit son utilisation. Une fois qu'une instance de pilote est disponible, vous pouvez l'utiliser pour effectuer plusieurs demandes authentifiées.

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

Exemple de requête Python OpenCypher utilisant Bolt avec authentification IAM

La classe Python ci-dessous vous permet de créer des requêtes OpenCypher en Python à l'aide de Bolt avec authentification IAM :

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)

Vous utilisez cette classe pour créer un pilote comme suit :

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

Un exemple de Node.js utilisant l'authentification IAM et Bolt

Le code Node.js ci-dessous utiliseAWSKit SDK pour JavaScript syntaxe des versions 3 et ES6 pour créer un pilote authentifiant les requêtes :

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

Comportement des connexions Bolt dans Neptune

Voici quelques éléments à garder à l'esprit sur les connexions Neptune Bolt :

  • Comme les connexions Bolt sont créées au niveau de la couche TCP, vous ne pouvez pas utiliserApplication Load Balancerdevant eux, comme vous pouvez le faire avec un point de terminaison HTTP.

  • Le port que Neptune utilise pour les connexions Bolt est le port de votre cluster de bases de données.

  • Sur la base du préambule Bolt qui lui a été transmis, le serveur Neptune sélectionne la version Bolt la plus appropriée (1, 2, 3 ou 4.0).

  • Le nombre maximum de connexions au serveur Neptune qu'un client peut ouvrir à tout moment est de 1 000.

  • Si le client ne ferme pas de connexion après une requête, cette connexion peut être utilisée pour exécuter la requête suivante.

  • Toutefois, si une connexion est inactive pendant 20 minutes, le serveur la ferme automatiquement.

  • Si l'authentification IAM n'est pas activée, vous pouvez utiliserAuthTokens.none()plutôt que de fournir un nom d'utilisateur et un mot de passe fictifs. Par exemple, en Java :

    GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", AuthTokens.none(), Config.builder().withEncryption().withTrustStrategy(TrustStrategy.trustSystemCertificates()).build());
  • Lorsque l'authentification IAM est activée, une connexion Bolt est toujours déconnectée quelques minutes plus de 10 jours après son établissement, si elle n'est pas déjà fermée pour une autre raison.

  • Si le client envoie une requête pour exécution via une connexion sans avoir consommé les résultats d'une requête précédente, la nouvelle requête est supprimée. Pour annuler les résultats précédents, le client doit envoyer un message de réinitialisation via la connexion.

  • Une seule transaction peut être créée à partir d'une connexion donnée à la fois.

  • Si une exception se produit lors d'une transaction, le serveur Neptune annule la transaction et ferme la connexion. Dans ce cas, le pilote crée une nouvelle connexion pour la requête suivante.

  • Sachez que les sessions ne sont pas sécurisées pour les threads. Les opérations parallel multiples doivent utiliser plusieurs sessions distinctes.