swoft 实战常见技巧

1.获取某个key值 ArrayHelper::getValue()

return [
            'tag'       => ArrayHelper::getValue($headers, 'tag'),
            'topic'     => ArrayHelper::getValue($headers, 'topic'),
            'requestId' => ArrayHelper::getValue($headers, 'request-id'),
            'key'       => ArrayHelper::getValue($headers, 'key'),
        ];

2.获取对应的value,有默认值。 ArrayHelper::get()

class ConsumerFactory
{
    const ORDER_CREATE = 1;
    const ORDER_CANCEL = 2;

    protected static $map = [
        self::ORDER_CREATE => OrderCreate::class,
        self::ORDER_CANCEL => OrderCancel::class,
    ];

    public static function create ($source) :ConsumerInterface {
        $class = ArrayHelper::get(self::$map, $source, '');
        return BeanFactory::getBean($class);
    }
}

3.Bean理解
获取 Bean
@Inject
scoreBean::SINGLETON 级别的 bean 可以通过 @Inject 属性注入,底层会通过反射自动注入属性中。

Bean::PROTOTYPE, Bean::REQUEST bean不能使用 @Inject 注入

/**
 * @Inject("config")
 *
 * @var Config
 */
private $config;

name 定义属性注入的bean名称。如果name为空,默认为@var 定义的类型。 这个name 可以是一个完整的类名,也可以是bean别名/bean名称。

如果 @Inject 没有指定 Bean 名称, 会自动根据 @var 的类型查找 Bean。
如果要使用 @Inject 属性注入必须有 类注解才可以,不然不会被解析。

BeanFactory

BeanFactory提供了一种先进的配置机制来管理任何种类的bean。

获取scoreBean::SINGLETONBean::PROTOTYPE

可以使用下面,以获取 wsRouter 为例:

$router = Swoft::getBean('wsRouter');
$router = BeanFactory::getBean('wsRouter');
$router = Container::$instance->get('wsRouter')
$router = BeanFactory::getContainer()->get('wsRouter');

$router = bean('wsRouter');  //通用函数

上述这几种方式都可以获取Bean::SINGLETONBean::PROTOTYPE类型的bean

4.model类获取表名

public function getTableName($class, $companyId)
    {
        $o = bean(Proxy::getClassName($class)); //理解为 获取到类名
        //设置@Entity  实体,可以获取到表名
        return $o::tableName() . '_' . $companyId % Constant::TABLE_COUNT;
    }

5.swoft多类异常捕捉

try {
            $body = $request->input();
            $rlt = $this->_checkListService->getCompanyCheckList($params);

        } catch (Swoft\Db\Exception\DbException $e1) {
            Log::error("get_checklist_db_exception, code: %d, err:%s,file:%s, line: %d", $e1->getCode(), $e1->getMessage(), $e1->getFile(), $e1->getLine());
        } catch (Swoft\Redis\Exception\RedisException $e2) {
            Log::error("get_checklist_redis_exception, code: %d, err:%s,file:%s, line: %d", $e2->getCode(), $e2->getMessage(), $e2->getFile(), $e2->getLine());
        } catch (Swoft\Exception\SwoftException $e3) {
            Log::error("get_checklist_swoft_exception, code: %d, err:%s,file:%s, line: %d", $e3->getCode(), $e3->getMessage(), $e3->getFile(), $e3->getLine());
        } catch (\Exception $e) {
            Log::error("get_checklist_exception, code: %d, err:%s,file:%s, line: %d", $e->getCode(), $e->getMessage(), $e->getFile(), $e->getLine());
        }

6.获取redis实例,以及使用方式

Redis::connection('redis.clusters-pool')->get('key');

7.mysql,redis,http各类I/O调用 协程并发

$result = Co::multi([
            'sku_purchase' => function() use($params, $skuIds) {
                return $this->_skuPurchaseStatisticDao->getSkuPurchaseTimes($skuIds, $params['company_id']);
            },
            'recommend_data'   => function() use($params, $skuIds) {
                return $this->_rpcCompanySku->getRecommend($params['city_id'], $params['area_id'], $skuIds, $params['longitude'], $params['latitude'], $params['fulfillment'], $params['warehouse_ids'], $params['channel']);
            },
            'saleclass1' => function() use($skuIds) {
                return $this->_rpcGoodsService->getSkuSaleClass1BySkuIds($skuIds);
            },
            'parent_saleclass' => function() {
                return $this->_rpcGoodsService->getParentSaleClass();
            }
        ]);

        return [
            $result['sku_purchase'],
            $result['recommend_data'],
            $result['saleclass1'],
            $result['parent_saleclass'],
        ];

list($skuPurchase, $companySkuData, $saleClassData, $parentSaleClass) = $this->_concurrentGet($params, $skuIds);

8.sgo() 协程并发
一些长耗时并不需要返回结果的操作可以采用单起协程来处理,不需要关心处理结果,这里之所以不适用异步task来做,是因为task进程处理时间长会阻塞worker进程的请求,容易出问题。task进程适合一般耗时的异步任务等情况。

//数据统计
        sgo(function() use ($params, $res) {
            $this->_setSkuStatistic($params, $res);
        });

父协程不会等待子协程的执行结果,当然也可以等待子协程的返回结果,使用defer模式或者channel通道模式,sgo()采用channel模式。

9.AOP切面类
项目中可以采用Aspect 来编写切面的方法,并且定义需要进行切面的控制器和方法,可以用来做服务降级通用配置等 类似公共访问处

10.Listener监听类
设置监听swoole各个阶段的事件,例如@Listener(event=SwooleEvent::WORKER_START)
监测在swoole的worker进程启动时,调用此方法,例如增加saberGm的处理方法,增加请求后的日志记录和异常的捕捉。

11.Process用户自定义进程池
bean.php加入配置后,

'processPool' => [
        'class' => \Swoft\Process\ProcessPool::class,
        'workerNum' => 1
    ],

@Process(workerId=0)
执行run方法,里面自行实现 while(true)

可以用来执行脚本任务等,单次跑,多次跑都可以。

12.Validator
①定义@Validator("PurchaseValidator")验证器,里面有具体验证属性的规则

    /**
     * @IsInteger(message="skuId必须是整型")
     */
    protected $skuId;

②当然也可以自定义规则,需要用到Annotation解析,实例:

//自定义一个规则
/**
     * @IsList(message="items必须是数组", fields={
     *  "biId:integer",
     *  "class1Code:integer",
     *  "brand:string",
     *  "level:string",
     *  "flavor:string",
     *  "sequence:integer",
     *  "dataSource:integer",
     *  "searchWord:string",
     *  "ssuId:integer",
     *  "skuId:integer:required",
     *  "ssuFp:integer"
     * })
     */
    protected $items;

规则文件:

message = $values['value'];
        }
        if (isset($values['message'])) {
            $this->message = $values['message'];
        }
        if (isset($values['name'])) {
            $this->name = $values['name'];
        }

        if (isset($values['fields'])) {
            $fields = [];
            foreach ($values['fields'] as $field) {
                $field = explode(":", $field);
                $fields[$field[0]] = [
                    'type' => $field[1],
                    'required' => isset($field[2]) && $field[2] ? 1 : 0
                ];
            }
            $this->fields = $fields;
        }
    }

    /**
     * @return string
     */
    public function getMessage(): string
    {
        return $this->message;
    }

    public function getFields(): array 
    {
        return $this->fields;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }
}

解析文件:

className, $this->propertyName, $annotationObject);
        return [];
    }
}

③自定义验证器,在用到的控制器和方法上注解进去。

$optionValue ) {
            if (!in_array($option, $optionKeys) ) {
                throw new ValidatorException("options选项${option}未定义");
            }
            if ( !is_bool($optionValue) ) {
                throw new ValidatorException("options选项${option}必须是bool类型");
            }
        }

        return $options;
    }
}

④控制器中使用,在对应方法需要验证加上
@Validate(validator="PurchaseValidator", fields={"skuId"})

⑤公共方法validate()

function validate(array $data, string $validatorName, array $fields = [], array $userValidators = []): array

全局函数使用,当验证器失败会抛出 Swoft\Validator\Exception\ValidatorException 异常

$data 需要验证的数据,必须是数组 KV 格式
$validatorName 使用的验证器( @Validator() 注解标记的 )
$fields 需要验证的字段,为空验证器所有字段
$userValidators 同时使用的自定义验证器,支持两种格式。

validate($request->getParsedBody(), 'CommonValidator',[], ['OptionsValidator']);

13.中间件Middleware
①配置全局中间件

当你的自定义中间件需要全局请求应用,则可以考虑将此中间件作为全局中间件去使用,只需在 Bean 配置文件内配置 httpDispatcher 的 middlewares 属性,在数组中加入你的自定义中间件的命名空间地址,相关配置通常在 app/bean.php 内

return [
    ...
    'httpDispatcher'=>[
        'middlewares'=>[
            AuthMiddleware::class,
            ApiMiddleware::class
        ]
    ]
    ...
];

然后所有请求会先经过中间件的process方法,这里可以做一些过滤,争对请求或者是用户鉴权,统一返回值结构等

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $path = $request->getUri()->getPath();

        if ($path === '/favicon.ico') {
            $response = Context::get()->getResponse();
            return $response->withStatus(404);
        }

        if ( !in_array($path, ['/status']) ) {
            validate($request->getParsedBody(), 'CommonValidator',[], ['OptionsValidator']);
        }

        /**
         * @var \App\Common\EnvParams
         */
        $envParams = EnvParams::getEnvPamrams();
        $envParams->set($request->getParsedBody());

        $response = $handler->handle($request); //具体的方法执行

        return $this->getStandResponse($response);
    }

②也可以通过注解使用
通过 @Middleware@Middlewares, 可以很方便的配置中间件到当前的 Controller 和 Action 内

  • 当将此注解应用于 Controller 上,则作用域为整个 Controller
  • 将此注解应用于 Action 上,则作用域仅为当前的 Action
  • @Middleware 用于配置单个中间件
  • @Middlewares 显而易见的是用于配置一组 @Middleware,按照定义顺序依次执行

14.获取别名目录

$path = Swoft::getAlias('@base');
$path = alias('@base')

你可能感兴趣的:(swoft 实战常见技巧)