Seneca란?
세네카란 마이크로서비스를 구축하기 위해 작성된 프레임워크이다. 코드에서 전송을 추상화하는 정교한 패턴 매칭 인터페이스를 마이크로서비스에 연결된다. 따라서 세네카를 이용하면 확장성이 높은 아키텍쳐를 쉽게 구축할 수 있다.
install
npm install -g seneca
패턴매칭
seneca.add 를 통해 패턴을 세네카에 패턴이 집합으로 새로운 함수를 추가한다.seneca.act 를 통해 전송된 패턴과 일치하는 서비스에 의해 명령을 실행될 세네카로 전송한다.
var seneca = require('seneca')();
seneca.add({role: 'math', cmd: 'sum'}, function(msg, respond) {
var sum = Number(msg.left) + Number(msg.right);
respond(null, {answer: sum});
});
seneca.add({role: 'math', cmd: 'product'}, function(msg, respond) {
var product = msg.left * msg.right;
respond(null, {answer: product});
});
// {answer: 3}
seneca.act({role: 'math', cmd: 'sum', left: 1, right: 2}, console.log);
// {answer: 12}
seneca.act({role: 'math', cmd: 'product', left: 3, right: 4}, console.log);
패턴 재사용
등록한 패턴을 다음과 같이 재사용할 수 있다.
seneca.add({role: 'math', cmd: 'sum'}, function(msg, respond) {
var sum = msg.left + msg.right;
respond(null, {answer: sum});
});
seneca.add({role: 'math'}, function(msg, respond) {
this.act({
role: 'math',
cmd: 'sum',
left: msg.left,
right: msg.right
}, respond);
});
//{answer: 7}
seneca.act({role: 'math', left: 3, right: 4}, console.log);
매턴 매칭은 어떻게 동작하는가?
매칭된 가장 긴 체인이 적합한 패턴이 된다.
{x:1}과 {x:1, y:2}가 패턴으로 등록되어있다고 가정해보자.
{x:1} 요청 -> {x:1}가 가장 적합
{x:1, y:2} 요청 -> {x:1, y:2}와 적합
{x:1, y:2, z:3} 요청 -> {x:1, y:2}와 적합
{y:2} -> 매칭된 패턴 없음.(앞에서부터 매칭 필요)
플러그인
아래와 같이 세네카 외부 함수를 만들고 use를 통해 세네카에 적용시켜 사용할 수 있다.
//math.js
function math(name) {
var name = name;
this.add('role:math, cmd:sum', function(msg, respond) {
respond(null, {name: Number(msg.left) + Number(msg.right)});
});
this.add('role:math, cmd:product', function(msg, respond) {
respond(null, {name: msg.left * msg.right});
});
}
//{bbo: 3}
require('seneca')()
.use(math, 'bbo')
.act('role:math, cmd:sum, left:1, right:2', console.log);
마이크로서비스 작성 예시
module.exports = function math(options) {
this.add('role:math,cmd:sum', function sum(msg, respond) {
respond(null, { answer: Number(msg.left) + Number(msg.right) });
});
this.add('role:math,cmd:product', function product(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);
});
};
위의 코드에서 주의깊게 봐야할 2개의 함수가 있다.
require('seneca')()
.use('math')
.act('role:math, cmd:sum, left:3, right:4', console.log);
require('seneca')()
.use('math')
.act('role:math, cmd:product, left:3, right:4', console.log);
서버 실행 - listen()
require('seneca')()
.use(math)
.listen({type: 'tcp', pin: 'role:math'});
curl을 이용하여 패턴과 함께 url을 실행시킨다.(url/act)
$ curl -d '{"role":"math", "cmd":"sum", "left":1, "right":2}' http://localhost:3000/act
클라이언트 접속 - client()
require('seneca')()
// 로컬에 패턴 추가
.add('say:hello', function(msg, respond) {
respond(null, {text: "Hi!"});
})
// 서버의 listen 매칭과 일치해야 한다.
// 위의 listen() 시 pin: 'role:math'로 하였으므로
// client() 에도 pin: 'role:math'가 되어야 한다.
.client({type: 'tcp', pin: 'role:math'})
// math 모듈의 패턴과 매칭
.act('role:math, cmd:sum, left:1, right:2',console.log)
// 로컬의 패턴과 매칭
.act('say:hello', console.log)
.act('role:math, cmd:product, left:3, right:3', console.log);
출력은 다음과 같을 것이다.
{text: 'hello'}
{answer: 3}
{answer: 9}
역시 비동기로 출력이 됨을 알 수가 있다.
마치며..
마이크로 서비스를 작성하기 앞서 세네카라는 프레임워크에 대해서 조사를 해보았다. 세네카를 사용하면 모듈단위로 확장할 때 패턴 매칭을 이용하기 때문에 확장성과 테스트 단위가 쉬울 것 같다. 다음 시간에는 express와 세네카를 적용해보아야겠다.