Netty是一款高效的NIO框架和工具,基于JAVA NIO提供的API实现。

在JAVA NIO方面Selector给Reactor模式提供了基础,Netty结合Selector和Reactor模式设计了高效的线程模型,Reactor模式的参与者主要有下面一些组件:

  

  1. Channel

  2. Selector
  3. EventLoopGroup/EventLoop

  4. ChannelPipeline

Channel

在网络IO方面,Channel的主要实现是ServerSocketChannel和SocketChannel。他们都代表一个面向流的可监听读写事件的socket。ServerSocketChannel是用于服务器端的socket,他提供了一个静态工具方法open来为用户提供获取Channel的工具:

public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}

ServerSocketChannel的使用方式是面向服务器端的,一般的开发流程是:

  1. 获取一个ServerSocketChannel。

  2. 设置网络操作,这些参数主要是和TCP协议有关。

  3. 将ServerSocketChannel注册到Selector(多路复用器)。

  4. 将ServerSocketChannel和某个具体的地址绑定。

  5. 用户像多路复用器设置感兴趣的IO事件。

  6. 用户线程以阻塞或非阻塞方式轮询Selector来查看是否有就绪的IO事件。

  7. 用户针对不同的IO事件对Channel进行具体的IO操作。

SocketChannel主要是面向客户端的开发的,也是以open方式获取channel,客户端的开发流程大致如下:

  1. 获取一个SocketChannel。

  2. 设置Channel为非阻塞方式。

  3. 获取Selector。

  4. 将channel注册到Selector,并监听CONNECT事件。

  5. 调用channel的connect方法连接指定的服务器和端口。

  6. 如果连接成功则进行IO操作,如果没成功则轮询Selector处理CONNECT事件。

Selector

Selector是JAVA NIO提供的SelectableChannel多路复用器,它内部维护着三个SelectionKey集合,负责配合select操作将就绪的IO事件分离出来,落地为SelectionKey,我前面有一篇文章的一部分对Selector进行了相对详细的介绍(这里)。

在Netty线程模型中,我认为Selector充当着demultiplexer的角色,而对于SelectionKey我们可以将它看成Reactor模式中的资源。

EventLoopGroup/EventLoop

EventLoopGroup是一组EventLoop的抽象,由于Netty对Reactor模式进行了变种,实际上为更好的利用多核CPU资源,Netty实例中一般会有多个EventLoop同时工作,每个EventLoop维护着一个Selector实例,类似单线程Reactor模式地工作着。

EventLoopGroup提供next接口,可以总一组EventLoop里面按照一定规则获取其中一个EventLoop来处理任务.

1. BossEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,

2. BoosEventLoop不断轮询Selector将连接事件分离出来,通常是OP_ACCEPT事件,

3. 然后将accept得到的SocketChannel(注意不是ServerSocketChannel)交给WorkerEventLoopGroup,

4. WorkerEventLoopGroup会由next选择其中一个EventLoopGroup来将这个SocketChannel注册到其维护的Selector并对其后续的IO事件(OP_READ, OP_WRITE等)进行处理。

在Reactor模式中BossEventLoopGroup主要是对多线程的扩展,而每个EventLoop的实现涵盖IO事件的分离,和分发(Dispatcher)。

定时任务和一般任务

在Netty的EventLoop线程中,这个线程主要需要处理IO事件和其他两种任务,分别为定时任务和一般任务。Netty提供可一个参数ioRatio用于用户调整单线程对于IO处理时间和任务处理时间的分配的比率。这样根据实际应用场景用户可以对这个值进行调整,默认值是50,也就是这个线程会将处理IO的时间和处理任务的时间控制为1:1。

final long ioStartTime = System.nanoTime();

processSelectedKeys();//处理IO事件

final long ioTime = System.nanoTime() - ioStartTime;//处理IO事件的时间
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);//计算用于处理任务的时间

  

