1.关于群发界面和消息界面关于群发界面1、订阅号每天可发送一条群消息,服务号每月(自然月)可发送四条消息。批量发送权限。在开发者模式下,可以通过高级群发接口实现更灵活的群发能力。2.注意事项●对于认证订阅号,每天可以成功调用一次群发接口,本次群发可以发送给所有用户或某个标签;●对于认证服务号,虽然开发者使用高级群发接口,每日调用次数限制为100次,但用户每月只能接收4条消息,无论是在公众平台网站还是通过接口,用户每月只能接收4条群消息,超过4条群消息将无法发送给用户;公众号拥有微信支付权限的可以在群发界面上传或群发图文消息类型时使用a标签加入外链;当用户在客服消息和模板消息界面与公众号进行了具体操作交互时(具体操作列表请参考下方说明),微信会将消息数据推送给开发者,开发者可以调用一段时间内(目前修改为48小时)的客服界面,通过POST一个JSON数据包给普通用户发送消息。该接口主要用于客服等需要人工处理消息的功能,方便开发者为用户提供更好的服务。模板消息仅用于公众号向用户发送重要服务通知,只能用于满足用户需求的服务场景,如刷卡通知、商品购买成功通知等。营销消息如广告和不支持所有其他可能骚扰用户的消息。2.背景简要描述业务背景。目前是供求微信公众号。每当用户付费发送一条供求消息时,我们需要将这条消息推送给所有用户,以便用户及时收到。留言,实现留言的实时性,保证留言的有效性。但是群发的接口根本不能满足我们的需求,所以我们使用模板消息和客服消息的接口来实现我们的需求。说到这里,可能有人会问为什么接收客服消息的请求这么变态我们还要用呢?,其实是因为在某些设备上收到群消息时,就像是微信好友发送的消息一样,更能引起用户的关注。此外,我们还需要知道,如果我们的用户使用几万人,那么群发的方式是非常耗时的。微信支付提供notify异步通知。我之前一直在尝试使用这个异步站点。通知是用来实现群发的,但是根据实际使用,这个异步IO的阈值是6分钟左右,但是实际上发送一个模板的网络时间加上数据库的IO其实是相当长的,所以我们有使用异步多线程实现我们的群发功能这里我使用swoole的扩展来完成这个需求。swoole的异步群发的demo可以参考我的另一篇文章thinkphp5+swoole实现异步群发(SMTP方式)。在这里你可以详细了解服务端和客户端的搭建。三、环境说明centos7swoole2.0+tp5.0+邮件发送crontab定时任务四、实现4.1创建服务器我这里直接贴代码,需要注意的地方写在相关代码的注释里,请采纳看一下/***description:server*/publicfunctionasyncSend(){$serv=new\swoole_server('0.0.0.0',9090);$serv->set(array('task_worker_num'=>60,'daemonize'=>true,'log_file'=>'/var/www/html/myswl/tp/swoole.log'));$serv->on('receive',function($serv,$fd,$from_id,$data){$task_id=$serv->task($data);echo"开始发布异步任务id=$task_id\n";});$serv->on('task',function($serv,$task_id,$from_id,$data){echo"接收异步任务[id=$task_id]".PHP_EOL;$data=json_decode($data,true);//这里特别说明,我这里使用的是数据库的远程链接,因为支付功能是在另外一个window虚拟云主机上,所以不得不使用远程访问。注意Db::connect($conn,true)的第二个参数;因为我们实际上会进行一次远程数据库连接,而不会发送一次,所以在频繁的连接中,肯定会出现连接失败的情况,所以我们需要配置断开重连$conn='mysql://用户名:数据库远程链接地址:3306/密码#utf8';$db=Db::connect($conn,true);$now=date('Y-m-dH:i:s');$users=$db->table('tp_receiver')->field('openid')->where("(`expire_time`>'{$now}'OR`send_status`=1)AND`receive_status`=1")->limit($data['flag']*500,500)->select();echo$db->getLastSql();echo'发送开始--'.日期('H:我:s')。PHP_EOL;foreach($usersas$user){$token=$db->table('tp_accesstoken')->field('accesstoken')->find();//这里是客服消息$templData2=array('touser'=>$user['openid'],'msgtype'=>'text','text'=>array('content'=>$data['content']."\n\n点击下方“信息查询”查看更多采购信息"."\n回复0取消接收消息,回复1重新接收消息"));$res=$this->sendCustomMessage($templData2,$token['accesstoken']);$res=json_decode($res,true);//判断客服消息是否发送成功if($res['errcode']==45047||$res['errcode']==45015){$templData=array('touser'=>$user['openid'],'template_id'=>'templatemessage','url'=>'','data'=>array('first'=>array('value'=>'嗨,你收到新提醒','color'=>'#173177'),'keyword1'=>array('value'=>'购买信息','color'=>'#173177'),'keyword2'=>array('value'=>$data['content'],'color'=>'#173177'),'keyword3'=>array('value'=>$data['phone'],'color'=>'#173177'),'keyword4'=>array('value'=>date('Y-m-dH:i:s'),'color'=>'#173177'),'remark'=>array('value'=>"点击下方“信息查询”查看更多purchaserequestsMessage".回复0取消接收消息,回复1重新接收消息",'color'=>'#173177')),);$res=$this->sendTemplateMessage($templData,$token['accesstoken']);Log::write('sendto'.$user['openid'].$res.PHP_EOL);}}echo'发送结束--'。日期('H:我:s')。PHP_EOL;$服务->完成('');});$serv->on('finish',function($serv,$task_id,$data){echo'finishtime--'.microtime(true).PHP_EOL;echo"异步任务[id=$task_id]完成"。PHP_EOL;});$服务->开始();然后我们以CLI方式进入项目根目录,执行phppublic/index.phpdemo/wechat/asyncSend这样,我们的服务器就一直以守护进程的方式运行到我们的后台。通过ps-aux|grepasyncSend,我们可以看到除了60个任务外,已经有62个进程处于S(休眠唤醒)状态。该进程还使用一个master和一个woker进程。4.2构建客户端代码如下。需要注意的地方写在相关代码的注释中。看一下/***description:client*/publicfunctionindex(){//因为这个群发比较敏感,所以我们需要做一个token机制,这里我用最简单的sender和receiver来做plain文本。$token=$_GET['token'];if($token!='test'){退出;}//content为发送的内容,因为里面的内容无法估计,所以加密解密$content=rawurldecode($_GET['content']);$flag=$_GET['flag'];$id=$_GET['id'];$phone=$_GET['phone'];$数据['内容']=$内容;$data['flag']=$flag;$数据['电话']=$电话;日志::写入(自我::json_encode($数据));$insert=['flag'=>$flag,'miaomu_id'=>$id,'status'=>1,'createtime'=>date('Y-m-dH:i:s'),'content'=>$内容];db::table('task')->insert($insert);//异步客户端$client=new\swoole_client(SWOOLE_SOCK_TCP,SWOOLE_SOCK_SYNC);$ret=$client->connect("127.0.0.1",9090);//当无法连接时,发送报警邮件if(empty($ret)){SendMail::postmail('kongwentao@zuihuibao.com','warning','error!连接swoole_server失败');SendMail::postmail('937069176@qq.com','警告','错误!连接swoole_server失败');Log::write('error!connecttoswoole_serverfailed');}else{$client->send(self::json_encode($data));}}4.3端口监控本次群发已经涉及到钱了,所以更需要关注服务的运行是否稳定。这里我们简单的使用crontab定时任务和php的一些shell相关函数来监听端口*/1****curlhttp://你的域名/index.php/demo/Jrmm/checkPortStatus?token=test是为了实施一个任务,每分钟执行下面的php代码。这里我没有直接使用shell来操作。有以下三个原因。一是我对shell命令不是很熟悉。2是我们对shell命令不是很熟悉,3是我写相关代码比较方便,用php中已有的一些方法,比如发邮件。虽然这样会多消耗一点网络资源,但是也很划算。具体的监控代码,这里实现的时候会遇到很多权限问题,就不多说了,遇到了就百度吧。/***描述:8082服务端口监听*/publicfunctioncheckPortStatus(){if(!isset($_GET['token'])||$_GET['token']!='test'){exit();}$res1=exec('sudonetstat-lpn|grep9090');日志::写($res1);如果($res1==''){Log::write('9090stop');SendMail::postmail('kongwentao@zuihuibao.com','warning','9090端口服务错误');SendMail::postmail('937069176@qq.com','warning','9090端口服务错误');//重启在我们服务器端,这里需要注意的是我没有使用提供的平滑重启功能通过swoole,可能会导致数据丢失。不要特别注意exec('sudophp/var/www/html/myswl/tp/public/index.phpdemo/jrmm/asyncSend');$res=exec('sudonetstat-lpn|grep8082');if($res!=''){Log::write('9090重启成功');SendMail::postmail('937069176@qq.com','警告去掉','9090端口重启成功');SendMail::postmail('kongwentao@zuihuibao.com','警告解除','9090端口重启成功');}}}4.4实现我们使用PHPcurl函数来模拟支付成功后调用我们的群发功能。$content='测试';for($j=0;$j<3;$j++){$url='http://你的域名/index.php/demo/Jrmm/index'.'?flag='.$j.'&id=1.'&token=test'.'&phone='110&content='.rawurlencode($content);$this->http_post($url);}functionhttp_post($url){$ch=curl_init();curl_setopt($ch,CURLOPT_URL,$url);curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);curl_setopt($ch,CURLOPT_HEADER,0);$res=curl_exec($ch);curl_close($ch);return$res;}为什么要循环三次,因为我们通过测试发送1500条消息的时候差不多用了6分钟,但是我们项目的并发度很低,所以不能充分利用我们开启的60个任务进程,所以我们把1500分三次发。事实上,我们消耗的网络消耗几乎可以忽略不计,这将我们发送的性能提高了三倍以上。在实际项目中,发送1500多条其实不到两分钟。当然,当并发量更大的时候,我们也可以使用队列来处理,这就需要我们的团队在任务流程管理上更加熟练。5.对收到的推送消息报警日志进行截图6.下载项目完整代码http://pan.baidu.com/s/1c2q7ik4
