Netty是一个高性能,异步的事件驱动的Java NIO框架,它的所有I/O操作都是异步非阻塞的,其使用了一种重要的设计模式,称为Reactor模式。
Reactor模式组件
1.Handle
- 它识别由操作系统管理的资源。这些资源通常包括网络连接,打开文件,定时器,同步对象等。
2.Synchronous Event Demultiplexer(同步事件分离器)
- 本质上是一个系统调用,用于等待事件的发生。调用它时会发生阻塞,直到同步事件分离器上有事件发生为止。对于linux操作系统来说,同步事件分离器就是i/o多路复用,如:select,poll,epoll。Java Nio中,同步事件分离器是selector,对应的方法是select()。
3.Event Handler(事件处理器)
- 由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。Netty相比于Java Nio来说,在事件处理器角色上进行了升级,为开发者提供了大量的回调方法,供我们在特定事件产生时来进行特定业务逻辑处理。
4.Concrete Event Handler(具体事件处理器)
- 它是事件处理器的具体实现,它实现了事件处理器所提供的各个回调方法。从而实现了特定于业务的逻辑。
5.Initiation Dispatcher(初始分发器)
- 实际上是Reactor角色,它本身定义了一些规范,这些规范用于控制事件的调度方式,同时又提供了应用进行事件处理器的注册,删除等方法。它本身是事件处理器的核心所在,会通过同步事件分离器等待事件的发生。一旦事件发生,它首先会分离出每一个事件,然后调用事件处理器,最后调用相关的回调方法来处理这些事件。
Reactor模式的流程
- 当应用向Initiation Dispatcher注册具体的事件处理器时,应用会标识出该事件处理器希望Initiation Dispatcher在某个事件发生时向其通知的该事件,该事件与Handle相关联。
- Initiation Dispatcher会要求每个事件处理器向其传递内部Handle,该Handle向操作系统标识了事件处理器。
- 当所有的事件处理器注册完毕后,应用会调用
handle_events()
方法来启动Initiation Dispatcher的事件循环。这时,Initiation Dispatcher会将每个注册的事件处理器的Handle合并起来,并使用同步事件分离器来等待这些事件的发生。比如说,tcp协议层会使用select同步事件分离器操作来等待客户端的数据到达连接的socket handle上。 - 当与某个事件源对应的Handle变为Ready状态时,同步事件分离器就会通知Initiation Dispatcher。
- Initiation Dispatcher会触发事件处理器的回调方法。从而响应这个处于Ready状态的handle。当事件发生时,Initiation Dispatcher会将被激活的Handler作为key来寻找并分发恰当的事件处理器回调方法。
- Initiation Dispatcher会回调事件处理器的
handle_event
方法来执行特定于应用的功能。从而响应这个事件,所发生的事件类型可以作为该方法参数并被该方法内部使用来执行额外的特定于服务的分离与分发。
代码示例
Reactor实现
/**
Java Nio实现Reactor模式
**/
public class Reactor {
private static final Logger LOGGER = LoggerFactory.getLogger(Reactor.class);
public static void main(String[] args)throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8899));
//selector对应同步事件分离器
Selector selector = Selector.open();
//同步事件分离器
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//阻塞等待事件的发生
while (selector.select() > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
//对应的事件发生,进行相关逻辑处理
if (selectionKey.isAcceptable()){
ServerSocketChannel accept = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = accept.accept();
socketChannel.configureBlocking(false);
LOGGER.info("Accept request from {}",socketChannel.getLocalAddress());
//同步事件分离器会等待socket变为可读状态
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (selectionKey.isReadable()){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int count = socketChannel.read(byteBuffer);
if (count <= 0){
socketChannel.close();
selectionKey.cancel();
LOGGER.info("Received invalid data,close the connection");
continue;
}
LOGGER.info("Received message {}",new String(byteBuffer.array()));
}
selectionKeys.remove(selectionKey);
}
}
}
}
上述代码中看出,多个channel
可以注册到一个Selector上,实现了一个线程同时监控多个请求状态,注册时需要指定所关注的事件。
多Reactor实现
/**
*多Reactor模式
**/
public class MultiReactor {
private static final Logger LOGGER = LoggerFactory.getLogger(MultiReactor.class);
public static void main(String[] args)throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8899));
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
int coreNum = Runtime.getRuntime().availableProcessors();
//subReactor的个数是当前核数的2倍,也是Netty的默认个数。
Processor[] processors = new Processor[coreNum];
for(int i = 0; i < processors.length;i++){
processors[i] = new Processor();
}
int index = 0;
while (selector.select() > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
for (SelectionKey selectionKey:selectionKeys){
if (selectionKey.isAcceptable()){
ServerSocketChannel acceptServerSocketChannel = (ServerSocketChannel)selectionKey.channel();
SocketChannel socketChannel = acceptServerSocketChannel.accept();
socketChannel.configureBlocking(false);
LOGGER.info("Accept request from {}",socketChannel.getRemoteAddress());
//轮询的方式
Processor processor = processors[(index++)%coreNum];
processor.addChannel(socketChannel);
processor.wakeup();
}
}
}
}
}
/**
*Processor.java
**/
public class Processor {
private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class);
private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(2*Runtime.getRuntime().availableProcessors());
private Selector selector;
public Processor()throws IOException {
//每个Processor包含一个selector
this.selector = SelectorProvider.provider().openSelector();
start();
}
public void addChannel(SocketChannel socketChannel)throws IOException{
socketChannel.register(selector, SelectionKey.OP_READ);
}
public void wakeup(){
this.selector.wakeup();
}
public void start(){
EXECUTOR_SERVICE.submit(()->{
while (true){
if (selector.select(500) <= 0){
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
iterator.remove();
if (selectionKey.isReadable()){
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
int count = socketChannel.read(buffer);
if (count < 0){
socketChannel.close();
selectionKey.cancel();
LOGGER.info("{}\t Read ended",socketChannel);
continue;
}else if (count == 0){
LOGGER.info("{}\t Message size is 0",socketChannel);
continue;
}else {
LOGGER.info("{}\t Read message {}",socketChannel,new String(buffer.array()));
}
}
}
}
});
}
}
Netty的注册流程可以看另外一篇文章https://blog.csdn.net/ws92dj/article/details/88783631。