Giter VIP home page Giter VIP logo

Comments (19)

panjf2000 avatar panjf2000 commented on June 1, 2024

这种问题请务必附上必要的信息:Go 版本,gnet 版本,OS 系统等等,不然根本无从查起;要不然就请选择 bug 的模板创建 issue

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

在eventhandler的ontraffic里主动调用gnet.Conn.Close()后然后返回gnet.Close是否会引起bug

不会,但是也不要这么做,如果是想直接关闭当前连接,那么就 return gnet.Close,如果是想在其他的地方随时关闭连接,那么可以选择调用 gnet.Conn.Close(),后者是并发安全且异步的,是为了让用户可以在任意地方任意时间关闭连接用的,前者是让用户可以快速直接抵关闭当前连接用的,是同步的;二者选其一就行了,没必要两个都做,但是即便是两个都做了也不会引起问题,因为内部会在关闭连接前检测其是否已经关闭,如果是则忽略掉。

在gnet的asyncwrite或者asyncwritev的回调里面,是否需要主动调用gnet.Close()呢,如果不调用是否其他地方有调用close的机会呢

我没懂你这问题,是否要关闭连接以及什么要关闭连接取决于你的需求,正如前面所说,如果你想在 event-loop 里直接关闭当前连接,那就在任意的 gnet 回调函数中 return gnet.Close 即可,如果你想在其他地方、特定的时间关闭连接,那就用 gnet.Conn.Close().

from gnet.

someview avatar someview commented on June 1, 2024

这种问题请务必附上必要的信息:Go 版本,gnet 版本,OS 系统等等,不然根本无从查起;要不然就请选择 bug 的模板创建 issue

谢谢,因为有一些具体的思路,所以也许提供一点点子就能解决了。
go: 1.20
gnet: v2.0
os: linux
现象:
压测的wsclient -> gnet实现的wsserver突然通讯,然后客户端直接选择关闭大量tcp连接, 发现服务端出现大量close_wait状态的连接

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

大量 CLOSE_WAIT 通常是服务端没有正确关闭连接导致的,如果是客户端主动关闭连接的话,gnet 会自动在服务端这边关闭对应的连接,通常不需要用户自己手动去关闭,而且会有 OnClose 回调,我建议你在这个回调里记录一下看看是否能和客户端关闭的连接数对应上。

from gnet.

someview avatar someview commented on June 1, 2024

在eventhandler的ontraffic里主动调用gnet.Conn.Close()后然后返回gnet.Close是否会引起bug

不会,但是也不要这么做,如果是想直接关闭当前连接,那么就 return gnet.Close,如果是想在其他的地方随时关闭连接,那么可以选择调用 gnet.Conn.Close(),后者是并发安全且异步的,是为了让用户可以在任意地方任意时间关闭连接用的,前者是让用户可以快速直接抵关闭当前连接用的,是同步的;二者选其一就行了,没必要两个都做,但是即便是两个都做了也不会引起问题,因为内部会在关闭连接前检测其是否已经关闭,如果是则忽略掉。

在gnet的asyncwrite或者asyncwritev的回调里面,是否需要主动调用gnet.Close()呢,如果不调用是否其他地方有调用close的机会呢

我没懂你这问题,是否要关闭连接以及什么要关闭连接取决于你的需求,正如前面所说,如果你想在 event-loop 里直接关闭当前连接,那就在任意的 gnet 回调函数中 return gnet.Close 即可,如果你想在其他地方、特定的时间关闭连接,那就用 gnet.Conn.Close().

也就是说conn.Close()和return gnet.Close不会出现异常,效果一样了. asyncwrite写数据的时候,应用程序并不知道会返回什么样的错误啊,也许是连接错误了,也许是其他错误。那应用程序如何知道asyncwrite的回调里面是否应该关闭连接呢.
对于客户端直接关闭tcp连接的情况,是在eventhandler的ontraffic里收到响应,还是在onclose里时收到响应呢

from gnet.

someview avatar someview commented on June 1, 2024

大量 CLOSE_WAIT 通常是服务端没有正确关闭连接导致的,如果是客户端主动关闭连接的话,gnet 会自动在服务端这边关闭对应的连接,通常不需要用户自己手动去关闭,而且会有 OnClose 回调,我建议你在这个回调里记录一下看看是否能和客户端关闭的连接数对应上。

头疼,。线上环境,复现不了异常情况, 当时查询的时候,是触发不了onclose回调,所以怀疑是不是ontraffic的时候触发的操作.
感谢回复

from gnet.

someview avatar someview commented on June 1, 2024

大量 CLOSE_WAIT 通常是服务端没有正确关闭连接导致的,如果是客户端主动关闭连接的话,gnet 会自动在服务端这边关闭对应的连接,通常不需要用户自己手动去关闭,而且会有 OnClose 回调,我建议你在这个回调里记录一下看看是否能和客户端关闭的连接数对应上。

