[关闭]
@wsd1 2017-04-05T20:33:54.000000Z 字数 4110 阅读 1432

ucast后端设计:skynet_uc部分

ucast skynet 201704


本文章作为设计指导文档,内含不少特定名词,假设读者理解 ot协议传输层大致逻辑、JSON-RPC的命名空间 这些背景知识,否则看起来可能晦涩难读。
若为使用故,可以仅看图和代码即可。

skynet_uc是skynet ucast的字面组合,ucast使用skynet框架构造物联网系统的服务端部分。传输层面用C撰写,分发层面使用lua撰写。
分发层面不仅支持独立撰写lua业务逻辑,还加入了deepstream的客户端,为业务层提供实时化的能力。

参考资源

Github项目:
https://github.com/cloudwu/skynet

wiki:
https://github.com/cloudwu/skynet/wiki

云大的心路历程(skynet改了很多,关注作者的思路,从以前版本可以发掘很多好东西,比如JSON支持):
http://blog.codingnow.com/eo/skynet/

Lua5.3的文档:
http://cloudwu.github.io/lua53doc/manual.html#6.4.1

代码功能与结构

目前的架构中代码关系如下:
uCast后端架构

1、配置文件 “examples/config.ucast” 指定“ucast.lua”作为引导代码;后者引导其他部件;

2、首先引导 “test/ds.lua”(deepstream客户端);

3、其次引导 “service/ot_router.lua”(上行方法分发器);

4、再次引导 “service/ot-agent/otc.lua”(_otc.xxx RPC provider)

5、“3rd/lua-ot/lua-ot.c” 实现了 “otu_service.c” 和 lua层面的pack、unpack功能;

6、“lualib/ot/ds_util.lua” 和 “lualib/ot/ot_util.lua” 用来提供api调用时的一些辅助功能;

7、“lualib/websocket.lua” 和 “test/ds.lua” 提供了一个deepstream客户端

“test/ds.lua” 和 “lualib/websocket.lua”逻辑

实现了ds服务器的客户端,通过“lualib/ot/ds_util.lua”为其他业务模块提供ds服务。比如,“ot_router.lua”、“otc.lua”都是通过其和外部沟通的。

“service/ot_router.lua”逻辑

这个代码会引导C实现的otu模块(otu_service.c),并且在之后处理otu上行的JSON_RPC;它提供了注册接口接受其他业务代码注册方法空间,上行RPC会转交给其他业务,比如service/ot-agent/otc.lua 就是专门处理 _otc.xxx类型rpc的业务模块。

“service/ot-agent/otc.lua”逻辑

otc在ucast.lua中被引导,同时也被触发向ot_router安装了自己的命名空间。
其会与ds模块建立联系,listen "event/ot/info/"和"event/ot/devlog/"两个event的订阅;若是有有客户端订阅了某个did的相应事件,就会被记录下来,一旦上行有_otc.info 和 _otc.devlog的请求,就会被emit到ds。

“otu_service.c”逻辑

这个是工作量最大的一个部分:向下实现了传输层面对UDP报文的处理和OT协议的实现,向上实现了和skynet lua层面交互接口。 其还应该包括 “3rd/lua-ot/lua-ot.c” ,完整的实现了与业务层面的对接。

组件关系接口

ot_router为ds-client模块提供的接口

ot_router.lua 作为与设备最近的lua层逻辑,负责分发上行ot-rpc的信息,其为ds-client实现了如下接口:

ot_router.lua 向 ds provide了 “rpc_otu_call” 接口。借此可以实现:

1、下行RPC控制设备

其他ds客户端可以make rpc实现下行控制设备,读取设备统计数据这些功能。

设备下行访问示例:
下行访问设备侧 uCast.info 接口。

  1. //该代码演示 其他ds客户端 如何访问到ot_router.lua的RPC服务
  2. var ds_rpc_param = {
  3. D:255, //D字段指示下发设备DID
  4. R:{ //R字段指示是下行请求
  5. method: "uCast.info",
  6. params:{},
  7. //json-rpc id,用于标定请求和回应对应关系
  8. id:234567 //只有需要RPC有返回时才需要该字段
  9. }
  10. }
  11. client.rpc.make("rpc_otu_call", ds_rpc_param, function(err, ret) {
  12. txt.innerText = JSON.stringify(ret, null, 2)
  13. console.log(ret)
  14. })

2、获取传输层设备统计信息

该信息来源于otu_service.c,其负责的传输层面对上下行交互过程中诸多的细节做了统计,"ot_router.lua"提供了访问接口,通过ds服务的 “rpc_otu_call” RPC可以获取该信息。

  1. var ds_stat_param = {
  2. D:255 //D字段指示下发设备DID
  3. // <-- 注意这里,没有 R字段
  4. }
  5. client.rpc.make("rpc_otu_call", ds_stat_param, function(err, ret) {
  6. txt.innerText = JSON.stringify(ret, null, 2)
  7. console.log(ret)
  8. })
  9. /* 结果类如:
  10. {
  11. "online": true, //当前是否在线
  12. "reqCost10": 11, //下行请求回应间隔时间近10次平均
  13. "reqCost100": 10, //下行请求回应间隔时间近100次平均
  14. "dnReq": 2, //下行请求数量
  15. "dnNotify": 0, //下行通知数量
  16. "dnReqFail": 0, //下行请求失败数量
  17. "dnReqOK": 2, //下行请求一次成功数量
  18. "upReq": 2, //上行请求数量
  19. "upReqDrop": 0, //上行请求被抛弃的数量(可能是busy)
  20. "upNotify": 46, //上行通知数量
  21. "upKplv": 29, //上行保活数量
  22. "upSync": 8, //上行同步数量
  23. "lastseen": 1491330240, //最近出现的utc时间(不算sync)
  24. "synDiff16": 0, //最近交互同步时差(绝对值)16次平均
  25. "synDiff128": 0 //最近交互同步时差(绝对值)128次平均
  26. }
  27. */

otc.lua 为ds-client模块提供的接口

otc实现了 _otc命名空间 的业务逻辑,其实现了包括
“_otc.login”、“_otc.info”、“_otc.devlog” 三个上行ot-rpc接口。

“_otc.login” 用于实现设备登录;
“_otc.devlog” 用于设备提交调试日志信息;
“_otc.info” 用于实现设备上行更新信息;

后两个暴露给ds-client,使ds客户端可以方便的订阅设备的调试日志和更新信息。具体如下:

devlog 订阅通道是: “event/ot/devlog/xxx”,xxx表示设备did,示例代码:

  1. client.event.subscribe('event/ot/devlog/255', function(value) {
  2. txt.innerText = JSON.stringify(value)
  3. })
  4. /* 返回值示例:这是推流器发出的log
  5. "000:32:14.424: [Rtmpc] @80241B/s"
  6. */

相对应的,在嵌入式Addon开发过程中,可以如下调用向云端_otc.devlog发送信息:

  1. api_ot.otc_log("[Rtmpc] @%dB/s.", Bps);

该API可以方便的提交字符串到云端,服务器侧通过订阅“event/ot/devlog/xxx”来获取该字符串。
注意,该过程通过 “_otc.devlog” 上行notify通知实现,没有添加method_id,没有重发逻辑保证抵达,建议作为调试手段使用。
同时请注意不要过于频繁调动该API,否则会被服务端禁止。

info 订阅通道是: “event/ot/info/xxx”,xxx表示设备did,示例代码:

  1. client.event.subscribe('event/ot/info/255', function(value) {
  2. txt.innerText = JSON.stringify(value)
  3. })
  4. /* 返回值示例:
  5. {
  6. "life": 15500,
  7. "mm": {
  8. "max": 3886796,
  9. "used": 3785700,
  10. "total": 9277124
  11. },
  12. "mac": "CA:09:00:0F:14:16",
  13. "netif": {
  14. "localIp": "192.168.1.179",
  15. "mask": "255.255.255.0",
  16. "gw": "192.168.1.1",
  17. "gw_mac": "8C:BE:BE:40:0A:A5"
  18. },
  19. "otu_stat": "20,20,16,4,12,5"
  20. }
  21. */

上行ot-rpc接口“_otc.info”背景:

设备侧使用该rpc接口更新自己的设备状态,缺省设计是每30分钟或者切换服务器“_otc.login”之后调用该接口。

更加详细可以参考 “ot协议传输层设计”细节。

内存风险管控

20170406:
gc之后:
mem

:0000000a 79.54 Kb (snlua ds)
:0000000b 50.33 Kb (snlua ot-router)
:0000000d 46.17 Kb (snlua ot-agent/otc)
:0000000c otu

多次下行call后,内存均增长,不过gc指令可以使其完全恢复原状。因此判断,通过DS下行call设备方法 lua层面路径正常。

推流后设备定时上行devlog,页面订阅后,内存均增长。但gc后恢复原状,判断上行订阅 lua层面路径正常。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注