【PHP】异步定时推送消息

一、目的

异步发送,添加端只管添加,不需要考虑发送,定时定点,误差不超过5s(任务多会排队),保证每条消息发送成功(尝试N次后自动丢弃),可设置多线程,保证不重复发送。

二、实现原理

crontab(定时执行) +php(实现推送)+mysql(消息存储)

三、步骤

1、创建消息表

创建消息表,保存消息(友情提示 注意索引)

CREATE TABLE `sent_tpl_task` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
   `tpl_id` varchar(64) NOT NULL DEFAULT '', COMMENT '模板消息id',
  `data` varchar(2048) NOT NULL DEFAULT '', COMMENT '模板消息json',
  `send_time` int(11) NOT NULL DEFAULT '0',
  `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0 删除 1待发送 2已成功 3发送失败',
  `do_times` tinyint(4) NOT NULL DEFAULT '0',
  `err_msg` varchar(255) NOT NULL DEFAULT '',
  `remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `dt_st_s` (`do_times`,`send_time`,`status`) USING BTREE,
  KEY `tpl_id` (`tpl_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2、定时任务入口(PHP MinuteTask)

<?php
/**
 * Created by PhpStorm.
 * User: 05
 * 此文件由Crontab每分钟执行一次
 */

$day=date('d');
$h = date('H');
$min = date('i');

$host=$_SERVER['HTTP_HOST'];
if (!empty($host)){
    return ;//外部访问 直接返回
}


$host='http://www.mano100.cn'; //Task 项目域名

doTplTask($host);//每分钟执行

//每10分钟执行
if ($min%10==0){
//TOOD other
}

//这里为了保证所有任务都能执行,使用http 非阻塞访问task,task 应该直接返回true 后在执行任务

function doTplTask($host){
    $token="m_token";//自定义token,防止攻击
    $url =$host.'/api/task/doTempleMsgTask?m_token='.$token;
    $task_count=1;//大于0 并发大要考虑带宽
    for ($i=0;$i<$task_count;$i++){
        httpsPost($url);
    }

}


//使用http 请求
function httpsPost($url, $data = null)
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    if (!empty($data)) {
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
    }

    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $output = curl_exec($curl);
    curl_close($curl);

    return $output;
}

3、Task处理

<?php

namespace app\Api\controller;
use think\Controller;
use think\Exception;

class Task extends Controller {

  //处理消息
    public function doTempleMsgTask(){
        echo true;
        fastcgi_finish_request();//直接返回成功 保证上游任务调用的实时完成

    //token 验证,防止外部攻击
        $token=input("m_token");
        if (empty($token)){
          //自行验证token
            exit();
        }


        $t=time();
        $wxmsg = load_wechat('message'); //加载微信模板消息类库

        //进入工作模式 
        for (;;){
            try{
        //每条进程最多执行一分钟 自动结束,等带下一个进程
                if (time()-$t>59){
                    exit("time over");
                }

        //这里必须开启事务,不然多任务时可能拿到同一个消息
                model("TplTask")->startTrans();

        //这里获取消息,并发高数据量大注意建立索引 或考虑使用redis 代替(注意持久化) 
                $task=model("TplTask")
                       ->field("id")  //只取主键,提高并发
                       //条件为状态 未发送 and 发送时间<= 当前时间,执行次数小于规定次数
                    ->where(array('status'=>1,'send_time'=>array('elt',time(),'do_times'=>array('lt',1))))
                    //排序,执行次数,发送时间
                    ->order('do_times,send_time')
                  //必须加锁写锁,防止并发重复读取
                    ->lock(true)
                    ->find();

        //任务表为空 则 休息5秒,防止频繁操作空数据库
                if (empty($task)){
                    model("TplTask")->commit();
                    sleep(5);
                    continue;
                }

                //根据id 修改状态,直接提交事务,减少事务时间
                model("TplTask")->where(array('id'=>$task['id']))->update(array('do_times'=>1,'status'=>2));
                model("TplTask")->commit();

        //根据id 找到数据
           $task=model("TplTask")
                    ->where(array('id'=>$task['id']))
                    ->find();
        //发送数据
                $data=json_decode($task['data'],1);
                $result=$wxmsg->sendTemplateMessage($data);
                if ($result['errcode']!=0){
                    //发送失败后修改状态跟次数,并记录错误信息
                    model("TplTask")->where(array('id'=>$task['id']))->update(array('status'=>3,'err_msg'=>$result['errcode']."|".$result['errmsg']));
                }

            }catch (Exception $e){

            }
        }
    }

}

4、crontab 执行

* * * * * php /home/wwwroot/Test/MinuteTask.php

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Netty是一个高性能的网络编程框架,它提供了一种基于事件驱动的异步、事件驱动的网络应用程序框架。而WebSocket是一种在单个TCP连接上进行全双工通信的协议,它可以在客户端和服务器之间实现实时的双向通信。 在Netty中使用WebSocket进行消息推送,可以通过以下步骤实现: 1. 创建一个WebSocket服务器: - 创建一个ServerBootstrap对象,并设置相关参数,如端口号、线程模型等。 - 添加一个ChannelInitializer,用于初始化每个新连接的Channel。 - 在ChannelInitializer中添加一个WebSocketServerProtocolHandler,用于处理WebSocket握手和帧的编解码。 - 添加自定义的ChannelHandler,用于处理具体的业务逻辑。 2. 处理WebSocket连接和消息: - 在自定义的ChannelHandler中,重写channelRead方法,处理接收到的WebSocket消息。 - 可以根据业务需求,对接收到的消息进行解析和处理,并将结果返回给客户端。 3. 推送消息给客户端: - 在需要推送消息的地方,获取到所有已连接的WebSocket客户端Channel。 - 遍历所有客户端Channel,将消息写入到Channel中,实现消息推送。 4. 异常处理和连接管理: - 在自定义的ChannelHandler中,重写exceptionCaught方法,处理异常情况。 - 可以根据具体需求,对异常进行处理,如关闭连接、记录日志等。 - 可以使用ChannelGroup来管理所有已连接的WebSocket客户端Channel,方便进行批量操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下页、再停留

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值