版本是v2 2.2.5是否有release修复bug呢

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

那应用程序如何知道asyncwrite的回调里面是否应该关闭连接呢.

callback 函数里有传入一个 err,这个值会告诉你在写数据回客户端的时候是否发生了错误,你可以根据这个错误值决定要不要主动关闭连接

对于客户端直接关闭tcp连接的情况,是在eventhandler的ontraffic里收到响应,还是在onclose里时收到响应呢

不管是你主动关闭连接还是被动关闭,都会有 OnClose 回调,不会有 OnTraffic

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

大量 CLOSE_WAIT 通常是服务端没有正确关闭连接导致的,如果是客户端主动关闭连接的话,gnet 会自动在服务端这边关闭对应的连接,通常不需要用户自己手动去关闭,而且会有 OnClose 回调,我建议你在这个回调里记录一下看看是否能和客户端关闭的连接数对应上。

版本是v2 2.2.5是否有release修复bug呢

现在还不知道是哪里的问题,要先查清楚再说,如果只是客户端大量断开连接就能复现的话,你可以写一个简单的 demo,模拟一下那个场景,看看是否能复现,然后 debug 一下看看是哪里有问题,或者你复现之后把代码贴上来,我来 debug。

from gnet.

someview avatar someview commented on June 1, 2024

只能成为悬案了。因为是阿里的mse网关做的ws的代理,并不清楚他们内部的实现细节是如何实现的。如何能测试和模拟tcp断线过程在gnet中的流程呢:

  1. 客户端发送fin1包
  2. 服务端收到fin1包
  3. 服务端发送fin1-ack包
  4. 客户端crash
  5. 服务端发送fin2包
    ....
    这种情况gnet可以正常处理吗,连接最终能关闭吗

from gnet.

someview avatar someview commented on June 1, 2024

大量 CLOSE_WAIT 通常是服务端没有正确关闭连接导致的,如果是客户端主动关闭连接的话,gnet 会自动在服务端这边关闭对应的连接,通常不需要用户自己手动去关闭,而且会有 OnClose 回调,我建议你在这个回调里记录一下看看是否能和客户端关闭的连接数对应上。

版本是v2 2.2.5是否有release修复bug呢

现在还不知道是哪里的问题,要先查清楚再说,如果只是客户端大量断开连接就能复现的话,你可以写一个简单的 demo,模拟一下那个场景,看看是否能复现,然后 debug 一下看看是哪里有问题,或者你复现之后把代码贴上来,我来 debug。

我们自己写的demo或者压测环境一切ok, 中间经过阿里mse的代理后出现了异常.咨询过他们网关的负责人,ws代理的时候是直接关闭tcp连接.

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

ws代理的时候是直接关闭tcp连接.

这是什么意思?什么时候 MSE 会直接关闭 TCP 连接?

from gnet.

someview avatar someview commented on June 1, 2024

ws代理的时候是直接关闭tcp连接.

这是什么意思?什么时候 MSE 会直接关闭 TCP 连接?

wsclient -> mse ws代理 -> wsserver

wsclient 直接ctrl + c 异常结束, mse直接关闭对上游服务的tcp连接. 主要是close_wait恢复不了,几个小时了,都还是大量的close_wait

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

CLOSE_WAIT 就是服务端没有主动 close 掉连接导致的,这种情况可能是你的 gnet server 没有正确感知到对端的连接断开事件,但这并不应该发生,gnet 是有做正确处理的,你也说了在测试环境没有用代理是没有问题的,感觉还是代理的问题,你能不能在测试环境也搞一个 MSE 然后再 debug 一下呢?

from gnet.

someview avatar someview commented on June 1, 2024

CLOSE_WAIT 就是服务端没有主动 close 掉连接导致的,这种情况可能是你的 gnet server 没有正确感知到对端的连接断开事件,但这并不应该发生,gnet 是有做正确处理的,你也说了在测试环境没有用代理是没有问题的,感觉还是代理的问题,你能不能在测试环境也搞一个 MSE 然后再 debug 一下呢?

我本地配置好环境试试。另外发现的一个可疑的点是这样,reuseport选项是否可能导致close_wait呢,如果大量的tcp连接频繁创建销毁

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

reuseport 可以用来强制重用还处于 TIME_WAIT 的连接,但通常不推荐这么搞,而且和 CLOSE_WAIT 没什么关系。

from gnet.

someview avatar someview commented on June 1, 2024

reuseport 可以用来强制重用还处于 TIME_WAIT 的连接,但通常不推荐这么搞,而且和 CLOSE_WAIT 没什么关系。
查看了一下gnet的源码,不太确定是不是某些条件下判断出现的bug:

//  eventloop_unix.go
func (el *eventloop) close(c *conn, err error) (rerr error) {
	if addr := c.localAddr; addr != nil && strings.HasPrefix(c.localAddr.Network(), "udp") {
		rerr = el.poller.Delete(c.fd)
		if c.fd != el.ln.fd {
			rerr = unix.Close(c.fd)
			el.connections.delConn(c)
		}
		if el.eventHandler.OnClose(c, err) == Shutdown {
			return errorx.ErrEngineShutdown
		}
		c.release()
		return
	}

	if !c.opened || el.connections.getConn(c.fd) == nil {
		return // ignore stale connections
	}

	// Send residual data in buffer back to the peer before actually closing the connection.
	if !c.outboundBuffer.IsEmpty() {
		for !c.outboundBuffer.IsEmpty() {
			iov := c.outboundBuffer.Peek(0)
			if len(iov) > iovMax {
				iov = iov[:iovMax]
			}
			if n, e := io.Writev(c.fd, iov); e != nil {
				el.getLogger().Warnf("close: error occurs when sending data back to peer, %v", e)
				break
			} else { //nolint:revive
				_, _ = c.outboundBuffer.Discard(n)
			}
		}
	}

	err0, err1 := el.poller.Delete(c.fd), unix.Close(c.fd)
	if err0 != nil {
		rerr = fmt.Errorf("failed to delete fd=%d from poller in event-loop(%d): %v", c.fd, el.idx, err0)
	}
	if err1 != nil {
		err1 = fmt.Errorf("failed to close fd=%d in event-loop(%d): %v", c.fd, el.idx, os.NewSyscallError("close", err1))
		if rerr != nil {
			rerr = errors.New(rerr.Error() + " & " + err1.Error())
		} else {
			rerr = err1
		}
	}

	el.connections.delConn(c)
	if el.eventHandler.OnClose(c, err) == Shutdown {
		rerr = errorx.ErrEngineShutdown
	}
	c.release()
	return
}

func (c *conn) release() {
	c.ctx = nil
	c.localAddr = nil
	c.remoteAddr = nil
	c.buffer = nil
	if addr, ok := c.localAddr.(*net.TCPAddr); ok && c.localAddr != c.loop.ln.addr && len(addr.Zone) > 0 {
		bsPool.Put(bs.StringToBytes(addr.Zone))
	}
	if addr, ok := c.remoteAddr.(*net.TCPAddr); ok && len(addr.Zone) > 0 {
		bsPool.Put(bs.StringToBytes(addr.Zone))
	}
	c.pollAttachment.FD, c.pollAttachment.Callback = 0, nil
	if !c.isDatagram {
		c.opened = false
		c.peer = nil
		c.inboundBuffer.Done()
		c.outboundBuffer.Release()
	}
}

在发生异常的情况下,比如,第一次调用close方法,
err0, err1 := el.poller.Delete(c.fd), unix.Close(c.fd)
err0执行成功,err1返回的错误是unix.EAGAIN(假设是系统中断引起的),然后执行c.release()将c.opened设置为false,第二次执行close方法的时候,

if !c.opened || el.connections.getConn(c.fd) == nil {
		return // ignore stale connections
	}

没有close的机会了.
是否需要在应用中自己处理err为unix.EAGAIN的这种错误

from gnet.

panjf2000 avatar panjf2000 commented on June 1, 2024

系统调用 close() 不会返回 EAGAIN,最多返回 EINTR,但是这种概率微乎其微,所以在实践中基本上不会专门去处理这种情况,而且就算真的出现这种情况,日志会打印 error occurs in event-loop: %v,你有看到这个日志吗?而且这种情况发生的概率太小了,就算真的有,也应该是极少数,怎么可能有大量的 close() 调用在短时间内都发生了 EINTR 错误, Linux 不至于这么不稳定,以前也没有别人反馈过使用 gnet 遇到过这种情况,我觉得不可能是这里的问题。

from gnet.

someview avatar someview commented on June 1, 2024

系统调用 close() 不会返回 EAGAIN,最多返回 EINTR,但是这种概率微乎其微,所以在实践中基本上不会专门去处理这种情况,而且就算真的出现这种情况,日志会打印 error occurs in event-loop: %v,你有看到这个日志吗?而且这种情况发生的概率太小了,就算真的有,也应该是极少数,怎么可能有大量的 close() 调用在短时间内都发生了 EINTR 错误, Linux 不至于这么不稳定,以前也没有别人反馈过使用 gnet 遇到过这种情况,我觉得不可能是这里的问题。

https://www.man7.org/linux/man-pages/man2/close.2.html
确实如此没有err.dragin. 看手册里是推荐出现EINTR错误时,推荐重试关闭文件描述符.
好像没有给gnet添加日志库.没有看到上面的日志

from gnet.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.