[date: 2018-07-03 21:37] [visits: 25]

Node.js Server之三:业务框架实战

此文是Node.js Server分享系列中的第三篇,上篇文章讲述了自己对业务框架的理解,在文章末尾提到用代码写一个简单的业务框架,具体实现的指导思想在前文讲过了,这篇就不再重复,这篇主要关注从代码层面如何实现,同时限于时间与篇幅,框架尽量精简,功能点到为止。

特意为此文写的示例代码放在Github上,此文的基础也基于该代码,有兴趣的朋友可能要花个10-20分钟看一遍,才能理解后文的讲述。

目录

精简版代码的目录结构如下:

example
├── app
│   ├── demo
│   │   ├── controller.js
│   │   ├── init.js
│   │   ├── manifest.json
│   │   └── service.js
│   └── exp
│       ├── controller.js
│       ├── init.js
│       ├── manifest.json
│       └── service.js
├── fw
│   ├── app.js
│   ├── appManage.js
│   ├── bootstrap.js
│   ├── httpServer.js
│   └── index.js
└── package.json

fw中是业务框架代码,目前完成了启动server,注册路由,以及提供跨模块调用,app中是两个最最简单应用举例。如果只看app中的代码,你无从得知它们是如何有效工作的,但如果让你写一个简单API,大概也能猜出要做些什么:

当我第一次在egg.js下面写API时,与第一次在app中写API是一样的感觉,只不过是egg.js非常强大,而上述的fw则弱的不行,但本质上它们都是业务框架,提供写业务代码的约束和实现指导。

fw

fw中的4个文件,其中app、appManage、bootstrap在前一篇文章中有详细描述,这里的httpServer是将express做了一层包装,可以选择其他Node.js Http基础框架,或者自己实现也是OK的。

App

App目前只实现了路由注册与外部服务调用,缺少中间件注册与restart、reload等生命周期方法的实现。

如果你理解了routes的注册逻辑,那中间件的实现,照着抄就行,比如在manifest中指定route所生效的中间件,然后start的过程中通过server.use进行注册即可。

AppManage

AppManage中缺少中间件注册和生命周期方法的问题与App是类似的,就不展开了。

但AppManage中的serviceCall是可以进行扩展的,目前利用appManage持有所有app的引用完成跨模块调用,这个可以扩展成利用RPC框架完成这个功能,具体是什么意思呢?

拿这个最基础的例子来说,exp下有一个route: /api/exp/random,它跨模块调用了demo模块下的echo服务,如果demo和exp部署在不同实例,那这个跨模块调用就会失败,而把RPC集成进来的话,这个调用就能实现。

有一个概念叫“微服务”,serviceCall如果做成RPC,也就是往这方面靠吧,但自己也缺乏经验,难以窥视微服务的本质,就不继续展开了。

bootstrap

目前bootstrap在这里没体现什么重要作用,只是简单作为桥梁用来启动Server。

manifest.json

manifest.json现在看起来有些简单,但已经体现出了其核心作用,剥离应用对底层框架甚至业务框架的依赖。

初看示例代码,会觉得manifest.json是依赖业务框架的,其实恰恰相反,manifest.json才是起点,每一个想要业务框架实现的功能与特性,第一步都是考虑manifest.json中应该如何表达,最终manifest.json是可以独立存在的,业务框架不过是将manifest.json翻译了一遍,有必要的话可以随时更改翻译的方式,只需保持manifest.json的含义得到体现。

目前示例代码中的manifest.json是相对简单的,如果有能力喜欢折腾的话,它一定会变复杂,比如下面这个:

{
    "name": "fsfl",
    "path": "/fsfl/api",
    "init": "init",
    "converters": [{
        "mode": "dev",
        "name": "user.mockClient"
    }],
    "routes": [{
        "name": "pay notify",
        "comment": "微信支付成功回调",
        "path": "pay/notify",
        "method": "post",
        "handler": "applet.payNotify",
        "body": "text/xml",
        "res": "text/xml"
    }],
    "child": [{
        "name": "fsfl-admin",
        "path": "admin",
        "converters": ["user.admin"],
        "interceptors": ["auth.noAdminUser"],
        "child": [{
            "name": "fsfl-admin-upload",
            "path": "upload",
            "routes": ["admin/upload"]
        }, {
            "name": "fsfl-admin-goods",
            "path": "goods",
            "routes": ["admin/goods"]
        }...]
    }, {
        "name": "fsfl-client",
        "path": "client",
        "converters": ["user.clientTicket", "user.client"],
        "interceptors": ["auth.noAppletTicket"],
        "child": [{
            "name": "fsfl-client-goods",
            "path": "goods",
            "routes": ["client/goods"]
        }, {
            "name": "fsfl-client-prize",
            "path": "prize",
            "routes": ["client/prize"]
        }...]
    }],
    "services": [{
        "name": "closeOrderById",
        "impl": "order.closeById"
    }, {
        "name": "finishOrderById",
        "impl": "order.finishById"
    }, {
        "name": "pushCheckInNotify",
        "impl": "push.pushCheckInNotify"
    }, {
        "name": "onlineBanner",
        "impl": "star.onlineBanner"
    }...]
}

总结

已经有很多成熟的框架了,为什么还要去捣鼓这些?可以说是为了学习,但也不单单是为了学习,我觉得这就像是windows与linux,我更喜欢linux。