Nkn-tunnel 报错,Message channel full, discarding msg

nkn-tunel 报错,Message channel full, discarding msg…
是不是客户端数量开少了导致的?

请问你这个是在 tunnel 的发起端还是接收端呀?是否使用了 tuna 模式?出现日志的机器是否有高 CPU 占用等情况?
出现这个日志几个比较常见的原因:

  1. 发送端极短时间内发送了大量的连接
  2. 接收端目标程序没有及时接收 TCP 连接

这个情况是在发送端,没有使用tuna模式,cpu没有出现高负载(但是发送数据的时候,接收端cpu会占用很高,本身我的服务器是四核的,占用大概百分之两三百)。我是使用测速软件对这个端口发送数据的。还有一个情况就是,接收端会报错,2023/09/26 09:11:28 write tcp 服务器内网ip:55390->nkn节点IP:30002: i/o timeout
2023/09/26 09:11:28 read tcp 服务器内网IP:55390->nkn节点IP:30002: use of closed network connection,会不会跟这个有关系
而且测速大概五六次,七八次后,内存占用会非常高,本身服务器内存大概4G,占用百分之二三十,运行一两天(往里面发送数据)内存占用会到四五十,。查看内存占用情况,发现func (session *Session) ReceiveWith(localClientID, remoteClientID string, buf []byte) error函数里面的if !isEstablished && packet.Handshake {
return session.handleHandshakePacket(packet)
},这段代码占用非常高
我用的是nkn-tunnel v0.3.4

而且很奇怪,我测试的出口节点都会报错,显示write tcp 服务器内网ip:55390->nkn节点IP:30002: i/o timeout
2023/09/26 09:11:28 read tcp 服务器内网IP:55390->nkn节点IP:30002: use of closed network connection

30002 的错误在国内比较常见,主要是和海外节点的连接因为各种原因被中断导致的。

!isEstablished && packet.Handshake 这一段代码说明收到了新的 session 请求。一个 session 对应着你的发送端发送了一个新的 TCP 连接。你的测试程序会有很大量的 TCP 请求吗?

是的,相当于在nkn-tunnel链路上做了测速,会有很多流量。这个30002的错误,我的节点跟tunnel都是布置在外网的vps上面的,通过代理访问,也会有这个问题么?

还有就是上面恢复提到的,内存占用很大的问题,也是session请求过多导致的么?

30002 这个不是只会在国内发生,只是国内很常见。不过这只是一个 INFO 级别的日志,自动会恢复,一般是没有什么问题的。内存占用这个不好一概而论,具体要看你有多少还未关闭的 session。内存占用是和进行中的 session 成正比的,如果有大量未关闭的 session 那是正常的,如果没有的话可能就是有内存泄露之类的问题了。

那有没有办法主动关闭长时间未响应的session呢,因为我用pprof,发现就是有很多session占用了很多内存

内存泄漏应该不是吧

Session 的处理方式和 TCP 是一样的,需要在拨号或者接收端主动调用 close。比较常用的检测未响应的 Session 或者 TCP 的方式是设置 timeout 或者 deadline,然后在 read 报错的时候 close 即可。这样的逻辑可以保证长时间不使用的 session 或者 TCP 连接被关闭。

对于 nkn-tunnel 来说,关闭了一侧的应用和 tunnel 之间的 TCP 连接,session 就应该会关闭了

也就是在拨号之前添加dialConfig的timeout就能让时间不使用的session或者tcp链接关闭么

dialTimeout控制的只是拨号时候的行为。你可以调用 session 的 SetDeadline 系列函数来实现,比如每次接收到新数据就重新调用一下 SetReadDeadline 来刷新读取数据的 Deadline

就是在pip函数内部,io.Copy()之后调用b.SetReadDeadline么?

还是在err = session.ReceiveWith(localClientID, remoteClientID, data)
if err != nil {
return err
}
这之后?

1 Like

你的意思是要修改 nkn-tunnel 的源代码嘛?那是在这一行返回之前 https://github.com/nknorg/nkn-tunnel/blob/master/tunnel.go#L228
如果不修改 nkn-tunnel 的话,在与 nkn-tunnel 通讯的应用层直接关闭对应的 tcp 连接也是可以达到一样的效果的

func (t *Tunnel) dial(addr string) (net.Conn, error) {
if t.toNKN {
return t.dialer.DialWithConfig(addr, t.config.DialConfig)
}
var dialTimeout time.Duration
if t.config.DialConfig != nil {
dialTimeout = time.Duration(t.config.DialConfig.DialTimeout) * time.Millisecond
}
return net.DialTimeout(“tcp”, addr, dialTimeout)
}
这行是nkn出口拨号的函数呀,关闭了还怎么通信呢。之前按照您说的不是设置session的deadline么

不好意思,之前理解错了你的意图。我之前说的那一行会返回 session,再那里获取到 session 之后,如果要在 nkn-tunnel 里面设置 deadline 就会麻烦一些,需要把 pipe 函数内的 io.Copy 改成自己的实现,每次读取到数据以后重新设置 deadline。一般来说最简单的方法是在应用层那边超时了关闭,不过前提是你能修改应用层那边的代码

也可能是我表达得不明确。改session的deadline应该sdk里面的DialWithConfig函数里面就能修改吧。必须要自己实现io.Copy每次收到数据重新设置deadline么。因为出现了这个内存占用情况,想知道为啥。