共计 6147 个字符,预计需要花费 16 分钟才能阅读完成。
一、Netty 的劣势
只管咱们后面学习 NIO 的时候,我曾经尽可能的简化代码,然而咱们依旧会发现,JDK NIO 的开发仍旧是极为简单,在业务开发中咱们还要思考到业务的解决流程、业务的复用、申请的并发量、申请过程中的编解码问题、网络传输中的半包粘包问题等等,会进一步减少 NIO 开发的难度!
- Netty 是基于上述难题提供了一个对立的解决方案,Netty 提供了一个对于 Socket 编程的对立模板,即便是一个没有网络编程根底的开发人员,也可能很轻松的开发出一个高并发、低延时的程序!
- Netty 提供了很多默认的编解码性能,可能很轻松的实现一些市面上常见的协定比方 FTP、HTTP、SMTP、WebSocket 等,可能通过很少的几行代码很轻松的解决网络通讯过程中的半包、粘包问题!
- 定制能力强,Netty 优良的代码格调和弱小的扩大能力,能够让咱们通过 ChannelHandler 对通信框架进行灵便的扩大,以及通过管道流(PipLine)实现业务性能的互相隔离与复用!
- 成熟、稳固,Netty 修复了当初 JDK 曾经发现了的,所有的 JDK NIO BUG,其中最驰名的就是臭名远扬的 JDK 空轮训 BUG!
- 社区沉闷,版本迭代周期短,发现的 BUG 能够被及时修复,同时,更多的新性能会退出;
二、Netty 的架构设计
这是来自官网的一张架构图,咱们能够大抵的理解 Netty 的模块!
- Core 核心层,是 Netty 的最次要的实现,后续的所有扩大都建设在 Core 之上,他提供了事件的可扩大模型、网络通讯编程的通用 API、数据零拷贝和数据载体的封装和复用!
- Transport Service 服务传输层,Netty 提供了底层网络通讯的能力,它形象了底层网络 TCP、UDP 等协定的网络通信,使得用户不在为一个网络通信开发的底层技术所头疼,似的注意力可能更加专一于业务!也正是因为这一层的封装,使得 NIO/BIO 之间可能无缝切换!
- Protocol Support 协定层,Netty 简直实现类市面上的大部分支流协定、包含 HTTP、SSL、Protobuf、压缩、大文件传输、WebSocket、文本、二进制等支流协定, 而且 Netty 反对自定义扩大协定。Netty 丰盛的协定使得用户的开发成本大大降低,应用内置的协定能够很轻松的开发一个相似于 Tomcat 的 Http 服务器!
三、Netty 的根本应用和介绍
通过下面的介绍,咱们大略理解了 Netty 的根本架构,上面咱们会看一下 Netty 的根本应用,而后我会逐行解析,心愿可能通过这一节课帮忙大家入门 Netty 编程,也为前面的源码学习更好的铺垫一下!
1. 开发一个服务端
咱们应用 Netty 开发一个简略的服务端代码:
服务端接管到客户端的音讯,打印进去,而后被动中断连贯!
启动服务端源代码:
/**
* 服务端
*
* @author 源码学徒
* @date 2021 年 4 月 19 日 12:39:21
*/
public class EchoServer {public static void main(String[] args) {
// 设置事件循环组
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();
try {
// 设置服务启动器
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.localAddress(8989)
// 设置服务管道
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new EchoServerHandler());
}
});
// 同步阻塞 直到绑定实现
ChannelFuture channelFuture = serverBootstrap.bind().sync();
// 监听通道敞开办法 期待服务端通道敞开实现
channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();
} finally {
// 优雅的敞开线程组
boss.shutdownGracefully();
worker.shutdownGracefully();}
}
}
服务端读取数据处理器源代码:EchoServerHandler
/**
* 服务端打印解决的 handler
*
* @author huangfu
* @date 2021 年 4 月 19 日 12:42:34
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf = (ByteBuf) msg;
try {
// 转换为字符串
String message = byteBuf.toString(StandardCharsets.UTF_8);
System.out.println(message);
}finally {ReferenceCountUtil.release(byteBuf);
ctx.channel().close();
}
}
}
2. 服务端源代码介绍
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();
定义两个事件循环组,还记得咱们第五章将的咱们对 NIO 的优化版本吗?当初临时你能够把它了解为两个selector 组:
boss
外部只蕴含一个 selector 选择器,用于接管新连贯!worker
外部默认是CPU*2
个 selector 选择器,用于解决咱们的业务逻辑!
boss
接管到新连贯后,将新连贯产生的 SocketChannel 交给 worker
线程组解决!用一个当初比拟风行的比喻:boss 相当于老板,worker 相当于工人,boss 不解决工作,只负责在里面跑业务拉活谈合同。接到新活之后,就把这个活交给 worker 来干,本人再去接下一个活!
这两行代码倡议参考上一章的 NIO 的优化版本学习!
ServerBootstrap serverBootstrap = new ServerBootstrap();
ServerBootstrap 是 Netty 为咱们提供的一个疾速启动配置类,为什么 Netty 相较于 NIO 开发比较简单,就是因为 Netty 为咱们提供了一个网络编程的代码的模板,那么想要应用这些模板,就必须要通知模板一些必要的参数,供模板读取,而 ServerBootstrap 正式这个作用,通过初始化的时候,咱们设置各种参数,咱们将之保留到 ServerBootstrap
中,后续 Netty 服务启动的时候,会读取咱们初始化的时候配置的各种参数,实现本人的初始化!
serverBootstrap.group(boss, worker)
将咱们后面初始化的两个事件循环组绑定起来,保留到 serverBootstrap 中,供后续读取!
.channel(NioServerSocketChannel.class)
咱们这里开发的是一个服务端程序,所以咱们应用的是 NioServerSocketChannel,在 Netty 中分为两种 SocketChannel,一种是 NioServerSocketChannel
一种是NioSocketChannel
,两者都继承与 JDK 的 ServerSocketChannel 和 SocketChannel,是 Netty 官网对于通信管道的扩大:
- NioServerSocketChannel: 服务端通道
- NioSocketChannel: 客户端通道
有对于两个通道,咱们前面会详细分析,这里你们先记着,NioServerSocketChannel代表着服务端通道!NioSocketChannel代表着客户端通道,不要搞混了!
.localAddress(8989)
设置一个端口号,服务端对外裸露的端口号!
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new EchoServerHandler());
}
});
这个代码及其重要,前面也会重点提及,这里次要是做服务编排的,想客户端 Socket 绑定一个通道,并增加咱们的业务解决类 xxxxHandler,当一个客户端连贯上服务端后,后续所有的业务解决,都会由这里注册的各种 Handler 来解决,这就是 Netty 提供的可能让开发人员专一于业务开发的次要逻辑所在,前面会对这一块代码进行一个重点的解说,这里也只须要记住,咱们注册这些 Handler 后,客户端发来的数据都会在这些 Handler 中流转解决!
ChannelFuture channelFuture = serverBootstrap.bind().sync();
下面的初始化实现了,开始进行服务器启动和端口绑定,依据下面设置的端口号绑定,仔细的同学可能会发现我还调用了一个 sync
办法,Netty 是基于异步事件来开发的,这里咱们进行 bind 调用之后,因为是异步执行,所以咱们并不知道什么时候实现,所以这里调用了一个阻塞的办法(sync),始终阻塞期待绑定实现后才持续往下走!
channelFuture.channel().closeFuture().sync();
获取一个服务端的通道对象,并且对服务端的通道对象增加一个敞开的监听,并调用阻塞办法(sync),调用后,程序会始终阻塞再这里,期待服务端管道敞开,才会持续往下走!一般来说,服务端管道除非咱们被动停机活因为异样解体,否则服务端管道会始终存活,那么改程序将会始终阻塞在这里,造成一个服务!
boss.shutdownGracefully();
worker.shutdownGracefully();
优雅停机,该段程序会将通道标记为不可用状态,期待程序处理完毕后,开释 Netty 所创立的所有资源!
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {....}
当服务端察觉到客户端发来数据时,回调该逻辑!
3. 开发一个客户端
/**
* 打印程序客户端
*
* @author huangfu
* @date 2021 年 4 月 19 日 13:58:16
*/
public class EchoClient {public static void main(String[] args) {EventLoopGroup worker = new NioEventLoopGroup();
try {Bootstrap bootstrap = new Bootstrap();
bootstrap.remoteAddress("127.0.0.1", 8989)
.group(worker)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect().sync();
channelFuture.channel().closeFuture().sync();} catch (Exception e) {e.printStackTrace();
} finally {worker.shutdownGracefully();
}
}
}
客户端解决 Handler
/**
* @author huangfu
* @date
*/
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
buffer.writeBytes("Hello Netty".getBytes(StandardCharsets.UTF_8));
ctx.channel().writeAndFlush(buffer);
}
}
4. 客户端源代码介绍
客户端的代码和服务端的代码基本一致,所以这里不做全副解说,只做不一样的解说:
EventLoopGroup worker = new NioEventLoopGroup();
这里客户端只有一个事件循环组,为什么?因为客户端不存在新连贯嘛!
Bootstrap bootstrap = new Bootstrap();
这个里和服务端的 ServerBootstrap 基本一致,是为了保留客户端的配置所设置的一个类!
ChannelFuture channelFuture = bootstrap.connect().sync();
连贯服务端,并期待链接实现!
Handler 的重载办法也产生了变动:
public void channelActive(ChannelHandlerContext ctx) throws Exception {....}
这里的含意是,当客户端被激活后既链接到服务端后,回调该逻辑!
仔细的同学在练习的时候可能会发现一点问题,咱们发现客户端会有 handler 和 childHandler 两种办法
.handler()
// 设置服务管道
.childHandler()
- handler: 是绑定在 ServerSocketChannel 之上的,负责服务端的逻辑解决!
- childHandler: 是绑定在 SockerChannel 之上的,当客户端绑定胜利后,会产生一个 SocketChannel 对象会调用该 handler 的绑定!
总结
本节课比较简单,次要是对 Netty 的根本应用有一个比较简单的认知,心愿大家课下多练习,争取会简略的应用 Netty!