这样尽管一个EventLoop会关联多个Channel,这些Channel在单个线程下并不会出现并发问题, 而是串行的顺序执行,同时对于异步任务的处理也一样,Netty这样设计即免去了并发问题的烦恼,有减少了多线程上下文切换带来的性能损耗,同时基于EventLoopGroup实现的有限的线程数能够充分利用CPU处理能力。

ChannelPipeline

在Netty中ChannelPipeline维护着一个ChannelHandler的链表队列,每个SocketChannel都有一个维护着一个ChannelPipeline实例,而每个ChannelPipeline实例通常维护着一个ChannelHandler链表队列,由于SocketChannel是和SelectionKey关联的,也就是Reactor模式中的资源,当EventLoop将SelectionKey分离出来的时候会将SelectionKey关联的Channel交给Channel关联的ChannelHandler链来处理,那么ChannelPipeline其实是担任着Reactor模式中的请求处理器这个角色。

ChannelPipeline的默认实现是DefaultChannelPipeline,DefaultChannelPipeline本身维护着一个用户不可见的tail和head的ChannelHandler,他们分别位于链表队列的头部和尾部。tail在更上从的部分,而head在靠近网络层的方向。

在Netty中关于ChannelHandler有两个重要的接口,ChannelInBoundHandler和ChannelOutBoundHandler。inbound可以理解为网络数据从外部流向系统内部,而outbound可以理解为网络数据从系统内部流向系统外部。用户实现的ChannelHandler可以根据需要实现其中一个或多个接口,将其放入Pipeline中的链表队列中,ChannelPipeline会根据不同的IO事件类型来找到相应的Handler来处理,同时链表队列是责任链模式的一种变种,自上而下或自下而上所有满足事件关联的Handler都会对事件进行处理.

其他扩展

select、poll和epoll

JAVA对NIO的支持是从1.4版本开始的,是基于多路复用技术,而在linux操作系统方面多路复用技术有三种常用的机制:select、poll和epoll,epoll的支持也只是linux2.6版本之后才提供,java在jdk5.0的update 9之后才对epoll进行支持。这三种机制本质上都是同步IO,主要是由于他们都需要在读写事件就绪的时候需要自己进行读写,也就是这个这个读写过程是阻塞的。下面对着三种机制进行简单总结:

  1. select函数:改函数允许进程指示内核等待多个事件中的任何一个发生的时候或者在一定时间之后被唤醒,select有个致命的缺点即在多路复用中文件描述符的数量有限制,如果需要突破限制需要重新编译操作系统内核。

  2. poll函数:poll机制与select机制类似,区别是poll没有最大描述符限制。

  3. epoll函数:epoll在linux2.6内核中被提出来,是之前的select和poll的增强版本。epoll也没有文件描述符数量限制,而且是用一个文件描述符来管理多个描述符。在性能上相比上面两种有了很大的优化。

    epoll原理图如下:

  4. epoll使用“事件”的方式通知用户程序数据就绪,并且使用内存拷贝的方式使用户程序直接读取内核准备好的数据,不用再读取数据

ByteBuffer

JAVA NIO直接和Channel打交道的Buffer是ByteBuffer,ByteBuffer接口提供主要的内存分配、IO读写等相关接口。

值得注意的是JAVA NIO提供了两种Buffer内存分配机制,一种是堆内存,另一种是直接内存,主要区别:

  1. 堆内存分配和回收比较快,但是网络数据需要从内核copy到堆中。

  2. 直接内存分配和回收比较慢,但是免去了从内核copy到堆中的一次copy。

这两种内存各有千秋,使用的时候要根据实际情况去选择。

参考: https://my.oschina.net/andylucc/blog/614295

