这样子把目标用户和消息推送这两个任务独立开,推送的时候可以开启多进程,大大的提高推送效率
分析一次公众号模板消息推送。
业务场景:
需要给不同的用户,推送不同的公众号模板消息
方案分析:
这里有两种执行方案
方案1.使用总关注用户的数据,循环校验每个用户属于哪个标签,然后调用公众号模板消息接口推送
方案2.
o1. 先跑出目标用户:一次查询源数据,一次查出标签用户,通过程序运算(数据交集,差集)得到目标用户,存到redis set集合中。
o2. 开启多进程,按照每个进程需要执行的目标数据量,从redis set 取出,直接循环调用公众号模板消息接口推送
方案优势对比:方案1只适用于数据量少的场景,优势是代码实现起来简单。数据一大对数据库io就很多。方案2先跑目标用户,然后在执行发送,对数据io很小,但对程序内存要求比较高,需要分配比较高的内存,执行效率高。 百万级别的用户推送得选择方案2。我们这里用代码实现下这一方案。
这里我们简化下业务标签,关注用户(100w),和标签1用户(100w)的数据,以下使用伪代码分析。
class Weixin_template {
o#模板消息执行方案:
#1.根据分层策略从数据库中筛选目标用户存储在redis中
#2.多进程调用消息接口推送
# 2.1 10w数据开启一个进程处理
#3.防重处理
# 3.1 一个用户只接收一个消息策略:redis集合不区分策略和公众号
# 3.2 -个消息策略多个公众只触达一个公众号:redis集合区分策略不区分公众号
#4.临时存储数据及时del掉
public function __construct() {
set_time_limit(0);
error_reporting(E_ALL);
ini_set('display_errors', 1);
//分配2G内存
ini_set('memory_limit', '2048M');
ini_set('max_execution_time', '0');
}
o//获取标签1目标用户
opublic function target_users_set_tag1($$appid, $redis_key) {
o echo $appid . ":target_users_set_tag1 start!\n";
o $targe_users = [];
o $users_subscribe = [];
o //关注用户
o echo "users_subscribe select doing.....\n";
o $users_subscribe = $this->get_db_users_subscribe($appid);
o //标签1用户
o echo "get_db_users_tag1 select doing.....\n";
o $users_tag1 = $this->get_db_users_tag1();
o
o //目标用户:关注用户并且是标签1的用户(交集运算)
o $targe_users = array_intersect($users_subscribe, $users_tag1);
o //目标用户保存到redis set 中
o $this->redis_set_add($redis_key, $targe_users);
o echo sprintf("target_users_set_tag1:%s\n", sizeof($targe_users));
o echo $appid . ":target_users_set_tag1 finish!\n\n";
o}
o//执行模板消息
opublic function send_template_msg($tag, $appid, $send_num, $is_prod=0) {
o// redis = get_redis();
if ($is_prod == 1) {
//获取目标用户:利用spop实现多进程处理目标用户
$send_users = $redis->sPop($target_users_key, $send_num);
} else {
//获取测试用户
$send_users = $this->users_test();
}
//获取发送内容
$content = get_content($tag, $appid);
echo $target_users_key . "\n";
$counts = 0;
$total_key = sprintf('total_send_template_msg_%s', $date);
foreach ($send_users as $send_user) {
//消息防重:防止消息轰炸
if ($redis->sIsMember($sended_users_key, $send_user) == 1) {
$redis->sAdd(sprintf('isset_sended_users_%s', $date), $send_user);
continue;
}
if ($redis->sIsMember($total_key, $send_user) == 1) {
// echo $send_user . "\n";
$redis->sAdd(sprintf('isset_total_users_%s', $date), $send_user);
continue;
}
//发送消息接口
$res = $this->api_template_msg($content);
//保存目标用户
// echo $send_user . "\n";
$redis->sAdd($sended_users_key, $send_user);
$redis->sAdd($total_key, $send_user);
$counts++;
if (($counts % 1000) == 0) {
echo sprintf('count:%s, %s', $counts, $send_user);
}
}
$redis->expire($sended_users_key, 3600*24*30);
$redis->expire($total_key, 3600*24*30);
echo sprintf("isset_sended_users:%s\n", $redis->sCard('isset_sended_users'));
echo sprintf("isset_total_users:%s\n", $redis->sCard('isset_total_users'));
echo sprintf("%s:%s\n", $sended_users_key, $redis->sCard($sended_users_key));
echo sprintf("%s:%s\n", $total_key, $redis->sCard($total_key));
o}
}
这样子把目标用户和消息推送这两个任务独立开,推送的时候可以开启多进程,大大的提高推送效率。
除特别注明外,本站所有文章均为作者原创。 或分享自己的编程经验,或探讨工作中的问题,或聊以人生趣事。 转载请注明出处来自 https://www.qiusuoweb.com/137.html
运营天数
总访问量
文章数量
-
-
-
交流群:157451741
新浪微博:草莽兴
发布评论