Web Worker讓JS有了多線程的能力,能夠將複雜耗時的操做都交付給Worker線程處理。WebSocket讓web端與服務端維持一個有效的長鏈接,實現服務端主動推送數據。將兩者一結合,業務系統信息流轉通知功能徹底就能夠剝離出來。javascript
Worker工做在一個專用的做用域DedicatedWorkerGlobalScope
,在這個做用域中,不能直接操做DOM節點,不能使用Window
對象的默認方法和屬性。不過對於網絡的訪問是徹底沒有問題的。具體能使用那些對象和方法請點擊這裏查看html
從上圖中可明顯的看出,Worker在當前架構中實現一個橋樑的左右,上鍊接socket端中的數據,下負責分發socket中的數據。此處咱們先了解下Worker自己的功能實現。前端
postMessage
相互傳遞信息onmessage
接收相互傳遞的消息importScripts([url,])
worker.terminate()
結束線程this.close()
結束自身線程新建一個webworker.js
文件,並在其中編寫以下代碼java
//author:herbert qq:464884492
onmessage = function (event) {
if (event.data.code) {
var code = event.data.code.toLowerCase();
switch (code) {
case "init":
var userId = event.data.loggedUserId;
var sessionId = event.data.sessionid;
if (!sessionId) {
this.close();
return;
}
postMessage({ code: "codeone", msg: "你好,組件1" });
postMessage({ code: "codetwo", msg: "你好,組件2" });
break;
default:
break;
}
}
}
複製代碼
注意:在 onmessage 前不能加var不然在IE下會接收不了消息。IE真是讓人充滿挫敗感的瀏覽器git
新建一個index.html頁面,在script塊中編寫如下代碼,實現與webworker.js通信github
//author:herbert qq:464884492
var work = new Worker('webworker.js')
, textone = document.querySelector("#textone")
, textTwo = document.querySelector("#texttwo")
textAll = document.querySelector("#textAll");
work.onmessage = function (event) {
var data = event.data;
if (!!data.code) {
switch (data.code) {
case "close":
work.terminate();
case "codeone":
textone.value = textone.value + JSON.stringify(data) + "\r\n";
textAll.value = textAll.value + JSON.stringify(data) + "\r\n";
break;
case "codetwo":
textTwo.value = textTwo.value + JSON.stringify(data) + "\r\n";
textAll.value = textAll.value + JSON.stringify(data) + "\r\n";
break;
default:
textAll.value = textAll.value + JSON.stringify(data) + "\r\n";
}
}
};
work.postMessage({
code: "init",
loggedUserId: 'demo',
sessionid: 'demo'
});
複製代碼
WebSocket和Http同樣都是基於Tcp協議。不一樣是WebSocket實現了服務端與客戶端的全雙工通信。在Websocket未出現以前,要是實現一個信息推送的功能,經過http來實現惟一方案就是輪訓,輪訓分長短,各有弊端。如今WebSocket一出現,一切都好辦了。web
接下來咱們開始創建一個WebSocket鏈接api
方法中的root表示當前做用域,在主線程是root=window,在WebWorker線程root=DedicatedWorkerGlobalScope瀏覽器
//author:herbert qq:464884492
var root = this,socket =null;
function connect(wsurl) {
if ('WebSocket' in root) {
socket = new WebSocket(wsurl);
} else if ('MozWebSocket' in root) {
socket = new MozWebSocket(wsurl);
} else {
alert("您的瀏覽器版本太低,將不能接收系統消息");
}
}
複製代碼
wsurl格式爲ws:\\
或者 wss:\\
,後者表示SSL加密傳輸。實際地址如: ws://localhost:8090/demo/demowebsocket
接下來,咱們須要爲socket處理事件,負責接收服務端推送的消息tomcat
//author:herbert qq:464884492
function onOpen() {
postMessage({ code: "openConnect" });
}
function onClose() {
postMessage({ code: "closewsconnect" });
}
function onMessaage(event) {
postMessage(JSON.parse(event.data));
}
function onError(event) {
socket = null;
if (event.target.readyState == 3) {
//斷線重連
setTimeout(function () {
connect(event.target.url);
initMessageEvent();
}, 1000);
}
}
function sendMessage(msg) {
if (socket == null) return;
socket.send(msg);
}
function initMessageEvent() {
socket.onopen = onOpen; //socket鏈接成功處理事件
socket.onclose = onClose; //socket鏈接關閉處理事件
socket.onmessage = onMessaage; //socket接收到新消息
socket.onerror = onError; //soket錯誤處理事件
}
複製代碼
Tomcat7x已經實現了標準WebScoket接口,在項目中只須要編寫一個普通的實體bean配置註解就能夠實現一個標準的WebSocket Api。開發中主要使用一些註解
pathParam
能夠在onOpen函數中經過函數參數 @PathParam 獲取被註解約束的函數均可以任意選擇須要的參數,可選擇的參數有 Session、EndpointConfig 以及 @PathParam, 服務端Bean代碼以下
//author:herbert qq:464884492
@ServerEndpoint(value = "/demowebsocket/{userId}/{sessionId}")
public class DemoWebSokcet {
private static final Set<DemoWebSokcet> connections = new CopyOnWriteArraySet<DemoWebSokcet>();
private Session session;
public DemoWebSokcet() {
}
@OnOpen
public void openConnection(Session session, EndpointConfig conf, @PathParam("userId") String userId, @PathParam("sessionId") String sessionId) {
this.session = session;
connections.add(this);
JSONObject jo = new JSONObject();
jo.put("code", "newuser");
jo.put("userid", userId);
jo.put("sessionid", sessionId);
jo.put("msg", "server:新鏈接用戶");
sendMessage(jo);
// 測試 代碼
JSONObject jo1 = new JSONObject();
jo1.put("code", "codeone");
jo1.put("userid", userId);
jo1.put("sessionid", sessionId);
jo1.put("msg", "Server:組件1你好");
sendMessage(jo1);
JSONObject jo2 = new JSONObject();
jo2.put("code", "codetwo");
jo2.put("userid", userId);
jo2.put("sessionid", sessionId);
jo2.put("msg", "server:組件2你好");
sendMessage(jo2);
}
@OnClose
public void closeConnection(@PathParam("userId") String userId, @PathParam("sessionId") String sessionId) {
connections.remove(this);
JSONObject jo = new JSONObject();
jo.put("code", "connectionClose");
jo.put("userid", userId);
jo.put("sessionid", sessionId);
jo.put("msg", "server:鏈接關閉");
sendMessage(jo);
}
// 處理文本消息
@OnMessage
public void handleTextMsg(Session session, String message, @PathParam("userId") String userId, @PathParam("sessionId") String sessionId) {
System.out.println("userId=>" + userId + " sessionId=>" + sessionId);
// 原樣轉發客戶端消息
sendMessage(JSONObject.parseObject(message));
}
// 處理二進制消息
@OnMessage
public void handleBinaryMsg(Session session, ByteBuffer msg, @PathParam("userId") String userId, @PathParam("sessionId") String sessionId) {
}
// 處理pong消息
@OnMessage
public void handlePongMsg(Session session, PongMessage msg, @PathParam("userId") String userId, @PathParam("sessionId") String sessionId) {
JSONObject jo = new JSONObject();
jo.put("code", "pong");
jo.put("userid", userId);
jo.put("sessionid", sessionId);
jo.put("msg", msg.getApplicationData().toString());
sendMessage(jo);
}
@OnError
public void onError(Throwable t, @PathParam("userId") String userId, @PathParam("sessionId") String sessionId) throws Throwable {
JSONObject jo = new JSONObject();
jo.put("code", "servererror");
jo.put("userid", userId);
jo.put("sessionid", userId);
jo.put("msg", t.getMessage());
sendMessage(jo);
}
private static void sendMessage(JSONObject msg) {
for (DemoWebSokcet client : connections) {
try {
synchronized (client) {
client.session.getBasicRemote()
.sendText(msg.toJSONString());
}
} catch (IOException e) {
JSONObject jo = new JSONObject();
jo.put("code", "servererror");
jo.put("userid",
client.session.getPathParameters().get("userid"));
jo.put("sessionid",
client.session.getPathParameters().get("sessionid"));
connections.remove(client);
try {
client.session.close();
} catch (IOException e1) {
}
jo.put("msg", "server:發送消息出現異常,鏈接已關閉" + e.getMessage());
sendMessage(jo);
}
}
}
}
複製代碼
在測試代碼編寫過程當中,經過pom方式引入javax.websocket-api,啓動後始終出現 Error during WebSocket handshake: Unexpected response code: 404
鏈接錯誤,後來經過直接件tomcat/bin下對應的tomcat實現的jar複製到webapp對應的bin文件夾下解決問題。
篇幅比較長,讀到這裏也不容易!WebWorker和WebSocket我也是第一次將兩者結合起來。感受如今javascript功能真的是愈來愈豐富了。demo地址,還有一點感悟,對於開發中的新知識點,首先你得學會怎麼用,其次在經過閱讀源碼,以及理論知識讓你使用的更順利,甚至改變它。
有喜歡聊技術朋友也歡迎入羣,若二維碼失效可加我微信回覆前端