1.配置文件javascript
server{ listen 8009; proxy_ignore_client_abort on; #不容許客戶端主動關閉鏈接 location /test/websocket { lua_check_client_abort on; #是否監視client提早關閉請求的事件,若是打開監視,會調用ngx.on_abort()註冊的回調 lua_socket_log_errors off; #當套接字發生錯誤時,底層ngx_lua模塊會進行錯誤記錄,若是你已經在本身的lua代碼中進行了適當的錯誤處理,那麼建議關閉lua_socket_log_errors指令來禁用此自動錯誤日誌記錄 content_by_lua_file /Users/guanguan/study/2017/or-websocket/websocket.lua; } access_log /usr/local/openresty/nginx/logs/websocket_access.log main; error_log /usr/local/openresty/nginx/logs/websocket_error.log debug; }
2.websocket.lua 利用redis的訂閱/發佈來實現消息的接收與發送html
--[[ - @desc 用於調試 lua數據輸出 - @param string 字符串 - return string --]] function dump(v) if not __dump then function __dump(v, t, p) local k = p or ""; if type(v) ~= "table" then table.insert(t, k .. " : " .. tostring(v)); else for key, value in pairs(v) do __dump(value, t, k .. "[" .. key .. "]"); end end end end local t = { '======== Lib:Dump Content ========' }; __dump(v, t); print(table.concat(t, "\n")); end local server = require "resty.websocket.server" local redis = require "resty.redis" local cjson = require "cjson" ngx.log(ngx.ERR, "HH") local function exit() --獲取URL參數 local _GET = ngx.req.get_uri_args() ngx.log(ngx.ERR, "用戶" .. _GET['rnd'] .. " 離開了房間 : ", err) --if is_ws == nil then ngx.eof() end ngx.flush(true) ngx.exit(ngx.HTTP_OK) return nil end local ok, err = ngx.on_abort(exit) --註冊一個函數 當客戶端斷開鏈接時執行 ngx.log(ngx.ERR,ok) if err then ngx.log(ngx.ERR,err) return exit() end --獲取聊天室id local channel_id = 800 local channel_name = "chat_" .. tostring(channel_id) ngx.log(ngx.ERR, "channel_name=> ", channel_name) --create connection local wb, err = server:new { timeout = 5000, max_payload_len = 65535 } ngx.log(ngx.ERR,"建立websocket服務成功") if not wb then ngx.log(ngx.ERR, "failed to new websocket: ", err) return exit() end ngx.log(ngx.ERR,"建立了~~~") --- -建立redis實例 local getRedis = function(key) if not key then return nil end if ngx.ctx[key] then return ngx.ctx[key] end --dump('-------------建立redis實例---------------------') local red = redis:new() --red:set_timeout(5000) -- 1 sec 設置鏈接超時1秒 local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "failed to connect redis: ", err) end ngx.ctx[key] = red return red end local pub = function() local red = getRedis('redisfn1') --redis 訂閱 local res, err = red:subscribe(channel_name) if not res then ngx.log(ngx.ERR, "failed to sub redis: ", err) wb:send_close() return exit() end -- 不斷讀取數據若是有值馬上發送給客戶端 while true do local res, err = red:read_reply() if res then local bytes, err = wb:send_text(cjson.encode(res)) if not bytes then wb:send_close() ngx.log(ngx.ERR, "failed to send text: ", err) return exit() end end ngx.sleep(0.5) end end local co = ngx.thread.spawn(pub) --main loop while true do -- 獲取數據 local data, typ, err = wb:recv_frame() -- 若是鏈接損壞 退出 if wb.fatal then ngx.log(ngx.ERR, "failed to receive frame: ", err) return exit() end if not data then local bytes, err = wb:send_ping() if not bytes then ngx.log(ngx.ERR, "failed to send ping: ", err) return exit() end elseif typ == "close" then break elseif typ == "ping" then local bytes, err = wb:send_pong() if not bytes then ngx.log(ngx.ERR, "failed to send pong: ", err) return exit() end elseif typ == "pong" then --ngx.log(ngx.ERR, "client ponged") elseif typ == "text" then --接收消息寫入redis local red = getRedis('redisfn2') local res, err = red:publish(channel_name, data) if not res then ngx.log(ngx.ERR, " 接收消息寫入redis錯誤 failed to publish redis: ", err) end else break end ngx.sleep(0.5) end getRedis('redisfn1'):set_keepalive(10000, 100) getRedis('redisfn2'):set_keepalive(10000, 100) wb:send_close() ngx.thread.wait(co)
3.頁面java
<html> <head> <title>lua_socket方式作聊天</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport" /> </head> <body> <script type="text/javascript"> var ws = null; function WebSocketConn() { if (ws != null && ws.readyState == 1) { log("已經在線"); return } if ("WebSocket" in window) { // ws = new WebSocket("ws://www.im.cn:80/test/websocket?rnd="+(Math.ceil(Math.random()*1000)) ); ws = new WebSocket("ws://127.0.0.1:8009/test/websocket?rnd="+(Math.ceil(Math.random()*1000)) ); 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!"); } } function SendMsg() { if (ws != null && ws.readyState == 1) { var msg = document.getElementById('msgtext').value; ws.send(msg); } else { log('請先進入聊天室'); } } function WebSocketClose() { if (ws != null && ws.readyState == 1) { ws.close(); log("發送斷開服務器請求"); } else { log("當前沒有鏈接服務器") } } function log(text) { var li = document.createElement('li'); li.appendChild(document.createTextNode(text)); document.getElementById('log').appendChild(li); return false; } </script> <div id="sse"> <code class="language-html hljs "> <a href="#" onclick="WebSocketConn();">進入聊天室</a> <a href="#" onclick="WebSocketClose();">離開聊天室</a> </code> <br /> <br /> <input id="msgtext" type="text" value=""/><br /> <a onclick="SendMsg();">發送信息</a><br /> <ol id="log"></ol> </div> </body> </html>
4.運行結果:nginx