一、 前言
Netty是一种可以轻松快速的开发类似协议服务器和客户端网络应用程序的NIO客户端服务器框架,它大大简化了TCP或者UDP服务器的网络编程,但是你仍然可以访问和使用底层的APIs,因为Netty提供了高层的抽象。
Netty的简易和快速开发并不意味着由它开发的程序将失去可维护性或者存在性能问题,Netty是被精心设计的,它的设计参考了许多协议的实现,比如FTP,SMTP,HTTP和各种二进制和基于文本的传统协议,因此 Netty成功的实现了兼顾快速开发,性能,稳定性,灵活性为一体,不需要为了考虑一方面原因而妥协其他方面。
二、 Netty特性
首先借用Netty官方的一个框架图,展示了Netty支持的传输和协议
2.1 设计(Design)
不同传输类型统一了API-传输类型:阻塞和非阻塞套接字
灵活和可扩展的事件模型
高度可定制的线程模型 - 单线程,一个或多个线程池
真正的无连接数据报套接字的支持
2.2 易使用(Ease of use)
完好的Javadoc文档和demo示例
除了JDK外不需要其他依赖.一些特性例如http/2可能有其它的依赖,但是这些都是可选的.
2.3 性能(Performance)
更大的吞吐量,更低的延迟
更小的资源消耗
最小化不必要的内存拷贝(零拷贝实现)
2.4 安全(Security)
完整的SSL/TLS和StartTLS支持
2.5 社区(Community)
比较活跃,经常会发布新版本
三、示例代码
下面从netty官方的一个echo的客户端服务器交互的例子讲解。
3.1 EchoClient
public final class EchoClient { static final boolean SSL = System.getProperty("ssl") != null; static final String HOST = System.getProperty("host", "127.0.0.1"); static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); 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();
}
}
}
EchoClientHandler
public class EchoClientHandler extends ChannelInboundHandlerAdapter { private final ByteBuf firstMessage; /**
* Creates a client-side handler.
*/
public EchoClientHandler() {
firstMessage = Unpooled.buffer(EchoClient.SIZE); for (int i = 0; i < firstMessage.capacity(); i ++) {
firstMessage.writeByte((byte) i);
}
} @Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage);
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg);
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
3.2 EchoServer
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();
}
}
}
@Sharablepublic class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg);
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
四、 原理分析
4.1 首先看下Netty的一部分类图
可知有两种BootStrap,客户端的Bootstrap,服务器端是ServerBootstrap,并且他们都是继承自AbstractBootstrap。在客户端和服务端程序一开始就是先实例化一个自己的Bootstrap。
并且从类图知道客户端只有一个EventLoopGroup就是继承自AbstractBootstrap的用途是管理与服务端的通信。而服务器端除了继承的了ServerBootstrap中的(boss)还维护一个子EventLoopGroup(worker),服务器端之所以维护两个是为了高性能,其中EventLoopGroup(boss)主要用来接受客户端的链接请求,请求到来后在把请求分发给EventLoopGroup(worker)来处理。
从类图知道一个EventLoopGroup里面多个EventLoop,每个channel与一个EventLoop关联,每个EventLoop有一个Selector, bossGroup用selector处理accept和任务, workerGroup则用selector处理read,write和任务等。
其中channel分两类ServerChannel和channel, ServerChannel对应着监听套接字(ServerSocketChannel)对应NIO来说是NioServerSocketChannel, 而channel对应着一个网络连接。
Handler和ChannelPipeline,如图每个channel里面都有一个ChannelPipeline,它是一个双向链表,里面存放了好多Handler用来处理数据流,类似于tomcat中的Filter链,每个handler可以比作一个filter.与filter中FilterConfig对应的是AbstractChannelHandlerContext。
* I/O Request
* via {@link Channel} or
* {@link ChannelHandlerContext}
* |
* +---------------------------------------------------+---------------+
* | ChannelPipeline | |
* | \|/ |
* | +---------------------+ +-----------+----------+ |
* | | Inbound Handler N | | Outbound Handler 1 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* | | \|/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler N-1 | | Outbound Handler 2 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ . |
* | . . |
* | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
* | [ method call] [method call] |
* | . . |
* | . \|/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler 2 | | Outbound Handler M-1 | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* | | \|/ |
* | +----------+----------+ +-----------+----------+ |
* | | Inbound Handler 1 | | Outbound Handler M | |
* | +----------+----------+ +-----------+----------+ |
* | /|\ | |
* +---------------+-----------------------------------+---------------+
* | \|/
* +---------------+-----------------------------------+---------------+
* | | | |
* | [ Socket.read() ] [ Socket.write() ] |
* | |
* | Netty Internal I/O Threads (Transport Implementation) |
* +-------------------------------------------------------------------+
另外netty中每个channel都有一个unsafe。
对应NioSocketChannel的unsafe是NioByteUnsafe
对应NioServerSocketChannel的unsafe是NioMessageUnsafe