句柄缓慢增长
MetSystem opened this issue · 11 comments
SpanNetty 0.7.2012.2221
940家设备1分钟传一次,大概整点传
Environment.SetEnvironmentVariable("io.netty.noPreferDirect", "true");
var dispatcher = new DispatcherEventLoopGroup();
bossGroup = dispatcher;
workerGroup = new WorkerEventLoopGroup(dispatcher, workerEventThreadCount);
bootstrap
.Option(ChannelOption.SoBacklog, 256)
.Option(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
.Handler(new LoggingHandler("SEV-LITN", LogLevel.ERROR))
.ChildOption(ChannelOption.SoKeepalive, true)
.ChildOption(ChannelOption.TcpNodelay, true)
.ChildOption(ChannelOption.SoSndbuf, 8 * 1024)
.ChildOption(ChannelOption.SoRcvbuf, 16 * 1024)
.ChildOption(ChannelOption.Allocator, PooledByteBufferAllocator.Default)
.ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
{
IChannelPipeline pipeline = channel.Pipeline;
pipeline.AddLast(new LoggingHandler("SEV-CONN", LogLevel.INFO));
pipeline.AddLast("timeout", new IdleStateHandler(3 * 60, 3 * 60, 3 * 60));
pipeline.AddFirst("device", new DeviceHandler());
pipeline.AddLast("LineBasedFrameDecoder", new LineBasedFrameDecoder(1024));
pipeline.AddLast("StringDecoder", new StringDecoder());
pipeline.AddLast("CustomEncoder", new CustomEncoder());
pipeline.AddLast("Check", new CheckChannelHandler());
pipeline.AddLast("default", new DefaultServerHandler());
}));
IChannel boundChannel = await bootstrap.BindAsync(AppSettings.Port);
Console.ReadLine();
服务端应答集成设备代码
var data = BuildPacket(context.Message.Content.ToString());
if (devcieInfo.Channel.IsWritable && devcieInfo.Channel.IsActive)
{
devcieInfo?.Channel.WriteAndFlushAsync(data);
}
else if (!devcieInfo.Channel.IsWritable)
{
devcieInfo?.Channel.WriteAndFlushAsync(data).Wait(TimeSpan.FromSeconds(5));
}
流程:
接入设备=》Server 产生消息=>rabbitmq=>业务消费消息=》产生应答消息=》rabbitmq =》Server 消费消息=》设备接入
日志中常见异常
异常1
DotNetty.Transport.Channels.ChannelException: Exception of type 'DotNetty.Transport.Channels.ChannelException' was thrown.
---> DotNetty.Transport.Libuv.Native.OperationException: ECONNRESET (ECONNRESET) : connection reset by peer
--- End of inner exception stack trace ---
异常2
00:00:50.032 [ERR] ExceptionCaught:Message=>远程主机强迫关闭了一个现有的连接。 ,StackTrace=> at DotNetty.Transport.ThrowHelper.ThrowSocketException(SocketError err)
at DotNetty.Transport.Channels.Sockets.AbstractSocketByteChannel`2.SocketByteChannelUnsafe.FinishRead(SocketChannelAsyncOperation`2 operation)
System.Net.Sockets.SocketException (10054): 远程主机强迫关闭了一个现有的连接。
at DotNetty.Transport.ThrowHelper.ThrowSocketException(SocketError err)
at DotNetty.Transport.Channels.Sockets.AbstractSocketByteChannel`2.SocketByteChannelUnsafe.FinishRead(SocketChannelAsyncOperation`2 operation)
net 5.0
如果句柄数是在一定范围内反复增减, 不会一直涨到几十万的话, 而且是稳定的定时增长的话,
那可能就是我上次发现的那个问题, GC的时候还是会回收的, 只是看起来比较烦, 但应该不会造成太大的性能问题
dotnet/runtime#47752
https://github.com/cuteant/SpanNetty/blob/future/src/DotNetty.Common/Concurrency/XThread.cs#L124
这个主要是由于这里的Task.Delay().Wait()造成的, 根据版本历史, 最初用主要是因为netstandard2.0之前不支持Thread.Sleep()
.
如果想改的话, 因为现在不需要再支持netstandard1.x的原因, 这里可以考虑用回Thread.Sleep(ms)
, 或者改用using(var e = new ManualResetEventSlim()) {e.Wait(ms);}
(这个netstandard1.3好像也支持, 参考https://stackoverflow.com/a/16374324)
我尝试下,非常感谢
这个里面是不是也需要改。
今天用dotnetty源码改了XThread.cs 发现稍微缓解了增长速度,但还是存在(spannetty改了后生成环境找不到DLL,所以改成dotnetty)。
- HashedWheelTimer如果你用到了可以改下看看, 框架本身只是移植了, 但没有用这个类
其他句柄就不知道是什么了, 一般情况下句柄数基本上应该等于线程数+连接数+一个基本稳定的值, 如果没有一直启动新线程或者建立连接应该比较稳定的, 严格来说没有无限增长(也就是最终会稳定或在一段时间后下降)或者像过山车那样反复极速的上升下降都可以不管它
-
如果你现在是用的libuv也可以试试不开还会不会有句柄
我对这行代码是如何工作的存在疑问, 感觉父类的实现才是正确的, 但没时间去测试
SpanNetty/src/DotNetty.Transport.Libuv/LoopExecutor.cs
Lines 181 to 184 in 7e2252b
-
如果你想的话可以用procexp或者process hacker(均需要管理员权限, 属于系统工具, 部分杀软可能报毒)自己检查下对应一直在增加的句柄是什么东西
process hacker, 注意关掉选项里的HIDE
procexp, View/Show Lower Pane+Lower Pane View=HANDLEs
大佬帮忙看看
998个端口,6914个Event,1781个线程
感觉你这个线程数有点多得不正常了, 线程和Event的比例基本还算正常. 理论上DotNetty的EventLoopGroup应该是在不同客户端间全局共享的, 每次new的默认值线程数是CPU核数(libuv)/CPU核数*2(其他).
建议调试下看看这些线程都在跑什么, 有可能是业务代码本身的问题
libuv 设置10,CPU 16核
1.消费应答队列(用的MassTransit库,加了失败重试)
2.一个hangfire的定时0点发送校准命令(我尝试把这个定时校准放到另一个进程处理)
3.主动发送命令(新开的后台线程跑)
现在句柄53K了
IotServer.exe.句柄53000.txt
ProcessHacker显示的进程1万个
ProcessHacker.txt
这种只能你自己调试下了
如果开发环境, vs调试涨上去后(或者直接附加到已经涨上去的进程)直接暂停, 然后调试/窗口/并行堆栈 或者 线程
/vscode看线程, 自己检查下大部分线程都在跑什么堆栈, 感觉应该是有大量的线程都在跑同一个东西的?
如果生产环境, 自己在服务器上抓个dump(任务管理器右键, 创建dump), 复制回开发环境, 丢vs然后步骤同上, 或者丢windbg里面看看吧(用法可以参考https://www.cnblogs.com/huangxincheng/p/14388296.html)
好,多谢指导