微信小程序从1月9号面世,到现在才1个多月,很多相关资料还不够完善,下面谈谈自己的理解,希望这篇文章能够给小程序开发者一点点帮助。因为团队开发需要,用的是腾讯云提供的解决方案Wafer。那里的文档是写的比较简略的。具体文档和源码请移步https://github.com/tencentyun/wafer。
首先,微信小程序解决方案解决的主要问题是小程序会话服务和的信道服务问题。
1、因为小程序不支持cookie会话服务需要自己搭建,要理解小程序的会话服务,请先读懂下面这张图,这十分重要
而信道服务问题的产生是由于https每个请求都需要建立一次连接,耗费比较多的资源。同时微信有最大连接数的限制(5个),所以实时通信的需求不好做,于是我们采用Websocket进行通信,这样整个过程只需建立一次连接,同时可以实现双向通信,广播等功能。同样也要理解下图的流程
腾讯云这次提供的解决方案主要分为两部分:微信小程序客户端腾讯云增强SDK和业务服务器端SDK,两者是相互配合的,下面从分别对他们的源码进行分析
Ⅰ、微信小程序客户端腾讯云增强SDK(wafer-client-sdk-master)
在官方提供的小程序demo里面,sdk可以直接安装到小程序目录中,使用的时候直接调用
// 引入 QCloud 小程序增强 SDK var qcloud = require(\'../../vendor/qcloud-weapp-client-sdk/index\');
var host = \'59431301.qcloud.la\'; //你的服务器地址 var config = { // 下面的地址配合云端 Demo 工作 service: { host, // 登录地址,用于建立会话 loginUrl: `https://${host}/login`, // 测试的请求地址,用于测试会话 requestUrl: `https://${host}/user`, // 测试的信道服务地址 tunnelUrl: `https://${host}/tunnel`, }
对于信道服务,demo里面提供了很多的方法,这里就不一一赘述了,举其中一个例子
/** * 连接到聊天室信道服务 */ connect() { this.amendMessage(createSystemMessage(\'正在加入群聊...\')); // 创建信道 var tunnel = this.tunnel = new qcloud.Tunnel(config.service.tunnelUrl); // 连接成功后,去掉「正在加入群聊」的系统提示 tunnel.on(\'connect\', () => this.popMessage()); // 聊天室有人加入或退出,反馈到 UI 上 tunnel.on(\'people\', people => { const { total, enter, leave } = people; if (enter) { this.pushMessage(createSystemMessage(`${enter.nickName}已加入群聊,当前共 ${total} 人`)); } else { this.pushMessage(createSystemMessage(`${leave.nickName}已退出群聊,当前共 ${total} 人`)); } }); // 有人说话,创建一条消息 tunnel.on(\'speak\', speak => { const { word, who } = speak; this.pushMessage(createUserMessage(word, who, who.openId === this.me.openId)); }); // 信道关闭后,显示退出群聊 tunnel.on(\'close\', () => { this.pushMessage(createSystemMessage(\'您已退出群聊\')); }); // 重连提醒 tunnel.on(\'reconnecting\', () => { this.pushMessage(createSystemMessage(\'已断线,正在重连...\')); }); tunnel.on(\'reconnect\', () => { this.amendMessage(createSystemMessage(\'重连成功\')); }); // 打开信道 tunnel.open(); },
主要思路:先通过url调用服务端的信道服务初始化一个信道(该方法定义在vendor/lib/tunnel.js),然后调用tunnel的on方法进行监听事件(定义如下)当eventType为对应的内置消息类型,触发相应的方法。
//========================================================================= // 暴露实例状态以及方法 //========================================================================= this.serviceUrl = serviceUrl; this.socketUrl = null; this.status = null; this.open = openConnect; this.on = registerEventHandler; this.emit = emitMessagePacket; this.close = close; this.isClosed = isClosed; this.isConnecting = isConnecting; this.isActive = isActive; this.isReconnecting = isReconnecting;
/** * 注册消息处理函数 * @param {string} messageType 支持内置消息类型("connect"|"close"|"reconnecting"|"reconnect"|"error")以及业务消息类型 */ function registerEventHandler(eventType, eventHandler) { if (typeof eventHandler === \'function\') { eventHandlers.push([eventType, eventHandler]); } }
Ⅱ、微信小程序客户端腾讯云增强SDK(wafer-client-sdk-master)
本人所在的团队用的Java SDK,详细的API可以查看 https://tencentyun.github.io/wafer-java-server-sdk/api/
同样使用之前必须对其进行初始化(比较方便的是通过配置文件直接初始化)
import com.qcloud.weapp.*; var configFilePath = "/etc/qcloud/sdk.config"; ConfigurationManager.setupFromFile(configFilePath);
同样的,还是对信道服务进行分析。客户端通过url访问,最先到达的是servlet层,在servlet中调用tunnelService,在service层中,我们要实例化信道处理器(TunnelHandler),在我们上文的Demo中,有实现相应功能的ChatTunnelHandler。我们开发者可以通过修改这个信道处理器实现自己的需要的功能。
import com.qcloud.weapp.*; import com.qcloud.weapp.tunnel.*; import com.qcloud.weapp.demo.ChatTunnelHandler; @WebServlet("/tunnel") public class TunnelServlet extends HttpServlet { /** * 把所有的请求交给 SDK 处理,提供 TunnelHandler 处理信道事件 */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 创建信道服务处理信道相关请求 TunnelService tunnelService = new TunnelService(request, response); try { // 配置是可选的,配置 CheckLogin 为 true 的话,会在隧道建立之前获取用户信息,以便业务将隧道和用户关联起来 TunnelHandleOptions options = new TunnelHandleOptions(); options.setCheckLogin(true); // 需要实现信道处理器,ChatTunnelHandler 是一个实现的范例 tunnelService.handle(new ChatTunnelHandler(), options); } catch (ConfigurationException e) { e.printStackTrace(); } } }
在TunnelHandler中,主要定义了四个方法(详见代码注释),只要弄懂这四个方法基本能搞懂信道服务了
package com.qcloud.weapp.tunnel; import com.qcloud.weapp.authorization.UserInfo; /** * <p>信道事件处理接口,实现该接口处理信道事件。</p> * <p>信道处理器需要处理信道的完整声明周期,包括:</p> * <ul> * <li>onTunnelRequest() - 当用户发起信道请求的时候,会得到用户信息,此时可以关联信道 ID 和用户信息</li> * <li>onTunnelConnect() - 当用户建立了信道连接之后,可以记录下已经连接的信道</li> * <li>onTunnelMessage() - 当用户消息发送到信道上时,使用该函数处理信道的消息</li> * <li>onTunnelClose() - 当信道关闭时,清理关于该信道的信息,以及回收相关资源</li> * </ul> * */ public interface TunnelHandler { /** * 当用户发起信道请求的时候调用,会得到用户信息,此时可以关联信道 ID 和用户信息 * @param tunnel 发起连接请求的信道 * @param userInfo 发起连接对应的用户(需要信道服务配置 checkLogin 为 true) * */ void onTunnelRequest(Tunnel tunnel, UserInfo userInfo); /** * 当用户建立了信道连接之后调用,此时可以记录下已经连接的信道 * @param tunnel 已经建立连接的信道,此时可以向信道发送消息 * */ void onTunnelConnect(Tunnel tunnel); /** * 当信道收到消息时调用,此时可以处理消息,也可以向信道发送消息 * @param tunnel 收到消息的信道 * @param message 收到的消息 * */ void onTunnelMessage(Tunnel tunnel, TunnelMessage message); /** * 当信道关闭的时候调用,此时可以清理信道使用的资源 * @param tunnel 已经关闭的信道 * */ void onTunnelClose(Tunnel tunnel); }
下面以ChatTunnelHandler的OnTunnelConnect方法举例
主要思路:假如userMap中包含TunneId,将这个信道装进Room这个容器中,然后将信道总数和和刚加入的信道(所对应的用户)的信息装进json数组之中,最后调用broadcast方法向所有在此房间内的信道广播消息。其中TunnelInvalidInfo是关于无效信道信息的VO类,EmitResult是存放无效信道的ArrayList
/** * 实现 OnTunnelConnect 方法<br/> * 在客户端成功连接 WebSocket 信道服务之后会调用该方法,此时通知所有其它在线的用户当前总人数以及刚加入的用户是谁 * */ @Override public void onTunnelConnect(Tunnel tunnel) { if (userMap.containsKey(tunnel.getTunnelId())) { room.addTunnel(tunnel); JSONObject peopleMessage = new JSONObject(); try { peopleMessage.put("total", room.getTunnelCount()); peopleMessage.put("enter", new JSONObject(userMap.get(tunnel.getTunnelId()))); } catch (JSONException e) { e.printStackTrace(); } broadcast("people", peopleMessage); } else { closeTunnel(tunnel); } }
/** * 广播消息到房间里所有的信道 * */ private void broadcast(String messageType, JSONObject messageContent) { try { EmitResult result = room.broadcast(messageType, messageContent); // 广播后发现的无效信道进行清理 for (TunnelInvalidInfo invalidInfo : result.getTunnelInvalidInfos()) { onTunnelClose(Tunnel.getById(invalidInfo.getTunnelId())); } } catch (EmitError e) { // 如果消息发送发生异常,这里可以进行错误处理或者重试的逻辑 e.printStackTrace(); } }
本人也是小白,希望通过这篇文章加深自己对小程序服务端的理解。如有错误能够及时指正我,假如这篇文章反响还不错,我再把其他的方法分析一下吧!