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

2017-05-24 21:46:48 +08:00
 anai1943

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 里面不能输出最新的数据。。求大神指教。谢谢!

4714 次点击
所在节点    PHP
3 条回复
torbrowserbridge
2017-05-25 09:24:11 +08:00
0. MessageListener::handle() 的作用是什么?执行了吗?

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

2. 广播执行了吗?你可能需要 listen queue 来执行队列任务。
anai1943
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
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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/363574

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX