共计 2969 个字符,预计需要花费 8 分钟才能阅读完成。
sentry 文档:
https://docs.sentry.io/
在熟悉了 hyperf 架构异常捕捉机制之后,觉得 sentry 可以以非常合理的方式使用在异常捕捉机制中。
首先安装 sentry:
composer require sentry/sdk:2.0.3
安装完后在 hyperf.php 文件中添加
Sentry\init(\['dsn' => 'your dsn']);
在 App\Exception\Handle\AppExceptionHandle 中
use Sentry;
public function handle(Throwable $throwable, ResponseInterface $response)
{Sentry\captureException($throwable);
return $response->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
}
这时候捕捉异常发现,sentry 服务端并没有接收到异常。查看源码发现,sentry 的发送机制是在于进程结束后才清空信息队列,而 hyperf 是基于 swoole 的架构,一次请求之后,并不会释放进程,所以 sentry 服务端无法接收到数据,Sentry\Transport\HttpTransport 构造方法中的第三个参数解释为:
This flag controls whether to delay sending of the events until the shutdown of the application
默认为 true,推送异常后 sentry 并不会第一时间就把异常推送到服务端去而将会等到进程结束后才发送异常。而 init 方法在 sentry\ClientBuilder->createTransportInstance 方法中创建的 HttpTransport 的第三个参数为 true,修改为 false 便可以接收到请求了。
但是这时随之而来又一个问题,sentry 的推送机制使用的是 curl 库,而 swoole 并不支持 curl 协程化,也就意味着,如果在生产环境中,同时出现大量的异常,会导致进程阻塞,hyperf 的性能将严重下降。
异步处理刻不容缓!
再查看了文档之后,curl 推送是通过 Transport 类来处理的,而默认的 Transport 是 HttpTransport 也就是同步推送机制,同时也提供了异步 Transport, spoolTransport。
删除 hyperf.php 的 Sentryinit 方法
在 App\Exception\Handle\AppExceptionHandle 中修改为如下
use Sentry\ClientBuilder;
use Sentry\Transport\SpoolTransport;
use Sentry\Transport\HttpTransport;
use Sentry\State\Hub;
use Sentry\Spool\MemorySpool;
use Http\Discovery\HttpAsyncClientDiscovery;
use Http\Discovery\MessageFactoryDiscovery;
use Sentry\Options;
public function handle(Throwable $throwable, ResponseInterface $response)
{$options = ['dsn' => 'https://<key>@sentry.io/<project>'\];
$optionObj = new Options($options);
$spool = new MemorySpool();
$transport = new SpoolTransport($spool);
$httpTransport = new HttpTransport($optionObj, HttpAsyncClientDiscovery::find(), MessageFactoryDiscovery::find());
$builder = ClientBuilder::create($options);
$builder->setTransport($transport);
Hub::getCurrent()->bindClient($builder->getClient());
Hub::getCurrent()->captureException($throwable);
// 调用这个方法就会开始清空之前捕捉异常的队列,思考之后觉的放在定时任务里定时清空队列比较合理。$spool->flushQueue($httpTransport);
}
开始捕捉异常,怎么 sentry 服务端没有接收到请求,留下了没有技术的泪水。
排查问题后发现是因为 HttpAsyncClientDiscovery::find()创建的异步 httpTransport 的 url 配置问题,仔细看了一遍源码后,发现可以使用 ClientBuilder->createTransportInstance 方法,该方法默认是 private,将其修改为 public 便可以使用它创建 transport 对象。修改后的代码如下所示
App\Exception\Handle\AppExceptionHandle 如下所示
use Sentry\ClientBuilder;
use Sentry\Transport\SpoolTransport;
use Sentry\State\Hub;
use Sentry\Spool\MemorySpool;
public function handle(Throwable $throwable, ResponseInterface $response)
{$options = ['dsn' => 'https://<key>@sentry.io/<project>'\];
$spool = new MemorySpool();
$transport = new SpoolTransport($spool);
$builder = ClientBuilder::create($options);
$httpTransport = $builder->createTransportInstance();
$builder->setTransport($transport);
Hub::getCurrent()->bindClient($builder->getClient());
Hub::getCurrent()->captureException($throwable);
// 调用这个方法就会开始清空之前捕捉异常的队列,思考之后觉的放在定时任务里定时清空队列比较合理。$spool->flushQueue($httpTransport);
}
捕捉异常,sentry 服务端接收到异常。在这里 $spool->flushQueue($httpTransport); 将会主动推送捕捉的队列异常,思考之后觉的放在定时任务里定时清空队列比较合理。