写点什么

实现微信小程序编译和运行环境系列 (核心篇二)

  • 2020-06-22
  • 本文字数:4005 字

    阅读完需:约 13 分钟

实现微信小程序编译和运行环境系列(核心篇二)

在上文中我们有点到小程序开发者工具里面的消息是通过 websocket 协议发送和接受处理的,当然这个不是凭空而说的,是在小程序的逻辑层 appservice.js 源码里面有代码表明的,至于它的消息格式还有一部分我没有列出来,比如它的数据分析和上报他们自己服务器的一些消息格式可以先先需要关注。


下面还是先给大家展示一下流程找到 appservice.js 源码文件



可以看到它的链接地址,数据发送和接收的部分代码,由于图片尺寸问题我折叠了部分代码,大家可以自己去细看看。


我还是先简述一些 webstocket 的知识,可能部分同学对这方面不是很熟悉。细节 webstocket 内容不会在本文描述,后期会写一篇专门的介绍。

websocket 是什么

其实这些内容我们通过谷歌搜索可以查阅很多材料,但有没有真正理解可以在自己项目里进行灵活设计运用还是只是简单使用文档 api,还是要靠自己多探索思考一些。


下图为webstockrt协议



可以理解为:WebSocket 协议允许在运行于受控环境中的不受信任代码的用户代理与已选择从该代码进行通信的远程主机之间进行双向通信。简单点描述就是:客户端和服务器之间存在持久连接,而且双方都可以随时随地相互发送数据

为什么用 websocket

一项新规范或者一门新技术的诞生肯定是为了解决或者完善前面方案的不足,这样才能一直进步下去。


在没有 websocket 之前我们采用 http 用的很好,但是随着一些应用的要求像聊天 股票 游戏 这种对实时性数据要求高的系统,才用 HTTP 协议发送数据的话只能有客户端单方面进行请求,服务端响应获取最新数据,如果服务端的数据变换很快比如股票的信息,因此只能定时去请求,就出出现效率低 浪费资源而且数据还不实时同步的情况,为了解决这些问题通过研究 websocket 协议就闪亮登场了。


websocket 具备的一些优点:


  • 支持双向通信,具有很强的实时性

  • 对二进制的支持比较友好

  • 相比与 http 协议的控制开销要少很多

  • 用户可以自由的扩展协议,自定义子协议例如(wss)

如何使用 websocket

这个点比较广泛一个新方案新技术的产生都会经过由浅入深的过程发展,主要看大家门自己的具体设计和使用了,下面一些链接知识点可以让大家先了解这个概念和基础使用,本章节不在这里衍生更多 websocket 相关内容。


(大家如果想对 websocket 深入学习感兴趣 希望可以关注我后面的 websocket 专栏文章)




这个是一个比较简单的可以在线看效果的网页


如果有同学希望自己动手试试的话,我在自己的 github 仓库写了一个最简化的服务端和客户端的案例,一共 10 多行代码比较方便,有兴趣的朋友可以看下案例地址


执行 index.js 后效果如下:



下面的内容我会结合在实现这个小程序运行环境里面的对于 websocket 的一些运用设计和部分代码展示。


我们回到主题先在源码 appservice.js 的发送和接收的地方添加了一些日志保存,这里一定要彻底退出工具进程在打开不然是不起作用的。


然后我们从新进入开发者工具打开一个小程序项目,我打开的是一个官方的云开发项目列子可以看到:



通过这个图我们可以看出一些信息先给大家简单介绍一下:


数据发送部分


send===>{"command":"APPSERVICE_INVOKE","data":{"api":"operateWXData","args":{"data":{"api_name":"qbase_commapi","data":{"qbase_api_name":"tcbapi_init","qbase_req":"{\"trace_user\":true}","qbase_options":{},"qbase_meta":{"session_id":"1587696384156","sdk_version":"wx-miniprogram-sdk/2.9.5 (1578926697000)"},"cli_req_id":"1587696386661_0.5287857917854695"},"operate_directly":false},"isImportant":false,"requestInQueue":false,"apiName":"qbase_commapi","reqData":{"qbase_api_name":"tcbapi_init","qbase_req":"{\"trace_user\":true}","qbase_options":{},"qbase_meta":{"session_id":"1587696384156","sdk_version":"wx-miniprogram-sdk/2.9.5 (1578926697000)"},"cli_req_id":"1587696386661_0.5287857917854695"}},"callbackID":20}}
复制代码


