Spring Webflux 未计算的时间损失
Spring Webflux unaccounted time loss
我们最近切换到基于 Spring Webflux 的 ExpediaGroups GraphQL library。
自从切换我们的 Jaeger Traces 以来,在创建最后一个数据库查询/跨度之前和之后显示了差距:
除了琐碎的实体 -> DTO 映射之外,在执行上述数据库查询之前或之后没有完成任何计算繁重的工作。通过 VisualVM 进行的初步调查显示没有明显的热点,但我们在本地环境中总共损失了 3-6 毫秒(开发、生产遭受的损失更多),这使整体响应时间增加了 100%。
我们不知所措:这是 WebFlux 问题吗?这是关于链接库的问题吗?我们在一个未饱和的单独线程池中执行我们所有的逻辑(这些结果出现在一个客户端非并发调用我们的 GraphQL 端点)所以我们不应该阻塞 Netty 的事件循环(即使我们这样做了:它应该如果我对 Webflux 的理解是正确的,就不要制造那些“差距”。
我正在寻找进一步调查此问题或任何配置旋钮的方法。
数据库调用之间的“差距”已被确定为与框架相关,可以通过重构我们的代码来规避,头部和尾部的“差距”无法在探查器中解决/解决。此外,我们不会丢失任何跨线程边界的跟踪相关信息,这已被考虑在内。
附加信息:
- 我们的平均响应大小低于 1 kb
- 在同样出现此问题的本地环境中此服务前面没有反向代理
- 所有流量都是 HTTP,而不是 HTTPS
它有许多不同的原因,例如:
- 电脑处理速度慢
- spring-webflux 故障
- 长/笨拙的代码
- 本地环境问题/无法运行
尝试 运行在另一台计算机或 public 环境中运行代码
经过大量调试,我们找到了以下答案:
- 每个请求前面和结尾的“间隙”只是传入请求和相应响应的纯 I/O。这也解释了当被请求者慢慢消耗响应/发送请求时生产中的更大差距,这对本地主机请求不成立
- 两者之间较大的差距可归因于缺乏预热 (JIT),这对于 WebFlux 堆栈来说似乎是必需的
- 总体“糟糕”的性能也与预热相关 (JIT)
此外,建议的 PreparsedDocumentProvider
GraphQL 缓存(谢谢 Korashen)似乎提供了不错的性能提升:
@Bean
fun preparsedDocumentProvider(): PreparsedDocumentProvider = object : PreparsedDocumentProvider {
@Suppress("MagicNumber")
private val cache: Cache<String, PreparsedDocumentEntry> = Cache2kBuilder
.of(String::class.java, PreparsedDocumentEntry::class.java)
// avoid cache attacks
.entryCapacity(1024)
.eternal(true)
.build()
override fun getDocument(
executionInput: ExecutionInput,
parseAndValidateFunction: Function<ExecutionInput, PreparsedDocumentEntry>
) = cache.computeIfAbsent(executionInput.query) { parseAndValidateFunction.apply(executionInput) }
}
我们最近切换到基于 Spring Webflux 的 ExpediaGroups GraphQL library。
自从切换我们的 Jaeger Traces 以来,在创建最后一个数据库查询/跨度之前和之后显示了差距:
我们不知所措:这是 WebFlux 问题吗?这是关于链接库的问题吗?我们在一个未饱和的单独线程池中执行我们所有的逻辑(这些结果出现在一个客户端非并发调用我们的 GraphQL 端点)所以我们不应该阻塞 Netty 的事件循环(即使我们这样做了:它应该如果我对 Webflux 的理解是正确的,就不要制造那些“差距”。
我正在寻找进一步调查此问题或任何配置旋钮的方法。
数据库调用之间的“差距”已被确定为与框架相关,可以通过重构我们的代码来规避,头部和尾部的“差距”无法在探查器中解决/解决。此外,我们不会丢失任何跨线程边界的跟踪相关信息,这已被考虑在内。
附加信息:
- 我们的平均响应大小低于 1 kb
- 在同样出现此问题的本地环境中此服务前面没有反向代理
- 所有流量都是 HTTP,而不是 HTTPS
它有许多不同的原因,例如:
- 电脑处理速度慢
- spring-webflux 故障
- 长/笨拙的代码
- 本地环境问题/无法运行
尝试 运行在另一台计算机或 public 环境中运行代码
经过大量调试,我们找到了以下答案:
- 每个请求前面和结尾的“间隙”只是传入请求和相应响应的纯 I/O。这也解释了当被请求者慢慢消耗响应/发送请求时生产中的更大差距,这对本地主机请求不成立
- 两者之间较大的差距可归因于缺乏预热 (JIT),这对于 WebFlux 堆栈来说似乎是必需的
- 总体“糟糕”的性能也与预热相关 (JIT)
此外,建议的 PreparsedDocumentProvider
GraphQL 缓存(谢谢 Korashen)似乎提供了不错的性能提升:
@Bean
fun preparsedDocumentProvider(): PreparsedDocumentProvider = object : PreparsedDocumentProvider {
@Suppress("MagicNumber")
private val cache: Cache<String, PreparsedDocumentEntry> = Cache2kBuilder
.of(String::class.java, PreparsedDocumentEntry::class.java)
// avoid cache attacks
.entryCapacity(1024)
.eternal(true)
.build()
override fun getDocument(
executionInput: ExecutionInput,
parseAndValidateFunction: Function<ExecutionInput, PreparsedDocumentEntry>
) = cache.computeIfAbsent(executionInput.query) { parseAndValidateFunction.apply(executionInput) }
}