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. Dynamics CRM 为Visual Studio 2015安装CRM Developer Toolkit

    从CRM2015的SDK以后Tools的文件夹里就没有了DeveloperToolkit,而DeveloperToolkit还是停留在VS2012版本,这对于我们这种用新版本的童鞋来说比较头疼,我本地 ...

  2. 【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态

    一. Objective-C 方法详解 1. 方法属性 (1) OC 方法传参机制 Object-C 方法传参机制 : OC 中得参数传递都是值传递, 传入参数的是参数的副本; -- 基本类型 (值传 ...

  3. Maven2插件开发入门

    一.创建Maven项目 首先创建一个Maven插件项目,可以手动或使用mvn archetype:create从原型创建.pom.xml配置如下: 1 2 3 4 5 6 7 8 9 10 11 12 ...

  4. Hibernate之持久化状态

    可持久化对象有以下三种状态: 临时状态(Transient):对象在保存进数据库之前为临时状态,这时数据库中没有该对象的信息,如果没有持久化,程序退出后临时状态的对象信息将会丢失.随时可能被垃圾回收器 ...

  5. Android开发学习之路--Content Provider之初体验

    天气说变就变,马上又变冷了,还好空气不错,阳光也不错,早起上班的车上的人也不多,公司来的同事和昨天一样一样的,可能明天会多一些吧,那就再来学习android吧.学了两个android的组件,这里学习下 ...

  6. 【一天一道LeetCode】#326. Power of Three

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  7. centos7安装jdk,tomcat,msyql(MariaDB)

    操作系统版本 CentOS Linux release 7.2.1511 (Core) 安装jdk 下载jdk-8u66-linux-x64.rpm上传到linux上 先改用户权限 然后 rpm -i ...

  8. 从JDK源码角度看并发锁的优化

    在CLH锁核心思想的影响下,JDK并发包以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点的结构与节点等待机 ...

  9. [WinForm]dataGridView自定动态设定序号列框

    你可以在数据绑定或者行数有很大变化时测量一下DataGridView最大行数的行数的宽度然后在绘制代码如下 SolidBrush solidBrush; StringFormat stringForm ...

  10. [信号处理技术]关于EMD的产生

    通俗易懂,有助于理解EMD和HHT,就原封不动的搬过来了. 原文链接:关于EMD的产生 自傅里叶变换与频谱分析技术产生,人们得以从另外一个角度观察时域信号,信号里各个点的密集程度,得以确定性地度量.之 ...