提前准备好如下代码, 从服务端构建着手,深入分析Netty服务端的启动过程。
public class NettyBasicServerExample {
public void bind(int port){
//netty的服务端编程要从EventLoopGroup开始,
// 我们要创建两个EventLoopGroup,
// 一个是boss专门用来接收连接,可以理解为处理accept事件,
// 另一个是worker,可以关注除了accept之外的其它事件,处理子任务。
//上面注意,boss线程一般设置一个线程,设置多个也只会用到一个,而且多个目前没有应用场景,
// worker线程通常要根据服务器调优,如果不写默认就是cpu的两倍。
EventLoopGroup bossGroup=new NioEventLoopGroup();
EventLoopGroup workerGroup=new NioEventLoopGroup();
try {
//服务端要启动,需要创建ServerBootStrap,
// 在这里面netty把nio的模板式的代码都给封装好了
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
//配置Server的通道,相当于NIO中的ServerSocketChannel
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)) //设置ServerSocketChannel对应的Handler
//childHandler表示给worker那些线程配置了一个处理器,
// 这个就是上面NIO中说的,把处理业务的具体逻辑抽象出来,放到Handler里面
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new NormalInBoundHandler("NormalInBoundA",false))
.addLast(new NormalInBoundHandler("NormalInBoundB",false))
.addLast(new NormalInBoundHandler("NormalInBoundC",true));
socketChannel.pipeline()
.addLast(new NormalOutBoundHandler("NormalOutBoundA"))
.addLast(new NormalOutBoundHandler("NormalOutBoundB"))
.addLast(new NormalOutBoundHandler("NormalOutBoundC"))
.addLast(new ExceptionHandler());
}
});
//绑定端口并同步等待客户端连接
ChannelFuture channelFuture=bootstrap.bind(port).sync();
System.out.println("Netty Server Started,Listening on :"+port);
//等待服务端监听端口关闭
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放线程资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new NettyBasicServerExample().bind(8080);
}
}
public class NormalInBoundHandler extends ChannelInboundHandlerAdapter {
private final String name;
private final boolean flush;
public NormalInBoundHandler(String name, boolean flush) {
this.name = name;
this.flush = flush;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("InboundHandler:"+name);
if(flush){
ctx.channel().writeAndFlush(msg);
}else {
throw new RuntimeException("InBoundHandler:"+name);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("InboundHandlerException:"+name);
super.exceptionCaught(ctx, cause);
}
}
public class NormalOutBoundHandler extends ChannelOutboundHandlerAdapter {
private final String name;
public NormalOutBoundHandler(String name) {
this.name = name;
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutBoundHandler:"+name);
super.write(ctx, msg, promise);
}
}
在服务端启动之前,需要配置ServerBootstrap的相关参数,这一步大概分为以下几个步骤
- 配置EventLoopGroup线程组
- 配置Channel类型
- 设置ServerSocketChannel对应的Handler
- 设置网络监听的端口
- 设置SocketChannel对应的Handler
- 配置Channel参数
Netty会把我们配置的这些信息组装,发布服务监听。
ServerBootstrap参数配置过程#
下面这段代码是我们配置ServerBootStrap相关参数,这个过程比较简单,就是把配置的参数值保存到ServerBootstrap定义的成员变量中就可以了。
bootstrap.group(bossGroup, workerGroup)
//配置Server的通道,相当于NIO中的ServerSocketChannel
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)) //设置ServerSocketChannel对应的Handler
//childHandler表示给worker那些线程配置了一个处理器,
// 这个就是上面NIO中说的,把处理业务的具体逻辑抽象出来,放到Handler里面
.childHandler(new ChannelInitializer<SocketChannel>() {
});
我们来看一下ServerBootstrap的类关系图以及属性定义
ServerBootstrap类关系图#
如图8-1所示,表示ServerBootstrap的类关系图。
- AbstractBootstrap,定义了一个抽象类,作为抽象类,一定是抽离了Bootstrap相关的抽象逻辑,所以很显然可以推断出Bootstrap应该也继承了AbstractBootstrap
- ServerBootstrap,服务端的启动类,
- ServerBootstrapAcceptor,继承了ChannelInboundHandlerAdapter,所以本身就是一个Handler,当服务端启动后,客户端连接上来时,会先进入到ServerBootstrapAccepter。
图8-1 ServerBootstrap类关系图
AbstractBootstrap属性定义#
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
@SuppressWarnings("unchecked")
private static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
@SuppressWarnings("unchecked")
private static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
/**
* 这里的EventLoopGroup 作为服务端 Acceptor 线程,负责处理客户端的请求接入
* 作为客户端 Connector 线程,负责注册监听连接操作位,用于判断异步连接结果。
*/
volatile EventLoopGroup group; //
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory; //channel工厂,很明显应该是用来制造对应Channel的
private volatile SocketAddress localAddress; //SocketAddress用来绑定一个服务端地址
// The order in which ChannelOptions are applied is important they may depend on each other for validation
// purposes.
/**
* ChannelOption 可以添加Channer 添加一些配置信息
*/
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
/**
* ChannelHandler 是具体怎么处理Channer 的IO事件。
*/