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);
}
}
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
等内容进行阻塞
<?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 热更新不生效
- 查看终端是否出现报错信息,有则处理
- 使用
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)
对象时是通过注入 HyperfHttpServerContractRequestInterface
和 HyperfHttpServerContractResponseInterface
来获取的,那对应的对象不也是个单例吗?这里是如何做到协程安全的呢?就 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