BITFINEXというビットコイントレードのプラットフォームを提供している会社がGrenacheというマイクロサービスを管理するフレームワークを提供しているので少し触ってみた。
例によってサンプルプログラム、ほぼそのままなのだけど多少咀嚼した(つもり)。
Grenache is a DHT based high-performance microservices framework by Bitfinex. Its decentralised and optimized for performance. Because its simple, it is easy to understand and to set up.
雑にまとめたのが下の図。
特徴としては、サービスを一箇所に登録するのではなく離散ハッシュテーブルで管理されるクラスタに登録できるところで、そこにサービスを登録することでそのサービスのIP等の接続情報が管理されていくところにある。
まずはGRAPEと呼ばれるデーモンを立ち上げてクラスタを構成する。npmで雑にインストール。
$ npm i -g grenache-grape
GRAPEを立ち上げる。3クラスタ構成にしてみる。
$ grape --dp 20001 --aph 30001 --bn '127.0.0.1:20002' $ grape --dp 20002 --aph 30002 --bn '127.0.0.1:20003' $ grape --dp 20003 --aph 30003 --bn '127.0.0.1:20001'
dp
はクラスタ間でのDHTを構成する際に通信するためのポート番号、aph
はAPIのポート番号になる。 bn
は立ち上げ時に参照するGRAPEを指定する。
これでクラスタは構成されたので次は実際にWorker(=サービス)を登録していく。サンプルではフィボナッチ数列の和だったけど、ここでは階乗を求めるサービスにしてみる。
GRAPEクラスタへWorkerを登録したり、登録したWorkerを呼び出すClientを作成するためのライブラリもnpmに登録されているのでこれを利用していく。
GitHub - bitfinexcom/grenache-nodejs-ws: Grenache Node.JS WebSocket implementation
まずはWorkerから。
'use strict' const { Link, PeerRPCServer } = require('grenache-nodejs-ws') function fact (n) { if (n == 0) { return 1 } let m = fact(n-1); return m * n; } const link = new Link({ grape: 'http://127.0.0.1:30001' }) link.start() const peer = new PeerRPCServer(link, {}) peer.init() const service = peer.transport('server') service.listen(1337) setInterval(() => { link.announce('fact_worker', service.port, {}) }, 1000) service.on('request', (rid, key, payload, handler) => { const result = fact(payload.number) handler.reply(null, result) })
なんとなく見て分かる通り fact_worker
という名前でサービスをGRAPEクラスタに登録している。リクエストが来たときに fact
というfunction呼び出してその結果をリプライしているという至極シンプルな形。
では、上のWorkerを立ち上げたままの状態で次にこれを実際に呼び出すクライアントを書いてみる。
同じく先のgrenache-nodejs-wsを利用して、
'use strict' const { Link, PeerRPCClient } = require('grenache-nodejs-ws') const link = new Link({ grape: 'http://127.0.0.1:30001', requestTimeout: 10000 }) link.start() const peer = new PeerRPCClient(link, {}) peer.init() const payload = { number: 10 } peer.request('fact_worker', payload, { timeout: 100000 }, (err, result) => { if (err) throw err console.log(`${payload.number}! = ${result}`); });
上を実行すると
が行われて、
$ node client.js 10! = 3628800
といった結果が得られる。
クライアントはWorkerの接続先を直接知ること無く、GRAPEクラスタに登録された階乗の結果を得るサービスの接続先情報をクラスタから得た上でWorkerに接続を行い処理結果を得ることができた。
確かにこの仕組があるとシンプルにクラスタを形成することもでき、更にそこに対してマイクロサービスを登録していくことが実現できそう。BITFINEXではこれを実際にサービスに投入しているとのことなので面白い仕掛けだなと思った。
参照
- Bitfinex - Bitcoin, Litecoin and Ethereum Exchange and Margin Trading Platform
- Microservices with Grenache - Bitfinex blog