openresty 很早就支援websocket了,但是早期的版本cosocket是單工的,處理起來比較麻煩參見郵件清單討論 websocket chat,後來的版本cosocket是雙全工的,就可以按照這個討論的方案來實現基於websocket的聊天,或者是push程式了,但是網路上沒有找到一個具體一點的例子,於是自己寫了個simple的例子。
1 思路
client的websocket串連到openresty之後,使用ngx.thread.spawn啟動兩個 輕線程,一個用來接收用戶端提交的資料往redis的channel寫,另一個用來訂閱channel,讀取redis的資料寫給用戶端。channel相當於一個chat room,多個client一起訂閱,有人發聊天資訊(pub),所有人都能得到資訊(sub)。代碼比較簡陋,簡單的思路的實現。
2 服務端代碼
依賴:
- openresty
- redis
- lua-resty-redis
- lua-resty-websocket 只支援RFC 6455
nginx的配置全貼了,就是兩個location,一個是頁面地址,一個是websocket地址。
配置片段
location = /sredis { content_by_lua_file conf/lua/ws_redis.lua; } location ~ /ws/(.*) { alias conf/html/$1.html; }
lua代碼
-- simple chat with redislocal server = require"resty.websocket.server"local redis = require"resty.redis"local channel_name = "chat"local msg_id = 0--create connectionlocal wb, err = server:new{ timeout = 10000, max_payload_len = 65535}--create successifnot wb then ngx.log(ngx.ERR, "failed to new websocket: ", err) return ngx.exit(444)endlocal push = function()-- --create redislocal red = redis:new() red:set_timeout(5000) -- 1 seclocal ok, err = red:connect("127.0.0.1", 6379) ifnot ok then ngx.log(ngx.ERR, "failed to connect redis: ", err) wb:send_close() returnend--sublocal res, err = red:subscribe(channel_name) ifnot res then ngx.log(ngx.ERR, "failed to sub redis: ", err) wb:send_close() returnend-- loop : read from rediswhiletruedolocal res, err = red:read_reply() if res thenlocal item = res[3] local bytes, err = wb:send_text(tostring(msg_id).." "..item) ifnot bytes then-- better error handling ngx.log(ngx.ERR, "failed to send text: ", err) return ngx.exit(444) end msg_id = msg_id + 1endendendlocal co = ngx.thread.spawn(push)--main loopwhiletruedo-- 擷取資料local data, typ, err = wb:recv_frame() -- 如果串連損壞 退出if wb.fatal then ngx.log(ngx.ERR, "failed to receive frame: ", err) return ngx.exit(444) endifnot data thenlocal bytes, err = wb:send_ping() ifnot bytes then ngx.log(ngx.ERR, "failed to send ping: ", err) return ngx.exit(444) end ngx.log(ngx.ERR, "send ping: ", data) elseif typ == "close"thenbreakelseif typ == "ping"thenlocal bytes, err = wb:send_pong() ifnot bytes then ngx.log(ngx.ERR, "failed to send pong: ", err) return ngx.exit(444) endelseif typ == "pong"then ngx.log(ngx.ERR, "client ponged") elseif typ == "text"then--send to redislocal red2 = redis:new() red2:set_timeout(1000) -- 1 seclocal ok, err = red2:connect("127.0.0.1", 6379) ifnot ok then ngx.log(ngx.ERR, "failed to connect redis: ", err) breakendlocal res, err = red2:publish(channel_name, data) ifnot res then ngx.log(ngx.ERR, "failed to publish redis: ", err) endendendwb:send_close()ngx.thread.wait(co)
3 頁面代碼
<html><head><metacharset="utf-8"><metaname="viewport"content="width=device-width, initial-scale=1.0, user-scalable=no"><scripttype="text/javascript">var ws = null; functionWebSocketConn() {if (ws != null && ws.readyState == 1) { log("已經線上"); return } if ("WebSocket"in window) { // Let us open a web socket ws = new WebSocket("ws://localhost:8008/sredis"); ws.onopen = function() { log('成功進入聊天室'); }; ws.onmessage = function(event) { log(event.data) }; ws.onclose = function() {// websocket is closed. log("已經和伺服器斷開"); }; ws.onerror = function(event) { console.log("error " + event.data); }; } else { // The browser doesn't support WebSocket alert("WebSocket NOT supported by your Browser!"); } } functionSendMsg() {if (ws != null && ws.readyState == 1) { var msg = document.getElementById('msgtext').value; ws.send(msg); } else { log('請先進入聊天室'); } } functionWebSocketClose() {if (ws != null && ws.readyState == 1) { ws.close(); log("發送斷開伺服器請求"); } else { log("當前沒有串連伺服器") } } functionlog(text) {var li = document.createElement('li'); li.appendChild(document.createTextNode(text)); document.getElementById('log').appendChild(li); returnfalse; } script>head><body><divid="sse"><ahref="javascript:WebSocketConn()">進入聊天室a> <ahref="javascript:WebSocketClose()">離開聊天室a><br><br><inputid="msgtext"type="text"><br><ahref="javascript:SendMsg()">發送資訊a><br><olid="log">ol>div>body>html>
4 效果
用iphone試了試,不好使,可能是websocket版本實現的問題。pc端測試可以正常使用。
Reading
- 郵件清單討論 websocket chat
- Aapo Websocket with openresty
').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('
').text(i)); }; $numbering.fadeIn(1700); }); });
以上就介紹了openresty+websocket+redis simple chat,包括了方面的內容,希望對PHP教程有興趣的朋友有所協助。