Netty 初识
Netty 是什么
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.
官网介绍是:
Netty是一个NIO客户端服务器框架,可以快速、轻松地开发网络应用程序,如协议服务器和客户端。它极大地简化和简化了网络编程,如TCP和UDP套接字服务器。
介绍中提到了NIO这个关键字,英文全称是Non blocking IO,非阻塞IO。学习过Java Socket编程的人应该会了解到阻塞IO。
Linux 网络I/O 模型简介
阻塞I/O
image.png非阻塞I/O
image.png轮询检查状态
I/O 复用
Linux 提供select/poll,进程将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样select/poll 可以帮我们侦测多个fd是否处于就绪状态。select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。Linux提供了一个epoll系统调用,epoll使用基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。
image.pngselect/poll 缺点
- 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
- 每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
- select支持的文件描述符数量太少了,默认是1024。
epoll
- 内核与用户空间共享一块内存。
- 通过回调解决遍历问题。
- fd没有限制,可以支撑10万连接。
信号驱动 I/O
首先开启套接口信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主函数处理数据。
image.png
异步I/O
告知内核启动某个操作,并让内核再整个操作完成后(包含将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是:信号驱动I/O由内核通知我们何时可以开始一个I/O操作;异步I/O通知我们I/O操作何时已完成。
image.png
Netty 的优势
quick and easy
Design
- Unified API for various transport types-blocking and non-blocking socket
- Flexible to use
- Simple but powerful thread-model
- True connectionless datagram socket support
- Chaining of logics to make reuse easy
Easy to Use
- Well-documented Javadoc and plenty of examples provided
- No additional dependencies except JDK 1.6 (or above). Some features are supported only in Java 1.7 and above. Other features may have other dependencies, but these are optional
Perfermance
- Better throughput; lower latency than core Java APIs
- Less resource consumption because of pooling and reuse
- Minimized unnecessary memory copy
Robobustness
- No more OutOfMemoryError due to fast, slow, or overloaded connection.
- No more unfair read/write ratio often found in a NIO application in high-speed
Security
- Complete SSL/TSL And Start TLS support
- Runs in a restricted environment such as Applet or OSGI
Community
- Release early,release often
- Active
Netty Demo
maven dependency
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.32.Final</version>
</dependency>
</dependencies>
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;
import java.net.InetSocketAddress;
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
// when a connection is accepted,which create a child channel.
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch){
// channelPipeline hold all off the different ChannelHandlers
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind().sync();
System.out.println(EchoServer.class.getName() +" started and listen on " + f.channel().localAddress());
// sync() method will cause this to block until the server is bound
f.channel().closeFuture().sync();
}catch(Exception e){
e.getMessage();
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
new EchoServer(9999).start();
}
}
EchoServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
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) {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
// Flush all previous written messages (that are pending) to the remote peer, and close the channel after the operation is complete.
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
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 final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host,port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch){
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = bootstrap.connect().sync();
f.channel().closeFuture().sync();
}catch(Exception e){
e.getMessage();
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
EchoClient echoClient = new EchoClient("127.0.0.1",9999);
echoClient.start();
}
}
EchoClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) {
// must Flush
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
}
@Override
public void channelRead0(ChannelHandlerContext ctx,
ByteBuf in) {
System.out.println("Client received: " + ByteBufUtil
.hexDump(in.readBytes(in.readableBytes())));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
Server console
com.daxiyan.study.netty.chapter2.EchoServer started and listen on /0:0:0:0:0:0:0:0:9999
Server received: Netty rocks!
Client console
Client received: 4e6574747920726f636b7321