[实战]laravel + redis订阅发布 +swoole实现实时订单通知

  1. redis生产者发布

    php artisan make:command Redis/PublishCommand
  2. redis消费者订阅

    php artisan make:command Redis/SubCommand
  3. 启动websocket

    php artisan ws
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use App\Facades\MyRedis;

class WebSocket extends Command
{
    protected $signature = 'ws';

    protected $ws;

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        //创建websocket服务器对象,监听0.0.0.0:9502端口,开启SSL隧道
        $this->ws = new \swoole_websocket_server("0.0.0.0", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);

        //配置参数
        $this->ws ->set([
            'daemonize' => false, //守护进程化。
            //配置SSL证书和密钥路径
            'ssl_cert_file' => env('SSL_CERT_FILE'),
            'ssl_key_file'  => env('SSL_KEY_FILE')
        ]);

        $this->ws->on("open", function (\Swoole\WebSocket\Server $ws, \Swoole\Http\Request $request) {
//            dump($request->fd, $request->get, $request->server);
            $ws->push($request->fd, "连接成功\n");
        });

        $this->ws->on("message", function (\Swoole\WebSocket\Server $ws, $frame) {
//            dump("消息: {$frame->data}\n");
            $ws->push($frame->fd, "服务端回复: {$frame->data}\n");
        });

        //监听WebSocket主动推送消息事件
        $this->ws->on('request', function ($request, $response) {
            $data = $request->post;
            //$this->ws->connections 遍历所有websocket连接用户的fd,给所有用户推送
            foreach ($this->ws->connections as $fd) {
                if ($this->ws->isEstablished($fd)) {
                    $this->ws->push($fd, json_encode($data));
                }
            }
        });

        $this->ws->on("close", function (\Swoole\WebSocket\Server $ws, $fd) {
            dump("{$fd}关闭");
        });

        $this->ws->start();
    }

}
  1. 启动redis消费者订阅
    php artisan sub:info
<?php

namespace App\Console\Commands\Redis;

use App\Facades\MyRedis;
use Illuminate\Console\Command;

class SubCommand extends Command
{

    protected $signature = 'sub:info';


    protected $description = 'redis消费者订阅';


    public function __construct()
    {
        parent::__construct();
    }


    public function handle()
    {
        MyRedis::subScribe();
        $this->comment('sub successful');
    }
}
  1. h5页面连接websocket
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>websocket</title>
</head>

<body>
    <script>
        var wsServer = 'wss://test.自己域名.com:9502';
        var websocket = new WebSocket(wsServer);
        websocket.onopen = function (evt) {
            console.log("Connected to WebSocket server.");
        };

        websocket.onclose = function (evt) {
            console.log("Disconnected");
        };

        websocket.onmessage = function (evt) {
            console.log('Retrieved data from server: ' + evt.data);
        };

        websocket.onerror = function (evt, e) {
            console.log('Error occured: ' + evt.data);
        };
    </script>

</body>

</html>

laravel + redis订阅发布 +swoole

  1. redis生产者发布
    php artisan publish:info
<?php

namespace App\Console\Commands\Redis;

use App\Facades\MyRedis;
use Illuminate\Console\Command;

class PublishCommand extends Command
{

    protected $signature = 'publish:info';

    protected $description = 'redis生产者发布';


    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $data = [
            'orders_id' => 1
        ];
        MyRedis::publish($data);
        $this->comment('publish successful');
    }
}

[实战]laravel + redis订阅发布 +swoole实现实时订单通知

  1. 查看h5 websocket页面实时返回通知结果

laravel + redis订阅发布 +swoole

相关Facades代码

<?php

namespace App\Facades;

use App\Services\MyRedis\MyRedisService;
use Illuminate\Support\Facades\Facade;

/**
 * Class Response
 * @package App\Facades
 * @method static boolean publish(array $data)
 * @method static subScribe()
 */

class MyRedis extends Facade
{
    protected static function getFacadeAccessor()
    {
        return MyRedisService::class;
    }
}
<?php

namespace App\Services\MyRedis;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class MyRedisService
{
    protected $subKey = 'ordersRemind';//订单提醒

    //生产者发布
    public function publish(array $data) : bool
    {
        try {
            Redis::publish($this->subKey, json_encode($data));
        } catch (\Exception $exception) {
            Log::error($exception->getMessage());
            return false;
        }
        return true;
    }

    //消费者订阅
    public function subScribe()
    {
        set_time_limit(0);
        ini_set('default_socket_timeout', -1);
        try {
            echo 'start' . "\n";
            Redis::subscribe($this->subKey, function ($json, $message) {
                $url = "https://test.自己的域名.com:9502";
                MyRedisService::httpPost($url, json_decode($json,true));
            });
        } catch (\Exception $exception) {
            Log::error($exception->getMessage());
        }
    }

    public static function httpPost($url, $param){
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_HEADER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $param);
        curl_exec($curl);
        curl_close($curl);
    }
}

[实战]laravel + redis订阅发布 +swoole实现实时订单通知

守护进程

  1. 借助 nohup 和 & 配合使用
    nohup php artisan sub:info &
  2. 通过 fg 命令让进程恢复到普通占用控制台的模式
    fg

关闭进程

netstat -anp | grep 9502
kill -9 1682674

相关文档
php实现守护进程的两种常见方式

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 1年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 10

:joy: 这两天刚想做一个订单实时功能,准备开始干活,上来learnku就看到首页就有这个文章,不由得让我害怕,这莫非是老天爷的算法推送吗难道!!!

1年前 评论
PHP-Coder 1年前
my38778570 (楼主) 1年前
微加加的朋友 1年前

你好 这种如果用laravel自带的消息队列来实现 和 自己写redis发布订阅这两者有什么区别 哪一种会比较好点

1年前 评论

不如弄个数据库、redis 降级的。 redis 崩了,根据配置自动切换到 数据库或内存

1年前 评论

直接用swoole开个服务多好 页面直接调用

1年前 评论
my38778570 (楼主) 1年前

大佬,curl 那里nginx是不是还要配一个http 到websocket的协议升级?

1年前 评论
my38778570 (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!