PHP项目怎样实现热门资讯推送?

wen PHP项目 76

本文目录导读:

PHP项目怎样实现热门资讯推送?

  1. 核心思路
  2. 方案一:基于Redis Zset + Timer(缓存+定时任务)
  3. 方案二:使用Workerman / Swoole(高性能实时推送)
  4. 方案三:利用消息队列(RabbitMQ / Kafka)+ 前端定时请求
  5. 方案四:第三方推送服务(极光/腾讯信鸽/阿里云推送)
  6. 总结:如何选择?

在PHP项目中实现热门资讯推送,通常需要结合数据采集(热度计算)存储(缓存与数据库)以及推送机制(异步/实时),以下是几种常见且实用的实现方案,从简单到复杂按阶梯排序。

核心思路

  1. 定义“热门”:通常基于点击量、评论数、点赞数、收藏数、分享数、时间衰减因子等维度计算一个热度分。
  2. 计算与存储:定期(如每5分钟或每小时)计算一次热度分,并将排名结果存入高性能缓存(Redis)或数据库。
  3. 推送机制:根据场景不同,采用长轮询SSE(Server-Sent Events)WebSocket第三方推送服务

基于Redis Zset + Timer(缓存+定时任务)

这是最经典、成本最低的方案,适合绝大多数资讯类网站(如新闻、博客、论坛)。

