如何针对 Node 运行时编写 js 脚本

声明

  • 本文测试所用设备系统为 Ubuntu16.04
  • node 版本为 8.5
  • 模拟 MQTT client 行为的客户端为 MQTTBox
  • 本文中基于 Hub 模块创建的服务名称为 localhub 服务。并且针对本文的测试案例中,对应的 localhub 服务、函数计算服务以及其他服务的配置统一如下:
  1. # 本地 Hub 配置
  2. # 配置文件位置: var/db/baetyl/localhub-conf/service.yml
  3. listen:
  4. - tcp://0.0.0.0:1883
  5. principals:
  6. - username: 'test'
  7. password: 'hahaha'
  8. permissions:
  9. - action: 'pub'
  10. permit: ['#']
  11. - action: 'sub'
  12. permit: ['#']
  13.  
  14. # 本地 baetyl-function-manager 配置
  15. # 配置文件位置: var/db/baetyl/function-manager-conf/service.yml
  16. hub:
  17. address: tcp://localhub:1883
  18. username: test
  19. password: hahaha
  20. rules:
  21. - clientid: localfunc-1
  22. subscribe:
  23. topic: node
  24. function:
  25. name: sayhi
  26. publish:
  27. topic: t/hi
  28. functions:
  29. - name: sayhi
  30. service: function-sayhi
  31. instance:
  32. min: 0
  33. max: 10
  34. idletime: 1m
  35.  
  36. # node function 配置
  37. # 配置文件位置: var/db/baetyl/function-sayjs-conf/service.yml
  38. functions:
  39. - name: 'sayhi'
  40. handler: 'index.handler'
  41. codedir: 'var/db/baetyl/function-sayhi'
  42.  
  43. # application.yml配置
  44. # 配置文件位置: var/db/baetyl/application.yml
  45. version: v0
  46. services:
  47. - name: localhub
  48. image: hub.baidubce.com/baetyl/baetyl-hub
  49. replica: 1
  50. ports:
  51. - 1883:1883
  52. mounts:
  53. - name: localhub-conf
  54. path: etc/baetyl
  55. readonly: true
  56. - name: localhub-data
  57. path: var/db/baetyl/data
  58. - name: localhub-log
  59. path: var/log/baetyl
  60. - name: function-manager
  61. image: hub.baidubce.com/baetyl/baetyl-function-manager
  62. replica: 1
  63. mounts:
  64. - name: function-manager-conf
  65. path: etc/baetyl
  66. readonly: true
  67. - name: function-manager-log
  68. path: var/log/baetyl
  69. - name: function-sayhi
  70. image: hub.baidubce.com/baetyl/baetyl-function-node85
  71. replica: 0
  72. mounts:
  73. - name: function-sayjs-conf
  74. path: etc/baetyl
  75. readonly: true
  76. - name: function-sayjs-code
  77. path: var/db/baetyl/function-sayhi
  78. readonly: true
  79. volumes:
  80. # hub
  81. - name: localhub-conf
  82. path: var/db/baetyl/localhub-conf
  83. - name: localhub-data
  84. path: var/db/baetyl/localhub-data
  85. - name: localhub-log
  86. path: var/db/baetyl/localhub-log
  87. # function manager
  88. - name: function-manager-conf
  89. path: var/db/baetyl/function-manager-conf
  90. - name: function-manager-log
  91. path: var/db/baetyl/function-manager-log
  92. # function node runtime sayhi
  93. - name: function-sayjs-conf
  94. path: var/db/baetyl/function-sayjs-conf
  95. - name: function-sayjs-code
  96. path: var/db/baetyl/function-sayjs-code

Baetyl 官方提供了 Node 运行时,可以加载用户所编写的 js 脚本。下文将针对 js 脚本的名称,执行函数名称,输入,输出参数等内容分别进行说明。

函数名约定

js 脚本的名称可以参照 js 的通用命名规范,Baetyl 并未对此做特别限制。如果要应用某 js 脚本对某条 MQTT 消息做处理,则相应的函数运行时服务的配置如下:

  1. functions:
  2. - name: 'sayhi'
  3. handler: 'index.handler'
  4. codedir: 'var/db/baetyl/function-sayhi'

这里,我们关注 handler 这一属性,其中 index 代表脚本名称,后面的 handler 代表该文件中被调用的入口函数。

  1. function-sayjs-code/
  2. └── index.js

更多函数运行时服务配置请查看 函数运行时服务配置释义

参数约定

  1. exports.handler = (event, context, callback) => {
  2. callback(null, event);
  3. };

Baetyl 官方提供的 Node 运行时支持 2 个参数: event 和 context,下面将分别介绍其用法。

  • event:根据 MQTT 报文中的 Payload 传入不同参数
    • 若原始 Payload 为一个 Json 数据,则传入经过 json.loads(Payload) 处理后的数据;
    • 若原始 Payload 为字节流、字符串(非 Json),则传入原 Payload 数据。
  • context:MQTT 消息上下文
    • context.messageQOS // MQTT QoS
    • context.messageTopic // MQTT Topic
    • context.functionName // MQTT functionName
    • context.functionInvokeID //MQTT function invokeID
    • context.invokeid // 同上,用于兼容 CFC提示:在云端 CFC 测试时,请注意不要直接使用 Baetyl 定义的上下文信息。推荐做法是先判断字段是否在 context 中存在,如果存在再读取。

Hello World!

下面我们实现一个简单的 js 脚本,目标是为每一条流经需要用该 js 脚本进行处理的 MQTT 消息附加一条 hello world 信息。对于字典类消息,将其直接返回即可,对于非字典类消息,则将之转换为字符串后返回。

  1. #!/usr/bin/env node
  2.  
  3. exports.handler = (event, context, callback) => {
  4. result = {};
  5.  
  6. if (Buffer.isBuffer(event)) {
  7. const message = event.toString();
  8. result["msg"] = message;
  9. result["type"] = 'non-dict';
  10. }else {
  11. result["msg"] = event;
  12. result["type"] = 'dict';
  13. }
  14.  
  15. result["say"] = 'hello world';
  16. callback(null, result);
  17. };
  • 发送字典类数据:../_images/write-node-script-dict.png发送字典类数据

  • 发送非字典类数据:../_images/write-node-script-none-dict.png发送非字典类数据

如上,对于一些常规的需求,我们通过系统 Node 环境的标准库就可以完成。但是,对于一些较为复杂的需求,往往需要引入第三方库来完成。如何解决这个问题?我们将在 如何针对 Node 运行时引入第三方包 小节详述。