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 中 监听JobProcessed和JobExceptionOccured这两个分别为job执行成功和失败的操作。建立QueueRollbackListener。写入以下代码:public function handle() { $connection = Db::connection(); if ($connection->transactionLevel() > 0) { $connection->rollback(0); } }

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等内容进行阻塞
<?php
use Hyperf\Utils\Exception\ParallelExecutionException;
use Hyperf\Utils\Coroutine;
use Hyperf\Utils\Parallel;
$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() 获取协程中出现的异常。
}
5. Inject 或 Value 注解不生效
6. 异步队列消息丢失
7. Error: No buffer space available
可以忽略此错误。这个错误就是 socket_buffer_size 选项过大,个别系统不接受,并不影响程序的运行。mac会偶现这种情况,Bsd系统不支持
8. 代码不生效
当碰到修改后的代码不生效的问题,请执行以下命令
composer dump-autoload -o
开发阶段,请不要设置 scan_cacheable 为 true,它会导致 收集器缓存 存在时,不会再次扫描文件。
当环境变量存在 SCAN_CACHEABLE 时,.env 中无法修改这个配置。
9. composer 安装依赖包爆内存
执行·
COMPOSER_MEMORY_LIMIT=-1 composer install
10. 语法错误导致服务无法启动
当项目启动时,抛出类似于以下错误时
Fatal error: Uncaught PhpParser\Error: 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 热更新不生效
- 查看终端是否出现报错信息,有则处理
- 使用
lsof -i:端口号查看进程id,kill掉并重启
15. Aop还有哪些场景或者说如何优雅的重写composer依赖包源码
- 比如你想要的内容与composer依赖包提供的内容不同,可以切入对应类优雅的进行重写方法
- 新建重写类,然后在
config/autoload/dependencies.php中进行替换
16. 避免协程间数据混淆
在传统的 PHP-FPM 的框架里,会习惯提供一个 AbstractController 或其它命名的 Controller 抽象父类,然后定义的 Controller 需要继承它用于获取一些请求数据或进行一些返回操作,在 Hyperf 里是 不能这样做 的,因为在 Hyperf 内绝大部分的对象包括 Controller 都是以 单例(Singleton) 形式存在的,这也是为了更好的复用对象,而对于与请求相关的数据在协程下也是需要储存到 协程上下文(Context) 内的,所以在编写代码时请务必注意 不要 将单个请求相关的数据储存在类属性内,包括非静态属性。
当然如果非要通过类属性来储存请求数据的话,也不是没有办法的,我们可以注意到我们获取 请求(Request) 与 响应(Response) 对象时是通过注入 Hyperf\HttpServer\Contract\RequestInterface 和 Hyperf\HttpServer\Contract\ResponseInterface 来获取的,那对应的对象不也是个单例吗?这里是如何做到协程安全的呢?就 RequestInterface 来举例,对应的 Hyperf\HttpServer\Request 对象内部在获取 PSR-7 请求对象 时,都是从 协程上下文(Context) 获取的,所以实际使用的类仅仅是一个代理类,实际调用的都是从 协程上下文(Context) 中获取的。
17. 路径问题
相对路径 DIR 时,会碰到的问题。请尽量使用 BASE_PATH 常量。
18. 使用随机数
使用随机数时需要重新播种。也可使用random_int获取真实随机数不需要重新播种
19. 不要使用静态匿名函数容易内存泄露
例如:
$values = array_map(
static function ($value) {
return empty($value) ? '""' : $value;
}, $values
);