zoukankan      html  css  js  c++  java
  • 异步任务推送的实践

    目录

    • 初识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 AppLogic;
    
    use AppLogicQueue;
    
    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 AppLogic;
    
    use AppLogicQueue;
    
    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();
    	}
    }
    

    至此主要功能已处理完毕,具体推送结果可以根据实际情况存入Redis、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,导致进程退出。
    • 全局变量、静态变量、需要自行销毁,特别要注意array类型的值,必要时清理大数组,防止内存溢出。
  • 相关阅读:
    借助magicwindow sdk plugin快速集成sdk
    Deeplink做不出效果,那是你不会玩!
    iOS/Android 浏览器(h5)及微信中唤起本地APP
    C#回顾 Ado.Net C#连接数据库进行增、删、改、查
    C# 文件操作(全部) 追加、拷贝、删除、移动文件、创建目录 修改文件名、文件夹名
    C#中的静态方法|如何调用静态方法
    SpringBoot实体类对象和json格式的转化
    SpringBoot + kaptcha 生成、校对 验证码
    SpringBoot配置自定义美化Swagger2
    Spring Boot关于layui的通用返回类
  • 原文地址:https://www.cnblogs.com/guandeng/p/8127193.html
Copyright © 2011-2022 走看看