Netty介绍
官网:Netty: 首页
Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
Netty是NIO的客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它大大简化和简化了网络编程,例如 TCP 和 UDP 套接字服务器。
“快速和简单”并不意味着生成的应用程序会遇到可维护性或性能问题。Netty 经过精心设计,积累了从许多协议(如 FTP、SMTP、HTTP 以及各种二进制和基于文本的遗留协议)的实施中获得的经验。因此,Netty 成功地找到了一种在不妥协的情况下实现易开发性、性能、稳定性和灵活性的方法。
Netty为什么使用NIO而不是AIO
Netty不看重Windows上的使用,在Linux系统上,AIO的底层实现仍使用EPOLL,没有很好实现AIO,因此在性能上没有明显的优势,而且被JDK封装了一层不容易深度优化。
AIO还有个缺点是接收数据需要预先分配缓存, 而不是NIO那种需要接收时才需要分配缓存, 所以对连接数量非常大但流量小的情况, 内存浪费很多。而且Linux上AIO不够成熟,处理回调结果速度跟不上处理需求。
Not faster than NIO (epoll) on unix systems (which is true)
There is no daragram suppport
Unnecessary threading model (too much abstraction without usage)
特点
设计
-
适用于各种传输类型的统一 API - 阻塞和非阻塞套接字
-
基于灵活且可扩展的事件模型,可以清晰地分离关注点
-
高度可定制的线程模型 - 单线程、一个或多个线程池,如 SEDA
-
真正的无连接数据报套接字支持(自 3.1 起)
易用性
-
有据可查的 Javadoc、用户指南和示例
-
没有其他依赖项,JDK 5 (Netty 3.x) 或 6 (Netty 4.x) 就足够了
-
注意:某些组件(如 HTTP/2)可能有更多要求。 有关详细信息,请参阅要求页面。
-
性能
-
更高的吞吐量,更低的延迟
-
减少资源消耗
-
最大限度减少不必要的内存拷贝
安全
-
完整的 SSL/TLS 和 StartTLS 支持
社区活跃
-
早发布,经常发布
-
作者自 2003 年以来一直在编写类似的框架,他仍然觉得您的反馈很宝贵!
Netty使用场景
-
互联网
在分布式系统中,各节点之间通常需要进行远程服务调用,而高性能的RPC框架是其中的关键组件。Netty作为一种异步高性能的通信框架,常被用作RPC框架的底层通信组件。
一些典型应用包括:
-
Dubbo:这是一个流行的分布式服务框架,主要用于RPC通信。Dubbo框架中的Dubbo协议负责节点间的通信,而Netty则作为其基础通信组件,为Dubbo提供异步和高性能的传输能力。
-
RocketMQ:这是阿里巴巴开发的分布式消息队列,其底层通信部分也使用了Netty。Netty在RocketMQ中充当基础通信组件,确保高性能的数据传输和节点间的内部通信。
这些应用展现了Netty在分布式系统中作为通信基础的重要性,它提供了异步、可扩展和高性能的通信能力,广泛应用于RPC框架和分布式消息系统中。
-
-
游戏
游戏行业中,Java语言在手游服务端和大型网络游戏中越来越受欢迎。作为高性能通信的基础组件,Netty提供了TCP/UDP和HTTP协议栈,成为支持游戏服务器和网络通信的重要工具。
-
大数据
在大数据领域,Hadoop的高性能通信和序列化组件Avro的RPC框架默认使用Netty来进行节点间的通信。Avro的Netty Service是基于Netty框架经过二次封装实现的,提供了可靠且高效的跨节点通信能力。
netty相关项目:Netty.docs: 相关项目
Netty使用示例
maven依赖:
官方推荐使用4.x
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
服务端代码:
public class NettyServer {
public static void main(String[] args) throws Exception {
//创建两个线程组bossGroup和workerGroup, 子线程NioEventLoop的个数默认为cpu核数的两倍
// bossGroup只是处理连接请求 ,workerGroup和客户端业务处理
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//创建服务器端的启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
//设置两个线程组
bootstrap.group(bossGroup, workerGroup)
//使用NioServerSocketChannel作为服务器的通道实现
.channel(NioServerSocketChannel.class)
// 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
// 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
//创建通道初始化对象,设置初始化参数
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//对workerGroup的SocketChannel设置处理器
ch.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start-->");
//绑定端口并且同步, 生成了一个ChannelFuture异步对象,通过isDone()等方法可以判断异步事件的执行情况
//启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕
ChannelFuture cf = bootstrap.bind(8989).sync();
//给ChannelFuture 注册监听器,监听关心的事件
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (cf.isSuccess()) {
System.out.println("listen 8989 sucess");
} else {
System.out.println("listen 8989 fail");
}
}
});
//对通道关闭进行监听,closeFuture异步操作,监听通道关闭
//通过sync方法同步等待通道关闭处理完毕,这里会阻塞,等待通道关闭完成
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
/**
* 自定义Handler需要继承netty规定好的某个HandlerAdapter(Handler规范)
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
/**
* 读取客户端发送的数据
*
* @param ctx 上下文对象, 含有通道channel,管道pipeline
* @param msg 就是客户端发送的数据
* @throws Exception
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("服务器读取线程 " + Thread.currentThread().getName());
//Channel channel = ctx.channel();
//本质是双向链接, 出站入站
//ChannelPipeline pipeline = ctx.pipeline();
//将 msg 转成一个 ByteBuf,类似NIO 的 ByteBuffer
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
}
/**
* 数据读取完毕处理方法
*
* @param ctx
* @throws Exception
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer("HelloClient", CharsetUtil.UTF_8);
ctx.writeAndFlush(buf);
}
/**
* 处理异常, 一般是需要关闭通道
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
客户端代码:
public class NettyClient {
public static void main(String[] args) throws Exception {
//客户端需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建客户端启动对象Bootstrap
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
//设置线程组
bootstrap.group(group)
.channel(NioSocketChannel.class)
// 使用 NioSocketChannel 作为客户端的通道实现
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//加入处理器
channel.pipeline().addLast(new NettyClientHandler());
}
});
System.out.println("netty client start");
//启动客户端去连接服务器端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8989).sync();
//对关闭通道进行监听
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
/**
* 当客户端连接服务器完成就会触发该方法
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = Unpooled.copiedBuffer("HelloServer", CharsetUtil.UTF_8);
ctx.writeAndFlush(buf);
}
//当通道有读取事件时会触发,即服务端发送数据给客户端
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("服务端的地址: " + ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Netty框架的目标是将业务逻辑与底层网络编程解耦,使开发者能够专注于业务开发,而无需编写繁杂的NIO网络处理代码。
Netty线程模型
如下图:
Netty框架通过抽象出两组线程池来管理网络操作,即BossGroup和WorkerGroup。以下是Netty的结构和运作方式:
-
BossGroup和WorkerGroup:BossGroup负责接受客户端的连接,WorkerGroup负责网络数据的读写操作。两者都属于
NioEventLoopGroup
类型。 -
NioEventLoopGroup:这是一个事件循环线程组,包含多个事件循环线程。每个事件循环线程被称为
NioEventLoop
。 -
NioEventLoop:每个NioEventLoop都包含一个
Selector
,用于监听注册在其上的SocketChannel
的网络通信事件。 -
BossGroup的操作流程:每个Boss NioEventLoop在内部循环中执行以下三步:
- 处理
accept
事件,与客户端建立连接并生成NioSocketChannel
。 - 将生成的
NioSocketChannel
注册到Worker NioEventLoop上的Selector。 - 执行任务队列中的任务(
runAllTasks
)。
- 处理
-
WorkerGroup的操作流程:每个Worker NioEventLoop在循环中执行以下步骤:
- 轮询自己Selector上的所有
NioSocketChannel
,处理read
和write
事件。 - 根据这些事件处理业务逻辑。
- 运行任务队列中的任务,允许耗时的业务操作在任务队列中异步处理,以避免影响主线的I/O操作。
- 轮询自己Selector上的所有
-
Pipeline和Handler:Worker NioEventLoop在处理
NioSocketChannel
的业务时,使用了Pipeline
(管道)。这个管道中维护了一系列的处理器(Handler),用于处理Channel
中的数据,提供灵活的处理能力。
通过这样的设计,Netty实现了高性能的网络操作,并将业务逻辑与底层网络编码分离,使开发者能够更专注于业务处理而非网络基础设施的编写。