本文基于上一篇文章的项目(pomelo解耦代码组织),介绍如何使用 bearcat 实现服务端热更新。

热更新的目的是在不重启服务器的情况下更新服务端代码。一个典型场景是:暴露给客户端的接口本身不需要更新,它只是对具体服务的包装;而这个服务的实现是可以热更新的。这样既实现了接口与实现的分离,又达到了热更新的目的。

架构说明

核心需求:暴露给客户端的 handler 与具体实现分离,具体实现可通过 bearcat 热更新。

  • GoodsService.js:服务端业务逻辑,可能需要热更新
  • gameHandler.js:客户端接口,无需更新,持有一个由 bearcat 维护的业务服务对象引用

由于涉及热更新,需要热更新的变量在定义时有几项约定,下面会逐一列出。

准备工作

安装 bearcat:

npm install bearcat

配置步骤

1. 在根目录新建 context.json(bearcat 配置文件)

//默认就行
{
    "name": "GameSrv",
    "scan": "app",
    "beans": []
}

2. 修改 app.js

var bearcat = require("bearcat");

bearcat.createApp([require.resolve('./context.json')], { // 刚新建的配置文件
    BEARCAT_LOGGER: 'off',
    BEARCAT_HOT: 'on',// 开启热更新,如果是off 那么不会热更新
    BEARCAT_FUNCTION_STRING: true
});

bearcat.start(function () {
    Config();
    // start app
    app.start();
});

3. 修改 gameHandler.js

其中有几个约定需要注意:

var bearcat = require("bearcat");

var Handler = function (app) {

    this.$id = "Handler"; //约定 ,this.$id= 代表该模块名字,该字段经测试 可去掉,但是官网demo 有该字段
    this.$GoodsService = null;  //  约定this.$需要更新的模块名字=null  bearcat就会把null标记的进行热更新
}


var handler = Handler.prototype;


handler.getNotify = function (msg, session, next) {

    if (!session.uid) {
        next(null, { msg: "玩家未登录!" });

        return;
    }

    next(null, { msg: "欢迎玩家" + msg.name + "进入游戏" });

};


handler.buyGoods = function (msg, session, next) {

    this.$GoodsService.buyGoods(msg.id, session.uid, next);
}


//导出约定:函数式返回 return bearcat.getBean(Handler); /Handler为该模块名字,也就是定义的function名字
module.exports = function (app) {
    return bearcat.getBean(Handler);// 此时getBean函数会给this$XXX=null的对象赋值
};

4. 修改 GoodsService.js

//该模块可能会热更新,和普通的js模块结构没什么区别,注意 导出不要new 直接导出模块名字即可
function GoodsService() {
    //  this.$id = "GoodsService";

    console.error("[GoodsService]:new GoodsService");
}

GoodsService.prototype.buyGoods = function (id, owner, next) {

    if (!owner) {
        next(null, { msg: "玩家未登录!" });
        return;
    }
    ///////////////////////////////

    if (true) {//验证购买条件 允许购买

        //修改数据库


        var sql = " insert into `goods` (`id`, `owner`) VALUES(?, ?)";

        var args = [id, owner];
        var dbclient = pomelo.app.get('dbclient');//获取全局mysql client

        console.log(dbclient);
        dbclient.query(sql, args, function (err, res) {//执行sql语句 函数insert和query等效
            if (err) { // 购买失败
                console.error("[Error]:数据库服务器错误");
                next(null, { msg: "购买失败,服务器错误!", code: 200 });

            }
            else {//购买成功
                next(null, { msg: "购买物品:#活血丹 成功", code: 200 });

            }
        });

    } else { // 不允许购买
        next(null, { msg: "你的金币不足,购买失败", code: 200 });
    }
};


module.exports = GoodsService;

验证热更新

启动服务器,运行结果正常后,修改业务逻辑代码 GoodsService.js 来测试热更新效果。例如添加一行日志:

    console.log("         ..............   new Version       .          .......");

保存修改后,可以在服务端日志中看到如下输出:

热更新成功。