异步任务推送的实践

Posted guandeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步任务推送的实践相关的知识,希望对你有一定的参考价值。

异步任务推送

目录

  • 初识Swoole
  • 异步进程介绍
  • 传统推送业务逻辑
  • 异步任务推送逻辑
  • 效率对比

初识Swoole

Swoole是php一个扩展,但和其他扩展只提供接口、函数不同,Swoole重新定义PHP,接管并重新处理数据,将处理好的数据返回给PHP,支持百万TCP连接,同时支持异步/同步/协程等功能

技术分享图片

Swoole采用多进程架构模型,比多线程架构更方便,不存在线程安全问题,缺点是进程与进程通信没有线程方便,另外PHP-FPM,nginx采用的也是多进程模型,学习起来还是容易接受。

异步Worker进程介绍

异步Worker进程是Swoole提供的一个非阻塞进程,投递一个异步任务到异步进程,执行完毕立即返回,异步进程可以继续处理新请求,进程间没有加锁争抢,性能异常优良。利用以上特性,可以用来处理耗时较长的事务。

传统推送业务逻辑

技术分享图片

传统推送通过设置自动执行,或者手动执行读取运营后台设定数据,写入队列(可选),接着将数据推给第三方,第三方将推送结果告知程序,这个告知的过程通常以对方网络稳定决定了推送效率,推送短板也在这块。

异步任务进程推送逻辑

技术分享图片
异步任务运用在从队列读取数据过程中,开启多个进程读取数据,接着发送数据给第三方,只要开启合适数量的进程,推送速度极快。

下面介绍用到技术

  • EasySwoole框架
  • Redis队列
  • 定时器
  • Task进程

话不多说,撸起代码就是干

1、注册定时器,写入任务

if ($workerId == 0) {
    Timer::Loop(1 * 1000, function () {
        PushLogic::addQueue();
    });
}       
<?php
namespace App\\Logic;

use App\\Logic\\Queue;

class PushLogic
{
    const REDIS_KEY = ‘push_list‘;
    public static function addQueue($task)
    {
        Queue::push(self::REDIS_KEY, $task);
    }
}

2、注册定时器 获取任务

if ($workerId == 1) {
    Timer::Loop(5 * 1000, function () {
        $share = ShareMemory::getInstance();
        // 启用16个异步Task进程
        if ($share->get(Sys::PUSH_TASK_NUM) < 16) {
            AsyncTaskManager::getInstance()->add(PushTask::class);
        }
    });
}       

3、封装异步执行逻辑

<?php
namespace App\\Logic;

use App\\Logic\\Queue;

class PushLogic
{
    conse REDIS_KEY = ‘push_list‘;
    public static function handle($task)
    {
            // 处理进程数据隔离  文件锁 记录Task进程数量
            $share = ShareMemory::getInstance();
            $share->startTransaction();
            $key = Sys::PUSH_TASK_NUM;
            $share->set($key, $share->get($key) + 1);
            $share->commit();
            while (true) {
                $task = Queue::pop(self::REDIS_KEY);
                // 调用GCM或者APNS逻辑
                Gcm::send($task);
            }
            $share->startTransaction();
            $share->set($key, $share->get($key) - 1);
            $share->commit();
    }
}

至此主要功能已处理完毕,具体推送结果可以根据实际情况存入内存、Swoole内存表、数据库

效率对比

  • 系统:CenOS release 5.11
  • CPU: 4核
  • 内存:32G
  • PHP:7.1
  • 总终端数量:1百万
  • 单次任务终端数量:1千
传统推送耗时:30分钟
异步多任务:3分钟
温馨提示
  • EasySwoole框架是常驻内存,连接数据库需要采用断线重连,否则会出现mysql server has gone away,可以参考这里的使用方法PHP-MySQLi-Database-Class
  • 获取任务时,应避免所有异步任务进程处于忙的状态
  • 代码不要出现sleep/usleep等睡眠函数,会使进程陷入睡眠阻塞
  • 禁用exit/die,导致进程退出

以上是关于异步任务推送的实践的主要内容,如果未能解决你的问题,请参考以下文章

从片段中调用分离的异步任务类

异步任务片段背景数据

Android 我应该在异步任务中将片段作为弱引用传递吗?

我应该在 Fragment 中的啥生命周期状态下执行异步任务?

当片段视图加载是异步任务的一部分时,如何在片段加载之前显示进度条?

微信后台异步消息队列的优化升级实践分享