[TOC]
1. Netty 在内部使用了回调来处理事件;当一个回调被触发时,相关的事件可以被一个 interfaceChannelHandler 的实现处理。当一个新的连接已经被建立时,ChannelHandler 的 channelActive()回调方法将会被调用,并将打印出一条信息。
2. 所有的 Netty 服务器都需要以下两部分。
* 至少一个 ChannelHandler—该组件实现了服务器对从客户端接收的数据的处理,即
它的业务逻辑。
* 引导—这是配置服务器的启动代码。至少,它会将服务器绑定到它要监听连接请求的
端口上。
在本小节的剩下部分,我们将描述 Echo 服务器的业务逻辑以及引导代码。
3. 每个 Channel 都拥有一个与之相关联的 **ChannelPipeline**,其持有一个 ChannelHandler 的实例链。也就是说一个pipeline上有多个channel,这些channel是串联起来的,并且上一个channel的结果会传递到下一个channel。
![](https://box.kancloud.cn/6a729eb2adb0e1ad312191a225300e1d_700x297.png)
:-: 图一
## 1. 引导(Bootstrap)
### 1.1 ServerBootstrap
服务端启动引导类,设置netty服务端启动参数
两种类型的引导:
> 1. 一种用于客户端(简单地称为 Bootstrap)
> 2. 而另一种( ServerBootstrap)用于服务器。
>
## 2. EventLoopGroup
EventLoop 定义了 Netty 的核心抽象, 用于处理连接的生命周期中所发生的事件。
![](https://box.kancloud.cn/792969533c082e041c425c17efa20952_833x641.png)
:-: 图二
1. 一个 EventLoopGroup 包含一个或者多个 EventLoop;
2. 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;
3. 所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;
4. 一个 Channel 在它的生命周期内只注册于一个 EventLoop;
5. 一个 EventLoop 可能会被分配给一个或多个 Channel。
注意,在这种设计中, 一个给定 Channel 的 I/O 操作都是由相同的 Thread 执行的, 实际上消除了对于同步的需要。
## 1. channel
基本的 I/O 操作( bind()、 connect()、 read()和 write())依赖于底层网络传输所提供的原语。在基于 Java 的网络编程中,其基本的构造是 class Socket。 Netty 的 Channel 接口所提供的 API,大大地降低了直接使用 Socket 类的复杂性。此外, Channel 也是拥有许多预定义的、专门化实现的广泛类层次结构的根,下面是一个简短的部分清单:
每个channel都对应一个物理连接,每个连接都有自己独立的TCP参数。
> 1. EmbeddedChannel;
> 2. LocalServerChannel;
> 3. NioDatagramChannel;
> 4. NioSctpChannel;
> 5. NioSocketChannel。
1. channel和childHandler
netty服务是父级的、初始的channel线程,负责监听整个服务。后接入的链接都会开启一个channel,成为childChannel。
在基类AbstractBootstrap有handler方法,目的是添加一个handler,监听Bootstrap的动作,客户端的Bootstrap中,继承了这一点。
在服务端的ServerBootstrap中增加了一个方法childHandler,它的目的是添加handler,用来监听已经连接的客户端的Channel的动作和状态。
handler在初始化时就会执行,而childHandler会在客户端成功connect后才执行,这是两者的区别。
![](https://box.kancloud.cn/1e3870519ff613cb8b95dc3538b32f59_859x272.png)
如图所示, 每个 Channel 都将会被分配一个 ChannelPipeline 和 ChannelConfig。ChannelConfig 包含了该 Channel 的所有配置设置, 并且支持热更新。由于特定的传输可能具有独特的设置, 所以它可能会实现一个 ChannelConfig 的子类型。
Netty 的 Channel 实现是线程安全的,因此你可以存储一个到 Channel 的引用,并且每当你需要向远程节点写数据时, 都可以使用它, 即使当时许多线程都在使用它。
## ChannelHandler
实际的数据业务处理逻辑
1. 针对不同类型的事件来调用 ChannelHandler;
2. 应用程序通过实现或者扩展 ChannelHandler 来挂钩到事件的生命周期,并且提供自定义的应用程序逻辑;
3. 在架构上, ChannelHandler 有助于保持业务逻辑与网络处理代码的分离。这简化了开发过程,因为代码必须不断地演化以响应不断变化的需求。
4. ChannelInitializer。这是关键。当一个新的连接被接受时,一个新的子 Channel 将会被创建,而 ChannelInitializer 将会把一个你的EchoServerHandler 的实例添加到该 Channel 的 ChannelPipeline 中。正如我们之前所
解释的,这个 ChannelHandler 将会收到有关入站消息的通知
### 方法
#### channelActive()
当有连接建立时调用
#### channelRead(ChannelHandlerContext ctx, Object msg)
1. 每当接收数据时,都会调用这个方法。
2. 需要注意的是,由服务器发送的消息可能会被分块接收。 也就是说,如果服务器发送了 5 字节, 那么不能保证这 5 字节会被一次性接收。 即使是对于这么少量的数据, channelRead0()方法也可能会被调用两次,第一次使用一个持有 3 字节的 ByteBuf( Netty 的字节容器),第二次使用一个持有 2 字节的 ByteBuf。作为一个面向流的协议, TCP 保证了字节数组将会按照服务器发送它们的顺序被接收。
## ChannelFuture
Netty 中所有的 I/O 操作都是异步的。因为一个操作可能不会立即返回,所以我们需要一种用于在之后的某个时间点确定其结果的方法。为此, Netty 提供了ChannelFuture 接口,其 addListener()方法注册了一个 ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。
## ChannelPipeline
### 与ChannelHandler的关系
1. 当ChannelHandler 被添加到ChannelPipeline 时,它将会被分配一个ChannelHandlerContext,其代表ChannelHandler 和 ChannelPipeline 之间的绑定。虽然这个对象可以被用于获取底层的 Channel,但是它主要还是被用于写出站数据。
2. **ChannelPipeline 提供了 ChannelHandler 链的容器**,并定义了用于在该链上传播入站和出站事件流的 API。当 Channel 被创建时, 它会被自动地分配到它专属的 ChannelPipeline。Handler安装过程如下:
> 1. 一个ChannelInitializer的实现被注册到了ServerBootstrap中
> 2. 当 ChannelInitializer.initChannel()方法被调用时, ChannelInitializer将在 ChannelPipeline 中安装一组自定义的 ChannelHandler
> 3. ChannelInitializer 将它自己从 ChannelPipeline 中移除
### 入、出站
![](https://box.kancloud.cn/55c6e14a3afd8613a5cd9052ca4c6984_858x209.png)
:-: 图三 数据、事件入站出站示意图
从一个客户端应用程序的角度来看,如果事件的运动方向是从客户端到服务器端, 那么我们称这些事件为出站的,反之则称为入站的。
~~~
ChannelInboundHandler :入站Handler
ChannelOutboundHandler :出站Handler
~~~
#### 入站
如果一个消息或者任何其他的入站事件被读取, 那么它会从 ChannelPipeline 的头部开始流动,并被传递给第一个 ChannelInboundHandler。这个 ChannelHandler 不一定会实际地修改数据, 具体取决于它的具体功能,在这之后,数据将会被传递给链中的下一个ChannelInboundHandler。最终,数据将会到达 ChannelPipeline 的尾端, 届时,所有处理就都结束了-**数据处理是一个Handler接着一个Handler执行的。**
#### 出站
数据的出站运动(即正在被写的数据)在概念上也是一样的。在这种情况下,数据将从ChannelOutboundHandler 链的尾端开始流动,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,这里显示为 Socket。通常情况下,这将触发一个写操作。
## ChannelHandlerContext
1. 通过使用作为参数传递到每个方法的 ChannelHandlerContext,事件可以被传递给当前ChannelHandler 链中的下一个 ChannelHandler。
2. 因为你有时会忽略那些不感兴趣的事件,所以 Netty提供了抽象基类 ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter。通过调用 ChannelHandlerContext 上的对应方法,每个都提供了简单地将事件传递给下一个 ChannelHandler的方法的实现。随后, 你可以通过重写你所感兴趣的那些方法来扩展这些类。
如果将两个类别的 ChannelHandler都混合添加到同一个 ChannelPipeline 中会发生什么。 虽然 ChannelInboundHandle 和ChannelOutboundHandle 都扩展自 ChannelHandler,但是 Netty 能区分 ChannelInboundHandler 实现和 ChannelOutboundHandler 实现,并确保数据只会在具有相同定向类型的两个 ChannelHandler 之间传递.
### netty发送数据方式
在 Netty 中, 有两种发送消息的方式:
1. 直接写到 Channel 中,消息从ChannelPipeline 的尾端开始流动
2. 写到和 ChannelHandler相关联的ChannelHandlerContext对象中,消息从 ChannelPipeline 中的下一个 ChannelHandler 开始流动。
### 区别
![](https://box.kancloud.cn/94a08bb290751e92aa1c80bf3bd9edbe_963x123.png)
1. ServerBootstrap 将绑定到一个端口,因为服务器必须要监听连接, 而 Bootstrap 则是由想要连接到远程节点的客户端应用程序所使用的。
2. 引导一个客户端只需要一个 EventLoopGroup,但是一个ServerBootstrap 则需要两个(也可以是同一个实例)。
**为什么ServerBootstrap需要两个EventLoopGroup?**
因为服务器需要两组不同的 Channel。第一组将只包含一个 ServerChannel,代表服务器自身的已绑定到某个本地端口的在监听的套接字。 而第二组将包含所有已创建的用来处理传入客户端连接( 对于每个服务器已经接受的连接都有一个) 的 Channel。如下图:
![](https://box.kancloud.cn/f94e81a67fb7a4c5e0f64428deb9676f_906x393.png)
与 ServerChannel 相关联的 EventLoopGroup 将分配一个负责为传入连接请求创建Channel的EventLoop。一旦连接被接受,第二个EventLoopGroup就会给它的Channel分配一个EventLoop。
实际上, ServerBootstrap 类也可以只使用一个 EventLoopGroup,此时其将在两个场景下共用同一个 EventLoopGroup。
## 传输
![](https://box.kancloud.cn/a42b3c16f9354a6efe06a7df0421d099_880x182.png)
![](https://box.kancloud.cn/ae083cf6b4019846fd1b52243acaff1d_876x212.png)
### 1 非阻塞I/O
选择器背后的基本概念是充当一个注册表,在那里你将可以请求在 Channel 的状态发生变化时得到通知并触发动作。可能的状态变化有:
> 1. 新的 Channel 已被接受并且就绪;
> 2. Channel 连接已经完成;
> 3. Channel 有已经就绪的可供读取的数据;
> 4. Channel 可用于写数据。
对应selector模式
![](https://box.kancloud.cn/9b78b20031c226efcd17d1753f7e0fdb_851x235.png)
## ByteBuf——Netty 的数据容器
### 读写索引
## 异常处理
## channel生命周期
![](https://box.kancloud.cn/089a7096c9703f94c54276a705e4f88a_1278x369.png)
- Docker
- 什么是docker
- Docker安装、组件启动
- docker网络
- docker命令
- docker swarm
- dockerfile
- mesos
- 运维
- Linux
- Linux基础
- Linux常用命令_1
- Linux常用命令_2
- ip命令
- 什么是Linux
- SELinux
- Linux GCC编译警告:Clock skew detected. 错误解决办法
- 文件描述符
- find
- 资源统计
- LVM
- Linux相关配置
- 服务自启动
- 服务器安全
- 字符集
- shell脚本
- shell命令
- 实用脚本
- shell 数组
- 循环与判断
- 系统级别进程开启和停止
- 函数
- java调用shell脚本
- 发送邮件
- Linux网络配置
- Ubuntu
- Ubuntu发送邮件
- 更换apt-get源
- centos
- 防火墙
- 虚拟机下配置网络
- yum重新安装
- 安装mysql5.7
- 配置本地yum源
- 安装telnet
- 忘记root密码
- rsync+ crontab
- Zabbix
- Zabbix监控
- Zabbix安装
- 自动报警
- 自动发现主机
- 监控MySQL
- 安装PHP常见错误
- 基于nginx安装zabbix
- 监控Tomcat
- 监控redis
- web监控
- 监控进程和端口号
- zabbix自定义监控
- 触发器函数
- zabbix监控mysql主从同步状态
- Jenkins
- 安装Jenkins
- jenkins+svn+maven
- jenkins执行shell脚本
- 参数化构建
- maven区分环境打包
- jenkins使用注意事项
- nginx
- nginx认证功能
- ubuntu下编译安装Nginx
- 编译安装
- Nginx搭建本地yum源
- 文件共享
- Haproxy
- 初识Haproxy
- haproxy安装
- haproxy配置
- virtualbox
- virtualbox 复制新的虚拟机
- ubuntu下vitrualbox安装redhat
- centos配置双网卡
- 配置存储
- Windows
- Windows安装curl
- VMware vSphere
- 磁盘管理
- 增加磁盘
- gitlab
- 安装
- tomcat
- Squid
- bigdata
- FastDFS
- FastFDS基础
- FastFDS安装及简单实用
- api介绍
- 数据存储
- FastDFS防盗链
- python脚本
- ELK
- logstash
- 安装使用
- kibana
- 安准配置
- elasticsearch
- elasticsearch基础_1
- elasticsearch基础_2
- 安装
- 操作
- java api
- 中文分词器
- term vector
- 并发控制
- 对text字段排序
- 倒排和正排索引
- 自定义分词器
- 自定义dynamic策略
- 进阶练习
- 共享锁和排它锁
- nested object
- 父子关系模型
- 高亮
- 搜索提示
- Redis
- redis部署
- redis基础
- redis运维
- redis-cluster的使用
- redis哨兵
- redis脚本备份还原
- rabbitMQ
- rabbitMQ安装使用
- rpc
- RocketMQ
- 架构概念
- 安装
- 实例
- 好文引用
- 知乎
- ACK
- postgresql
- 存储过程
- 编程语言
- 计算机网络
- 基础_01
- tcp/ip
- http转https
- Let's Encrypt免费ssl证书(基于haproxy负载)
- what's the http?
- 网关
- 网络IO
- http
- 无状态网络协议
- Python
- python基础
- 基础数据类型
- String
- List
- 遍历
- Python基础_01
- python基础_02
- python基础03
- python基础_04
- python基础_05
- 函数
- 网络编程
- 系统编程
- 类
- Python正则表达式
- pymysql
- java调用python脚本
- python操作fastdfs
- 模块导入和sys.path
- 编码
- 安装pip
- python进阶
- python之setup.py构建工具
- 模块动态导入
- 内置函数
- 内置变量
- path
- python模块
- 内置模块_01
- 内置模块_02
- log模块
- collections
- Twisted
- Twisted基础
- 异步编程初探与reactor模式
- yield-inlineCallbacks
- 系统编程
- 爬虫
- urllib
- xpath
- scrapy
- 爬虫基础
- 爬虫种类
- 入门基础
- Rules
- 反反爬虫策略
- 模拟登陆
- problem
- 分布式爬虫
- 快代理整站爬取
- 与es整合
- 爬取APP数据
- 爬虫部署
- collection for ban of web
- crawlstyle
- API
- 多次请求
- 向调度器发送请求
- 源码学习
- LinkExtractor源码分析
- 构建工具-setup.py
- selenium
- 基础01
- 与scrapy整合
- Django
- Django开发入门
- Django与MySQL
- java
- 设计模式
- 单例模式
- 工厂模式
- java基础
- java位移
- java反射
- base64
- java内部类
- java高级
- 多线程
- springmvc-restful
- pfx数字证书
- 生成二维码
- 项目中使用log4j
- 自定义注解
- java发送post请求
- Date时间操作
- spring
- 基础
- spring事务控制
- springMVC
- 注解
- 参数绑定
- springmvc+spring+mybatis+dubbo
- MVC模型
- SpringBoot
- java配置入门
- SpringBoot基础入门
- SpringBoot web
- 整合
- SpringBoot注解
- shiro权限控制
- CommandLineRunner
- mybatis
- 静态资源
- SSM整合
- Aware
- Spring API使用
- Aware接口
- mybatis
- 入门
- mybatis属性自动映射、扫描
- 问题
- @Param 注解在Mybatis中的使用 以及传递参数的三种方式
- mybatis-SQL
- 逆向生成dao、model层代码
- 反向工程中Example的使用
- 自增id回显
- SqlSessionDaoSupport
- invalid bound statement(not found)
- 脉络
- beetl
- beetl是什么
- 与SpringBoot整合
- shiro
- 什么是shiro
- springboot+shrio+mybatis
- 拦截url
- 枚举
- 图片操作
- restful
- java项目中日志处理
- JSON
- 文件工具类
- KeyTool生成证书
- 兼容性问题
- 开发规范
- 工具类开发规范
- 压缩图片
- 异常处理
- web
- JavaScript
- 基础语法
- 创建对象
- BOM
- window对象
- DOM
- 闭包
- form提交-文件上传
- td中内容过长
- 问题1
- js高级
- js文件操作
- 函数_01
- session
- jQuery
- 函数01
- data()
- siblings
- index()与eq()
- select2
- 动态样式
- bootstrap
- 表单验证
- 表格
- MUI
- HTML
- iframe
- label标签
- 规范编程
- layer
- sss
- 微信小程序
- 基础知识
- 实践
- 自定义组件
- 修改自定义组件的样式
- 基础概念
- appid
- 跳转
- 小程序发送ajax
- 微信小程序上下拉刷新
- if
- 工具
- idea
- Git
- maven
- svn
- Netty
- 基础概念
- Handler
- SimpleChannelInboundHandler 与 ChannelInboundHandler
- 网络编程
- 网络I/O
- database
- oracle
- 游标
- PLSQL Developer
- mysql
- MySQL基准测试
- mysql备份
- mysql主从不同步
- mysql安装
- mysql函数大全
- SQL语句
- 修改配置
- 关键字
- 主从搭建
- centos下用rpm包安装mysql
- 常用sql
- information_scheme数据库
- 值得学的博客
- mysql学习
- 运维
- mysql权限
- 配置信息
- 好文mark
- jsp
- jsp EL表达式
- C
- test