背景
関わっているプロジェクトで触る機会があったので備忘録的にメモ
Senecaとは
Node.js環境でメッセージベースのMicrorServiceを簡単に構築出来るパッケージ。メッセージはJSON形式です。
Senecaの3つの重要な機能
- Pattern matching: Instead of fragile service discovery, you just let the world know what sort of messages you care about.
- Transport independence: You can send messages between services in many ways, all hidden from your business logic.
- Componentisation: Functionality is expressed as a set of plugins which can be composed together as microservices.
パターンマッチング、独立した転送、コンポーネント化ということで、ソースコードに触れながらこれらの恩恵を感じていきます(笑)
Senecaの基本的な使い方
varseneca=require('seneca')()seneca.add('role:math,cmd:sum',(msg,reply)=>{reply(null,{answer:(msg.left+msg.right)})})seneca.act({role:'math',cmd:'sum',left:1,right:2},function(err,result){if(err)returnconsole.error(err)console.log(result)})
参考:http://senecajs.org/getting-started/
サンプルが凄くシンプルで解りやすかった。
seneca.addがアクションの登録、seneca.actがメッセージの送信。
seneca.add
seneca.add('role:math,cmd:sum',(msg,reply)=>{reply(null,{answer:(msg.left+msg.right)})})
1つ目のパラメータが処理対処とするメッセージ(JSON形式)のパターン
2つ目のパラメータが実際に処理対象のメッセージが来た時に実行するFunction(アクション)
アクションはmsgとreplyという2つのパラメータを持っていてmsgはメッセージのPlain Object、replyはコールバックでerrorとresponsdのシグネチャを持っています。
seneca.act
seneca.act({role:'math',cmd:'sum',left:1,right:2},function(err,result){if(err)returnconsole.error(err)console.log(result)})
1つ目のパラメータがメッセージ
2つ目のパラメータがコールバック
この例だとseneca.addのreply(null, {answer: (msg.left + msg.right)})
で指定された情報がfunctin(errr, rersult)
に入ってくる。
その他
seneca.prior
varseneca=require('seneca')()seneca.add('role:math,cmd:sum',function(msg,respond){varsum=msg.left+msg.rightrespond(null,{answer:sum})}).add('role:math,cmd:sum',function(msg,respond){// make an error if msg.left or msg.right is infinite valueif(!Number.isFinite(msg.left)||!Number.isFinite(msg.right)){returnrespond(newError("Expected left and right to be numbers."))}this.prior({role:'math',cmd:'sum',left:msg.left,right:msg.right,},function(err,result){if(err)returnrespond(err)result.info=msg.left+'+'+msg.rightrespond(null,result)})}).act('role:math,cmd:sum,left:1.5,right:2.5',console.log// prints { answer: 4, info: '1.5+2.5' })
priorを利用することで、メッセージに対するアクションの前に特定の処理を実行することができる。
1つ目のパラメータは事前処理を追加したいメッセージ
2つ目のパラメータは事前処理の内容
また、サンプルコードの中では1つ目のaddで追加したアクションに対して2つ目のaddでアクションのオーバーライドを行なっている。
seneca.use
require('seneca')().use(plugin,options)
useを利用することで、パッケージ化したロジックを利用することが出来る。
1つ目のパラメータは定義した関数名かプラグイン名
2つ目のパラメータは関数やプラグインに渡すオブジェクト
functionmath(options){this.add('role:math,cmd:sum',function(msg,respond){respond(null,{answer:msg.left+msg.right})})this.add('role:math,cmd:product',function(msg,respond){respond(null,{answer:msg.left*msg.right})})}require('seneca')().use(math).act('role:math,cmd:sum,left:1,right:2',console.log)
こちらが、関数名を指定したケース。
useで指定されるパッケージの場合はthisでsenecaのインスタンスにアクセス出来る。
module.exports=functionmath(options){this.add('role:math,cmd:sum',functionsum(msg,respond){respond(null,{answer:msg.left+msg.right})})this.add('role:math,cmd:product',functionproduct(msg,respond){respond(null,{answer:msg.left*msg.right})})}
// ①ファイルパスを指定するケースrequire('seneca')().use(require('./math.js')).act('role:math,cmd:sum,left:1,right:2',console.log)// ②パッケージ名を指定するケースrequire('seneca')().use('math')// finds ./math.js in local folder.act('role:math,cmd:sum,left:1,right:2',console.log)
こちらが、パッケージ名を指定したケース。
seneca.wrap
module.exports=functionmath(options){this.add('role:math,cmd:sum',functionsum(msg,respond){respond(null,{answer:msg.left+msg.right})})this.add('role:math,cmd:product',functionproduct(msg,respond){respond(null,{answer:msg.left*msg.right})})this.wrap('role:math',function(msg,respond){msg.left=Number(msg.left).valueOf()msg.right=Number(msg.right).valueOf()this.prior(msg,respond)})}
wrapを利用すると、特定のパターンにマッチしたメッセージのアクションをオーバーライドすることができる。上記ケースの場合はaddされた2つのアクションの事前処理としてmsg.left、msg.rghtを数値に変換している。
1つ目のパラメータは対象とするメッセージのパターン
2つ目のパラメータはオーバーライドする処理内容
For MicroService
module.exports=functionmath(options){this.add('role:math,cmd:sum',functionsum(msg,respond){respond(null,{answer:msg.left+msg.right})})this.add('role:math,cmd:product',functionproduct(msg,respond){respond(null,{answer:msg.left*msg.right})})this.wrap('role:math',function(msg,respond){msg.left=Number(msg.left).valueOf()msg.right=Number(msg.right).valueOf()this.prior(msg,respond)})}
require('seneca')().use('math').listen({type:'tcp',pin:'role:math'})
require('seneca')().client({type:'tcp',pin:'role:math'}).act('role:math,cmd:sum,left:1,right:2',console.log)
listenを利用することで、特定のパターンのメッセージをリッスンすることが出来る。便利!typeにはtcpやamqpなどパッケージをインストールすることで様々なタイプのメッセージを指定出来る。
clientを利用することで、特定のパターンのメッセージを指定したタイプにメッセージを発信出来る。
ここからは環境に依存するものが多いので、パラメーターの紹介は割愛。
参考:http://senecajs.org/getting-started/