时间:2021-07-01 10:21:17 帮助过:35人阅读
openresty 很早就支持websocket了,但是早期的版本cosocket是单工的,处理起来比较麻烦参见邮件列表讨论 websocket chat,后来的版本cosocket是双全工的,就可以按照这个讨论的方案来实现基于websocket的聊天,或者是push程序了,但是网络上没有找到一个具体一点的例子,于是自己写了个simple的例子。
client的websocket连接到openresty之后,使用ngx.thread.spawn
启动两个 轻线程
,一个用来接收客户端提交的数据往redis的channel写,另一个用来订阅channel,读取redis的数据写给客户端。channel相当于一个chat room,多个client一起订阅,有人发聊天信息(pub),所有人都能得到信息(sub)。代码比较简陋,简单的思路的实现。
依赖:
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)
<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>
用iphone试了试,不好使,可能是websocket版本实现的问题。pc端测试可以正常使用。
以上就介绍了openresty+websocket+redis simple chat,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。