Skip to content

request context pool 回收机制影响到了 bytebuffer pool 的分配计算,导致系统容易发生OOM #1026

@jesse-engineer

Description

@jesse-engineer

Describe the bug

request context pool 回收机制影响到了 bytebuffer pool 的分配机制,导致系统弹性变低,系统在遇到偶发异常的情况下,会更容易OOM。

代码分析:

image

这里是用 response body 的 len 进行判断的,而不是 cap。如果 body 的 cap 较大,但是 len 较小时,这个 body 对象是不会被 put 回 responseBody pool 中的。也就是说,如果最初 Hertz 从 reponseBody pool 中取出的是一个较大的对象时,而这次请求返回的 response 数据又较小时,这个 body 对象是不会被重新 put 回 responseBody pool 中的,而是继续被 requestContext 持有,并一起被 put 回 requestContext pool 中。

如果 response body 的 len,小于 MaxKeepBodySize(默认 4M),那他会被放在 requestContext 内。而大于 MaxKeepBodySize 会被放回 reponseBody pool 中。因此这个过程相当于起到了一层过滤的作用,只有大的 body 会被 Put 回 reponseBody pool 中,也就是说在 Put 方法中统计到的 body 大小,都是大于 MaxKeepBodySize 的。这就导致了 reponseBody pool 在触发 Calibirate 方法对 defaultSize 进行重计算时,会将 defaultSize 调大到一个大于 MaxKeepBodySize 的值。

image

线上流量源源不断,Hertz 框架总会调用到 responseBody pool 中的 Get 方法创建新的 body 使用,而新分配到的 body 大小都将是大于 MaxKeepBodySize 的 defaultSize(我们场景下为 16M),这些新分配的 body 如果遇到了 response 数据较小的接口,那又会因为缓存机制分析一节中提到的 len 和 cap 的判断差异问题,这些大 cap 小 len 的 body 无法被放回到 responseBody pool 中,最终导致 requestContext pool 中所有 requestContext 的 response body 的大小都会变为 defaultSize(即 16 M)。
这就会使得在突发流量到来,或者旧的的请求在异常情况下无法结束使得 requestContext 无法释放时,框架会为新来的请求创建大量 defaultSize (16M)大小的对象,导致系统弹性很低,进而发生 OOM。

Expected behavior

框架不要做超出正常流量的内存分配

Screenshots

image

Hertz version:

v0.6.7

Environment:
go 1.21

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions