netty服务端启动原理

Netty 是一个事件驱动模式的高性能的 NIO 框架,对内封装了 Java NIO 的大量复杂且繁琐的实现细节,对外提供了高度抽象的组件和易用的 API,提供了针对市面上大部分通信协议的支持,是当下 Java 生态中最流行的远程通信框架之一。

Netty 框架本质上是对于 Java NIO 的封装,Java NIO 本质上是对于操作系统提供的 IO 多路复用功能的封装,因此其设计思路是一脉相承的,即采取了事件驱动的 Reactor 模式。

其中的 bossGroup 就是 mainReactor 的具体实现,主要用于监听服务端口,接收客户端的 TCP 连接请求。 workerGroup 就是 subReactor 的具体实现,主要用于消息的读取发送、编解码以及其他业务逻辑的处理。

它们本质上都可以视作一个线程池,里面的每个线程(NioEventLoop)都唯一绑定了一个 Java NIO 中的 Selector 对象,用于实现 IO 多路复用的功能,来监听多个(文件描述符)客户端的输入。

启动demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class DiscardServer {

private int port;

public DiscardServer(int port) {
this.port = port;
}

public void run() throws Exception {
// mainReactor(Acceptor)线程池,主要用于监听服务端口,处理客户端连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// subReactor线程池,主要用于实现对于消息的接收发送、编解码以及其他业务处理
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// Netty的启动辅助类
ServerBootstrap b = new ServerBootstrap();
// 初始化ServerBootstrap实例
b.group(bossGroup, workerGroup)
// 指定Channel类型为NioServerSocketChannel
.channel(NioServerSocketChannel.class)
// 添加自定义Handler,会用于workerGroup中的Channel的处理
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
// 添加Channel选项
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);

// Bind and start to accept incoming connections.
// 服务端启动核心逻辑,初始化Channel并绑定指定服务端口开始监听
ChannelFuture f = b.bind(port).sync();

// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
// 服务端关闭
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

public static void main(String[] args) throws Exception {
int port = 8080;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}

new DiscardServer(port).run();
}
}
  1. 初始化: 创建 ServerBootstrap 实例并进行初始化
  2. 启动服务: ServerBootstrap 绑定服务端口并开始监听

netty模型

定义#

netty是一个高性能、异步事情驱动的网络通信框架
NIO IO多路复用 、事情驱动模型

1.设置主从reactor模式
2.指定IO类型
3.指定handler
4.绑定端口

netty优点#

  1. API使用简单,开发门槛低;

  2. 功能强大,预置了多种编解码功能,支持多种主流协议;

  3. 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;

  4. 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;

  5. 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;

  6. 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;

  7. 经历了大规模的商业应用考验,质量得到验证。在互联网、大数据、网络游戏、企业应用、电信软件等众多行业得到成功商用,证明了它已经完全能够满足不同行业的商业应用了。

高性能三要素#

  1. reactor模型
  2. IO多路复用
  3. 协议

reactor模型#

单线程模型#

多线程模型#

主从线程模型#

组件#

Bootstrap/ServerBootstrap#

顾名思义就是启动类,分别负责启动客户端和服务器端。这个类用来配置相关参数,比如设置EventLoopGroup,IO类型,handler等等,Netty的一切都从这里开始

EventLoopGroup#

主从多线程Reactor模型,分别有两个线程池: EventLoopGroupA和EventLoopGroupB。一个用于接收请求,另一个用于处理IO操作。

一个EventLoopGroup就相当于一个线程池,而每一个EventLoop就是一个线程,当新的Channel被创建时(有新的请求进来),就会在EventLoopGroup里注册一下,同时会分配一个EventLoop给这个Channel,

从此开始直到这个Channel被销毁,这个Channel只能被它绑定的这个EventLoop执行,这也就是为什么Netty可以不用考虑并发的原因。

EventLoop是处理各个event的具体线程。除了处理IO读写等event外,EventLoop还需要进行系统任务和定时任务进行执行

Channel#

Netty的Channel接口所提供的的API,大大降低了直接使用Socket类的复杂性

ChannelPipeline & ChannelHander#

Netty采用了一种叫做数据流(data flow)的处理机制,类似于Unix中的管道。即每一个Channel都有一个自己的ChannelPipeline,每一个pipeline里会有多个ChannelHandler。数据会像水流一样依次通过每一个handler被逐一处理。
流处理是双向混合的,分为Inbound和Outbound, 分别对应request和response。

这个handler被分成两类:ChannelOutboundHandler和ChannelInboundHandler。当服务器处理进来的请求时,则只会调用实现了ChannelInboundHandler的handler;当服务器返回信息给客户端时,则只会调用实现了ChannelOutboundHandler的handler

Encoders & Decoders#

我们在解析处理请求时通常需要对数据格式进行转换,比如把字节变成对象,或者把对象转换为字节。针对这种常见的场景,Netty提供了编码和解码的接口:MessageToByteEncoder和ByteToMessageEncoder。

其实两个抽象类分别继承了ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter,说白了,使用起来和普通的handler没什么区别。自己写的类只要重写decode()或者encode()方法对数据进行处理即可

BIO NIO AIO 阻塞 非阻塞

BIO:同步阻塞IO(面向流的)#

特点:一个连接建立一个线程,连接如果没有IO请求时,则会浪费线程资源开销,可以通过线程池技术来改善

适用场景:连接数小,并发数小,架构固定(java1.4之前唯一的IO)

NIO:同步非阻塞(面向Buffer的)#

特点:客户的发送的连接请求会注册到多路复用器上,多路复用器轮询到连接存在有效请求时才会启动一个线程来进行处理
适用场景:连接数目多,且连接比较短的架构,比如聊天服务器(java1.4开始支持)

AIO:异步非阻塞#

特点:针对客户端连接发出的IO请求,会由OS先完成IO操作后,在通知服务器启动线程进行处理
适用场景:连接数目多,连接比较长,比如相册服务器,充分调用OS参与并发操作(java1.7开始支持)

同步、异步(关注点:消息通信机制、IO请求发送)#

同步:发送一个请求后,会不断的去轮询、等待请求结果返回后再发送下一个请求,可以避免死锁,脏读的发生

异步:发送一个请求后,不需要等待结果返回也可以发送下一个请求,可以提高效率,保证并发,OS执行完成后会通过回调函数通知应用程序获取结果

阻塞、非阻塞(关注点:程序在等待调用结果时的状态、IO操作结果获取)#

阻塞:程序在等待请求结果的时候,线程会被挂起,调用线程只有在得到结果之后才会返回

非阻塞:虽然不能立马得到结果,但是该调用不会阻塞当前线程,此线程还可以干其他事

NIO IO多路复用#

NIO是概念,IO多路复用是一套方案

当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求“事件”,并把这个“事件”分配给相应的函数处理。我们可以把这个处理函数放到线程中去执行,执行完就把线程归还,这样一个线程就可以异步的处理多个线程

而阻塞式 I/O 的线程的大部分时间都被浪费在等待请求上了

对于同步非阻塞,一个线程可以执行多个连接中的请求,而同步阻塞则时一个线程对应一个连接,所以阻塞会很严重, 线程浪费严重

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×