分享php两个实用类:单线程调用和集合类

单线程实现思路:(适用于秒杀、抢号等业务场景)

1. 在前端或(业务端)添加两个redis缓存A(list),B(string),A中的元素是B的key,异步执行dealHandle任务方法,直接返回B的状态为waitting;

2. 异步执行调用dealHandle任务执行方法:该方法要执行的逻辑如下

  2.1. 判断任务是否在执行中。。。(redis 缓存的状态锁),如果在执行中,直接返回

  2.2. pop list中的数据

  2.3. while循环,条件为A(list)pop出来的数据不为空,则往下执行,

    2.3.1 先写redis缓存的状态锁setNx

    2.3.2 根据pop出来的key,查询B(string)中的业务数据,处理相关业务逻辑,在B中标记该任务的执行状态是否成功(如果业务报错或异常是否删除redis缓存的任务状态锁),

    2.3.3 pop list中的数据(同2.2)创造循环条件 

  2.4 任务处理完毕删除redis缓存的任务状态锁

3. 前端利用定时器setTimeInterval 轮询查询 缓存B的任务执行状态是否成功,如果成功删除缓存B(也可以不删除B,给B添加过期时间,用来校验重复性提交),如果失败提示给用户端

单线程类

<?php
/**
 * 单线程执行方法
 */

namespace backend\components;

use ReflectionException;

class SingleThread
{
    /**
     * 单线程执行方法
     * @param $method string '\backend\components\Tool::test'
     * @param $param array 方法$method的参数
     * @return array
     * @throws ReflectionException
     */
    public static function dealMethod(string $method, ...$param): array
    {
        $return = ['code' => 0, 'msg' => 'success'];
        $methodArray = explode("::", $method);
        $class = new \ReflectionClass($methodArray[0]);
        $methodIsStatic = $class->getMethod($methodArray[1])->isStatic();
        if (!$methodIsStatic) return ['code' => 1, 'msg' => '调用的方法必须为静态方法'];
        $keySign = RedisKey::$storePrefix . 'single::thread::';/*单线程 redis 关键标识符*/
        $methodKey = $keySign . $method . '___' . time() . mt_rand(1000, 9999);
        $paramJson = json_encode($param, JSON_UNESCAPED_UNICODE);
        RedisKey::set($methodKey, $paramJson);/*方法 和 参数相关信息*/

        $waitingKey = $keySign . 'waiting::' . $method;
        RedisKey::lPush($waitingKey, $methodKey);/*添加写队列*/

        $statusKey = $keySign . 'status::' . $method;/*任务状态判断是任务否在执行中*/
        if (RedisKey::exists($statusKey)) return $return;/*队列执行中,则直接返回*/
        if (Tool::getEnv() == 'd') {/*本地环境*/
            static::polling($method);
        } else {/*配合swoole 异步执行*/
            /*异步执行*/
            $timers = new Timers();
            $timers->TimerAfter("backend\components\SingleThread::polling", 1000, true, $method);
        }
        return $return;
    }

    /**
     * 执行方法
     * @param $method
     * @return true|void
     */
    public static function polling($method)
    {
        $keySign = RedisKey::$storePrefix . 'single::thread::';/*单线程 redis 关键标识符*/
        $waitingKey = $keySign . 'waiting::' . $method;
        $statusKey = $keySign . 'status::' . $method;/*任务状态判断是任务否在执行中*/
        if (RedisKey::exists($statusKey)) return true;/*队列执行中,则直接返回*/
        /*此处逻辑应该用异步去执行*/
        $waitingData = RedisKey::rPop($waitingKey);/*待执行的方法*/
        while (!empty($waitingData)) {
            RedisKey::set($statusKey, 1);
            try {
                $currentMethod = explode('___', $waitingData);
                $currentMethod = str_replace($keySign, '', $currentMethod);/*要执行的方法*/
                $currentParam = RedisKey::get($waitingData);/*参数*/
                RedisKey::del($waitingData);
                call_user_func_array($currentMethod[0], json_decode($currentParam, true));
            } catch (\Exception $e) {
                RedisKey::del($statusKey);/*任务结束,删除状态*/
            }
            $waitingData = RedisKey::rPop($waitingKey);/*待执行的方法*/
            if (empty($waitingData)) {
                /*执行完*/
                RedisKey::del($statusKey);/*任务结束,删除状态*/
                RedisKey::del($waitingKey);/*任务结束,删除队列*/
            }
        }
    }
}

 

 

集合类

<?php
/**
 * php 使用数据模拟集合数据类型
 */

namespace app\components;

class Set
{
    /**
     * 数组转换为集合数据类型(数组去重)
     * @param array|mixed $data 数组
     * @return void
     */
    public static function set($data)
    {
        $set = [];
        /*数组转集合*/
        if (is_array($data)) {
            if (empty($data)) return $set;/**/
            foreach ($data as $value) {
                if (!in_array($value, $set)) $set[] = $value;
            }
            return $set;
        }
    }

    /**
     * @param string|int $element
     * @param $set array 集合
     * @return array
     */
    public static function add($element, array $set): array
    {
        if (!in_array($element, $set)) {
            $set[] = $element;
        }
        return $set;
    }

    /**
     * 删除元素
     * @param string|int $element
     * @param $set array 集合
     * @return array
     */
    public static function remove($element, array $set): array
    {
        if (in_array($element, $set)) {
            foreach ($set as $key => $value) {
                if ($value == $element) {
                    unset($set[$key]);
                }
            }
        }
        return array_values($set);
    }

    /**
     * 随机弹出一个元素
     * @param $set array
     * @return void
     */
    public static function pop(array $set)
    {
        if (empty($set)) return $set;
        $len = count($set);
        $key = mt_rand(0, $len - 1);
        unset($set[$key]);
        return array_values($set);
    }
}

 

posted @ 2024-07-19 17:10  龍飛鳯舞  阅读(1)  评论(0编辑  收藏  举报