Netty 核心组件笔记的更多相关文章

  1. Netty学习笔记(二)——netty组件及其用法

    1.Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. 原生NIO存在的问题 1) NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector.Se ...

  2. Netty学习笔记(二) 实现服务端和客户端

    在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...

  3. Netty 学习笔记(1)通信原理

    前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始.   Netty 的通信原理 Netty 底层 ...

  4. Netty 核心组件 Pipeline 源码分析(二)一个请求的 pipeline 之旅

    目录大纲: 前言 针对 Netty 例子源码做了哪些修改? 看 pipeline 是如何将数据送到自定义 handler 的 看 pipeline 是如何将数据从自定义 handler 送出的 总结 ...

  5. Netty核心组件介绍及手写简易版Tomcat

    Netty是什么: 异步事件驱动框架,用于快速开发高i性能服务端和客户端 封装了JDK底层BIO和NIO模型,提供高度可用的API 自带编码解码器解决拆包粘包问题,用户只用关心业务逻辑 精心设计的Re ...

  6. Netty学习笔记-入门版

    目录 Netty学习笔记 前言 什么是Netty IO基础 概念说明 IO简单介绍 用户空间与内核空间 进程(Process) 线程(thread) 程序和进程 进程切换 进程阻塞 文件描述符 文件句 ...

  7. 【Netty】Netty核心组件介绍

    一.前言 前篇博文体验了Netty的第一个示例,下面接着学习Netty的组件和其设计. 二.核心组件 2.1. Channel.EventLoop和ChannelFuture Netty中的核心组件包 ...

  8. Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例

    在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习. 创建客户端 接着第五个笔记 ...

  9. Netty 核心组件 EventLoop 源码解析

    前言 在前文 Netty 启动过程源码分析 (本文超长慎读)(基于4.1.23) 中,我们分析了整个服务器端的启动过程.在那篇文章中,我们重点关注了启动过程,而在启动过程中对核心组件并没有进行详细介绍 ...

随机推荐

  1. Java多线程-----线程池详解

    1. 线程池的实现原理 提交一个任务到线程池中,线程池的处理流程如下: 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务.如果 ...

  2. Spark学习之路 (二十七)图简介

    一.图 1.1 基本概念 图是由顶点集合(vertex)及顶点间的关系集合(边edge)组成的一种数据结构. 这里的图并非指代数中的图.图可以对事物以及事物之间的关系建模,图可以用来表示自然发生的连接 ...

  3. 【Hive学习之七】Hive 运行方式&权限管理

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 hadoop-3.1.1 apache-hive-3.1.1 ...

  4. 设置Source Insight显示格式

    调整字体大小 默认的忍不了,百度之,解决方案如下:1.Document Options -> Screen Fonts -> 字体设置为新宋体(等宽)或者其他支持中文的字体,字符集选GB2 ...

  5. 【转】机器学习笔记之(3)——Logistic回归(逻辑斯蒂回归)

    原文链接:https://blog.csdn.net/gwplovekimi/article/details/80288964 本博文为逻辑斯特回归的学习笔记.由于仅仅是学习笔记,水平有限,还望广大读 ...

  6. Markdown编辑器使用说明

    Markdown编辑器使用说明 #编辑器使用说明编辑器仅用来编辑文章的样式,建议在其他文档中写好内容,再复制到此处编辑样式--- ## 编辑器使用介绍—非开发者 非开发者,可以将编辑框内容全部删掉,使 ...

  7. nginx负载均衡六种策略

    Nginx服务器之负载均衡策略(6种)   一.关于Nginx的负载均衡 在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独一个服务器压力过大,将来自用户的请求转发给不同的 ...

  8. BIOS备忘录之通过Windbg来追踪ASL code的运行

    通过Windbg来追踪ASL code的运行: 目标机的配置: 第一步: 在BIOS Setup下面 disable secure boot(不然下面debug on 命令会失败):关闭防火墙. 第二 ...

  9. Introduction to the Standard Directory Layout

    Having a common directory layout would allow for users familiar with one Maven project to immediatel ...

  10. vue.JS 介绍

    vueJS 介绍 首先,vueJS 是我很早之前就想要接触学习的东西,但是呢,一直没时间,主要是在学校,事太多,没心思定下心来学习,我学生生涯的最后一个假期的第一天晚上,万事开头难,那就先写点儿什么东 ...