本文目录导读:

- 核心思路
- 方案一:基于Redis Zset + Timer(缓存+定时任务)
- 方案二:使用Workerman / Swoole(高性能实时推送)
- 方案三:利用消息队列(RabbitMQ / Kafka)+ 前端定时请求
- 方案四:第三方推送服务(极光/腾讯信鸽/阿里云推送)
- 总结:如何选择?
在PHP项目中实现热门资讯推送,通常需要结合数据采集(热度计算)、存储(缓存与数据库)以及推送机制(异步/实时),以下是几种常见且实用的实现方案,从简单到复杂按阶梯排序。
核心思路
- 定义“热门”:通常基于点击量、评论数、点赞数、收藏数、分享数、时间衰减因子等维度计算一个热度分。
- 计算与存储:定期(如每5分钟或每小时)计算一次热度分,并将排名结果存入高性能缓存(Redis)或数据库。
- 推送机制:根据场景不同,采用长轮询、SSE(Server-Sent Events)、WebSocket 或 第三方推送服务。
基于Redis Zset + Timer(缓存+定时任务)
这是最经典、成本最低的方案,适合绝大多数资讯类网站(如新闻、博客、论坛)。
步骤:
-
设计热度数据存储:使用Redis的
Zset(有序集合)。key为hot:articles,member为文章ID,score为热度分。 -
计算热度分(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; } -
定时更新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); -
推送实现(SSE或长轮询):
- 用户端:浏览器通过JS发起一个持续连接,请求
hot.php。 - 服务端(PHP):
hot.php每隔几秒查询Redis的hot:articlesZset前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秒检查一次 }
- 用户端:浏览器通过JS发起一个持续连接,请求
优点:实现简单,对服务器压力适中,不使用WebSocket也能做到接近实时。
缺点:PHP的SSE会占用一个进程或端口,并发多时需配合Nginx或Swoole。
使用Workerman / Swoole(高性能实时推送)
如果项目是后端管理系统或需要真正的毫秒级推送(如新文章立刻提醒编辑),推荐使用常驻内存的PHP框架。
实现步骤:
-
安装Workerman或Swoole:它们让PHP可以像Node.js一样运行在常驻内存的进程中,支持TCP/WebSocket。
-
建立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(); -
触发推送:当文章有新的点赞/评论时(在业务代码中),计算出新热度,更新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)+ 前端定时请求
这种方案结合了异步解耦和可靠投递,适合大型项目。
流程:
- 用户行为上报:用户点赞、评论等行为后,业务代码发送一条消息到消息队列(
article.event)。 - 消费者计算热度:一个独立的PHP消费者进程(或Swoole进程)消费消息,更新Redis中的热度Zset。
- 前端定时轮询:前端页面每隔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用户的场景(手机通知栏)。
实现:
-
PHP后端:当计算出新的热门文章或榜单更新时,调用第三方SDK。
// 使用极光推送 $client = new \JPush\Client($appKey, $masterSecret); $push = $client->push() ->setPlatform('all') ->addAllAudience() ->message('热门资讯已更新', [ 'article_id' => $topArticleId, 'type' => 'hot_update' ]); $push->send(); -
用户端:APP收到推送后,静默请求后台最新热门列表。
优点:省去自己维护长连接,用户即使不打开APP也能收到通知。
缺点:有第三方依赖和可能的推送延迟(几秒到几分钟),且会消耗用户流量/电量。
如何选择?
| 项目规模 | 推荐方案 | 原因 |
|---|---|---|
| 个人博客/小站点 | 方案一(Redis+定时SSE) | 简单,够用,一台服务器即可跑。 |
| 中型CMS/资讯网站 | 方案一(优化版) + 前端轮询 | Redis Zset + 定时任务计算热点,前端每隔30秒请求最新列表,成本最低且稳定。 |
| 实时排行榜、弹幕、直播间 | 方案二(Workerman/Swoole) | 需要毫秒级更新,PHP自带的长连接方案。 |
| 大型分布式、高并发 | 方案三(消息队列+轮询) 或 方案一架构优化 | 通过MQ解耦,避免热点计算阻塞主业务;前端轮询仍是最稳定保障。 |
| 需要推送APP通知 | 方案四 + 方案一 | 用方案一提供数据,用方案四触达用户。 |
关键建议:从方案一(Redis Zset + 定时任务 + SSE或短轮询) 开始,这是PHP项目的黄金组合,等用户量和业务复杂度上升后,再逐步引入Workerman或消息队列。