可以观察到一些字段和对象(这个是一个普通云开发项目默认打开的时候的状态,不做任何操作是个例子对象是比较复杂的)


  • command

  • data

  • api

  • args

  • data

  • api_name

  • qbase_api_name

  • qbase_req

  • callbackID


看到这个 api operateWXData 可能大家不是很熟悉,因为这个 api 微信没有对外的是内部使用的,这个不是我们现在要讲的重点,我们现在要描述的是 webstocket 相关的,至于 api 的实现会在下文如何实现小程序对外 api 来描述讲解,我们在这里只要知道他的消息传输格式就可以了


  • command 消息类型

  • data 各种数据组合

  • callbackID 标示这个很重要


数据接收部分


<====12receive {"command":"APPSERVICE_INVOKE_CALLBACK","data":{"callbackID":20,"res":{"errMsg":"operateWXData:ok","data":{"data":"{\"baseresponse\":{\"errcode\":0,\"stat\":{\"qbase_cost_time\":141}},\"tcb_api_list\":[{\"apiname\":\"tcbapi_db_adddocument\",\"status\":1},{\"apiname\":\"tcbapi_callfunction\",\"status\":1},{\"apiname\":\"tcbapi_component_gettempfileurl\",\"status\":1},{\"apiname\":\"tcbapi_db_countdocument\",\"status\":1},{\"apiname\":\"tcbapi_db_deletedocument\",\"status\":1},{\"apiname\":\"tcbapi_deletefile\",\"status\":1},{\"apiname\":\"tcbapi_downloadfile\",\"status\":1},{\"apiname\":\"tcbapi_gettempfileurl\",\"status\":1},{\"apiname\":\"tcbapi_db_querydocument\",\"status\":1},{\"apiname\":\"tcbapi_db_setdocument\",\"status\":1},{\"apiname\":\"tcbapi_slowcallfunction\",\"status\":1},{\"apiname\":\"tcbapi_slowcallfunction_v2\",\"status\":1},{\"apiname\":\"tcbapi_traceuser\",\"status\":1},{\"apiname\":\"tcbapi_uploadfile\",\"status\":1},{\"apiname\":\"tcbapi_db_updatedocument\",\"status\":1},{\"apiname\":\"tcbapi_init\",\"status\":1}],\"config\":{\"db_doc_size_limit\":524288,\"upload_max_file_size\":52428800,\"get_temp_file_url_max_requests\":50,\"call_function_poll_max_retry\":10,\"call_function_max_req_data_size\":5242880,\"call_function_client_poll_timeout\":15000,\"call_function_valid_start_retry_gap\":100000}}"}}}}
复制代码


对比可以看出在上面核心篇里面讲的内容:


send===>   "command":"APPSERVICE_INVOKE" "callbackID":20receive===>"command":"APPSERVICE_INVOKE_CALLBACK" "callbackID":20
复制代码


APPSERVICE_INVOKE 的消息类型是 service 层发送给 service 进行接收处理

代码实现浏览器运行环境 websocket 服务通信设计

这边采用 node 方式来启动的服务先创建一个服务端:


const ws = require('ws');const EventEmitter = require('events');class SocketServer extends EventEmitter {  constructor (options) {    super();    this.port = options.port;    this.wss = new ws.Server({ port: this.port });    this.socketClientMap = new SocketClientMap();  }
async start () { this.wss.on('connection', ws => { this.socketClientMap.addSocketClient(ws); ws.on('close', () => { this.socketClientMap.removeSocketClient(ws.protocol); });
ws.on('message', async message => { await this.handle(message); }); });
this.on(SEND_MSG_TO_CONTROLLER, (message) => { this.sendMessageToController(message); });
this.on(SEND_MSG_TO_SPECIAL_WEBVIEW, ({ webviewId, message }) => { this.sendMessageToSpecialWebview(webviewId, message); }); this.running = true; }}
复制代码


创建客户端链接发送和接收:


const WebSocket = require('ws');class SocketClient {  constructor (ws) {    this.ws = ws;    this.msgQueue = [];  }
setWebSocket (ws) { this.ws = ws; this.msgQueue.forEach(msg => { this.ws.send(JSON.stringify(msg)); }); this.msgQueue = []; }
removeWebSocket () { this.ws = null; }
send (msg) { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.msgQueue.push(msg); } else { this.ws.send(JSON.stringify(msg)); } }}
复制代码


上面两个类文件就是比较简单的服务和客户端的创建。这里创建了一个 client 集合类:


