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 redis
local server = require "resty.websocket.server"
local redis = require "resty.redis"

local channel_name = "chat"
local msg_id = 0

--create connection
local wb, err = server:new{
  timeout = 10000,
  max_payload_len = 65535
}

--create success
if not wb then
  ngx.log(ngx.ERR, "failed to new websocket: ", err)
  return ngx.exit(444)
end

local push = function()
    -- --create redis
    local red = redis:new()
    red:set_timeout(5000) -- 1 sec
    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.log(ngx.ERR, "failed to connect redis: ", err)
        wb:send_close()
        return
    end

    --sub
    local res, err = red:subscribe(channel_name)
    if not res then
        ngx.log(ngx.ERR, "failed to sub redis: ", err)
        wb:send_close()
        return
    end

    -- loop : read from redis
    while true do
        local res, err = red:read_reply()
        if res then
            local item = res[3]
            local bytes, err = wb:send_text(tostring(msg_id).." "..item)
            if not bytes then
                -- better error handling
                ngx.log(ngx.ERR, "failed to send text: ", err)
                return ngx.exit(444)
            end
            msg_id = msg_id + 1
        end
    end
end

local co = ngx.thread.spawn(push)

--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 ngx.exit(444)
    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 ngx.exit(444)
        end
        ngx.log(ngx.ERR, "send ping: ", data)
    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 ngx.exit(444)
        end
    elseif typ == "pong" then
        ngx.log(ngx.ERR, "client ponged")
    elseif typ == "text" then
        --send to redis
        local red2 = redis:new()
        red2:set_timeout(1000) -- 1 sec
        local ok, err = red2:connect("127.0.0.1", 6379)
        if not ok then
            ngx.log(ngx.ERR, "failed to connect redis: ", err)
            break
        end
        local res, err = red2:publish(channel_name, data)
        if not res then
            ngx.log(ngx.ERR, "failed to publish redis: ", err)
        end
    end
end

wb:send_close()
ngx.thread.wait(co)

3 页面代码

<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <script type="text/javascript">
    var ws = null;

    function WebSocketConn() {
        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!");
        }
    }

    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>
</head>

<body>
    <div id="sse">
        <a href="javascript:WebSocketConn()">进入聊天室</a> &nbsp;
        <a href="javascript:WebSocketClose()">离开聊天室</a>
        <br>
        <br>
        <input id="msgtext" type="text">
        <br>
        <a href="javascript:SendMsg()">发送信息</a>
        <br>
        <ol id="log"></ol>
    </div>
</body>

</html>

4 效果

用iphone试了试,不好使,可能是websocket版本实现的问题。pc端测试可以正常使用。

Reading

openresty+websocket+redis simple chat的更多相关文章

  1. CentOS6.4 安装OpenResty和Redis 并在Nginx中利用lua简单读取Redis数据

    1.下载OpenResty和Redis OpenResty下载地址:wget http://openresty.org/download/ngx_openresty-1.4.3.6.tar.gz Re ...

  2. openresty websocket 使用

      openresty websocket 使用 1. 代码如下: local server =require"resty.websocket.server" local wb, ...

  3. Canvas + WebSocket + Redis 实现一个视频弹幕

    原文出自:https://www.pandashen.com 页面布局 首先,我们需要实现页面布局,在根目录创建 index.html 布局中我们需要有一个 video 多媒体标签引入我们的本地视频, ...

  4. Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis

    转载请标明出处: http://blog.csdn.net/forezp/article/details/78616714 本文出自方志朋的博客 Lua模块开发 在实际的开发过程中,不可能把所有的lu ...

  5. swoole+websocket+redis实现一对一聊天

    如同web端的QQ和微信一样,这是一个web端的聊天程序. 环境:ubuntu + php + swoole扩展 + redis + mysql Redis 实现每个连接websocket的服务都唯一 ...

  6. openresty + lua 2、openresty 连接 redis,实现 crud

    redis 的话,openresty 已经集成,ng 的话,自己引入即可. github 地址:https://github.com/openresty/lua-resty-redis github  ...

  7. 使用 Django WebSocket Redis 搭建在线即时通讯工具

    话不多说先上效果图演示 项目:http://112.74.164.107:9990/ 1.安装组建 redis: yum install redis/apt install redis 2.创建虚拟化 ...

  8. Openresty+Lua+Redis灰度发布

    灰度发布,简单来说,就是根据各种条件,让一部分用户使用旧版本,另一部分用户使用新版本.百度百科中解释:灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式.AB test就是一种灰度发布方式,让一部分 ...

  9. openresty && hashids&& redis 生成短链接

    1. 原理     a. 从redis 获取需要表示的短链接的id( redis incr)     b. hashids 编码 id     c. openresty  conteent_by_lu ...

随机推荐

  1. windows平台下 c/c++进行http通信的教训

    由于需要使用c++开发一个桌面应用软件,需要用到http请求进行通讯,也是本人第一次进行网络相关的开发工作,遇到了不少坑. 由于是在windows下开发和使用的应用软件,自然而然想到了调用Window ...

  2. How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer

    How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer 1 ...

  3. 剑指offer面试题4 替换空格(c)

  4. 6.5、Android Studio的Android Device Monitor

    Android Device Monitor是一个独立的工具,可以对Android应用进行调试和分析.Android Device Monitor无需安装整合在一个IDE中,比如像Android St ...

  5. Oracle AP Invoice APIs

     These APIs are handful when you want to do Insert, Update or Delete programmatically for some bus ...

  6. 没想到你是这样的UDP

    UDP是国际标准化组织为互联网设定的标准中的传输层中的一个协议.TCP/IP协议簇是一个很庞大的家族,但是今天我们就来看一看这个面向无连接的传输层在Java中是怎样通过编程实现的. 原理性知识 在Ja ...

  7. javascript之键盘事件

     键盘事件包含onkeydown.onkeypress和onkeyup这三个事件 事件初始化 function keyDown(){} document.onkeydown = keyDown; ...

  8. iOS中崩溃调试的使用和技巧总结 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博 在iOS开发调试过程中以及上线之后,程序经常会出现崩溃的问题.简单的崩溃还好说,复杂的崩溃就需要我们通过解析Cras ...

  9. [C++学习历程]Visual Studio 2010 的HelloWorld

    大学时期曾学过C++的知识,那时候也没有使用VS这样高档的IDE工具,就是C++6.0那样来的.对于重新拾起C++来说,换了个IDE,那么就先从使用IDE学起吧~ 作者:苏生米沿 本文链接:http: ...

  10. RTB--Real TimeBidding模式的互联网广告(实时竞价的广告投放)

    RTB(real time bidding)实时竞价允许广告买家根据活动目标.目标人群以及费用门槛等因素对每一个广告及每次广告展示的费用进行竞价.竞价成功后获得广告展示机会,在展示位置上展示广告. 其 ...