实现单机的百万连接,瓶颈有以下几点:
1、如何模拟百万连接
2、突破局部文件句柄的限制
3、突破全局文件句柄的限制
在linux系统里面,单个进程打开的句柄数是非常有限的,一条TCP连接就对应一个文件句柄,而对于我们应用程序来说,一个服务端默认建立的连接数是有限制的。
如下图所示,通常一个客户端去除一些被占用的端口之后,可用的端口大于只有6w个左右,要想模拟百万连接要起比较多的客户端,而且比较麻烦,所以这种方案不合适。
image.png
在服务端启动800~8100,而客户端依旧使用1025-65535范围内可用的端口号,让同一个端口号,可以连接Server的不同端口。这样的话,6W的端口可以连接Server的100个端口,累加起来就能实现近600W左右的连接,TCP是以一个四元组概念,以原IP、原端口号、目的IP、目的端口号来确定的,当原IP 和原端口号相同,但目的端口号不同,最终系统会把他当成两条TCP 连接来处理,所以TCP连接可以如此设计。
image.png
测试环境
netty客户端 ,和netty服务端 都是springboot项目。
运行环境:linux
netty版本:4.1.6.Final
netty服务端代码
netty maven
<properties>
<netty-all.version>4.1.6.Final</netty-all.version>
</properties>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty-all.version}</version>
</dependency>
@SpringBootApplicationpublic class NettyserverApplication { private static final int BEGIN_PORT = 8000; private static final int N_PORT = 100; public static void main(String[] args) { SpringApplication.run(NettyserverApplication.class, args); new Server().start(BEGIN_PORT, N_PORT); } } /----------------------------------------------------------------------------------import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;public final class Server { public void start(int beginPort, int nPort) { System.out.println("server starting...."); EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup); bootstrap.channel(NioServerSocketChannel.class); bootstrap.childOption(ChannelOption.SO_REUSEADDR, true); bootstrap.childHandler(new ConnectionCountHandler()); /** * 绑定100个端口号 */ for (int i = 0; i < nPort; i++) { int port = beginPort + i; bootstrap.bind(port).addListener((ChannelFutureListener) future -> { System.out.println("bind success in port: " + port); }); } System.out.println("server started!"); } } /-------------------------------------------------------------------------------------------------import io.netty.channel.Channel;import io.netty.channel.ChannelHandler.Sharable;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;@Sharablepublic class ConnectionCountHandler extends ChannelInboundHandlerAdapter { //jdk1.5 并发包中的用于计数的类 private AtomicInteger nConnection = new AtomicInteger(); public ConnectionCountHandler() { /** * 每两秒统计一下连接数 */Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { System.out.println("connections: " + nConnection.get()); }, 0, 2, TimeUnit.SECONDS); } /** * 每次过来一个新连接就对连接数加一 * @param ctx */ @Override public void channelActive(ChannelHandlerContext ctx) { nConnection.incrementAndGet(); } /** * 端口的时候减一 * @param ctx */ @Override public void channelInactive(ChannelHandlerContext ctx) { nConnection.decrementAndGet(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); Channel channel = ctx.channel(); if(channel.isActive()){ ctx.close(); } //…… }
作者:s_j_x
链接:https://www.jianshu.com/p/490e2981545c
共同学习,写下你的评论
评论加载中...
作者其他优质文章