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

Laravel框架
543
0
0
2022-10-20
  1. redis生产者发布
php artisan make:command Redis/PublishCommand
  1. redis消费者订阅
php artisan make:command Redis/SubCommand
  1. 启动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 &
  1. 通过 fg 命令让进程恢复到普通占用控制台的模式
fg

关闭进程

netstat -anp | grep 9502
kill -9 1682674