V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
anai1943
V2EX  ›  PHP

Laravel 5.2 使用 swoole websocket ,事件广播不成功?

  •  
  •   anai1943 · 2017-05-24 21:46:48 +08:00 · 4716 次点击
    这是一个创建于 2741 天前的主题,其中的信息可能已经有所发展或是发生改变。

    app/Console/Commands/Swoole.php

    <?php
    namespace App\Console\Commands;
    use App\Handlers\SwooleHandler;
    use Illuminate\Console\Command;
    
    
    class Swoole extends Command
    {
        /**
         * The name and signature of the console command.
         *
         * @var string
         */
        protected $signature = 'swoole {action}';
    
        /**
         * The console command description.
         *
         * @var string
         */
        protected $description = 'swoole socket';
    
        protected $ws;
    
        /**
         * Create a new command instance.
         *
         * @return void
         */
        public function __construct()
        {
            parent::__construct();
        }
    
        /**
         * Execute the console command.
         *
         * @return mixed
         */
        public function handle()
        {
            $action = $this->argument('action');
            $this->ws = new \swoole_websocket_server( "http://" .env('SOCKET_DOMAIN'), env('SOCKET_PORT') );
            $this->ws->set(['worker_num' => env('SOCKET_WORKER_NUM')]);
            switch ($action) {
                case 'start':
                    $handler = new SwooleHandler();
                    $this->ws->on('Open', [$handler, 'onOpen']);
                    $this->ws->on('Message', [$handler, 'onMessage']);
                    $this->ws->on('Close', [$handler, 'onClose']);
                    $this->ws->start();
                    break;
                case 'reload':
                    $this->ws->reload();
                    break;
                case 'stop':
                    $this->ws->stop();
                    break;
            }
    
        }
    }
    

    app/Handlers/SwooleHandler.php

    <?php
    namespace App\Handlers;
    
    use LRedis;
    use App\Models\Message;
    
    class SwooleHandler
    {
        public function onOpen($ws, $request)
        {
            $user_id = $request->get['user_id'];
            echo "client - {$user_id} is opened\n";
            LRedis::hSet('FRONT_USERS', $user_id, $request->fd);
        }
    
        public function onMessage($ws, $frame)
        {
            $user_id = $frame->data;
            $fd = LRedis::hGet('FRONT_USERS', $user_id);
            echo "client - {$fd} is send\n";
            $num = Message::query()->where('user_id',$user_id)->count();
            $ws->push($fd, $num);
        }
    
        public function onClose($ws, $fd)
        {
            echo "client - {$fd} is closed\n";
            $all = LRedis::hGetAll('FRONT_USERS');
            foreach ($all as $key => $val) {
                if ($fd == LRedis::hGet('FRONT_USERS', $key)) {
                    LRedis::hDel('FRONT_USERS', $key);
                    echo "del {$key}\n";
                }
            }
        }
    
    }
    

    app/Events/MessageEvent.php

    <?php
    
    namespace App\Events;
    
    use App\Models\Message;
    use App\Events\Event;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    
    class MessageEvent extends Event implements ShouldBroadcast
    {
        use SerializesModels;
        public $user_id;
        /**
         * Create a new event instance.
         *
         * @return void
         */
        public function __construct($from_user_id, $user_id, $content)
        {
            $this->model = new Message();
            $this->from_user_id = $from_user_id;
            $this->user_id = $user_id;
            $this->content = $content;
        }
    
        /**
         * Get the channels the event should be broadcast on.
         *
         * @return array
         */
        public function broadcastOn()
        {
            return ['message-channel'];
        }
    }
    

    app/Listeners/MessageListener.php

    <?php
    namespace App\Listeners;
    
    use App\Events\MessageEvent;
    use Illuminate\Queue\InteractsWithQueue;
    use Illuminate\Contracts\Queue\ShouldQueue;
    
    class MessageListener
    {
        /**
         * Create the event listener.
         *
         * @return void
         */
        public function __construct()
        {
            //
        }
    
        /**
         * Handle the event.
         *
         * @param  MessageEvent  $event
         * @return void
         */
        public function handle(MessageEvent $event)
        {
            $model = $event->model;
            $model->from_user_id = $event->from_user_id;
            $model->user_id = $event->user_id;
            $model->content = $event->content;
            $model->save();
        }
    }
    
    

    app/Providers/EventServiceProvider.php

    <?php
    namespace App\Providers;
    
    use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
    use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
    
    class EventServiceProvider extends ServiceProvider
    {
        /**
         * The event listener mappings for the application.
         *
         * 事件侦听器映射到应用程序
         *
         * @var array
         */
        protected $listen = [
            // 站内信事件监听
            'App\Events\MessageEvent' => [
                'App\Listeners\MessageListener',
            ],
        ];
    
        /**
         * Register any other events for your application.
         *
         * @param  \Illuminate\Contracts\Events\Dispatcher  $events
         * @return void
         */
        public function boot(DispatcherContract $events)
        {
            parent::boot($events);
        }
    }
    

    前台模板

    <!doctype html>
    <html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>OA 首页</title>
    </head>
    <body>
        <div style="width: 300px;margin: 100px auto;">
            <h1>Hi, {{auth('front')->user()->name}}</h1>
            <a href="{{url('auth/logout')}}">Logout</a>
        </div>
        <script type="text/javascript">
            var exampleSocket = new WebSocket("ws://{{env('SOCKET_DOMAIN')}}:{{env('SOCKET_PORT')}}?user_id={{auth('front')->user()->id}}");
            exampleSocket.onopen = function (event) {
                exampleSocket.send({{auth('front')->user()->id}});
            };
            exampleSocket.onmessage = function (event) {
                console.log(event.data);
            }
        </script>
    </body>
    </html>
    

    运行 php artisan swoole start,前台页面第一次加载的时候,websocket 链接的 onopen, onmessage, onclose 都是可以正常运行的。

    Mac-Pro:xxx.com xxx$ php artisan swoole start
    client - 22 is opened
    client - 1 is send
    client - 1 is closed
    

    但是执行 \Illuminate\Support\Facades\Event::fire(new \App\Events\MessageEvent($from_user_id, $user_id, $content)) 触发事件的时候,前台页面 onmessage 里面不能输出最新的数据。。求大神指教。谢谢!

    3 条回复    2017-05-26 08:54:08 +08:00
    torbrowserbridge
        1
    torbrowserbridge  
       2017-05-25 09:24:11 +08:00
    0. MessageListener::handle() 的作用是什么?执行了吗?

    1. 广播驱动器是 Redis 吗?没看到在哪里订阅 channel 的呢?

    2. 广播执行了吗?你可能需要 listen queue 来执行队列任务。
    anai1943
        2
    anai1943  
    OP
       2017-05-25 21:29:12 +08:00
    @torbrowserbridge
    0. MessageListener::handle() 执行了,是将 message 数据写入数据库
    1. 广播驱动是 redis,订阅 channel 不明白怎么写。。
    2. 运行 php artisan queue:listen 显示 Processed: Illuminate\Broadcasting\BroadcastEvent
    torbrowserbridge
        3
    torbrowserbridge  
       2017-05-26 08:54:08 +08:00
    1. 写入数据库的目的是?为了统计 count ?
    2. 你这段没实现订阅。
    3. “运行 php artisan queue:listen 显示 Processed: Illuminate\Broadcasting\BroadcastEvent ” 这里证明 event 被广播出去了。
    4. 可以在 redis 命令行中输入:psubscribe * 来实现订阅全部 channel。看看当事件被广播后,终端是否有对应的输出。
    5. 如果 4 正常输出,尝试在 swoole 中实现 redis 订阅,然后将相应的结果写回对应的 fd
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3439 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 11:46 · PVG 19:46 · LAX 03:46 · JFK 06:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.