前言
众所周知我们在进行网络连接的时候,建立套接字连接是一个非常消耗性能的事情,特别是在分布式的情况下,用线程池去保持多个客户端连接,是一种非常消耗线程的行为。那么我们该通过什么技术去解决上述的问题呢,那么就不得不提一个网络连接的利器——Netty
.
正文 Netty
Netty是一个NIO
客户端服务器框架:
- 它可快速轻松地开发网络应用程序,例如协议服务器和客户端。
- 它极大地简化和简化了网络编程,例如
TCP
和UDP
套接字服务器。
NIO
是一种非阻塞IO
,它具有以下的特点本文来源gaodaima#com搞(代@码$网6
- 单线程可以连接多个客户端。
- 选择器可以实现但线程管理多个
Channel
,新建的通道都要向选择器注册。 - 一个
SelectionKey
键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。 selector
进行select()
操作可能会产生阻塞,但是可以设置阻塞时间,并且可以用wakeup()
唤醒selector
,所以NIO
是非阻塞IO
。
Netty模型selector模式
它相对普通NIO
的在性能上有了提升,采用了:
NIO
采用多线程的方式可以同时使用多个selector
- 通过绑定多个端口的方式,使得一个
selector
可以同时注册多个ServerSocketServer
- 单个线程下只能有一个
selector
,用来实现Channel
的匹配及复用
半包问题
TCP/IP
在发送消息的时候,可能会拆包,这就导致接收端无法知道什么时候收到的数据是一个完整的数据。在传统的BIO
中在读取不到数据时会发生阻塞,但是NIO
不会。为了解决NIO
的半包问题,Netty
在Selector
模型的基础上,提出了reactor
模式,从而解决客户端请求在服务端不完整的问题。
netty模型reactor模式
在selector
的基础上解决了半包问题。
上图,简单地可以描述为”boss接活,让work干”:manReactor
用来接收请求(会与客户端进行握手验证),而subReactor
用来处理请求(不与客户端直接连接)。
SpringBoot使用Netty实现远程调用
maven依赖
<!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <optional>true</optional> </dependency> <!--netty--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.17.Final</version> </dependency>
服务端部分
NettyServer.java:服务启动监听器
@Slf4j public class NettyServer { public void start() { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8082); //new 一个主线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(1); //new 一个工作线程组 EventLoopGroup workGroup = new NioEventLoopGroup(200); ServerBootstrap bootstrap = new ServerBootstrap() .group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ServerChannelInitializer()) .localAddress(socketAddress) //设置队列大小 .option(ChannelOption.SO_BACKLOG, 1024) // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文 .childOption(ChannelOption.SO_KEEPALIVE, true); //绑定端口,开始接收进来的连接 try { ChannelFuture future = bootstrap.bind(socketAddress).sync(); log.info("服务器启动开始监听端口: {}", socketAddress.getPort()); future.channel().closeFuture().sync(); } catch (InterruptedException e) { log.error("服务器开启失败", e); } finally { //关闭主线程组 bossGroup.shutdownGracefully(); //关闭工作线程组 workGroup.shutdownGracefully(); } } }