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类型的值,必要时清理大数组,防止内存溢出。
  • 相关阅读:
    LA 2038 Strategic game(最小点覆盖,树形dp,二分匹配)
    UVA 10564 Paths through the Hourglass(背包)
    Codeforces Round #323 (Div. 2) D 582B Once Again...(快速幂)
    UVALive 3530 Martian Mining(贪心,dp)
    UVALive 4727 Jump(约瑟夫环,递推)
    UVALive 4731 Cellular Network(贪心,dp)
    UVA Mega Man's Mission(状压dp)
    Aizu 2456 Usoperanto (贪心)
    UVA 11404 Plalidromic Subsquence (回文子序列,LCS)
    Aizu 2304 Reverse Roads(无向流)
  • 原文地址:https://www.cnblogs.com/guandeng/p/8127193.html
Copyright © 2011-2022 走看看