openresty 很早就支持websocket了,但是早期的版本cosocket是单工的,处理起来比较麻烦参见邮件列表讨论 websocket chat,后来的版本cosocket是双全工的,就可以按照这个讨论的方案来实现基于websocket的聊天,或者是push程序了,但是网络上没有找到一个具体一点的例子,于是自己写了个simple的例子。
client的websocket连接到openresty之后,使用ngx.thread.spawn
启动两个 轻线程
,一个用来接收客户端提交的数据往redis的channel写,另一个用来订阅channel,读取redis的数据写给客户端。channel相当于一个chat room,多个client一起订阅,有人发聊天信息(pub),所有人都能得到信息(sub)。代码比较简陋,简单的思路的实现。
依赖:
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>
<code><span><span>html</span>></span><span>head</span>><span>meta</span><span>charset</span>=<span>"utf-8"</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>script</span><span>type</span>=<span>"text/javascript"</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>body</span>><span>div</span><span>id</span>=<span>"sse"</span>><span>a</span><span>href</span>=<span>"javascript:WebSocketConn()"</span>>进入聊天室<span><span>a</span>></span> <span>a</span><span>href</span>=<span>"javascript:WebSocketClose()"</span>>离开聊天室<span><span>a</span>></span><span>br</span>><span>br</span>><span>input</span><span>id</span>=<span>"msgtext"</span><span>type</span>=<span>"text"</span>><span>br</span>><span>a</span><span>href</span>=<span>"javascript:SendMsg()"</span>>发送信息<span><span>a</span>></span><span>br</span>><span>ol</span><span>id</span>=<span>"log"</span>><span><span>ol</span>></span><span><span>div</span>></span><span><span>body</span>></span><span><span>html</span>></span></code>
用iphone试了试,不好使,可能是websocket版本实现的问题。pc端测试可以正常使用。
以上就介绍了openresty+websocket+redis simple chat,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。