前言
在目前许多开源的框架中,避免不了服务与服务之间相互通信,相互查询数据等情况,而往往我们知道的
grpc等等,底层也都是由Netty去实现,这里就简单的给大家扫下盲。
Netty简单介绍
什么是经典的三种I/O模型
| 模式名 | 版本 | 说明 |
|---|---|---|
| BIO(阻塞I/O) | JDK1.4之前 | 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善 |
| NIO(非阻塞I/O) | JDK1.4(2002年,java.nio包) | 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 |
| AIO(异步I/O) | JDK1.7(2011年) | 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理 |
使用场景
-
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。 -
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 -
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
Netty对三种I/O模型的支持
| NIO | ||||
|---|---|---|---|---|
| COMMON | Linux | MacOs/BSD | ||
| NioEventLoopGroup | EpollEventLoopGroup | KQueueEventLoopGroup | ||
| NioEventLoop | EpollEventLoop | KQueueEventLoop | ||
| NioServerSocketChannel | EpollServerSocketChannel | KQueueServerSocketChannel | ||
| NioSocketChannel | EpollSocketChannel | KQueueSocketChannel | ||
为什么Netty仅支持NIO?
- 为什么不建议阻塞
I/O(BIO/OIO)?- 连接数高的情况下:阻塞->耗资源、效率低
- 为什么删除已经做好的
AIO支持?Window实现成熟,但是很少用作服务器Linux常用来做服务器,但是AIO实现不够成熟Linux下AIO相比较NIO的性能提升不明显
为什么Netty有多种NIO实现?
实现的更好:
Netty暴露了更多的可控参数JDK的NIO默认实现是水平触发Netty是边缘触发和水平触发可切换
Netty实现的垃圾回收更少、性能更好
NIO一定优于BIO么?
BIO代码简单- 特定场景:连接数少,并发度低。
源码解读Netty怎么切换I/O模式?
工厂+反射
Netty如何支持三种Reactor
什么是Reactor
反应器设计模式(
Reactor pattern)是一种为处理并发服务请求,并将请求提交到一个或者多个服务处理程序的事件设计模式。当客户端请求抵达后,服务处理程序使用多路分配策略,由一个非阻塞的线程来接收所有的请求,然后派发这些请求至相关的工作线程进行处理。
Reactor有几种模式
Reactor单线程Reactor多线程Reactor主从多线程
| BIO | NIO | AIO |
|---|---|---|
| Thread=Per-Connection | Reactor | Proactor |
注册感兴趣的事件 -->扫描是否有感兴趣的事件发生 -->时间发生后做出相应的处理
| client/Server | SocketChannel/ServerSocketChannel | OP_ACCEPT | OP_CONNECT | OP_WRITE | OP_READE |
|---|---|---|---|---|---|
| client | SocketChannel | Y | Y | Y | |
| server | SewrverSocketChannel | Y | |||
| server | SocketChannel | Y | Y |
如何在Netty中使用Reactor模式
| 模式名称 | 使用方法 |
|---|---|
| Reactor单线程模式 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup) |
| 非主从Reactor多线程模式 | EventLoopGroup bossGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup) |
| 主从Reactor多线程模式 | EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup,workerGroup) |
Netty对Reactor模式的支持
通过上面的三种简单的介绍,我们知道
Netty针对于Reactor模式是如何支持的,我们除了知道是如何支持的之外,还是需要了解其中是如何实现的,这里采用主从Reactor多线程模式简单的说明下。
主要代码部分:
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
我们可以从代码启动开始寻找:
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
上面即是serverChannel的注册。
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}
上面即是Channel的注册。
通过上面的两步操作即可完成对应的Reactor模式支持。
总结
上文中简单的介绍了我们底层框架中
Netty的一些介绍,当然强大的框架远远不止这些东西去支持,还有需要进行处理的(例如粘包、Metric等)。