在 Laravel 7.x 中,如何将 Generator 通过广播 broadcast 做消息推送?

我将大量的数据存储在redis中,然后查询数据时使用 Generator 作为结果,然后通过 broadcast 做消息推送,具体不知道怎么实现?

获取数据

<?php
namespace App\DataCenter;

use Generator;
use Predis\Client;

/**
 * 用户相关的数据
 */
class UserData
{
    /**
     * 各项数据的数量
     */
    public static function count(Client $redis, int $uid): array
    {
        return [
            'messages' => $redis->scard("users:$uid:messages"),
            'managed_teams' => $redis->scard("users:$uid:teams:managed"),
            'served_teams' => $redis->scard("users:$uid:teams:served"),
            'customer_types' => $redis->hlen("customers_types"),
            'customers' => $redis->scard("users:$uid:customers"),
            'districts' => $redis->scard("users:$uid:districts"),
            // 可能还有其它数据
        ];
    }

    /**
     * 用户的未读消息
     */
    public static function messages(Client $redis, int $uid): Generator
    {
        $messages = $redis->smembers("users:$uid:messages");
        foreach($messages as $messageId) {
            yield $redis->hgetall("messages:$messageId");
        }
    }

    /**
     * 用户负责地团队
     */
    public static function managedTeams(Client $redis, int $uid): Generator
    {
        $managedTeams = $redis->smembers("users:$uid:teams:managed");
        foreach($managedTeams as $teamId) {
            yield $redis->hgetall("teams:$teamId");
        }
    }

    /**
     * 用户服务地团队
     */
    public static function servedTeams(Client $redis, int $uid): Generator
    {
        $servedTeams = $redis->smembers("users:$uid:teams:served");
        foreach($servedTeams as $teamId) {
            yield $redis->hgetall("teams:$teamId");
        }
    }

    // customer_types、customers、districts 及其它数据实现方式同上
}

定义事件

<?php
namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use function iterator_to_array;
use function predis;

/**
 * 用户数据同步事件
 *
 * @example
 *
 * ```php
 * <?php
 * use  App\Events\UserDataSyncEvent;
 * event(new UserDataSyncEvent(getUser()->id, 'count'))
 * event(new UserDataSyncEvent(getUser()->id, 'count'))
 * event(new UserDataSyncEvent(getUser()->id, 'managed_teams'))
 * ```
 */
class UserDataSyncEvent implements ShouldBroadcast
{
    public int $uid;
    public string $target;

    public function __construct(int $uid, string $target)
    {
        $this->uid = $uid;
        $this->target = $target;
    }

    public function broadcastOn()
    {
        return new Channel("user_sync.$this->uid");
    }

    /**
     * 根据属性 $target 返回相应的数据;通过类 \App\DataCenter\UserData
     * 获取的结果基本上都是生成器 \Generator ,而函数 iterator_to_array 会将
     * 数据一次性加载完成,导致 PHP 占用了大量内存,也就导致了程序崩溃。
     */
    public function broadcastWith()
    {
        $redis = predis();
        switch($this->target) {
            case 'districts':
                return iterator_to_array(UserData::districts($redis, $this->uid));
            case 'messages':
                return iterator_to_array(UserData::messages($redis, $this->uid));
            case 'managed_teams':
                return iterator_to_array(UserData::managedTeams($redis, $this->uid));
            case 'managed_teams':
                return iterator_to_array(UserData::managedTeams($redis, $this->uid));
            case 'customer_types':
                return iterator_to_array(UserData::customerTypes($redis, $this->uid));
            case 'customers':
                return iterator_to_array(UserData::customers($redis, $this->uid));
            // case '...':
            // return iterator_to_array(UserData::...($redis, $this->uid));
            default:
                return UserData::count($redis, $this->uid);
        }
    }
}

解决方案

我希望能够像数据流一样,利用生成器一条一条的持续推送;但是我使用的是 laravel-echo-server 实现推送功能, laravel 没有办法控制WebSocket,请问如何实现?

《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 2
3年前 评论
tube (楼主) 3年前

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