mirror of
https://github.com/youthlql/JavaYouth.git
synced 2026-03-13 21:33:42 +08:00
图床更新,请注意查看readme
This commit is contained in:
@@ -7,7 +7,7 @@ categories:
|
||||
- 入门
|
||||
keywords: Netty
|
||||
description: 第一话对BIO和NIO进行了讲解,为后续做准备。
|
||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg'
|
||||
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/netty_logo.jpg'
|
||||
abbrlink: 3f9283e7
|
||||
date: 2021-04-08 14:21:58
|
||||
---
|
||||
@@ -33,7 +33,7 @@ date: 2021-04-08 14:21:58
|
||||
|
||||
相对简单的一个体系图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0001.png"/>
|
||||
|
||||
## Netty 的应用场景
|
||||
|
||||
@@ -67,7 +67,7 @@ date: 2021-04-08 14:21:58
|
||||
|
||||
## Netty 的学习资料参考
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0002.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0002.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -97,11 +97,11 @@ date: 2021-04-08 14:21:58
|
||||
2. `Java` 共支持 `3` 种网络编程模型 `I/O` 模式:`BIO`、`NIO`、`AIO`。
|
||||
3. `Java BIO`:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。【简单示意图】
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0003.png"/>
|
||||
|
||||
4. `Java NIO`:同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 `I/O` 请求就进行处理。【简单示意图】
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0004.png"/>
|
||||
|
||||
5. `Java AIO(NIO.2)`:异步非阻塞,`AIO` 引入异步通道的概念,采用了 `Proactor` 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
|
||||
6. 我们依次展开讲解。
|
||||
@@ -120,7 +120,7 @@ date: 2021-04-08 14:21:58
|
||||
|
||||
## Java BIO 工作机制
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0005.png"/>
|
||||
|
||||
对 `BIO` 编程流程的梳理
|
||||
|
||||
@@ -207,7 +207,7 @@ public class BIOServer {
|
||||
}
|
||||
```
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0006.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public class BasicBuffer {
|
||||
3. `BIO` 基于字节流和字符流进行操作,而 `NIO` 基于 `Channel`(通道)和 `Buffer`(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。`Selector`(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。
|
||||
4. Buffer和Channel之间的数据流向是双向的
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0007.png"/>
|
||||
|
||||
## NIO 三大核心原理示意图
|
||||
|
||||
@@ -287,7 +287,7 @@ public class BasicBuffer {
|
||||
|
||||
关系图的说明:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0008.png"/>
|
||||
|
||||
1. 每个 `Channel` 都会对应一个 `Buffer`。
|
||||
2. `Selector` 对应一个线程,一个线程对应多个 `Channel`(连接)。
|
||||
@@ -303,17 +303,17 @@ public class BasicBuffer {
|
||||
|
||||
缓冲区(`Buffer`):缓冲区本质上是一个**可以读写数据的内存块**,可以理解成是一个**容器对象(含数组)**,该对象提供了一组方法,可以更轻松地使用内存块,,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。`Channel` 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由 `Buffer`,如图:【后面举例说明】
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0009.png"/>
|
||||
|
||||
### Buffer 类及其子类
|
||||
|
||||
1. 在 `NIO` 中,`Buffer` 是一个顶层父类,它是一个抽象类,类的层级关系图:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0010.png" />
|
||||
|
||||
2. `Buffer` 类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0011.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -321,13 +321,13 @@ public class BasicBuffer {
|
||||
|
||||
3. `Buffer` 类相关方法一览
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0013.png" />
|
||||
|
||||
### ByteBuffer
|
||||
|
||||
从前面可以看出对于 `Java` 中的基本数据类型(`boolean` 除外),都有一个 `Buffer` 类型与之相对应,最常用的自然是 `ByteBuffer` 类(二进制数据),该类的主要方法如下:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0014.png"/>
|
||||
|
||||
## 通道(Channel)
|
||||
|
||||
@@ -343,7 +343,7 @@ public class BasicBuffer {
|
||||
5. `FileChannel` 用于文件的数据读写,`DatagramChannel` 用于 `UDP` 的数据读写,`ServerSocketChannel` 和 `SocketChannel` 用于 `TCP` 的数据读写。
|
||||
6. 图示
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0015.png"/>
|
||||
|
||||
### FileChannel 类
|
||||
|
||||
@@ -444,7 +444,7 @@ public class NIOFileChannel02 {
|
||||
2. 拷贝一个文本文件 `1.txt`,放在项目下即可
|
||||
3. 代码演示
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0016.png"/>
|
||||
|
||||
```java
|
||||
package com.atguigu.nio;
|
||||
@@ -721,7 +721,7 @@ public class ScatteringAndGatheringTest {
|
||||
|
||||
### Selector 示意图和特点说明
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0017.png"/>
|
||||
|
||||
说明如下:
|
||||
|
||||
@@ -733,7 +733,7 @@ public class ScatteringAndGatheringTest {
|
||||
|
||||
### Selector 类相关方法
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0018.png"/>
|
||||
|
||||
### 注意事项
|
||||
|
||||
@@ -748,7 +748,7 @@ public class ScatteringAndGatheringTest {
|
||||
|
||||
`NIO` 非阻塞网络编程相关的(`Selector`、`SelectionKey`、`ServerScoketChannel` 和 `SocketChannel`)关系梳理图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0019.png"/>
|
||||
|
||||
对上图的说明:
|
||||
|
||||
@@ -938,21 +938,21 @@ public static final int OP_ACCEPT = 1 << 4;
|
||||
|
||||
2. `SelectionKey` 相关方法
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0020.png"/>
|
||||
|
||||
### ServerSocketChannel
|
||||
|
||||
1. `ServerSocketChannel` 在服务器端监听新的客户端 `Socket` 连接,负责监听,不负责实际的读写操作
|
||||
2. 相关方法如下
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0021.png"/>
|
||||
|
||||
### SocketChannel
|
||||
|
||||
1. `SocketChannel`,网络 `IO` 通道,**具体负责进行读写操作**。`NIO` 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
|
||||
2. 相关方法如下
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0022.png"/>
|
||||
|
||||
## NIO网络编程应用实例 - 群聊系统
|
||||
|
||||
@@ -965,7 +965,7 @@ public static final int OP_ACCEPT = 1 << 4;
|
||||
5. 目的:进一步理解 `NIO` 非阻塞网络编程机制
|
||||
6. 示意图分析和代码
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0023.png"/>
|
||||
|
||||
代码:
|
||||
|
||||
@@ -1243,7 +1243,7 @@ socket.getOutputStream().write(arr);
|
||||
|
||||
### 传统 IO 模型
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0024.png"/>
|
||||
|
||||
**DMA**:`direct memory access` 直接内存拷贝(不使用 `CPU`)
|
||||
|
||||
@@ -1252,19 +1252,19 @@ socket.getOutputStream().write(arr);
|
||||
1. `mmap` 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图
|
||||
2. `mmap` 示意图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0025.png"/>
|
||||
|
||||
### sendFile 优化
|
||||
|
||||
1. `Linux2.1` 版本提供了 `sendFile` 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 `SocketBuffer`,同时,由于和用户态完全无关,就减少了一次上下文切换
|
||||
2. 示意图和小结
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0026.png"/>
|
||||
|
||||
3. 提示:零拷贝从操作系统角度,是没有 `cpu` 拷贝
|
||||
4. `Linux在2.4` 版本中,做了一些修改,避免了从内核缓冲区拷贝到 `Socketbuffer` 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图和小结:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_001/0027.png"/>
|
||||
|
||||
5. 这里其实有一次 `cpu` 拷贝 `kernel buffer` -> `socket buffer` 但是,拷贝的信息很少,比如 `lenght`、`offset` 消耗低,可以忽略
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ categories:
|
||||
- 入门
|
||||
keywords: Netty
|
||||
description: 对前面两话一些迷惑的点进行细说,讲解handler调用机制,TCP粘包,以及用netty写一个十分简单的RPC。
|
||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg'
|
||||
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/netty_logo.jpg'
|
||||
abbrlink: 429acc6d
|
||||
date: 2021-04-21 17:38:58
|
||||
---
|
||||
@@ -25,7 +25,7 @@ date: 2021-04-21 17:38:58
|
||||
1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码[示意图]
|
||||
2. `codec`(编解码器)的组成部分有两个:`decoder`(解码器)和 `encoder`(编码器)。`encoder` 负责把业务数据转换成字节码数据,`decoder` 负责把字节码数据转换成业务数据
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>
|
||||
|
||||
## Netty 本身的编码解码的机制和问题分析
|
||||
|
||||
@@ -54,7 +54,7 @@ date: 2021-04-21 17:38:58
|
||||
8. 然后通过 `protoc.exe` 编译器根据 `.proto` 自动生成 `.java` 文件
|
||||
9. `protobuf` 使用示意图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>
|
||||
|
||||
## Protobuf 快速入门实例
|
||||
|
||||
@@ -91,7 +91,7 @@ message Student { //会在 StudentPOJO 外部类生成一个内部类 Student,
|
||||
protoc.exe --java_out=.Student.proto
|
||||
将生成的 StudentPOJO 放入到项目使用
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>
|
||||
|
||||
生成的StudentPOJO代码太长就不贴在这里了
|
||||
|
||||
@@ -676,7 +676,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
||||
2. `ChannelHandler` 充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现 `ChannelInboundHandler` 接口(或 `ChannelInboundHandlerAdapter`),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从 `ChannelInboundHandler` 冲刷数据。业务逻辑通常写在一个或者多个 `ChannelInboundHandler` 中。`ChannelOutboundHandler` 原理一样,只不过它是用来处理出站数据的
|
||||
3. `ChannelPipeline` 提供了 `ChannelHandler` 链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过 `pipeline` 中的一系列 `ChannelOutboundHandler`,并被这些 `Handler` 处理,反之则称为入站的
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>
|
||||
|
||||
> 出站,入站如果搞不清楚,看下面的**Netty的handler链的调用机制**,通过一个例子和图讲清楚
|
||||
|
||||
@@ -689,12 +689,12 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
1. 关系继承图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>
|
||||
|
||||
2. 由于不可能知道远程节点是否会一次性发送一个完整的信息,`tcp` 有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.【后面有说TCP的粘包和拆包问题】
|
||||
3. 一个关于 `ByteToMessageDecoder` 实例分析
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -710,7 +710,7 @@ public class NettyClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
> 读者可以看下这个图,带着这个图去看下面的例子。
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -978,11 +978,11 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
|
||||
|
||||
### 效果
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0008.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0008.png"/>
|
||||
|
||||
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1002,14 +1002,10 @@ public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
|
||||
|
||||
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>
|
||||
|
||||
|
||||
|
||||
> 下面是Netty官方源码给的图,我个人觉的不是太好理解,上面的图好理解一些
|
||||
|
||||

|
||||
|
||||
## ByteToMessageDecoder的小细节
|
||||
|
||||
|
||||
@@ -1090,13 +1086,13 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
|
||||
|
||||
如下图验证结果:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>
|
||||
|
||||
|
||||
|
||||
2. 同时又引出了一个小问题
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>
|
||||
|
||||
当我们`MyClientHandler`传一个Long时,会调用我们的`MyLongToByteEncoder`的编码器。那么控制台就会打印这样一句话:**MyLongToByteEncoder encode 被调用**。但是这里并没有调用编码器,这是为什么呢?
|
||||
|
||||
@@ -1149,7 +1145,7 @@ public class MyByteToLongDecoder extends ByteToMessageDecoder {
|
||||
ctx.writeAndFlush(Unpooled.copiedBuffer("abcdabcdabcdabcd",CharsetUtil.UTF_8));
|
||||
```
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />
|
||||
|
||||
|
||||
|
||||
@@ -1198,7 +1194,7 @@ public class MyByteToLongDecoder2 extends ReplayingDecoder<Void> {
|
||||
|
||||
## 其它编解码器
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1249,7 +1245,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
|
||||
|
||||
3. 演示整合
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>
|
||||
|
||||
|
||||
|
||||
@@ -1261,7 +1257,7 @@ log4j.appender.stdout.layout.ConversionPattern=[%p]%C{1}-%m%n
|
||||
2. 由于 `TCP` 无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包、拆包问题,看一张图
|
||||
3. `TCP` 粘包、拆包图解
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>
|
||||
|
||||
|
||||
假设客户端分别发送了两个数据包 `D1` 和 `D2` 给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况:
|
||||
@@ -1502,11 +1498,11 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
|
||||
**Client**
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>
|
||||
|
||||
**Server**
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1514,13 +1510,13 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
|
||||
**Client**
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>
|
||||
|
||||
|
||||
|
||||
**Server**
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1538,7 +1534,7 @@ public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
1. 要求客户端发送 `5` 个 `Message` 对象,客户端每次发送一个 `Message` 对象
|
||||
2. 服务器端每次接收一个 `Message`,分 `5` 次进行解码,每读取到一个 `Message`,会回复一个 `Message` 对象给客户端。
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1996,7 +1992,7 @@ MyMessageEncoder encode 方法被调用
|
||||
1. `RPC(Remote Procedure Call)`—远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
|
||||
2. 两个或多个应用程序都分布在不同的服务器上,它们之间的调用都像是本地方法调用一样(如图)
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>
|
||||
|
||||
过程:
|
||||
|
||||
@@ -2018,11 +2014,11 @@ MyMessageEncoder encode 方法被调用
|
||||
|
||||
3. 常见的 `RPC` 框架有:比较知名的如阿里的 `Dubbo`、`Google` 的 `gRPC`、`Go` 语言的 `rpcx`、`Apache` 的 `thrift`,`Spring` 旗下的 `SpringCloud`。
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>
|
||||
|
||||
## 我们的RPC 调用流程图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>
|
||||
|
||||
**RPC 调用流程说明**
|
||||
|
||||
@@ -2052,7 +2048,7 @@ MyMessageEncoder encode 方法被调用
|
||||
3. 创建一个消费者,该类需要透明的调用自己不存在的方法,内部需要使用 `Netty` 请求提供者返回数据
|
||||
4. 开发的分析图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ categories:
|
||||
- 入门
|
||||
keywords: Netty
|
||||
description: 对Netty的架构进行了解析,主要是Reactor设计模式的多种解决方案。同时讲解了Netty的核心模块组件。
|
||||
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg'
|
||||
cover: 'https://upyunimg.imlql.cn/lql_static@latest/logo/netty_logo.jpg'
|
||||
abbrlink: f846f3f
|
||||
date: 2021-04-15 14:21:58
|
||||
---
|
||||
@@ -29,7 +29,7 @@ date: 2021-04-15 14:21:58
|
||||
|
||||
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0001.png"/>
|
||||
|
||||
## Netty 的优点
|
||||
|
||||
@@ -80,7 +80,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
1. 当并发数很大,就会创建大量的线程,占用很大系统资源
|
||||
2. 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的`read` 操作,导致上面的处理线程资源浪费
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0002.png" />
|
||||
|
||||
## Reactor 模式
|
||||
|
||||
@@ -97,7 +97,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
1. 基于线程池复用线程资源:不必再为每个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务。(解决了当并发数很大时,会创建大量线程,占用很大系统资源)
|
||||
2. 基于 `I/O` 复用模型:多个客户端进行连接,先把连接请求给`ServiceHandler`。多个连接共用一个阻塞对象`ServiceHandler`。假设,当C1连接没有数据要处理时,C1客户端只需要阻塞于`ServiceHandler`,C1之前的处理线程便可以处理其他有数据的连接,不会造成线程资源的浪费。当C1连接再次有数据时,`ServiceHandler`根据线程池的空闲状态,将请求分发给空闲的线程来处理C1连接的任务。(解决了线程资源浪费的那个问题)
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0003.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### I/O 复用结合线程池,就是 Reactor 模式基本设计思想,如图
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0004.png"/>
|
||||
|
||||
对上图说明:
|
||||
|
||||
@@ -132,7 +132,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
原理图,并使用 `NIO` 群聊系统验证
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0005.png" />
|
||||
|
||||
### 方案说明
|
||||
|
||||
@@ -155,7 +155,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### 方案说明
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0006.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -178,11 +178,11 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
针对单 `Reactor` 多线程模型中,`Reactor` 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 `Reactor` 在多线程中运行
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0007.png" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0007.png" />
|
||||
|
||||
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0008.jpg" />
|
||||
|
||||
> SubReactor是可以有多个的,如果只有一个SubReactor的话那和`单 Reactor 多线程`就没什么区别了。
|
||||
|
||||
@@ -197,7 +197,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### Scalable IO in Java 对 Multiple Reactors 的原理图解
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0009.jpg"/>
|
||||
|
||||
### 方案优缺点说明
|
||||
|
||||
@@ -230,7 +230,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
`Netty` 主要基于主从 `Reactors` 多线程模型(如图)做了一定的改进,其中主从 `Reactor` 多线程模型有多个 `Reactor`
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0010.png"/>
|
||||
|
||||
**对上图说明**
|
||||
|
||||
@@ -240,7 +240,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### 工作原理示意图2 - 进阶版
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0011.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ Netty is an asynchronous event-driven network application framework for rapid de
|
||||
|
||||
### 工作原理示意图3 - 详细版
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0012.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -669,9 +669,9 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
下面第一张图就是管道,中间会经过多个handler
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0013.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0013.png"/>
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0014.png"/>
|
||||
|
||||
说明:
|
||||
|
||||
@@ -917,11 +917,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
2. `ChannelHandler` 本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类
|
||||
3. `ChannelHandler` 及其实现类一览图(后)
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0015.png"/>
|
||||
|
||||
4. 我们经常需要自定义一个 `Handler` 类去继承 `ChannelInboundHandlerAdapter`,然后通过重写相应方法实现业务逻辑,我们接下来看看一般都需要重写哪些方法
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0016.png"/>
|
||||
|
||||
## Pipeline 和 ChannelPipeline
|
||||
|
||||
@@ -931,7 +931,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
2. `ChannelPipeline` 实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及 `Channel` 中各个的 `ChannelHandler` 如何相互交互
|
||||
3. 在 `Netty` 中每个 `Channel` 都有且仅有一个 `ChannelPipeline` 与之对应,它们的组成关系如下
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0017.png"/>
|
||||
|
||||
4. 常用方法
|
||||
`ChannelPipeline addFirst(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的第一个位置`ChannelPipeline addLast(ChannelHandler... handlers)`,把一个业务处理类(`handler`)添加到链中的最后一个位置
|
||||
@@ -940,7 +940,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
|
||||
从head看一下debug
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0018.jpg"/>
|
||||
|
||||
- `TestServerInitializer`和`HttpServerCodec`这些东西本身也是`handler`
|
||||
- 一般来说事件从客户端往服务器走我们称为出站,反之则是入站。
|
||||
@@ -950,7 +950,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
1. 保存 `Channel` 相关的所有上下文信息,同时关联一个 `ChannelHandler` 对象
|
||||
2. 即 `ChannelHandlerContext` 中包含一个具体的事件处理器 `ChannelHandler`,同时 `ChannelHandlerContext` 中也绑定了对应的 `pipeline` 和 `Channel` 的信息,方便对 `ChannelHandler` 进行调用。
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0019.jpg"/>
|
||||
|
||||
3. 常用方法
|
||||
|
||||
@@ -959,14 +959,14 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
- `ChannelFuture writeAndFlush(Object msg)`,将数据写到
|
||||
- `ChannelPipeline` 中当前 `ChannelHandler` 的下一个 `ChannelHandler` 开始处理(出站)
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0020.png"/>
|
||||
|
||||
## ChannelOption
|
||||
|
||||
1. `Netty` 在创建 `Channel` 实例后,一般都需要设置 `ChannelOption` 参数。
|
||||
2. `ChannelOption` 参数如下:
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0021.png"/>
|
||||
|
||||
## EventLoopGroup 和其实现类 NioEventLoopGroup
|
||||
|
||||
@@ -974,7 +974,7 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
2. `EventLoopGroup` 提供 `next` 接口,可以从组里面按照一定规则获取其中一个 `EventLoop` 来处理任务。在 `Netty` 服务器端编程中,我们一般都需要提供两个 `EventLoopGroup`,例如:`BossEventLoopGroup` 和 `WorkerEventLoopGroup`。
|
||||
3. 通常一个服务端口即一个 `ServerSocketChannel` 对应一个 `Selector` 和一个 `EventLoop` 线程。`BossEventLoop` 负责接收客户端的连接并将 `SocketChannel` 交给 `WorkerEventLoopGroup` 来进行 `IO` 处理,如下图所示
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0022.png"/>
|
||||
|
||||
4. 常用方法
|
||||
`public NioEventLoopGroup()`,构造方法
|
||||
@@ -985,11 +985,11 @@ public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObjec
|
||||
1. `Netty` 提供一个专门用来操作缓冲区(即 `Netty` 的数据容器)的工具类
|
||||
2. 常用方法如下所示
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0023.png"/>
|
||||
|
||||
3. 举例说明 `Unpooled` 获取 `Netty` 的数据容器 `ByteBuf` 的基本使用
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0024.png"/>
|
||||
|
||||
案例 1
|
||||
|
||||
@@ -1096,7 +1096,7 @@ public class NettyByteBuf02 {
|
||||
|
||||
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0025.png"/>
|
||||
|
||||
代码如下:
|
||||
|
||||
@@ -1523,7 +1523,7 @@ public class MyServerHandler extends ChannelInboundHandlerAdapter {
|
||||
4. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知
|
||||
5. 运行界面
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0026.png"/>
|
||||
|
||||
|
||||
|
||||
@@ -1724,7 +1724,7 @@ public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<Tex
|
||||
|
||||
可以看到并不是发一次数据,连接就关闭了,而是可以继续发送。
|
||||
|
||||
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>
|
||||
<img src="https://upyunimg.imlql.cn/youthlql@1.0.0/netty/introduction/chapter_002/0027.png"/>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user