openresty+websocket+redis simple chat
程序员文章站
2023-12-31 13:20:46
...
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>
推荐阅读
-
openresty+websocket+redis simple chat
-
simple_html_dom.php 的使用
-
在Wordpress iNove主题中利用Simple Tags插件实现输出相关日志的方法
-
for循环 - 新手PHP代码问题,求解关于simple_html_dom
-
用Go+WebSocket快速实现一个chat服务
-
too simple too naive PHP中simplexml_load_string函数使用说明
-
抓取微博热词,使用simple_html_dom来操作html数据
-
C#使用TcpListener及TcpClient开发一个简单的Chat工具实例
-
C#使用TcpListener及TcpClient开发一个简单的Chat工具实例
-
PHP解析html类库simple_html_dom的转码bug