class SocketClientMap {  constructor () {    this.socketClients = new Map();  }
addSocketClient (ws) { let socketClient = this.socketClients.get(ws.protocol); if (!socketClient) { socketClient = new SocketClient(ws); } else { socketClient.setWebSocket(ws); } this.socketClients.set(ws.protocol, socketClient); }
getSocketClient (protocol) { let socketClient = this.socketClients.get(protocol); if (!socketClient) { socketClient = new SocketClient(protocol); this.socketClients.set(protocol, socketClient); } return socketClient; }
removeSocketClient (protocol) { this.socketClients.delete(protocol); }
loop (cb) { this.socketClients.forEach((value, key) => cb(value, key)); }};
复制代码


新添加的一个 addSocketClient 方法


表示如果 SocketClient 不存在,则根据 ws 创建一个新的 SocketClient,否则,将旧的 ws 替换为新的 ws,这样消息队列中的消息就可以被替换后立即发送到新的 ws,保证可用性。


getSocketClient 方法


调用这个函数总是可以返回一个 SocketClient 实例,以便用户可以在任何时候发送消息。


上文点主要关注的就是消息的格式内容组成和几个接收方和发送方的顺序,下篇我通过几个大家常用的对外 api,用具体代码实现来给大家描述下具体过程。


2020-06-22 11:082037

评论

发布
暂无评论
发现更多内容

如何高效利用YashanDB数据库进行数据挖掘

数据库砖家

如何进行YashanDB的安全审计

数据库砖家

微信小游戏开发的技术框架

北京木奇移动技术有限公司

软件外包公司 游戏外包公司 游戏开发公司

为AI推理需求的未来做准备:第一部分

ScaleFlux

大数据-09-HDFS集群 JavaClient 代码上手实战!详细附代码 安装依赖 上传下载文件 扫描列表 PUT GET 进度条显示

武子康

大数据 hdfs mapreduce hive

哈尔滨企业如何做信息安全等级保护工作?

等保测评

YashanDB与大数据分析平台集成方案详解

数据库砖家

什么是WMS系统?WMS仓库管理系统到底具备哪些功能?

积木链小链

数字化转型 wms 仓库管理

OCR 识别质量如何影响 RAG 系统的性能?有何解决办法?

Baihai IDP

AI OCR LLM Baihai IDP 检索增强生成

如何利用YashanDB数据库加强数据分析能力

数据库砖家

轻量级防护软件,火绒安全软件下载,病毒查杀、防护中心、访问控制,拦截黑客入侵、网络钓鱼攻击

小焱

杀毒软件 防护软件 火绒 杀毒

中烟创新数字档案:开启烟草行业“智”理新动能

中烟创新

为AI推理需求的未来做准备:第二部分

ScaleFlux

开源夜莺支持MySQL数据源,更方便做业务指标监控了

巴辉特

夜莺监控 运维监控 开源监控

如何利用YashanDB实现业务数据的高效管理?

数据库砖家

Go 语言中如何操作二维码?

左诗右码

综合治理系统(源码+文档+讲解+演示)

深圳亥时科技

Unity开发游戏的技术框架

北京木奇移动技术有限公司

游戏开发 软件外包公司 游戏外包

目前黑龙江对等保三级要求是什么?

等保测评

YashanDB支持的数据导入导出工具介绍

数据库砖家

企业为什么选择YashanDB数据库?七大核心优势解析

数据库砖家

巧用YashanDB数据库实现海量数据快速查询

数据库砖家

确保YashanDB高可用性的实用策略

数据库砖家

MyCat应用实战:分布式数据库中间件的实践与优化(篇幅一)

肖哥弹架构

Java mycat

当AI开始辅助编程,度量代码还有意义吗?

思码逸研发效能

研发管理 研发效能 代码 效能度量 思码逸

dbops 助力 GreatSQL MGR架构安装部署

GreatSQL

企业如何使用YashanDB实现数据资产最大化?

数据库砖家

HarmonyOS运动开发:如何选择并上传运动记录

王二蛋和他的张大花

Flutter驱动的小程序容器化:SuperApp开发效率革命

xuyinyin

深入研究:亚马逊amazon商品详情API接口Python攻略

tbapi

亚马逊API 亚马逊商品详情API 亚马逊商品列表接口 亚马逊数据采集

狄耐克脑电波交互事业部走访四大实验室,共探脑电波交互技术新局

新消费日报

实现微信小程序编译和运行环境系列(核心篇二)_语言 & 开发_风逝_InfoQ精选文章