流量转发中 fd 泄露
理想的工作流程是下面这个样子:
+--------+ +--------+ +--------+
| Client | ---> | Agent | ---> | Server |
+--------+ +--------+ +--------+
+--------+ +--------+ +--------+
| Client | <--- | Agent | <--- | Server |
+--------+ +--------+ +--------+
完成回话之后的关闭顺序可能是:
Client --X-- Agent
Agent --X-- Server
Server --X-- Agent
Agent --X-- Client
总之就是大多数情况下,如果不是因为网络错误都是没有问题的。我们通常将转发通道分为两个,接收和发送通道。
在一个连接中,关闭一段的发送通道,另外一端也还是可以继续发送。当我们将两个通道分开处理的时候,如果出现
下面的情况就会卡住,导致 fd 没有正确释放。
+--------+ +--------+ +--------+
| Client | ----> | Agent | --!-> | Server |
+--------+ +--------+ +--------+
+--------+ +--------+ +--------+
| Client | <---- | Agent | <---- | Server |
+--------+ +--------+ +--------+
假设 Client 和 Agent 都是在本地,那么基本上是能保证数据传输不会出现错误,但是在 Agent 到 Server 段出现网络错误,C->A->S
的这个通道就会关闭,对于 Client 来说已经认为数据发送出去了,对于 Server 来说就是等待数据的状态。所以
Server 不会主动关闭 S->A
的这个连接。这个时候就会导致 S->A->C
的这个通道悬挂着。解决的办法也很简单,我在使用
Rust Tokio 库拷贝流量的时候,使用try_join!
代替join!
,前者需要等待所有异步表达式返回,或者某个表达式出错
也会提前返回。join!
则是等待所有异步表达式返回。