<h2>服务端</h2>
以下是Netty官方的一个Echo服务示例:
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
上面代码创建Server端服务的流程如下:
- 创建ServerBootstrap实例
- 设置EventLoopGroup
- 设置创建的Channel类型
- option配置属性
- 设置Handler,处理请求
- 设置ChildHandler,处理对应channel的请求
- 通过bind创建Chnnel并绑定,启动服务
服务端创建时序图
服务端重要组件
ServerBootstrap
ServerBootstrap是Netty服务端的启动辅助类,提供一系列的方法用于设置服务端的参数和配置,简化开发。(衍生一点:ServerBootstrap的构造方法是无参的,因为参数太多所以采用了Builder模式)
继承自AbstractBootstrap,核心属性有childGroup和childHandler。
- childGroup:负责调度和执行客户端的接入、网络读写事件的处理、用户自定义任务和定时任务的执行
- childHandler:自定义的业务Handler
AbstractBootstrap核心属性有group和handler。
- group:处理客户端的链接请求,并转交给childGroup(读取的数据是穿件的NioSocketChannel)
Reactor线程池
Netty的Reactor线程池是EventLoopGroup,实际上是一个EventLoop的数组。
EventLoop的职责是处理所有注册到本线程多路复用器Selector上的Channel,Selector的轮训操作有EventLoop线程run方法驱动。
另外用户自定义的Task和定时任务Task也由统一的EventLoop负责处理。
Channel
作为Nio服务,需要创建ServerSocketChannel,Netty对原生NIO类库做了封装,对应实现类为NioServerSocketChannel。
用户只需要制定Channel的实现类型,内部通过反射机制来创建对应的实例。
因为只在监听端口时创建,所以反射的性能影响并不大。
ChannelPipeline
ChannelPipeline是网络事件处理的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转。
ChannelHandler
ChannelHandler是提供给用户定制和扩展的关键接口,包括编解码,业务处理等都是通过ChannelHandler进行的。
Selector
Selector轮训操作由NioEventLoop调度和执行,选择准备就绪的Channel集合。
NioServerSocketChannel
绑定Server端地址的Server,读取客户端的链接请求(只有一个,在bind时创建)。
NioSocketChannel
和客户端之间的链接。
服务端线程模型
- mainReactor:parentGroup
- subReactor:childGroup
- ThreadPool:如果没指定,使用childGroup执行,如果指定了则是业务线程(执行业务Handler的线程)
Handler模型
Server启动的关键流程
- bind操作创建了NioServerSocketChannel并注册到NioEventLoop中(parent group中会有一个线程执行selector的轮训操作)
Server端的请求接入流程
- NioEventLoop轮训到到就绪时间后,调用Unsafe.read(NioMessageUnsafe实现)创建NioSocketChannel并传递到ServerBootstrap的ServerBootstrapAcceptor的channelRead方法中。
- ServerBootstrapAcceptor的channelRead方法将NioSocketChannel注册到childGroup的Selector上(实现代码是AbstractNioSocket的doRegister;之后NioSocketChannel的事件就由child group处理,这点和NioServerSocketChannel的注册、处理是一样的;和上面的线程模型也是呼应的:两个reactor)。
<h2>客户端</h2>
public static void main(String[] args) throws Exception {
// Configure SSL.git
final SslContext sslCtx;
if (SSL) {
sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} else {
sslCtx = null;
}
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect(HOST, PORT).sync();
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
}
创建客户端的大致流程:
- 创建Bootstrap实例
- 设置EventLoop
- 指定Channel类型
- option配置
- 指定Handler
- connect
客户端创建时序图
客户端和服务端的模式基本一致,由线程轮训Selector的事件,由Pipeline进行事件传递,EventLoop进行处理。