layout: posttitle: PHP,Swoole,Redis PubSub实现简单消息推送
subtitle: PHP,Swoole,Redis PubSub实现简单消息推送
date: 2019-02-16
author: he xiaodong
header-img: img/default-post-bg.jpg
catalog: true
tags:
- PHP
- Redis
- Swoole
- Redis PubSub
- 消息推送

这个是最简单的初级版本

应用场景:限于不很严谨的消息推送系统,主要是给在线用户推送消息

  • 类似论坛新加精/置顶一篇文章,需要通知一下在线的用户
  • 活动已经通知过全部用户,在开始前通知一下在线用户的场景

  前提先确保安装并启动了 nginx/apache,php,redis服务,php的swoole扩展已经安装才可以,这些具体安装和配置方法请自行搜索。执行一下 php -m 后可以看到swoole扩展,就是安装成功。

  个人的测试系统是Ubuntu 18.04 LTS,带用户界面,因为需要浏览器进行测试。

2019-02-16-PHP,Swoole,Redis PubSub实现简单消息推送 - 图1

在网站目录,默认的nginx配置的目录为/var/www/html,个人自定义为了/var/www 目录,直接建立 swoole.html , swoole.php

swoole.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>swoole chat room</title>
  5. <meta charset="UTF-8">
  6. <script type="text/javascript">
  7. if(window.WebSocket){
  8. // 端口和ip地址对应不要写错
  9. var webSocket = new WebSocket("ws://127.0.0.1:9502");
  10. webSocket.onopen = function (event) {
  11. //webSocket.send("Hello,WebSocket!");
  12. };
  13. webSocket.onmessage = function (event) {
  14. var content = document.getElementById('content');
  15. content.innerHTML = content.innerHTML.concat('<p style="margin-left:20px;height:20px;line-height:20px;">'+event.data+'</p>');
  16. }
  17. var sendMessage = function(){
  18. var data = document.getElementById('message').value;
  19. webSocket.send(data);
  20. }
  21. }else{
  22. console.log("您的浏览器不支持WebSocket");
  23. }
  24. </script>
  25. </head>
  26. <body>
  27. <div style="width:600px;margin:0 auto;border:1px solid #ccc;">
  28. <div id="content" style="overflow-y:auto;height:300px;"></div>
  29. <hr/>
  30. <div style="height:40px">
  31. <input type="text" id="message" style="margin-left:10px;height:25px;width:450px;">
  32. <button onclick="sendMessage()" style="height:28px;width:75px;">发送</button>
  33. </div>
  34. </div>
  35. </body>
  36. </html>

swoole.php

  1. <?php
  2. $server = new swoole_websocket_server("0.0.0.0", 9502);
  3. $server->on('workerStart', function ($server, $workerId) {
  4. $client = new swoole_redis;
  5. $client->on('message', function (swoole_redis $client, $result) use ($server) {
  6. if ($result[0] == 'message') {
  7. var_dump($result);
  8. foreach($server->connections as $fd) {
  9. $server->push($fd, '频道为: ' $result[1] . ' 发送消息:' . $result[2]);
  10. }
  11. }
  12. });
  13. $client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) {
  14. $client->subscribe('message'); // 队列名称可以自定义
  15. });
  16. });
  17. $server->on('open', function ($server, $request) {
  18. $server->push($request->fd, "hello;\n");
  19. });
  20. // 屏蔽这段会报错
  21. $server->on('message', function (swoole_websocket_server $server, $request) {
  22. $server->push($request->fd, "hello");
  23. });
  24. $server->on('close', function ($server, $fd) {
  25. echo "client-{$fd} is closed\n";
  26. $server->close($fd);
  27. });
  28. $server->start();

/var/www 目录下,执行 php swoole.php 启动 swoole 进程,然后通过浏览器访问 localhost://swoole.html ,就可以看到聊天室页面,新起终端,使用 redis-cli 命令进入redis, 可以通过 pubsub channels 命令查看当前的频道,就可以看到 message 频道,然后 publish message 消息体, 在 message 频道发布一条消息,在聊天室页面就可以看到消息,效果如同:

2019-02-16-PHP,Swoole,Redis PubSub实现简单消息推送 - 图2

这个有一些缺憾是:redis pubsub 自身的缺憾,消息不能持久化,重启redis消息都会丢失。swoole 创建的频道在 swoole 服务退出的时候,频道也会关闭,消息丢失。所以适合搭一个简单给所有在线的用户推消息的体系,对消息必达的要求不高。

todo 更完善严谨的消息推送系统

参考链接:

  1. 我的sf文章
  2. Redis订阅消息转发到 Webscoket
  3. Swoole 速查表

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券