文章目录
- Netty初步认识
- Netty简单介绍
- 为什么jdk已经实现了NIO还要用netty框架:
- Reactor 线程模型
- Reactor 单线程模型
- Netty线程模型
- Netty 简单实现
- EchoClient端实现:
- ClientHandler实现
- EchoServer实现
- ServerHandler实现:
Netty初步认识
Netty简单介绍
Netty 是一个基于 Java NIO 的异步事件驱动的网络应用框架,用于快速开发高性能、可维护的网络服务器和客户端。它提供了简单易用的 API,使得网络编程更加轻松。Netty 的设计重点在于提供高性能、高可靠性和灵活性,适用于各种网络应用场景,包括但不限于服务器通信、分布式系统、即时通讯等。
Netty 的主要特点包括:
1.异步事件驱动:Netty 使用基于事件驱动的模型,通过回调机制实现非阻塞式的网络通信,提高了系统的并发性能。
高性能:Netty 的内部实现对网络 I/O 进行了优化,采用了零拷贝技术和基于内存池的内存管理,以提升网络通信的效率和吞吐量。
2.组件丰富:Netty 提供了丰富的组件和工具,包括 Channel、EventLoop、Codec、Handler 等,方便开发者构建各种复杂的网络应用。
3.跨平台性:Netty 的设计考虑了跨平台性,可以在不同的操作系统上运行,并提供了对不同网络协议的支持,如 TCP、UDP、HTTP 等。
4.易用性:Netty 提供了简单易用的 API,使得开发者能够快速构建高性能的网络应用,同时也提供了丰富的文档和示例,方便开发者学习和使用。
传统的socketl连接请求:
NIO架构图:
NIO:是同步非阻塞的,服务器实现模式为 一个线程处理多个连接。服务端只会创建一个线程负责管理Selector(多路复用器),Selector(多路复用器)不断的轮询注册其上的Channel(通道)中的 I/O 事件,并将监听到的事件进行相应的处理。每个客户端与服务端建立连接时会创建一个 SocketChannel 通道,通过 SocketChannel 进行数据交互。
为什么jdk已经实现了NIO还要用netty框架:
原生 NIO 存在问题:
1.NIO 的类库和 API 繁杂
2.需要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,必须对多线程和网络编程非常熟悉, 才能编写出高质量的 NIO 程序
3.开发工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常 流的处理等等处理起来难度会比较大。
4.JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。直到 JDK 1.7 版本该问题仍旧存在,没有被根本解决。
Netty的优点:
Netty 对 JDK 自带的 NIO 的 API 进行了封装,解决了上述问题。
1.设计优雅:适用于各种传输类型的统一 API 阻塞和非阻塞 Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型 - 单线程,一个或多个线程池.
2.使用方便:详细记录的 Javadoc,用户指南和示例;没有其他依赖项,JDK 5(Netty 3.x)或 6(Netty 4.x)就足够了。
3.高性能、吞吐量更高:延迟更低;减少资源消耗;最小化不必要的内存复制。
4.安全:完整的 SSL/TLS 和 StartTLS 支持。
5.社区活跃、不断更新:社区活跃,版本迭代周期短,发现的 Bug 可以被及时修复,同时更多的新功能会被加入
Reactor 线程模型
Reactor 单线程模型
Reactor 单线程模型是一种简单的事件驱动模型,适用于处理低负载的网络应用。在 Reactor 单线程模型中,所有的 I/O 操作都由一个 Reactor 线程来处理。主要特点包括:
1.单线程:整个应用只有一个 Reactor 线程,负责监听所有的事件,并且串行地处理这些事件。
2.事件循环:Reactor 线程采用事件循环(Event Loop)的方式,不断地轮询注册在其上的 I/O 事件,当有事件发生时,React 线程会调用相应的处理方法进行处理。
3.非阻塞 I/O:在 Reactor 单线程模型中,所有的 I/O 操作都是非阻塞的,即当有事件发生时,Reactor 线程会立即处理,而不会阻塞等待。
4.简单性:由于整个应用只有一个线程,因此编程模型比较简单,不需要考虑多线程并发的复杂性,适合于初学者或者对性能要求不高的场景。
5.适用性:Reactor 单线程模型适用于处理连接数较少、业务逻辑简单、并发负载较低的网络应用场景。
尽管 Reactor 单线程模型简单,但其并发能力有限,容易成为性能瓶颈,因此在高并发、高负载的场景下,通常会选择使用多线程模型或者线程池来提高系统的性能和可扩展性。
Netty线程模型
Boss Group相当于主Reactor、而Worker Group 相当于从Reactor 。流程还是相对简单;Boss 接收处理请求通道事件、转发到Work Group去处理、通过ServerBootstrap组装启动、再通过Pipeline去处理Handler 处理器(一个Pipeline可自由组装多个Handler 、每个通道都会一一对应一个Channel连接通道)。
Netty 简单实现
EchoClient端实现:
public final class EchoClient {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.gitfinal SslContext sslCtx = ServerUtil.buildSslContext();// 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>() {@Overridepublic 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();}}
}
ClientHandler实现
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);}}@Overridepublic void channelActive(ChannelHandlerContext ctx) {ctx.writeAndFlush(firstMessage);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ctx.write(msg);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// Close the connection when an exception is raised.cause.printStackTrace();ctx.close();}
}
EchoServer实现
public final class EchoServer {static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));public static void main(String[] args) throws Exception {// Configure SSL.final SslContext sslCtx = ServerUtil.buildSslContext();// Configure the server.EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();final EchoServerHandler serverHandler = new EchoServerHandler();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>() {@Overridepublic 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(serverHandler);}});// 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();}}
}
ServerHandler实现:
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ctx.write(msg);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// Close the connection when an exception is raised.cause.printStackTrace();ctx.close();}
}