やりたいこと
前のエントリで、JanusGraphをインメモリで立ち上げるということやりました。立ち上げたサーバに対しては、Gramlin-consoleに加え、websocket等を介して様々な言語からアクセスできます。
Pythonでは、それほど考えずにできたのですが、scalaでの接続にはちょっと工夫が必要です。
シリアライズ
クライアント用のプログラムと、JanusGraphのサーバ間の通信はwebsocketのようですが、平文で流れているわけではなくて、適宜シリアライズされています。これは、サーバ起動時のパラメータに指定があります。例えば、以下はデフォルトの設定で、serializersというのが確認できます。速さに定評があるKryoが採用されているようです。(設定ファイルでの名前はGryoになっています。これって違うものかしら…?)
host: 0.0.0.0
port: 8182
scriptEvaluationTimeout: 30000
channelizer: org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer
graphs: {
graph: conf/gremlin-server/janusgraph-cql-es-server.properties
}
scriptEngines: {
gremlin-groovy: {
plugins: { org.janusgraph.graphdb.tinkerpop.plugin.JanusGraphGremlinPlugin: {},
org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}}
serializers:
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
# Older serialization versions for backwards compatibility:
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0, config: {ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { serializeResultToString: true }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
processors:
- { className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }}
- { className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor, config: { cacheExpirationTime: 600000, cacheMaxSize: 1000 }}
metrics: {
consoleReporter: {enabled: true, interval: 180000},
csvReporter: {enabled: true, interval: 180000, fileName: /tmp/gremlin-server-metrics.csv},
jmxReporter: {enabled: true},
slf4jReporter: {enabled: true, interval: 180000},
gangliaReporter: {enabled: false, interval: 180000, addressingMode: MULTICAST},
graphiteReporter: {enabled: false, interval: 180000}}
maxInitialLineLength: 4096
maxHeaderSize: 8192
maxChunkSize: 8192
maxContentLength: 65536
maxAccumulationBufferComponents: 1024
resultIterationBatchSize: 64
writeBufferLowWaterMark: 32768
writeBufferHighWaterMark: 65536
Scalaのクライアント
今回のクライアントは、gremlin-scalaを使います。わかりやすいDSLを提供していていい感じです。容易に接続できるのですが、上記と同じ仕様のデシリアライザをクライアント側でも使う必要があります。
そのために、必要ライブラリをインポートしておきます。Janus-coreに付属しているシリアライザを使うのがポイントです。サンプルにあるようにTinkerIoRegistryV3d0を使うと失敗します。ここ重要。
name := "BotImporter"
version := "0.1"
scalaVersion := "2.13.0"
libraryDependencies ++= Seq("org.apache.jena" % "jena-core" % "3.12.0",
"com.typesafe" % "config" % "1.3.4",
"com.michaelpollmeier" %% "gremlin-scala" % "3.4.1.8",
"org.apache.tinkerpop" % "gremlin-driver" % "3.3.1",
"org.apache.tinkerpop" % "tinkergraph-gremlin" % "3.3.1",
"ch.qos.logback" % "logback-classic" % "1.2.3",
"org.janusgraph" % "janusgraph-core" % "0.3.1",
)
fork in Test := true
resolvers += Resolver.mavenLocal
resolvers += "Apache public" at "https://repository.apache.org/content/groups/public/"
後は、サンプルを参考に組み立てます。最後に適当にトレースしていますが、きちんと変更が反映されているのが分かります。
object TinkerpopTest {
val level = "WARN"
val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
context.getLogger(this.getClass).setLevel(Level.valueOf(level))
def main(args: Array[String]): Unit = {
val serializer = new GryoMessageSerializerV3d0(GryoMapper.build.addRegistry(JanusGraphIoRegistry.getInstance()))
val cluster = Cluster.build.addContactPoint("localhost").port(8182).serializer(serializer).create
implicit val graph = EmptyGraph.instance.asScala.configure(_.withRemote(DriverRemoteConnection.using(cluster, "g")))
val g = graph.traversal
// see org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory.generateClassic()
// Keys for properties which can later be used for type safe traversals
val Founded = Key[String]("founded")
val Distance = Key[Int]("distance")
// create labelled vertex
val paris = graph + "Paris"
// create vertex with typed properties
val london = graph + ("London", Founded -> "43 AD")
// create labelled edges
paris --- "OneWayRoad" --> london
paris <-- "OtherWayAround" --- london
paris <-- "Eurostar" --> london
g.V().toList().foreach(e => println(e.label()))
cluster.close()
System.exit(0)
}
}