search
HomeBackend DevelopmentPHP Tutorialopenresty+websocket+redis simple chat

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

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教程有兴趣的朋友有所帮助。

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
What data can be stored in a PHP session?What data can be stored in a PHP session?May 02, 2025 am 12:17 AM

PHPsessionscanstorestrings,numbers,arrays,andobjects.1.Strings:textdatalikeusernames.2.Numbers:integersorfloatsforcounters.3.Arrays:listslikeshoppingcarts.4.Objects:complexstructuresthatareserialized.

How do you start a PHP session?How do you start a PHP session?May 02, 2025 am 12:16 AM

TostartaPHPsession,usesession_start()atthescript'sbeginning.1)Placeitbeforeanyoutputtosetthesessioncookie.2)Usesessionsforuserdatalikeloginstatusorshoppingcarts.3)RegeneratesessionIDstopreventfixationattacks.4)Considerusingadatabaseforsessionstoragei

What is session regeneration, and how does it improve security?What is session regeneration, and how does it improve security?May 02, 2025 am 12:15 AM

Session regeneration refers to generating a new session ID and invalidating the old ID when the user performs sensitive operations in case of session fixed attacks. The implementation steps include: 1. Detect sensitive operations, 2. Generate new session ID, 3. Destroy old session ID, 4. Update user-side session information.

What are some performance considerations when using PHP sessions?What are some performance considerations when using PHP sessions?May 02, 2025 am 12:11 AM

PHP sessions have a significant impact on application performance. Optimization methods include: 1. Use a database to store session data to improve response speed; 2. Reduce the use of session data and only store necessary information; 3. Use a non-blocking session processor to improve concurrency capabilities; 4. Adjust the session expiration time to balance user experience and server burden; 5. Use persistent sessions to reduce the number of data read and write times.

How do PHP sessions differ from cookies?How do PHP sessions differ from cookies?May 02, 2025 am 12:03 AM

PHPsessionsareserver-side,whilecookiesareclient-side.1)Sessionsstoredataontheserver,aremoresecure,andhandlelargerdata.2)Cookiesstoredataontheclient,arelesssecure,andlimitedinsize.Usesessionsforsensitivedataandcookiesfornon-sensitive,client-sidedata.

How does PHP identify a user's session?How does PHP identify a user's session?May 01, 2025 am 12:23 AM

PHPidentifiesauser'ssessionusingsessioncookiesandsessionIDs.1)Whensession_start()iscalled,PHPgeneratesauniquesessionIDstoredinacookienamedPHPSESSIDontheuser'sbrowser.2)ThisIDallowsPHPtoretrievesessiondatafromtheserver.

What are some best practices for securing PHP sessions?What are some best practices for securing PHP sessions?May 01, 2025 am 12:22 AM

The security of PHP sessions can be achieved through the following measures: 1. Use session_regenerate_id() to regenerate the session ID when the user logs in or is an important operation. 2. Encrypt the transmission session ID through the HTTPS protocol. 3. Use session_save_path() to specify the secure directory to store session data and set permissions correctly.

Where are PHP session files stored by default?Where are PHP session files stored by default?May 01, 2025 am 12:15 AM

PHPsessionfilesarestoredinthedirectoryspecifiedbysession.save_path,typically/tmponUnix-likesystemsorC:\Windows\TemponWindows.Tocustomizethis:1)Usesession_save_path()tosetacustomdirectory,ensuringit'swritable;2)Verifythecustomdirectoryexistsandiswrita

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

EditPlus Chinese cracked version

EditPlus Chinese cracked version

Small size, syntax highlighting, does not support code prompt function

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools