微信小程序從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(); } }
本人也是小白,希望通過這篇文章加深自己對小程序服務端的理解。如有錯誤能夠及時指正我,假如這篇文章反響還不錯,我再把其他的方法分析一下吧!