PHP redis workman实现定时广播,远程控制功能
来源:互联网 发布:徐家福 知乎 编辑:程序博客网 时间:2024/04/27 21:35
<?phpnamespace app\terminal\library;use app\terminal\model\TerminalMessageLogModel;use Predis\Client;use think\Config;abstract class MessagePool{ protected $messageType; protected $redis; protected $aheadTime = 5 * 60;// 提前发送时间,单位:秒 protected $expiredTime = 5 * 60;// 过期时间,单位:秒 protected $message; protected $isInstant = false; protected abstract function _getTerminals($terminalMessage); protected abstract function _sendMessage($terminalMessage, $params, $terminalId); public function __construct($isInstant = false) { Config::load(APP_PATH . 'terminal/config.php'); $options = Config::get('redis'); $this->redis = new Client($options); if ($isInstant) { $this->isInstant = true; $this->messageType = $this->messageType . 'Instant'; } } public function add($message) { $redis = $this->redis; $messageType = $this->messageType; $redis->hmset($this->_getMessageSetName($messageType, $message['id']), $message); // 添加待发送列表 $nextExecuteTime = $this->isInstant ? time() : $this->_getNextExecuteTime($message); //dump(date('Y-m-d H:i:s', $nextExecuteTime)); $redis->zadd($this->_getSendingQueueName($messageType), $nextExecuteTime , $message['id']); // 添加待过期列表 $lastExecuteTime = $this->isInstant ? time() : $this->_getLastExecuteTime($message); //dump(date('Y-m-d H:i:s', $lastExecuteTime)); $redis->zadd($this->_getExpiringQueueName($messageType), $lastExecuteTime , $message['id']); // 即时执行 if ($this->isInstant) { $this->consume(); } } /** * 更新消息 * @param $message */ public function update($message) { $this->remove($message['id']); $this->add($message); } /** * 删除某个消息 * @param $messageId */ public function remove($messageId) { $redis = $this->redis; $messageType = $this->messageType; $messageSetName = $this->_getMessageSetName($this->messageType, $messageId); $this->deleteFromHset($messageSetName); $redis->zrem($this->_getSendingQueueName($messageType), $messageId); $redis->zrem($this->_getExpiringQueueName($messageType), $messageId); } /** * 用于定时地生成发送日志并发送消息 * 处理流程:从有序集合里面获取最近要执行的消息 -> 获取目标终端 ID -> 记录发送日志 -> 更新有序集合 -> 发送消息 */ public function consume() { $redis = $this->redis; $messageType = $this->messageType; // 获取待发送的消息 $sendTimeRange = time() + $this->aheadTime; //dump(date('Y-m-d H:i:s', $sendTimeRange)); $sending = $redis->zrangebyscore($this->_getSendingQueueName($messageType) , 0 , $sendTimeRange, 'withscores'); // 获取待发送消息,写发送日志 foreach ($sending as $messageId => $sendTime) { // 获取待发送消息详情 $terminalMessage = $redis->hgetall($this->_getMessageSetName($messageType, $messageId)); // 获取目标终端 $terminals = $this->_getTerminals($terminalMessage); // 记录发送日志 foreach ($terminals as $terminal) { // 把发送日志记录到 MySQL $log = new TerminalMessageLogModel([ 'message_type' => TerminalMessageLogModel::MESSAGE_TYPES[$messageType], 'message_id' => $messageId, 'device_id' => $terminal['device_id'], 'terminal_id' => $terminal['id'], 'send_time' => date('Y-m-d H:i:s', $sendTime) ]); $log->save(); // 把发送日志记录到 Redis $sendLog = [ 'message_id' => $messageId, 'terminal_id' => $terminal['id'], 'send_time' => $sendTime ]; $redis->hmset($this->_getSendLogSetName($messageType, $log['id']), $sendLog); $redis->zadd($this->_getSendLogQueueName($messageType), $sendTime , $log['id']); } } // 删除有序集合里面的发送任务 $redis->zremrangebyscore($this->_getSendingQueueName($messageType), 0, $sendTimeRange); // 根据发送日志调用消息接口发送 $this->message = new Message(); $sendLogKeys = $redis->keys($this->_getSendLogSetName($messageType, '*')); foreach ($sendLogKeys as $sendLogKey) { $sendLog = $redis->hgetall($sendLogKey); $terminalMessage = $redis->hgetall($this->_getMessageSetName($messageType, $sendLog['message_id'])); $params = [ 'log_id' => $this->_getSendLogId($sendLogKey), 'send_time' => date('Y-m-d H:i:s', $sendLog['send_time']), ]; $this->_sendMessage($terminalMessage, $params, $sendLog['terminal_id']); } } /** * 定时清除过期的消息和发送日志 */ public function clear() { $redis = $this->redis; $messageType = $this->messageType; // 清理过期的消息 $expiredTimeRange = time() - $this->expiredTime; $expiring = $redis->zrangebyscore($this->_getExpiringQueueName($messageType), 0, $expiredTimeRange); foreach ($expiring as $messageId) { $messageSetName = $this->_getMessageSetName($messageType, $messageId); $this->deleteFromHset($messageSetName); } $redis->zremrangebyscore($this->_getExpiringQueueName($messageType), 0, $expiredTimeRange); // 清理过期的发送日志 $expiring = $redis->zrangebyscore($this->_getSendLogQueueName($messageType), 0, $expiredTimeRange); foreach ($expiring as $logId) { $sendLogSetName = $this->_getSendLogSetName($messageType, $logId); $this->deleteFromHset($sendLogSetName); } $redis->zremrangebyscore($this->_getSendLogQueueName($messageType), 0, $expiredTimeRange); return true; } /** * 定时任务,每晚11点50分执行,创建第二天的执行时间有序集合 * @param $message * @return false|int * @throws MessageException */ public function createExecuteTimeSet() { } private function _getNextExecuteTime($message) { $today = date('Y-m-d'); $week = explode(',', $message['week']); if (trim($week[0]) == '') { throw new MessageException('星期数据为空'); } if ($message['end_date'] < $today) { throw new MessageException('任务已过期'); } $beginDate = $message['begin_date'] < $today ? $today : $message['begin_date']; $executeDate = date('H:i:s') < $message['time'] ? strtotime($beginDate) : strtotime('+1 days', strtotime($beginDate));// 下次执行的日期,时间戳格式 $executeDayInWeek = date('N', $executeDate);// 一周中的第几天(星期一为第一天) sort($week); $week[count($week)] = $week[0] + 7;// 一周七天 foreach ($week as $w) { if ($executeDayInWeek <= $w) { $executeDate = strtotime('+'.strval($w-$executeDayInWeek).' days', $executeDate); $executeDate = date('Y-m-d', $executeDate); if ($executeDate > $message['end_date']) { throw new MessageException('此任务没有执行时间'); } return strtotime($executeDate . ' ' . $message['time']); } } throw new MessageException('获取下次执行时间失败'); } private function _getLastExecuteTime($message) { $today = date('Y-m-d'); $week = explode(',', $message['week']); if (trim($week[0]) == '') { throw new MessageException('星期数据为空'); } if ($message['end_date'] < $today || ($message['end_date'] == $today && $message['time'] < date('H:i:s')) ) { throw new MessageException('此任务已过期'); } $executeDate = strtotime($message['end_date']); $executeDayInWeek = date('N', $executeDate);// 一周中的第几天(星期一为第一天) rsort($week);// 降序排列 $week[count($week)] = $week[0] - 7;// 一周七天 foreach ($week as $w) { if ($executeDayInWeek >= $w) { $executeDate = strtotime('-'.strval($executeDayInWeek-$w).' days', $executeDate); $executeDate = date('Y-m-d', $executeDate); if ($executeDate . $message['time'] < $today . date('H:i:s')) { throw new MessageException('此任务已过期'); } return strtotime($executeDate . ' ' . $message['time']); } } throw new MessageException('获取最后执行时间失败'); } private function _getMessageSetName($messageType, $messageId) { return "messageSet:$messageType:$messageId"; } /** * 获取待发送的消息的有序集合名。集合元素为 message_id,分值为为此消息的下一次发送时间 * @param $messageType * @return string */ private function _getSendingQueueName($messageType) { return "sendingQueue:$messageType"; } /** * 获取待过期的消息的有序集合名。集合元素为 message_id,分值为此消息的最后一次发送时间 * @param $messageType * @return string */ private function _getExpiringQueueName($messageType) { return "expiringQueue:$messageType"; } private function _getSendLogSetName($messageType, $logId) { return "sendLog:$messageType:$logId"; } private function _getSendLogId($sendLogKey) { return explode(':', $sendLogKey)[2]; } private function _getSendLogQueueName($messageType) { return "sendLogQueue:$messageType"; } /** * 根据键名删除一个 Redis 哈希集合 * @param $key */ private function deleteFromHset($hsetName) { $redis = $this->redis; $keys = $redis->hkeys($hsetName); foreach ($keys as $key) { $redis->hdel($hsetName, $key); } return true; }}
阅读全文
0 0
- PHP redis workman实现定时广播,远程控制功能
- PHP实现定时功能
- OnTimer()函数用于实现定时控制功能
- php+redis实现电商秒杀功能
- php+redis实现电商秒杀功能
- 通过广播接收者来实现定时提醒功能
- php的ssh2扩展实现远程控制
- 使用PHP实现采集远程图片功能
- 使用PHP实现采集远程图片功能
- Redis:redis远程访问控制
- Android广播事件机制,并实现简单的定时提醒功能
- Android广播事件机制及应用(实现简单的定时提醒功能)
- Android广播事件机制,并实现简单的定时提醒功能
- Android广播事件机制及应用(实现简单的定时提醒功能)
- 极客DIY:20多元改造家用开关,实现远程控制、定时控制
- C#实现简单远程控制及桌面监控功能(客户端)
- C#实现远程控制及桌面监控功能(服务端)
- php redis 并发控制
- ssm框架 jsp页面的图片等文件
- 云计算连涨了9个季度背后,迅雷完成了一次「长征」
- 心情特别糟糕
- 酷科技 | 全新智能存储HBA和RAID阵列卡
- C3P0管理Oracle连接池
- PHP redis workman实现定时广播,远程控制功能
- 关于 const* 与 *const 的测试
- 框架相关的面试题
- [leetcode]#111. Minimum Depth of Binary Tree
- DSS 代码分析【Reliable UDP之超时时间计算】
- Nginx 反向代理学习及实例笔记
- Centos系统基于Docker安装tensorflow
- 用java程序实现mysql的分页效果
- 第一部分:基础问题 1.3