Netty线程模型以及Reactor模式
Reactor设计模式:基于事件驱动的设计模式,在事件驱动的应用中,将一个或者多个客户的服务请求分离和调度。
Reactor单线程模型:一个NIO线程用来处理所有请求,队列过大,等待时间过长,导致大量的超时,而且万一这个线程挂了,整个服务器就崩了。所以这个模型使用的比较少。
Reactor多线程模型:一个Acceptor线程,一组NIO线程,一个Acceptor线程就是用来查看哪些客户端需要处理,然后分配给NIO线程进行处理。但是当Acceptor线程需要做复杂操作的时候也会有并发问题,比如需要让Acceptor线程进行认证操作。
Reactor主从线程模型:Acceptor不只是一个线程,而是一组线程,IO线程也是一组线程,两个线程池去处理接入连接和处理IO请求。Netty推荐使用的线程模型。
Netty案例 搭建Echo服务
什么是Echo服务:就是回显服务,可以用于调试和检测的服务,来检测线程模型的并发量。
服务端代码
package com.huahuo.netty.echo.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServer {
private final int port;
private EchoServer(int port) {
this.port = port;
}
public void run() {
//前台巡逻线程组
EventLoopGroup frontGroup = new NioEventLoopGroup();
//工作线程组
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(frontGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoServerHandler());
}
});
System.out.println("Echo 服务器启动中...");
try {
//绑定端口,同步等待
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
//优雅退出
workerGroup.shutdownGracefully();
frontGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
int port = 8080;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new EchoServer(port).run();
}
}
package com.huahuo.netty.echo.server;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf data = (ByteBuf) msg;
System.out.println("服务端接收到数据:"+data.toString(CharsetUtil.UTF_8));
// 创建新的 ByteBuf,用来保存处理后的数据
ByteBuf response = Unpooled.copiedBuffer("处理后的数据:" + data.toString(CharsetUtil.UTF_8), CharsetUtil.UTF_8);
// 写回处理后的数据
ctx.writeAndFlush(response);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("EchoServerHandler channelReadComplete");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端代码
package com.huahuo.netty.echo.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
public class EchoClient {
private String host;
private int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
//连接到服务器,connect异步连接,同步等待连接成功
ChannelFuture channelFuture = bootstrap.connect().sync();
//阻塞,直到客户端关闭。
channelFuture.channel().closeFuture().sync();
} finally {
//优雅退出
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoClient("127.0.0.1", 8080).start();
}
}
package com.huahuo.netty.echo.client;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received:" + byteBuf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Active");
ctx.writeAndFlush(Unpooled.copiedBuffer("hello fireworks", CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("client read complete");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Echo服务流程分析
- EventLoop 和 EventLoopGroup
这就是相当于线程和线程组的关系。
- BootStrap
就是启动类,会自带一些配置
- Channel
就是一个到实体的连接的桥梁,生命周期,去配置生命周期会执行的函数
- ChannelHandler和ChannelPipline
就是用来配置Channel的处理类,pipline就是流水线
EventLoop和EventLoopGroup
EventLoop相当于一个线程,1个EventLoop可以服务多个Channel,1个Channel只有一个EventLoop,可以有多个EventLoop来优化性能,也就是EventLoopGroup。 EventLoopGroup负责分配EventLoop到新创建Channel。里面包含很多EventLoop。EventLoop会维护一个Selector模型,默认创建的线程数量是核数*2。
BootStrap
- group:把两个线程池绑定进来,可以传入单个线程池,如果传入单个线程池,而且这个线程池数量指定为1,那就是单线程的reactor模型。如果不为1那就是多线程模型,传入两个线程池那就是主从线程模型了。
-
channel:用来设置IO通道的类型,一般设置为NIO
-
option:用来设置TCP连接的一些参数
-
SO_BACKLOG:存放已完成三次握手的请求的等待队列的长度
- 半连接队列 tcp_max_syn_backlog
- 全连接队列 net.core.somaxconn
-
TCP_NODELAY:如果要求实时性高,那就设置成true,他就关闭Nagle算法,就不会延迟发送了。
-
-
handler:用来设置处理器的
Channel
Channel:相当于一个通道
ChannelHandler:负责Channel的逻辑处理
ChannelPipeline:负责管理ChannelHandler的有序容器
Channel状态出现变化就会触发对应的事件