步骤:

  1. 设计热度数据存储:使用Redis的 Zset(有序集合)。keyhot:articlesmember 为文章ID,score 为热度分。

  2. 计算热度分(PHP端)

    // 假设每个用户行为权重不同
    function calculateHotScore($articleId) {
        // 从数据库或统计表中获取原始数据
        $views = getArticleViews($articleId); 
        $likes = getArticleLikes($articleId);
        $comments = getArticleComments($articleId);
        $timeFactor = getTimeDecayFactor($articleId); // 过去24小时的数据权重更高
        // 加权计算公式 (可自定义)
        $score = $views * 0.5 + $likes * 2 + $comments * 3;
        // 加入时间衰减,防止旧文章霸榜
        $createdAt = getArticleCreatedAt($articleId);
        $timeDiff = time() - strtotime($createdAt);
        $decay = 1 / ($timeDiff / (3600 * 12) + 1); // 半衰期12小时
        $finalScore = $score * $decay;
        return $finalScore;
    }
  3. 定时更新Redis:使用Linux的 crontab 或 PHP任务调度器(如Laravel的 schedule)每隔5-10分钟执行一次。

    // 伪代码:cron.php
    $redis = new Redis();
    $redis->connect('127.0.0.1');
    $articles = getAllActiveArticles();
    foreach ($articles as $article) {
        $score = calculateHotScore($article['id']);
        $redis->zAdd('hot:articles', $score, $article['id']);
    }
    // 只保留前50名热点
    $redis->zRemRangeByRank('hot:articles', 0, -51);
  4. 推送实现(SSE或长轮询)

    • 用户端:浏览器通过JS发起一个持续连接,请求 hot.php
    • 服务端(PHP)hot.php 每隔几秒查询Redis的 hot:articles Zset前10名,如果排名有变化,则推送给客户端。
      // hot.php (SSE)
      header('Content-Type: text/event-stream');
      header('Cache-Control: no-cache');

    $lastList = []; while (true) { $currentList = $redis->zRevRange('hot:articles', 0, 9, true); // 获取前10名及分数 if ($currentList != $lastList) { echo "data: " . json_encode($currentList) . "\n\n"; $lastList = $currentList; } ob_flush(); flush(); sleep(5); // 每5秒检查一次 }

优点:实现简单,对服务器压力适中,不使用WebSocket也能做到接近实时。
缺点:PHP的SSE会占用一个进程或端口,并发多时需配合Nginx或Swoole。


使用Workerman / Swoole(高性能实时推送)

如果项目是后端管理系统或需要真正的毫秒级推送(如新文章立刻提醒编辑),推荐使用常驻内存的PHP框架。

实现步骤:

  1. 安装Workerman或Swoole:它们让PHP可以像Node.js一样运行在常驻内存的进程中,支持TCP/WebSocket。

  2. 建立WebSocket服务

    // 基于Workerman的WebSocket服务端
    use Workerman\Worker;
    use Workerman\Connection\TcpConnection;
    $ws_worker = new Worker('websocket://0.0.0.0:8282');
    $ws_worker->onMessage = function(TcpConnection $connection, $data) {
        // 客户端可能发送请求获取热门列表
        $hotList = $redis->zRevRange('hot:articles', 0, 9, true);
        $connection->send(json_encode(['type' => 'hot_list', 'data' => $hotList]));
    };
    Worker::runAll();
  3. 触发推送:当文章有新的点赞/评论时(在业务代码中),计算出新热度,更新Redis,并主动向所有连接的客户端广播更新事件。

    // 在点赞控制器中
    function likeArticle($articleId) {
        // ... 处理点赞逻辑
        $newScore = calculateHotScore($articleId);
        $redis->zAdd('hot:articles', $newScore, $articleId);
        // 广播给所有在线客户端
        $redis->publish('hot_channel', json_encode(['article_id' => $articleId, 'new_score' => $newScore]));
    }
    // 在Workerman的worker中订阅Redis频道
    $worker->onWorkerStart = function() use ($redis, $worker) {
        $redis->subscribe(['hot_channel'], function($instance, $channelName, $message) use ($worker) {
            foreach ($worker->connections as $connection) {
                $connection->send($message);
            }
        });
    };

优点:真正的实时推送,性能极高,适合大规模并发。
缺点:需要部署常驻进程,运维成本略高,对PHP版本和扩展有要求。


利用消息队列(RabbitMQ / Kafka)+ 前端定时请求

这种方案结合了异步解耦和可靠投递,适合大型项目。

流程:

  1. 用户行为上报:用户点赞、评论等行为后,业务代码发送一条消息到消息队列(article.event)。
  2. 消费者计算热度:一个独立的PHP消费者进程(或Swoole进程)消费消息,更新Redis中的热度Zset。
  3. 前端定时轮询:前端页面每隔10秒或30秒发起一个HTTP请求(/hot-list),后端从Redis读取前10名返回。
    // /hot-list 接口
    public function hotList() {
        $list = Redis::zrevrange('hot:articles', 0, 9, true);
        return response()->json($list);
    }

优点:架构清晰,解耦,容错性好(消息不丢失)。
缺点:不是“服务端推送”,而是“客户端轮询”,有一定时间差(10-30秒),流量消耗稍大。


第三方推送服务(极光/腾讯信鸽/阿里云推送)

适合需要同时推送到APP用户的场景(手机通知栏)。

实现:

  1. PHP后端:当计算出新的热门文章或榜单更新时,调用第三方SDK。

    // 使用极光推送
    $client = new \JPush\Client($appKey, $masterSecret);
    $push = $client->push()
        ->setPlatform('all')
        ->addAllAudience()
        ->message('热门资讯已更新', [
            'article_id' => $topArticleId,
            'type' => 'hot_update'
        ]);
    $push->send();
  2. 用户端:APP收到推送后,静默请求后台最新热门列表。

优点:省去自己维护长连接,用户即使不打开APP也能收到通知。
缺点:有第三方依赖和可能的推送延迟(几秒到几分钟),且会消耗用户流量/电量。


如何选择?

项目规模 推荐方案 原因
个人博客/小站点 方案一(Redis+定时SSE) 简单,够用,一台服务器即可跑。
中型CMS/资讯网站 方案一(优化版) + 前端轮询 Redis Zset + 定时任务计算热点,前端每隔30秒请求最新列表,成本最低且稳定。
实时排行榜、弹幕、直播间 方案二(Workerman/Swoole) 需要毫秒级更新,PHP自带的长连接方案。
大型分布式、高并发 方案三(消息队列+轮询) 或 方案一架构优化 通过MQ解耦,避免热点计算阻塞主业务;前端轮询仍是最稳定保障。
需要推送APP通知 方案四 + 方案一 用方案一提供数据,用方案四触达用户。

关键建议:从方案一(Redis Zset + 定时任务 + SSE或短轮询) 开始,这是PHP项目的黄金组合,等用户量和业务复杂度上升后,再逐步引入Workerman或消息队列。

抱歉,评论功能暂时关闭!