1、netty百万连接设计及优化

文章详细介绍了如何通过调整操作系统文件描述符限制、用户文件描述符限制以及单进程文件描述符限制来支持Netty处理百万级别的长连接。重点讨论了Netty的端口监听、流量整形和系统参数优化,包括TCP相关参数的调整,以提升系统的并发能力。此外,还提出了测试方案和针对大规模连接的治理策略。
摘要由CSDN通过智能技术生成

一、系统架构设计

1、操作系统文件描述符限制

1.1、操作系统file-max修改

理论上系统内存有多少就可以打开多少的文件描述符,但是在实际中内核是会做相应的处理,一般最大打开文件数会是系统内存的10%(以KB来计算),称之为系统级限制。这个数字可以通过 cat /proc/sys/fs/file-max 或者 sysctl -a | grep fs.file-max 命令查看。

1.2、用户文件描述符file-nr修改

用户打开文件描述符默认是1024,文件描述符范围1-65535,这个数字可以通过 ulimit -n查看,可以在65535范围内调整。

1.3、单进程文件描述符nr_open修改

单进程文件描述符必须小于等于file-max,对于netty单进程要支持100万长连接,需要将这个值设置为100万,可以通过cat /proc/sys/fs/nr_open查看。

1、操作系统最大句柄数查询(机器内存越大,默认值越大)
$ cat /proc/sys/fs/file-max

2、查询当前打开文件句柄数
$ cat /proc/sys/fs/file-nr
704(已经打开的句柄数)	0	100000(file-max数)

3、手工修改file-max
$ vi /etc/sysctl.conf
fs.file-max = 1100000 #环境变量值修改大点
sudo sysctl -p  #使环境变量生效

2、用户文件描述符限制

1、用户打开句柄限制(最大值65535)
$ ulimit -n
1024

2、用户句柄修改(user用户句柄软限制修改未30000,硬限制修改为65535,系统可以提前告警)
$ sudo vi /etc/security/limits.conf
user soft nofile 30000
user hard nofile 65535

3、单进程文件描述符限制 

1、查看单进程打开文件句柄数
$ cat /proc/sys/fs/nr_open

2、修改单进程打开文件句柄数
$ vim /etc/sysctl.conf
fs.nr_open = 1000000

4、端口监听范围

单个IP+端口最多只能打开65535个文件描述符,要实现100万长连接必须要监听多个端口。具体设置要求:file-max数量>=nr_open数量>=ulimit -n(65535)*端口数量。按照单用户打开文件描述符为5万算,100万连接需要开20个端口。  理论单机最大支持长连接数:65535(单进程文件描述符)*64535(端口数)= 4.29亿

二、方案设计

1、netty百万链接核心代码

netty代码比较简单,核心就是开多端口逻辑。确立端口段前要检查这个端口是否有其他应用占用。具体程序可以设置为随机端口这样做更安全,也可以使用固定端口,好处是更明确。

try {
	    	//netty入口引导类
	     	ServerBootstrap b = new ServerBootstrap();
			//设置核心线程组和业务工作线程组
			b.group(bossGroup, workGroup);
			//快速复用端口,避免端口冲突,原理tcp连接需要2ML时间单位回收,这个配置加快进度
			b.option(ChannelOption.SO_REUSEADDR, true);
			//设置nio类型的channel
			b.channel(NioServerSocketChannel.class);
            //流量整形 读写最大流量100M/s
            GlobalTrafficShapingHandler globalTrafficShapingHandler = new GlobalTrafficShapingHandler(eventLoopGroupForTrafficShaping, gatewayConfig.getNettyConfig().getTrafficShapingWriteLimit(), gatewayConfig.getNettyConfig().getTrafficShapingReadLimit());
		    //netty日志级别,用于排查问题使用
            LoggingHandler debugLogHandler = new LoggingHandler(LogLevel.DEBUG);
            LoggingHandler infoLogHandler = new LoggingHandler(LogLevel.INFO);
            
	        b.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ChannelPipeline pipeline = ch.pipeline();
					//最大字符支持及分隔符
					pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(gatewayConfig.getNettyConfig().getMaxFrameLength(), Delimiters.lineDelimiter()));
					//编码类型
					pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
					pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
					//心跳超时检测 及超时清除通道
					pipeline.addLast("heartBeatTimeOutCheckHandler", new HeartBeatTimeOutCheckHandler(gatewayConfig.getNettyConfig().getIdleTimeOut()));
					//心跳、读、写超时拦截
					pipeline.addLast("timeServerHandler", new TimeServerHandler());
                    //在线设备统计
					pipeline.addLast("metricHandler", metricsHandler);
					//日志级别
					pipeline.addLast("debegLog", debugLogHandler);
					pipeline.addLast("infoLog", infoLogHandler);
					//流量整形
				    pipeline.addLast("tsHandler", globalTrafficShapingHandler);
					//通道合法性校验
					pipeline.addLast("sessionAuthHandler",sessionAuthHandler); 
					//心跳消息
					pipeline.addLast("heartBeatRespHandler",heartBeatRespHandler); 
					//业务接收消息
					pipeline.addLast("businessHandler",businessServerHandler);
				    pipeline.addLast(businessGroup, new OrderServerProcessHandler());
			}});
	        
	        /**
	         * 监听端口范围
	         * */
	        for(int port = 4000;port<=4030;port++) {
	        	int portTemp = port;
	        	b.bind(port).sync().addListener(future -> {
	        		if (future.isSuccess()) {
                        log.info("netty监听端口成功:{}", portTemp);
                    } else {
                        log.info("netty监听端口失败:{}", portTemp);
                    }
	            });
	        }
		} catch (Exception e) {
			log.error("netty异常:{}",e.getMessage()); 
			stopNetty();
		} 

2、方案测试

百万长连接最难的是测试环节。通常压测使用jmeter,一般一台服务器也就1、5万线程,远远低于100万的要求。

1、Jmeter集群方案

应为我没有这样的集群,只能按照一台的情况推断,单台jmeter能支持5w长连接。预计百万链接需要20台Jmeter。

2、其他方案

这里最大问题是客户端会阻塞线程,所以客户端有多少线程,才能模拟多少活跃的客户端。 【这里谁有更好的测试方案私我

三、操作系统参数优化

这部分参数优化不是强制的,观察系统是否有异常在做优化。

net.ipv4.tcp_max_tw_buckets = 10000
#表示系统同时保持TIME_WAIT套接字的最大数量
 
net.ipv4.tcp_timestamps = 0
#关闭TCP时间戳
#以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项
 
net.ipv4.tcp_tw_recycle = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
 
net.ipv4.tcp_tw_reuse = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
 
net.ipv4.tcp_retries2 = 1
#活动TCP连接重传次数,超过次数视为掉线,放弃连接。缺省值:15,建议设为 2或者3.
    
net.ipv4.tcp_fin_timeout = 1
#FIN_WAIT状态的TCP连接的超时时间

四、李林峰百万连接治理方案

Netty系列之Netty百万级推送服务设计要点_语言 & 开发_李林锋_InfoQ精选文章

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值