咱们好,我是老三,之前里,咱们评论了Java的三种IO模型,提到了网络通讯结构Netty,它简化和优化了NIO的运用,这期,咱们正式开端走近Netty。
为什么要用Netty?
首要当然是NIO的运用,自身比较复杂,而且还存在一些问题。
除此之外,如果在项目的开发中,要完成安稳的网络通讯,就得考虑网络的闪断、客户端的重复接入、客户端的安全认证、音讯的编解码、半包读写……
所以,巧了,恰好有这么一个成熟安稳、功能强大、开箱即用的网络结构摆在咱们面前,相比较Java NIO,Netty愈加出色:
- 易用性: Netty 在 NIO 基础上进行了更高层次的封装,屏蔽了 NIO 的复杂性,大大降低了开发难度;Netty 供给了许多开箱即用的工具,例如常用的行解码器、长度域解码器等,不需要自己再完成。
- 安稳性: Netty 愈加牢靠安稳,修正和完善了 JDK NIO 较多已知问题,例如臭名昭著的 select 空转导致 CPU 耗费 100%,TCP 断线重连,keep-alive 检测等问题。
- 可扩展性: Netty 的的可扩展性做的十分好,比如支持可定制化的线程模型。
咱们有什么理由回绝这么一款优异的网络通讯结构呢?代码怎样写不是写喽!
初识Netty
什么是Netty?
Netty官方是这么界说Netty的:
Netty 是一个异步事情驱动的网络应用程序结构,用于快速开发可保护的高功能协议服务器和客户端。
-
Netty是一个开源的、单线程模型的 Java 网络编程结构。
-
Netty根据 NIO ,被广泛应用于各种网络应用程序开发。
-
Netty支持多种协议,包含但不限于 HTTP、WebSocket、TCP、UDP 和 SSL/TLS 协议等。
-
Netty 对错阻塞的,事情驱动的结构。
-
Netty具有高功能、可扩展和易于运用的长处。
Netty的现状?
Netty 由 JBoss 社区开发保护的,它的社区相对比较活泼:
- github.com/netty/netty…
- netty.io/:官方网站,供给了比较…
官方目前最新的版别是5.x,,可是很不幸,现已被社区放弃开发保护,归于抛弃版别,最新的安稳版别是4.x 。
一般运用,引荐4.x,Netty 4.x对3.x不做兼容,咱们后续的学习也根据Netty 4.x版别。
谁在用Netty?
作为最盛行的网络通讯结构,大量的公司挑选它作为底层网络通讯结构,包含不限于:
咱们可能自己没有直接用过Netty,但其实了解的许多开源中间件,都用到了Netty,比如:
- 服务管理:Apache Dubbo、gRPC。
- 大数据:Hbase、Spark、Flink、Storm。
- 搜索引擎:Elasticsearch。
- 音讯行列:RocketMQ、ActiveMQ。
用到Netty的优异产品十分多,咱们感兴趣能够看看:netty.io/wiki/relate…
从”Hello World”开端
气氛衬托到这,不写个Demo也过不去,仍是从”Hello World”开端,咱们领会一下Netty的风貌。
- 创立一个Maven项目:这个就不必多说了吧
- 导入依靠:咱们直接用4.x最新的版别
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.92.Final</version>
</dependency>
-
编写代码:那么咱们就开端编写这个Demo的服务器和客户端相关代码
-
NettyServer
:根据Netty的客户端/** * <p>Date: 2023/5/14 10:29</p> * <p>Author: fighter3</p> * <p>Description: Netty服务端Demo</p> */ public class NettyServer{ // 服务器监听的端口号 private int port; public NettyServer(int port) { this.port = port; } /** * 发动Netty服务器 * @throws InterruptedException */ public void run() throws InterruptedException { // 创立boss线程组和worker线程组 // bossGroup 用于监听客户端的衔接恳求,将衔接恳求发送给 workerGroup 进行处理 NioEventLoopGroup bossGroup = new NioEventLoopGroup(); // workerGroup 用于处理客户端衔接的数据读写 NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 创立 ServerBootstrap 目标,用于发动 Netty 服务器 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 绑定线程池事情组 serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 通道初始化回调函数,在发动的时分能够主动调用 .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 增加音讯处理器 pipeline.addLast(new NettyServerHandler()); } }); // 绑定端口,开端接纳客户端恳求 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); System.out.println("Netty服务器监听端口:"+port); // 等候服务端监听端口关闭 channelFuture.channel().closeFuture().sync(); } finally { //开释线程组资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { // 创立服务器目标,监听端口号为 8888 NettyServer server = new NettyServer(8888); System.out.println("============Netty服务器发动...============="); // 发动服务器 server.run(); System.out.println("============Netty服务器停止...============="); } }
-
NettyServerHandler
:服务器的音讯处理器,用于处理各种事情/** * <p>Date: 2023/5/14 10:30</p> * <p>Author: fighter3</p> * <p>Description: Netty服务器音讯处理器</p> */ public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 当客户端上线的时分会触发这个办法 * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { String message="你好,靓仔!"; ByteBuf hello = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8); // 发送音讯 ctx.writeAndFlush(hello); } /** *当 Channel 中有来自客户端的数据时就会触发这个办法 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; System.out.println("客户端发来的音讯:" + buf.toString(CharsetUtil.UTF_8)); // 接纳音讯并打印输出 } /** * 当有反常时触发这个办法 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
-
-
NettyClient
:运用Netty的客户端,通过ip和端口衔接服务端/** * <p>Date: 2023/5/14 10:32</p> * <p>Author: fighter3</p> * <p>Description: Netty客户端Demo</p> */ public class NettyClient { // 服务器 IP private String host; // 服务器监听的端口号 private int port; public NettyClient(String host, int port) { this.host = host; this.port = port; } /** * 发动 Netty 客户端 */ public void run() throws InterruptedException { // 创立事情循环组 NioEventLoopGroup group = new NioEventLoopGroup(); try { // 创立 Bootstrap 目标 Bootstrap bootstrap = new Bootstrap(); // 装备 Bootstrap 目标 // 设置线程组 bootstrap.group(group) // 设置客户端通讯的通道类型为NIO类型 .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { // 通道初始化回调函数,在发动的时分能够主动调用 @Override public void initChannel(SocketChannel ch) throws Exception { // 增加音讯处理器 ch.pipeline().addLast(new NettyClientHandler()); } }); // 衔接服务器,异步等候衔接成功 ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); System.out.println("===========Netty客户端衔接服务端========="); // 等候客户端衔接关闭 channelFuture.channel().closeFuture().sync(); } finally { //开释资源 group.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { // 创立客户端目标,并衔接到服务器 NettyClient client = new NettyClient("127.0.0.1", 8888); // 发动客户端,开端发送音讯 client.run(); } }
-
NettyClientHandler:Netty客户端处理器,用于处各种事情
/** * <p>Date: 2023/5/14 10:33</p> * <p>Author: fighter3</p> * <p>Description: Netty客户端处理器</p> */ public class NettyClientHandler extends ChannelInboundHandlerAdapter { /** * 当 Channel 准备就绪时就会触发这个办法 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { String message="大佬,带带我!"; ByteBuf hello = Unpooled.copiedBuffer(message, CharsetUtil.UTF_8); // 发送音讯 ctx.writeAndFlush(hello); } /** * 当 Channel 中有来自服务器的数据时就会触发这个办法 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; System.out.println("服务端发来的音讯:" + buf.toString(CharsetUtil.UTF_8)); // 接纳音讯并打印输出 } /** * 产生反常就会触发这个办法 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
-
运转一下:先发动NettyServer,再发动NettyClient,看下运转结果
============Netty服务器发动...============= Netty服务器监听端口:8888 客户端发来的音讯:大佬,带带我!
===========Netty客户端衔接服务端========= 服务端发来的音讯:你好,靓仔!
好了,一个简略的Netty入门Demo就写完了,Netty是一个双工通讯的网络结构,能够看到,服务端和客户端,流程基本上共同,首要包含这么几个过程:
- 创立事情循环组和相关目标,用于监听和处理网络事情;
- 装备 Netty 服务器或客户端的发动参数,包含线程组、通道类型、TCP 参数等;
- 给服务器或客户端的 ChannelPipeline 增加各种 ChannelHandler,用于处理不同的网络事情;
- 绑定端口发动服务器或衔接服务器;
- 等候服务器或客户端衔接关闭,开释相关资源。
虽然这个Demo比较简略,但其实现已用到了Netty里几个比较要害的组件:
-
ByteBuf
:Netty 的字节容器,类似于 Java 的 ByteBuffer,可是供给了愈加强大、简便且安全的 API,用于在网络中传递二进制数据; -
EventLoopGroup
:Netty 的事情循环组,用于管理和调度衔接到服务器或许从服务器衔接出去的所有 Channel 上的事情循环; -
ServerBootstrap
:Netty 的服务器发动类,用于发动和装备一个 TCP/IP 服务器; -
Bootstrap
:Netty 的客户端发动类,用于发动和装备一个 TCP/IP 客户端; -
Channel
:Netty 的中心概念,用于表明一个通讯通道,能够读取和写入数据; -
ChannelPipeline
:Netty 的 Channel 处理器,用于在传入的数据上执行一组 ChannelHandler; -
ChannelHandler
:Netty 的中心组件,用于处理各种通讯事情,例如读取数据、写数据、树立衔接等;
后续,咱们还会和这些组件打更多的交道。
好了,那么这期内容就到这了,这期里咱们初步了解了Netty,包含什么是Netty、Netty现状、Netty的应用,还写了一个简略的Demo。下一期,咱们持续深入了解Netty,敬请期待。
参考:
[1].netty.io/
[2].《Netty威望指南》
[3]. 《Netty中心原理剖析与RPC实践》