nodejs微服务

为什么选择nodejs

  • 学习门槛低
  • 易于扩展
  • 对测试友好
  • 易于部署
  • npm依赖管理
  • 有着大量与主流标准协议相集成的库

基于Seneca和PM2构建Node.js微服务

Seneca

一个用于构建微服务的框架,它使用完备的模式匹配接口来连接各个服务,从代码中将数据传输抽象出来,使得编写具有高可扩展性的软件变得x相当容易。

核心功能

  • 模式匹配:不同于脆弱的服务发现,模式匹配旨在告诉这个世界你真正关心的消息是什么
  • 无依赖传输:你可以以多种方式在服务之间发送消息,所有这些都隐藏至你的业务逻辑之后
  • 组件化:功能被表示为一组可以一起组成微服务的插件

实例

var seneca = require('seneca')();

seneca.add({role:'math',cmd:'sum'},function(msg,respond){
    var sum = msg.left + 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});
});

var respond = function(err,result){
    if(err) {
        console.error(err);
    }else{
        console.log(result);
    }
};
seneca.act({role:'math',cmd:'sum',left:1,right:2},respond);
seneca.act({role:'math',cmd:'product',left:3,right:4},respond);

控制反转

可以把seneca.add看作生产者,seneca.act看着消费者

模式匹配

var seneca = require('seneca')();
var age = require('../js/mod');

seneca.add({cmd:'age'},function(msg,respond){
    var birthdate = new Date(msg.birthdate);
    var a = age.getAge(birthdate);
    respond(null,{age:a});
});

seneca.act({cmd:'age',birthdate:'2000-10-10'},function(err,res){
   if(err){
       console.error(err);
   }
   console.log(res);
});

服务发布

module.exports = function math(options) {

    this.add('role:urp,cmd:grade', function sum(msg, respond){
        var res = {
            bh:msg.bh,
            courseId:123,
            grade:99
        };
        console.log(res);
        respond(null,res );
    });


    this.wrap('role:urp', function (msg, respond) {
        this.prior(msg, respond)
    });
};

require('seneca')()
    .use('urp')
    .listen(7002,'127.0.0.1');

PM2

PM2是一款可以为服务器实例带来负载均衡功能的生产级别的进程管理器,通过PM2可以自由伸缩Node.jsy应用。此外,它还能确保进程持续进行,解决Node.js单线程模型带来的副作用:一个没有被捕获的异常通过杀死线程,进而杀死整个应用。
其实PM2就是一个任务执行器,当应用出现异常意外退出时,可以重启来确保能正常运行,与之类似的还有forever。

主要特性

  • 内建负载均衡
  • 后台运行
  • 和nginx的reload命令一样可以快速重载
  • 控制台检测
  • 提供HTTP API

主要命令

  • pm2 list 列出由pm2管理的所有进程信息
  • pm2 monit 监视每个node进程的CPU和内存的使用情况
  • pm2 show id 输出进程信息
  • pm2 logs 显示所有进程日志
  • pm2 stop all 停止所有进程
  • pm2 restart all 重启所有进程
  • pm2 reload all 0秒停机重载进程
  • pm2 stop 0 停止指定的进程
  • pm2 restart 0 重启指定的进程
  • pm2 startup 产生 init 脚本 保持进程活着
  • pm2 web运行健壮的 computer API endpoint
  • pm2 delete 0 杀死指定的进程
  • pm2 delete all 杀死全部进程

API 聚合

stu.js

module.exports = function math(options) {

    this.add('role:stu,cmd:skipClassList', function sum(msg, respond) {
        var bh = msg.bh;
        respond(null,
            [{"bh":bh,"courseId":"123","time":1451577600000},
                {"bh":bh,"courseId":"123","time":1454342400000},
                {"bh":bh,"courseId":"123","time":1456934400000},
                {"bh":bh,"courseId":"123","time":1459699200000},
                {"bh":bh,"courseId":"123","time":1462377600000}]);
    });

    this.add('role:stu,cmd:stuInfo', function sum(msg, respond) {

        var bh = msg.bh;
        var res = {
            "bh":bh,
            "name":"小明",
            "sex":"男",
            "age":"24"
        }
        console.log(res);
        respond(null, res);
    });



    this.wrap('role:stu', function (msg, respond) {
        this.prior(msg, respond)
    })
};

stu-server.js

require('seneca')()
    .use('stu')
    .listen(7001,'127.0.0.1');

urp.js

module.exports = function math(options) {

    this.add('role:urp,cmd:grade', function sum(msg, respond){
        var res = {
            bh:msg.bh,
            courseId:123,
            grade:99
        };
        console.log(res);
        respond(null,res );
    });


    this.wrap('role:urp', function (msg, respond) {
        this.prior(msg, respond)
    });
};

urp-server.js

require('seneca')()
    .use('urp')
    .listen(7002,'127.0.0.1');

api.js

var senceaStu = require('seneca')().client({
    host : "127.0.0.1",
    port : 7001
});
var senceaUrp = require('seneca')().client({
    host : "127.0.0.1",
    port : 7002
});

module.exports = function api(options) {


    this.add('role:api,path:stu', function (msg, respond) {
        var operation = msg.args.params.operation;
        var param = msg;
        param.cmd = operation;
        //console.log(param)
        senceaStu.act('role:stu', param, respond)
    });

    this.add('role:api,path:urp', function (msg, respond) {
        //var operation = msg.args.params.operation;
        var operation = msg.args.params.operation;
        var param = msg;
        param.cmd = operation;
        //console.log(param)
        senceaUrp.act('role:urp', param, respond)
    });

    this.add('init:api', function (msg, respond) {
        this.act('role:web',{routes:{
                prefix: '/api',
                pin: 'role:api,path:*',
                map: {
                    stu: { GET:true, suffix:'/{operation}' },
                    urp: { GET:true, suffix:'/{operation}' }
                }
            }}, respond)
    })

}

index.js

const Hapi = require('hapi');
const Seneca = require('seneca');
const SenecaWeb = require('seneca-web');

const config = {
    adapter: require('seneca-web-adapter-hapi'),
    context: (() => {
        const server = new Hapi.Server();
        server.connection({
            port: 8888
        });

        server.route({
            path: '/routes',
            method: 'get',
            handler: (request, reply) => {
                const routes = server.table()[0].table.map(route => {
                    return {
                        path: route.path,
                        method: route.method.toUpperCase(),
                        description: route.settings.description,
                        tags: route.settings.tags,
                        vhost: route.settings.vhost,
                        cors: route.settings.cors,
                        jsonp: route.settings.jsonp,
                    }
                })
                reply(routes)
            }
        });

        return server;
    })()
};

const seneca = Seneca()
    .use(SenecaWeb, config)
    //.use('stu')
    .use('api')
    //.client({type: 'tcp', pin: 'role:math'})
    .ready(() => {
        const server = seneca.export('web/context')();
        server.start(() => {
            server.log('server started on: ' + server.info.uri);
        });
    });
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