1. 功能描述

  1. 系统需要每天对7天内UV(访问过系统的用户)进行推送

    2. 开发

    2.1 项目背景

  2. 记录uv是用户端,使用easyswoole框架,原先已经有pv统计埋点接口。

  3. 定实脚本运行服务端,使用yii2框架。

    2.2 开发思路

    1. 方案1

  4. 需要记录7天内UV

  5. 定时脚本,每天运行一次,查询发送

    2. 方案2

  6. 使用reids的bitmap记录用户id

    3. 方案3

  7. 固化用户最后一次访问系统的时间到数据表,同时做30秒最近操作redis记录(防止大并发记录最后一次访问时间)

    4. 方案4

  8. redis记录一天访问用户id(bitmap或hash)

  9. 定时脚本每天凌晨固化前一天用户访问到数据库
  10. 定时脚本取最近7天访问用户推送

    2.3 开发落地

    1. 方案1

  • 用户端

    1. 在Csp类中添加一个记录用户UV的任务,可以并发(见:)的进行原有工作与当前7天uv统计。(采用redis存储,key是 sevenDaysUv:{$userId} ) ```php <? function main() { $csp = new Csp(); /$csp->add(‘t1’, function () use ($spm, $browseTime, $unionId) { storeSPM($spm, $browseTime, $unionId); });/

      // 记录7天内访问过的用户 if ($unionId) { $csp->add(‘sevenDaysUv’, function () use ($unionId) {

      1. saveUserSevenDaysUv($unionId);

      }); }

      $csp->exec(); }

function saveUserSevenDaysUv($userId) { $key = ‘sevenDaysUv:’ . $userId;

  1. $redis = RedisPool::defer();
  2. $redis->set($key, 1, 7 * 24 * 3600);

}

  1. - 服务端
  2. 1. 通过redisscan函数轮训扫描所有前缀是 `sevenDaysUv:` key,并过滤出userid
  3. 1. (需要特别注意的是redisscan查询)(见:)
  4. 1. 批量1000条一次推送到用户端
  5. ```php
  6. <?php
  7. namespace console\controllers;
  8. use common\Manager\PushMessage\AliFacade\Live;
  9. use Yii;
  10. class PushMsgToSevenDaysVisitUserController extends ConsoleBaseController
  11. {
  12. private static $perSendLimit = 1000;
  13. private static $prefix = 'sevenDaysUv:';
  14. public function actionSendLiveMsg()
  15. {
  16. $week = date('w');
  17. $serverName = Live::$serverRemindWeekMap[$week]['serverName'];
  18. $progress = Live::$serverRemindWeekMap[$week]['progress'];
  19. $page = Live::$serverRemindWeekMap[$week]['page'];
  20. $aliUserIds = [];
  21. $liveMsgObj = new Live();
  22. $perArrCount = 0;
  23. $matchPattern = self::$prefix . '*';
  24. /** @var yii\redis\Connection $redis */
  25. $redis = Yii::$app->redis;
  26. // 迭代扫描游标
  27. $cursor = 0;
  28. while ($ret = $redis->scan($cursor, 'MATCH', $matchPattern, 'COUNT', self::$perSendLimit)) {
  29. $scanData = $ret[1] ?? [];
  30. if ($scanData) {
  31. foreach ($scanData as $datum) {
  32. $aliUserIds[] = substr($datum, strlen(self::$prefix));
  33. $perArrCount++;
  34. if ($perArrCount == self::$perSendLimit) {
  35. $liveMsgObj->serverRemind($aliUserIds, $serverName, $progress, '', $page);
  36. $aliUserIds = [];
  37. }
  38. }
  39. }
  40. $cursor = $ret[0] ?? 0;
  41. if ($cursor == 0) {
  42. break;
  43. }
  44. }
  45. if ($aliUserIds) {
  46. $liveMsgObj->serverRemind($aliUserIds, $serverName, $progress, '', $page);
  47. }
  48. }
  49. }