前面说过easywechat日常微信开发基本不会有性能问题,但当公众号粉丝数超百万时,大批量推送模板消息会遇见很多难以解决的问题,比如推送慢,推送超时,内存溢出等情况。
使用hyperf可以一定程度解决这些问题,但是在此说明,hyperf协程不管是大批量爬取数据,还是大批量推送速度肉眼可见的加快,但hyperf不是性能“银弹”,当服务器运行大运算量程序时,仍然会有瓶颈,服务器cpu,内存等限制,这时候程序已经是最优情况,推送速度提升数倍,但仍然不是自己想要的速度,这时需要升级服务器才能解决问题。
如果小伙伴有对hyperf不了解的可以先去看swoole文档和hyperf文档。
这里将自己写的代码贴出,希望能给小伙伴一些帮助。
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Controller;
use QL\QueryList;
use Hyperf\DbConnection\Db;
use Hyperf\HttpServer\Contract\RequestInterface;
use App\Utils\Cache;
use EasyWeChat\Factory;
use Illuminate\Support\Facades\Auth;
use EasyWeChat\Kernel\Messages\Image;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Utils\ApplicationContext;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use Hyperf\Guzzle\CoroutineHandler;
class SendwechatmsgCoprogramController extends AbstractController
{
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
protected $config;
public function __construct(LoggerFactory $loggerFactory)
{
// 第一个参数对应日志的 name, 第二个参数对应 config/autoload/logger.php 内的 key
$this->logger = $loggerFactory->get('1.log', 'default');
$this->config= [
'app_id' => '',
'secret' => '',
'token' => '',
'aes_key' => '',
// 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
'response_type' => 'array',
//...
'oauth' => [
'scopes' => ['snsapi_userinfo'],//snsapi_base不询问,snsapi_userinfo询问
'callback' => '/wechat/oauth_callback',
],
];
}
public function wuxianji($app,$next_openid="",$lv=1){
if(!Cache::get('wuxianji5_'.$lv)){
$userlists=$app->user->list($next_openid);
$userlists['page']=$lv;
Cache::set('wuxianji5_'.$lv,$userlists);
}
$userlists=Cache::get('wuxianji5_'.$lv);
$count=$userlists['count'];
if($count>0){
$openids=$userlists['data']['openid'];
$next_openid=$userlists['next_openid'];
$page=$userlists['page'];
$this->wuxianji($app,$next_openid,$lv+1);
}else{
$total=$userlists['total'];
$page=intval(ceil($total/10000));
Cache::set('wuxianji5_page',$page);
return "ok";
}
}
//模板消息通知
public function start1(RequestInterface $request){
$container = ApplicationContext::getContainer();
$app = Factory::officialAccount($this->config);
$handler = new CoroutineHandler();
// 设置 HttpClient,部分接口直接使用了 http_client。
$config = $app['config']->get('http', []);
$config['handler'] = $stack = HandlerStack::create($handler);
$app->rebind('http_client', new Client($config));
// 部分接口在请求数据时,会根据 guzzle_handler 重置 Handler
$app['guzzle_handler'] = $handler;
// 如果使用的是 OfficialAccount,则还需要设置以下参数
$app->oauth->setGuzzleOptions([
'http_errors' => false,
'handler' => $stack,
]);
$this->wuxianji($app);
$page=Cache::get('wuxianji5_page');
for ($lv=$page;$lv>=1; $lv--){
// $lv=$request->input('lv',1);
$userlists=Cache::get('wuxianji5_'.$lv);
$logger=$this->logger;
echo("第".$lv."页开始时间:".time().PHP_EOL);
$logger->info("第".$lv."页开始时间:".time().PHP_EOL);
$count=$userlists['count'];
// echo($count."条数据".PHP_EOL);
// $logger->info($count."条数据".PHP_EOL);
if($count>0){
$limit=100;
$batch=intval(ceil($count/$limit));
echo("分".$batch."批推送".PHP_EOL);
$logger->info("分".$batch."批推送".PHP_EOL);
$openids=$userlists['data']['openid'];
// $next_openid=$userlists['next_openid'];
for ($b=$batch-1;$b>=0; $b--){
$openidsbatch= array_slice($openids,$b*$limit,$limit);
$count=count($openidsbatch);
//等待协程 如果不加这个一下执行几千几万条,会有Allowed memory size of 268435456 bytes exhausted,内存溢出,所有我们协程一次执行一百和协程,等待一百协程执行成功后在执行下一个一百协程。
// 计数器
echo($b."批开始推送".PHP_EOL);
$logger->info($b."批开始推送".PHP_EOL);
$wg = new \Hyperf\Utils\WaitGroup();
$wg->add($count);
// echo($b."批数量:".$count.PHP_EOL);
// $logger->info($b."批数量:".$count.PHP_EOL);
for ($i=$count-1;$i>=0; $i--){
co(function () use ($app,$openidsbatch,$i,$logger,$wg) {
// echo($openidsbatch[$i]);
// $logger->info($openidsbatch[$i]);
$app->template_message->send([
'touser' => $openidsbatch[$i],
'template_id' => '',
'url' => '',
'data' => [
'first' => [
'value' => "重要通知!重要通知!\n【小初学习帮】特邀北京一线特级教师来助力寒假,为孩子们线上授课!",
],
'keyword1' => [
'value' => '(限时免费)(限时免费)',
],
'keyword2'=>[
'value' => '小学、初中、高中都可学习',
],
'keyword3'=>[
'value' => '解题大招+干货技巧+思维方法',
],
'keyword4'=>[
'value' => '请家长们尽快领取!',
],
'remark'=>""
],
// //跳小程序所需数据,不需跳小程序可不用传该数据
// 'miniprogram' => [
// // 所需跳转到的小程序appid(该小程序 appid 必须与发模板消息的公众号是绑定关联关系,暂不支持小游戏)
// 'appid' => "appid",
// // 所需跳转到小程序的具体页面路径,支持带参数,(示例index?foo=bar),要求该小程序已发布,暂不支持小游戏
// 'pagepath' => 'pages/xxx',
// ],
]);
// sleep(1);
// echo($i.PHP_EOL);
// $logger->info($i.PHP_EOL);
$wg->done();
});
}
$wg->wait();
}
}else{
echo("全部完成".PHP_EOL);
// $logger->info("全部完成".PHP_EOL);
echo("结束时间:".time().PHP_EOL);
// $logger->info("结束时间:".time().PHP_EOL);
}
}
return "ok";
}
}
如果是正常推三天两夜能推完给百万用户,加协程可以在10小时内推送完毕,推送速度快了六倍,将服务器2核8G服务器升级为2核16G,百万用户推送仅用了不到一个小时,服务器性能提升一倍单推送速度快了10倍,我觉得是因为加入协程情况下2核8G服务器根本带不动程序,程序是协程与阻塞方式共同进行的,当性能提升到可以带动程序时,运行速度就有了质的飞升。这个特意注明,当有推送给公众号百万用户该需求时一定选择性能超过2核16G的服务器。
下载荣德基的资料,找到了这里,留个名 🙂