multithreading - 使用 NIO 或 Netty 等事件驱动框架时如何跟踪请求过程

标签 multithreading netty trace thread-local event-driven

我们正在运行一个 Web 应用程序,该应用程序本身由多个微服务组成,对于每个请求,我们最终都需要调用第 3 方服务,这非常耗时,通常需要几秒钟的时间。我们有一个需求,需要使用请求traceId跟踪每个请求的所有服务之间的所有处理日志。

在当前的实现中,我们使用基于线程的并发模型,在每个服务中从头到尾分配一个线程来处理请求,并在等待远程服务的响应时阻塞。将 traceId 放入 ThreadLocal 中是很自然的,这样我们就可以在需要时随时随地取回它。

但是基于线程的并发模型扩展性不好,我们倾向于改为NIO/事件驱动模型,并尝试了Netty,性能提升非常大。但是每个请求处理的不同阶段可能由 Netty 的不同线程处理,这使得日志跟踪非常棘手。

我们当前的考虑因素包括:

  • traceId作为方法参数传递,反正已经在请求中了,但是如果深度嵌套的方法需要它就非常不方便了。
  • 在每个回调开始时将traceId设置为ThreadLocal。但我个人认为这种方法很容易出错,并且可能会引入难以发现的竞争条件错误。

那么在 NIO/事件驱动模型中解决此类跟踪问题的复杂/优雅的方法是什么?

最佳答案

这是所有试图适应异步世界的 Java EE 框架的致命弱点(也是为什么现有框架永远不会真正适应异步世界的原因)——数十年来在 ThreadLocal 中存储状态。

基本上,您需要将要传递的状态与正在处理的 channel 或请求联系起来,以便接下来获取它的任何代码都可以使用它 - 并且您不能假设这会发生在同一线程上。

两种解决方法:

  1. Channel.attr() - 如果状态可以绑定(bind)到连接(一次仅用于一件事),则创建一个静态 AttributeKey 并将其传递给 Channel.attr() - 您将返回一个最初为 null 的属性 - 在您的第一个处理程序中,将其分配给某些内容,之后的所有内容都可以将其从那里拉出(如果要在不关闭连接的情况下重用该连接,请确保在知道完成后清除它,就像 HTTP 保持事件连接)。
  2. 将其附加到您解码的某个对象 - 对 HTTP 请求的解码器进行子类化(如果您正在执行 HTTP 操作),并使用 ID 创建您自己的子类。

使用 ThreadLocals 来做这些事情看起来很自然,因为我们的行业花了一两年的时间才以与计算机实际执行的操作无关的方式来制作程序模型 I/O - 尽管这卖出了很多硬件( async 更像是我在 1983 年左右编写的中断处理程序):-)

关于multithreading - 使用 NIO 或 Netty 等事件驱动框架时如何跟踪请求过程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41507423/

相关文章:

C 读和线程安全 (linux)

java - 搭建netty开发环境

java - 从自定义 Netty 服务器发送 "MOTD"到 Minecraft 客户端

bind - 为什么 Netty 不绑定(bind)公共(public) IP 地址,而是绑定(bind) 127.0.0.1?

r - 为什么当 ... = [.data.table 时,trace(..., edit=TRUE) 不起作用

go - gRPC - GoLang - Stackdriver 追踪器

android - 如何从android中的代码读取/data/anr/traces.txt?

c# - EventWaithandle 与 while(true) Thread.Sleep

c - 唤醒多个条件变量

java - 我在sleep()时中断了方法并且没有异常