首頁 > 後端開發 > php教程 > openresty+websocket+redis簡單聊天

openresty+websocket+redis簡單聊天

WBOY
發布: 2016-07-29 08:58:46
原創
1567 人瀏覽過

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地址。

配置片段

<code>    location = /sredis <span>{
        content_by_lua_file conf/lua/ws_redis.lua;
    }</span>    location ~ /ws/(.*) <span>{
        alias conf/html/$1.html;
    }</span></code>
登入後複製

lua代码

<code><span>-- simple chat with redis</span><span>local</span> server = <span>require</span><span>"resty.websocket.server"</span><span>local</span> redis = <span>require</span><span>"resty.redis"</span><span>local</span> channel_name = <span>"chat"</span><span>local</span> msg_id = <span>0</span><span>--create connection</span><span>local</span> wb, err = server:new{
  timeout = <span>10000</span>,
  max_payload_len = <span>65535</span>
}

<span>--create success</span><span>if</span><span>not</span> wb <span>then</span>
  ngx.log(ngx.ERR, <span>"failed to new websocket: "</span>, err)
  <span>return</span> ngx.exit(<span>444</span>)
<span>end</span><span>local</span> push = <span><span>function</span><span>()</span></span><span>-- --create redis</span><span>local</span> red = redis:new()
    red:set_timeout(<span>5000</span>) <span>-- 1 sec</span><span>local</span> ok, err = red:connect(<span>"127.0.0.1"</span>, <span>6379</span>)
    <span>if</span><span>not</span> ok <span>then</span>
        ngx.log(ngx.ERR, <span>"failed to connect redis: "</span>, err)
        wb:send_close()
        <span>return</span><span>end</span><span>--sub</span><span>local</span> res, err = red:subscribe(channel_name)
    <span>if</span><span>not</span> res <span>then</span>
        ngx.log(ngx.ERR, <span>"failed to sub redis: "</span>, err)
        wb:send_close()
        <span>return</span><span>end</span><span>-- loop : read from redis</span><span>while</span><span>true</span><span>do</span><span>local</span> res, err = red:read_reply()
        <span>if</span> res <span>then</span><span>local</span> item = res[<span>3</span>]
            <span>local</span> bytes, err = wb:send_text(<span>tostring</span>(msg_id)..<span>" "</span>..item)
            <span>if</span><span>not</span> bytes <span>then</span><span>-- better error handling</span>
                ngx.log(ngx.ERR, <span>"failed to send text: "</span>, err)
                <span>return</span> ngx.exit(<span>444</span>)
            <span>end</span>
            msg_id = msg_id + <span>1</span><span>end</span><span>end</span><span>end</span><span>local</span> co = ngx.thread.spawn(push)

<span>--main loop</span><span>while</span><span>true</span><span>do</span><span>-- 获取数据</span><span>local</span> data, typ, err = wb:recv_frame()

    <span>-- 如果连接损坏 退出</span><span>if</span> wb.fatal <span>then</span>
        ngx.log(ngx.ERR, <span>"failed to receive frame: "</span>, err)
        <span>return</span> ngx.exit(<span>444</span>)
    <span>end</span><span>if</span><span>not</span> data <span>then</span><span>local</span> bytes, err = wb:send_ping()
        <span>if</span><span>not</span> bytes <span>then</span>
          ngx.log(ngx.ERR, <span>"failed to send ping: "</span>, err)
          <span>return</span> ngx.exit(<span>444</span>)
        <span>end</span>
        ngx.log(ngx.ERR, <span>"send ping: "</span>, data)
    <span>elseif</span> typ == <span>"close"</span><span>then</span><span>break</span><span>elseif</span> typ == <span>"ping"</span><span>then</span><span>local</span> bytes, err = wb:send_pong()
        <span>if</span><span>not</span> bytes <span>then</span>
            ngx.log(ngx.ERR, <span>"failed to send pong: "</span>, err)
            <span>return</span> ngx.exit(<span>444</span>)
        <span>end</span><span>elseif</span> typ == <span>"pong"</span><span>then</span>
        ngx.log(ngx.ERR, <span>"client ponged"</span>)
    <span>elseif</span> typ == <span>"text"</span><span>then</span><span>--send to redis</span><span>local</span> red2 = redis:new()
        red2:set_timeout(<span>1000</span>) <span>-- 1 sec</span><span>local</span> ok, err = red2:connect(<span>"127.0.0.1"</span>, <span>6379</span>)
        <span>if</span><span>not</span> ok <span>then</span>
            ngx.log(ngx.ERR, <span>"failed to connect redis: "</span>, err)
            <span>break</span><span>end</span><span>local</span> res, err = red2:publish(channel_name, data)
        <span>if</span><span>not</span> res <span>then</span>
            ngx.log(ngx.ERR, <span>"failed to publish redis: "</span>, err)
        <span>end</span><span>end</span><span>end</span>wb:send_close()
ngx.thread.wait(co)</code>
登入後複製

3 页面代码

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

    <span><span>function</span><span>WebSocketConn</span><span>()</span> {</span><span>if</span> (ws != <span>null</span> && ws.readyState == <span>1</span>) {
            log(<span>"已经在线"</span>);
            <span>return</span>
        }

        <span>if</span> (<span>"WebSocket"</span><span>in</span> window) {
            <span>// Let us open a web socket</span>
            ws = <span>new</span> WebSocket(<span>"ws://localhost:8008/sredis"</span>);

            ws.onopen = <span><span>function</span><span>()</span> {</span>
                log(<span>'成功进入聊天室'</span>);
            };

            ws.onmessage = <span><span>function</span><span>(event)</span> {</span>
                log(event.data)
            };

            ws.onclose = <span><span>function</span><span>()</span> {</span><span>// websocket is closed.</span>
                log(<span>"已经和服务器断开"</span>);
            };

            ws.onerror = <span><span>function</span><span>(event)</span> {</span>
                console.log(<span>"error "</span> + event.data);
            };
        } <span>else</span> {
            <span>// The browser doesn't support WebSocket</span>
            alert(<span>"WebSocket NOT supported by your Browser!"</span>);
        }
    }

    <span><span>function</span><span>SendMsg</span><span>()</span> {</span><span>if</span> (ws != <span>null</span> && ws.readyState == <span>1</span>) {
            <span>var</span> msg = document.getElementById(<span>'msgtext'</span>).value;
            ws.send(msg);
        } <span>else</span> {
            log(<span>'请先进入聊天室'</span>);
        }
    }

    <span><span>function</span><span>WebSocketClose</span><span>()</span> {</span><span>if</span> (ws != <span>null</span> && ws.readyState == <span>1</span>) {
            ws.close();
            log(<span>"发送断开服务器请求"</span>);
        } <span>else</span> {
            log(<span>"当前没有连接服务器"</span>)
        }
    }

    <span><span>function</span><span>log</span><span>(text)</span> {</span><span>var</span> li = document.createElement(<span>'li'</span>);
        li.appendChild(document.createTextNode(text));
        document.getElementById(<span>'log'</span>).appendChild(li);
        <span>return</span><span>false</span>;
    }
    </span><span></<span>script</span>></span><span></<span>head</span>></span><span><<span>body</span>></span><span><<span>div</span><span>id</span>=<span>"sse"</span>></span><span><<span>a</span><span>href</span>=<span>"javascript:WebSocketConn()"</span>></span>进入聊天室<span></<span>a</span>></span>  
        <span><<span>a</span><span>href</span>=<span>"javascript:WebSocketClose()"</span>></span>离开聊天室<span></<span>a</span>></span><span><<span>br</span>></span><span><<span>br</span>></span><span><<span>input</span><span>id</span>=<span>"msgtext"</span><span>type</span>=<span>"text"</span>></span><span><<span>br</span>></span><span><<span>a</span><span>href</span>=<span>"javascript:SendMsg()"</span>></span>发送信息<span></<span>a</span>></span><span><<span>br</span>></span><span><<span>ol</span><span>id</span>=<span>"log"</span>></span><span></<span>ol</span>></span><span></<span>div</span>></span><span></<span>body</span>></span><span></<span>html</span>></span></code>
登入後複製

4 效果

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

openresty+websocket+redis  simple chat

Reading

  • 邮件列表讨论 websocket chat
  • Aapo Websocket with openresty
').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i ').text(i)); }; $numbering.fadeIn(1700); }); });

以上就介绍了openresty+websocket+redis simple chat,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板