欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

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>

上一篇:

下一篇: