带你学习hyperf-6.1 问题汇总

2021-12-24 18:32:08 浏览数 (1)

6.1 问题汇总

1. Guzzle Http客户端 请求时未记录日志文件,并将日志打印至终端

解决方案: 升级swoole至最新

2. hyperf与laravel 队列事务失败处理
  • 场景: 当前有数据1,2,3投入队列,假设1,2,3都有创建的需求,投递至1时开启事务,并在未提交时,出现异常抛出了throw。此时数据2又进行消费执行创建,此时数据2会创建成功吗?
  • 框架分别的处理方案: laravel:数据2不会创建成功,并且会影响之后的所有数据的创建 hyperf:数据2创建成功,并在终端抛出您可能上一个事务未回滚的提醒,并且执行了一次回滚操作。
  • 为什么?
  • laravel框架消费时为线性消费,例如运用了supervisor,第一次事务未关闭影响第二次消费的事务,这是由于抛异常时没有及时回滚事务所导致的。
  • hyperf在消费时会新建新的协程进行操作,使用create 或者 Parallel进行创建新的协程进行消费,在DBconnect中会判断当前协程是否已经有链接,如果检查到有链接会调用defer进行release操作,判断是否在事务内,如果在,则立即回滚
  • 解决方案: laravel中,在providers/EventServiceProvide 的$listen 中 监听 JobProcessedJobExceptionOccured 这两个分别为job执行成功和失败的操作。建立QueueRollbackListener。写入以下代码:
代码语言:javascript复制
public function handle()
{
    $connection = Db::connection();
    if ($connection->transactionLevel() > 0) {
        $connection->rollback(0);
    }
}

PHP

Copy

3.hyperf如何返回两个同名不同值的header头?
  • 场景: 在返回前端内容需要输出 header1:1, header:2的情况
  • 处理: hyperf的处理,会以header1:1:2的情况展示,然后可以前端分割处理
  • 更好的解决方案 swoole4.6 通过重写ResponseEmitter 类 中 buildSwooleResopnse,修改类映射config/dependencies.php中将 ResponseEmitter接管到新重构后的类中,在buildSwooleResopnse 中删除implode(':', value);修改为swooleResponse->header(key, value);
4.在子协程获取父协程信息失效?
  • 案例
  • 原因 父协程由于没有阻塞操作,执行完进行了释放
  • 解决方案 使用waitgroup,Parallel等内容进行阻塞
代码语言:javascript复制
<?php
use HyperfUtilsExceptionParallelExecutionException;
use HyperfUtilsCoroutine;
use HyperfUtilsParallel;

$parallel = new Parallel();
$parallel->add(function () {
    sleep(1);
    return Coroutine::id();
});
$parallel->add(function () {
    sleep(1);
    return Coroutine::id();
});

try{
    // $results 结果为 [1, 2]
   $results = $parallel->wait();
} catch(ParallelExecutionException $e){
    // $e->getResults() 获取协程中的返回值。
    // $e->getThrowables() 获取协程中出现的异常。
}

PHP

Copy

5. Inject 或 Value 注解不生效

文档:https://hyperf.wiki/2.1/#/zh-cn/quick-start/questions?id=inject-或-value-注解不生效

6. 异步队列消息丢失

文档:https://hyperf.wiki/2.1/#/zh-cn/quick-start/questions?id=异步队列消息丢失

7. Error: No buffer space available

可以忽略此错误。这个错误就是 socket_buffer_size 选项过大,个别系统不接受,并不影响程序的运行。mac会偶现这种情况,Bsd系统不支持

8. 代码不生效

当碰到修改后的代码不生效的问题,请执行以下命令

代码语言:javascript复制
composer dump-autoload -o

Bash

Copy

开发阶段,请不要设置 scan_cacheable 为 true,它会导致 收集器缓存 存在时,不会再次扫描文件。 当环境变量存在 SCAN_CACHEABLE 时,.env 中无法修改这个配置。

9. composer 安装依赖包爆内存

执行·

代码语言:javascript复制
COMPOSER_MEMORY_LIMIT=-1 composer install

Bash

Copy

10. 语法错误导致服务无法启动

当项目启动时,抛出类似于以下错误时

Fatal error: Uncaught PhpParserError: Syntax error, unexpected T_STRING on line 27 in vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php:315 可以执行脚本 composer analyse,对项目进行静态检测,便可以找到出现问题的代码段

11. 避免使用Inject 依赖注入带有链接(例如rpc的类)

因为他会在框架启动前去链接一次

12. 注意协程上下文处理

例如在处理中间件时忘记注入上下文,导致当前获取的上下文内容不属于此协程

13. 避免使用全局变量和静态成员属性

会导致与结果不符,可以使用协程上下文Context作为管理。

14. watch 热更新不生效
  1. 查看终端是否出现报错信息,有则处理
  2. 使用lsof -i:端口号查看进程id,kill掉并重启
15. Aop还有哪些场景或者说如何优雅的重写composer依赖包源码
  1. 比如你想要的内容与composer依赖包提供的内容不同,可以切入对应类优雅的进行重写方法
  2. 新建重写类,然后在config/autoload/dependencies.php中进行替换
16. 避免协程间数据混淆

在传统的 PHP-FPM 的框架里,会习惯提供一个 AbstractController 或其它命名的 Controller 抽象父类,然后定义的 Controller 需要继承它用于获取一些请求数据或进行一些返回操作,在 Hyperf 里是 不能这样做 的,因为在 Hyperf 内绝大部分的对象包括 Controller 都是以 单例(Singleton) 形式存在的,这也是为了更好的复用对象,而对于与请求相关的数据在协程下也是需要储存到 协程上下文(Context) 内的,所以在编写代码时请务必注意 不要 将单个请求相关的数据储存在类属性内,包括非静态属性。

当然如果非要通过类属性来储存请求数据的话,也不是没有办法的,我们可以注意到我们获取 请求(Request)响应(Response) 对象时是通过注入 HyperfHttpServerContractRequestInterfaceHyperfHttpServerContractResponseInterface 来获取的,那对应的对象不也是个单例吗?这里是如何做到协程安全的呢?就 RequestInterface 来举例,对应的 HyperfHttpServerRequest 对象内部在获取 PSR-7 请求对象 时,都是从 协程上下文(Context) 获取的,所以实际使用的类仅仅是一个代理类,实际调用的都是从 协程上下文(Context) 中获取的。

17. 路径问题

相对路径 DIR 时,会碰到的问题。请尽量使用 BASE_PATH 常量。

18. 使用随机数

使用随机数时需要重新播种。也可使用random_int获取真实随机数不需要重新播种

19. 不要使用静态匿名函数容易内存泄露

例如:

代码语言:javascript复制
        $values = array_map(
            static function ($value) {
                return empty($value) ? '""' : $value;
            }, $values
        );

PHP

Copy

0 人点赞