diff --git a/README.md b/README.md
index 11f3a7db..641ebdcf 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 4c6f199d..00d043b5 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -55,7 +55,7 @@ module.exports = {
},
],
sidebarDepth: 2, // 侧边栏显示深度,默认1,最大2(显示到h3标题)
- logo: 'https://raw.githubusercontent.com/dunwu/images/dev/common/dunwu-logo.png', // 导航栏logo
+ logo: 'https://gitee.com/quyangzhao/images/tree/dev/common/dunwu-logo.png', // 导航栏logo
repo: 'dunwu/java-tutorial', // 导航栏右侧生成Github链接
searchMaxSuggestions: 10, // 搜索结果显示最大数
lastUpdated: '上次更新', // 更新的时间,及前缀文字 string | boolean (取值为git提交时间)
diff --git "a/docs/02.JavaEE/01.JavaWeb/03.JavaWeb\344\271\213Filter\345\222\214Listener.md" "b/docs/02.JavaEE/01.JavaWeb/03.JavaWeb\344\271\213Filter\345\222\214Listener.md"
index f13e99be..909b249a 100644
--- "a/docs/02.JavaEE/01.JavaWeb/03.JavaWeb\344\271\213Filter\345\222\214Listener.md"
+++ "b/docs/02.JavaEE/01.JavaWeb/03.JavaWeb\344\271\213Filter\345\222\214Listener.md"
@@ -24,7 +24,7 @@ permalink: /pages/5ecb29/
Filter 提供了过滤链(Filter Chain)的概念,一个过滤链包括多个 Filter。客户端请求 request 在抵达 Servlet 之前会经过过滤链的所有 Filter,服务器响应 response 从 Servlet 抵达客户端浏览器之前也会经过过滤链的所有 FIlter。
-
+
### 过滤器方法
diff --git "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/01.Tomcat\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/01.Tomcat\345\277\253\351\200\237\345\205\245\351\227\250.md"
index 77264e08..bfa42a7c 100644
--- "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/01.Tomcat\345\277\253\351\200\237\345\205\245\351\227\250.md"
+++ "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/01.Tomcat\345\277\253\351\200\237\345\205\245\351\227\250.md"
@@ -111,7 +111,7 @@ tar -zxf apache-tomcat-8.5.24.tar.gz
启动后,访问 `http://localhost:8080` ,可以看到 Tomcat 安装成功的测试页面。
-
+
### 2.2. 配置
@@ -364,7 +364,7 @@ public class SimpleTomcatServer {
- 设置启动应用的端口、JVM 参数、启动浏览器等。
- 成功后,可以访问 `http://localhost:8080/`(当然,你也可以在 url 中设置上下文名称)。
-
+
> **说明**
>
@@ -374,7 +374,7 @@ public class SimpleTomcatServer {
## 3. Tomcat 架构
-
+
Tomcat 要实现 2 个核心功能:
@@ -402,7 +402,7 @@ Tomcat 支持的应用层协议有:
Tomcat 支持多种 I/O 模型和应用层协议。为了实现这点,一个容器可能对接多个连接器。但是,单独的连接器或容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作 Service 组件。Tomcat 内可能有多个 Service,通过在 Tomcat 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。
-
+
**一个 Tomcat 实例有一个或多个 Service;一个 Service 有多个 Connector 和 Container**。Connector 和 Container 之间通过标准的 ServletRequest 和 ServletResponse 通信。
@@ -418,13 +418,13 @@ Tomcat 支持多种 I/O 模型和应用层协议。为了实现这点,一个
Tomcat 设计了 3 个组件来实现这 3 个功能,分别是 **`EndPoint`**、**`Processor`** 和 **`Adapter`**。
-
+
组件间通过抽象接口交互。这样做还有一个好处是**封装变化。**这是面向对象设计的精髓,将系统中经常变化的部分和稳定的部分隔离,有助于增加复用性,并降低系统耦合度。网络通信的 I/O 模型是变化的,可能是非阻塞 I/O、异步 I/O 或者 APR。应用层协议也是变化的,可能是 HTTP、HTTPS、AJP。浏览器端发送的请求信息也是变化的。但是整体的处理逻辑是不变的,EndPoint 负责提供字节流给 Processor,Processor 负责提供 Tomcat Request 对象给 Adapter,Adapter 负责提供 ServletRequest 对象给容器。
如果要支持新的 I/O 方案、新的应用层协议,只需要实现相关的具体子类,上层通用的处理逻辑是不变的。由于 I/O 模型和应用层协议可以自由组合,比如 NIO + HTTP 或者 NIO2 + AJP。Tomcat 的设计者将网络通信和应用层协议解析放在一起考虑,设计了一个叫 ProtocolHandler 的接口来封装这两种变化点。各种协议和通信模型的组合有相应的具体实现类。比如:Http11NioProtocol 和 AjpNioProtocol。
-
+
#### 3.2.1. ProtocolHandler 组件
@@ -444,7 +444,7 @@ EndPoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而 Ab
Processor 是一个接口,定义了请求的处理等方法。它的抽象实现类 AbstractProcessor 对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有 AJPProcessor、HTTP11Processor 等,这些具体实现类实现了特定协议的解析方法和请求处理方式。
-
+
从图中我们看到,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。
@@ -471,7 +471,7 @@ Tomcat 是怎么确定请求是由哪个 Wrapper 容器里的 Servlet 来处理
举例来说,假如有一个网购系统,有面向网站管理人员的后台管理系统,还有面向终端客户的在线购物系统。这两个系统跑在同一个 Tomcat 上,为了隔离它们的访问域名,配置了两个虚拟域名:`manage.shopping.com`和`user.shopping.com`,网站管理人员通过`manage.shopping.com`域名访问 Tomcat 去管理用户和商品,而用户管理和商品管理是两个单独的 Web 应用。终端客户通过`user.shopping.com`域名去搜索商品和下订单,搜索功能和订单管理也是两个独立的 Web 应用。如下所示,演示了 url 应声 Servlet 的处理流程。
-
+
假如有用户访问一个 URL,比如图中的`http://user.shopping.com:8080/order/buy`,Tomcat 如何将这个 URL 定位到一个 Servlet 呢?
@@ -490,7 +490,7 @@ Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理
先来了解一下 Valve 和 Pipeline 接口的设计:
-
+
- 每一个容器都有一个 Pipeline 对象,只要触发这个 Pipeline 的第一个 Valve,这个容器里 Pipeline 中的 Valve 就都会被调用到。但是,不同容器的 Pipeline 是怎么链式触发的呢,比如 Engine 中 Pipeline 需要调用下层容器 Host 中的 Pipeline。
- 这是因为 Pipeline 中还有个 getBasic 方法。这个 BasicValve 处于 Valve 链表的末端,它是 Pipeline 中必不可少的一个 Valve,负责调用下层容器的 Pipeline 里的第一个 Valve。
@@ -499,7 +499,7 @@ Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理
- 各层容器对应的 basic valve 分别是 `StandardEngineValve`、`StandardHostValve`、 `StandardContextValve`、`StandardWrapperValve`。
- 由于 Valve 是一个处理点,因此 invoke 方法就是来处理请求的。注意到 Valve 中有 getNext 和 setNext 方法,因此我们大概可以猜到有一个链表将 Valve 链起来了。
-
+
整个调用过程由连接器中的 Adapter 触发的,它会调用 Engine 的第一个 Valve:
@@ -511,7 +511,7 @@ connector.getService().getContainer().getPipeline().getFirst().invoke(request, r
### 4.1. Tomcat 的启动过程
-
+
1. Tomcat 是一个 Java 程序,它的运行从执行 `startup.sh` 脚本开始。`startup.sh` 会启动一个 JVM 来运行 Tomcat 的启动类 `Bootstrap`。
2. `Bootstrap` 会初始化 Tomcat 的类加载器并实例化 `Catalina`。
@@ -731,12 +731,12 @@ ContextConfig 解析 web.xml 顺序:
### 4.3. LifeCycle
-
+
#### 4.3.1. 请求处理过程
-

+
1. 根据 server.xml 配置的指定的 connector 以及端口监听 http、或者 ajp 请求
@@ -747,25 +747,25 @@ ContextConfig 解析 web.xml 顺序:
### 4.4. Connector 流程
-

+
#### 4.4.1. 阻塞 IO
-

+
#### 4.4.2. 非阻塞 IO
-

+
#### 4.4.3. IO 多路复用
-

+
阻塞与非阻塞的区别在于进行读操作和写操作的系统调用时,如果此时内核态没有数据可读或者没有缓冲空间可写时,是否阻塞。
@@ -775,7 +775,7 @@ IO 多路复用的好处在于可同时监听多个 socket 的可读和可写事
#### 4.4.4. Tomcat 各类 Connector 对比
-

+
- JIO:用 java.io 编写的 TCP 模块,阻塞 IO
@@ -796,7 +796,7 @@ Apache Portable Runtime 是一个高度可移植的库,它是 Apache HTTP Serv
**NIO 处理相关类**
-

+
Poller 线程从 EventQueue 获取 PollerEvent,并执行 PollerEvent 的 run 方法,调用 Selector 的 select 方法,如果有可读的 Socket 则创建 Http11NioProcessor,放入到线程池中执行;
@@ -829,7 +829,7 @@ Note:
### 4.6. 异步 Servlet
-

+
传统流程:
@@ -839,7 +839,7 @@ Note:
- 最后,根据处理的结果提交响应,Servlet 线程结束
-

+
异步处理流程:
diff --git "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/02.Tomcat\350\277\236\346\216\245\345\231\250.md" "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/02.Tomcat\350\277\236\346\216\245\345\231\250.md"
index d76dc06d..c280645c 100644
--- "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/02.Tomcat\350\277\236\346\216\245\345\231\250.md"
+++ "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/02.Tomcat\350\277\236\346\216\245\345\231\250.md"
@@ -20,7 +20,7 @@ permalink: /pages/3c954b/
Tomcat 的 NioEndPoint 组件利用 Java NIO 实现了 I/O 多路复用模型。
-
+
NioEndPoint 子组件功能简介:
@@ -123,7 +123,7 @@ private final SynchronizedQueue events = new SynchronizedQueue<>();
Nio2Endpoint 工作流程跟 NioEndpoint 较为相似。
-
+
Nio2Endpoint 子组件功能说明:
@@ -218,7 +218,7 @@ Tomcat 本身是 Java 编写的,为了调用 C 语言编写的 APR,需要通
### 3.1. AprEndpoint 工作流程
-
+
#### 3.1.1. Acceptor
@@ -282,7 +282,7 @@ java my.class
这个命令行中的`java`其实是**一个可执行程序,这个程序会创建 JVM 来加载和运行你的 Java 类**。操作系统会创建一个进程来执行这个`java`可执行程序,而每个进程都有自己的虚拟地址空间,JVM 用到的内存(包括堆、栈和方法区)就是从进程的虚拟地址空间上分配的。请你注意的是,JVM 内存只是进程空间的一部分,除此之外进程空间内还有代码段、数据段、内存映射区、内核空间等。从 JVM 的角度看,JVM 内存之外的部分叫作本地内存,C 程序代码在运行过程中用到的内存就是本地内存中分配的。下面我们通过一张图来理解一下。
-
+
Tomcat 的 Endpoint 组件在接收网络数据时需要预先分配好一块 Buffer,所谓的 Buffer 就是字节数组`byte[]`,Java 通过 JNI 调用把这块 Buffer 的地址传给 C 代码,C 代码通过操作系统 API 读取 Socket 并把数据填充到这块 Buffer。Java NIO API 提供了两种 Buffer 来接收数据:HeapByteBuffer 和 DirectByteBuffer,下面的代码演示了如何创建两种 Buffer。
@@ -323,7 +323,7 @@ Tomcat 中的 AprEndpoint 就是通过 DirectByteBuffer 来接收数据的,而
从下面的图你会发现这个过程有 6 次内存拷贝,并且 read 和 write 等系统调用将导致进程从用户态到内核态的切换,会耗费大量的 CPU 和内存资源。
-
+
而 Tomcat 的 AprEndpoint 通过操作系统层面的 sendfile 特性解决了这个问题,sendfile 系统调用方式非常简洁。
@@ -337,7 +337,7 @@ sendfile(socket, file, len);
第二步:数据并没有从内核缓冲区复制到 Socket 关联的缓冲区,只有记录数据位置和长度的描述符被添加到 Socket 缓冲区中;接着把数据直接从内核缓冲区传递给网卡。这个过程你可以看下面的图。
-
+
## 4. Executor 组件
@@ -508,7 +508,7 @@ Tomcat 用 ProtocolHandler 组件屏蔽应用层协议的差异,其中 Protoco
WebSocket 是通过 HTTP 协议来进行握手的,因此当 WebSocket 的握手请求到来时,HttpProtocolHandler 首先接收到这个请求,在处理这个 HTTP 请求时,Tomcat 通过一个特殊的 Filter 判断该当前 HTTP 请求是否是一个 WebSocket Upgrade 请求(即包含`Upgrade: websocket`的 HTTP 头信息),如果是,则在 HTTP 响应里添加 WebSocket 相关的响应头信息,并进行协议升级。具体来说就是用 UpgradeProtocolHandler 替换当前的 HttpProtocolHandler,相应的,把当前 Socket 的 Processor 替换成 UpgradeProcessor,同时 Tomcat 会创建 WebSocket Session 实例和 Endpoint 实例,并跟当前的 WebSocket 连接一一对应起来。这个 WebSocket 连接不会立即关闭,并且在请求处理中,不再使用原有的 HttpProcessor,而是用专门的 UpgradeProcessor,UpgradeProcessor 最终会调用相应的 Endpoint 实例来处理请求。
-
+
你可以看到,Tomcat 对 WebSocket 请求的处理没有经过 Servlet 容器,而是通过 UpgradeProcessor 组件直接把请求发到 ServerEndpoint 实例,并且 Tomcat 的 WebSocket 实现不需要关注具体 I/O 模型的细节,从而实现了与具体 I/O 方式的解耦。
diff --git "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/03.Tomcat\345\256\271\345\231\250.md" "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/03.Tomcat\345\256\271\345\231\250.md"
index d72a1c6b..cd75cf0d 100644
--- "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/03.Tomcat\345\256\271\345\231\250.md"
+++ "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/01.Tomcat/03.Tomcat\345\256\271\345\231\250.md"
@@ -335,7 +335,7 @@ Tomcat 作为 Web 容器,需要解决以下问题:
2. 两个 Web 应用都依赖同一个第三方的 JAR 包,比如 Spring,那 Spring 的 JAR 包被加载到内存后,Tomcat 要保证这两个 Web 应用能够共享,也就是说 Spring 的 JAR 包只被加载一次,否则随着依赖的第三方 JAR 包增多,JVM 的内存会膨胀。
3. 需要隔离 Tomcat 本身的类和 Web 应用的类。
-
+
#### WebAppClassLoader
diff --git "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/02.Jetty.md" "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/02.Jetty.md"
index 369b7409..34ca9a47 100644
--- "a/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/02.Jetty.md"
+++ "b/docs/02.JavaEE/02.\346\234\215\345\212\241\345\231\250/02.Jetty.md"
@@ -254,7 +254,7 @@ mvn jetty:run
### Jetty 架构简介
-
+
Jetty Server 就是由多个 Connector(连接器)、多个 Handler(处理器),以及一个线程池组成。
@@ -379,7 +379,7 @@ getEndPoint().fillInterested(_readCallback);
到此你应该了解了 Connector 的工作原理,下面我画张图再来回顾一下 Connector 的工作流程。
-
+
1. Acceptor 监听连接请求,当有连接请求到达时就接受连接,一个连接对应一个 Channel,Acceptor 将 Channel 交给 ManagedSelector 来处理。
@@ -427,7 +427,7 @@ public interface Handler extends LifeCycle, Destroyable
Handler 只是一个接口,完成具体功能的还是它的子类。那么 Handler 有哪些子类呢?它们的继承关系又是怎样的?这些子类是如何实现 Servlet 容器功能的呢?
-
+
在 AbstractHandler 之下有 AbstractHandlerContainer,为什么需要这个类呢?这其实是个过渡,为了实现链式调用,一个 Handler 内部必然要有其他 Handler 的引用,所以这个类的名字里才有 Container,意思就是这样的 Handler 里包含了其他 Handler 的引用。
diff --git "a/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/01.Maven\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/01.Maven\345\277\253\351\200\237\345\205\245\351\227\250.md"
index 265088c7..62fa8248 100644
--- "a/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/01.Maven\345\277\253\351\200\237\345\205\245\351\227\250.md"
+++ "b/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/01.Maven\345\277\253\351\200\237\345\205\245\351\227\250.md"
@@ -135,9 +135,9 @@ export PATH=$MAVEN_HOME/bin:$PATH
右键 "计算机",选择 "属性",之后点击 "高级系统设置",点击"环境变量",来设置环境变量,有以下系统变量需要配置:
-
+
-
+
### 检测安装成功
@@ -265,15 +265,15 @@ java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
依次点击 File -> New -> Project 打开创建工程对话框,选择 Maven 工程。
-
+
(2)输入项目信息
-
+
(3)点击 Intellij 侧边栏中的 Maven 工具界面,有几个可以直接使用的 maven 命令,可以帮助你进行构建。
-
+
### 在 Eclipse 中创建 Maven 工程
@@ -285,7 +285,7 @@ java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
点击 Help -> Eclipse Marketplace,搜索 maven 关键字,选择安装红框对应的 Maven 插件。
-
+
(2)Maven 环境配置
@@ -293,7 +293,7 @@ java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
如下图所示,配置 settings.xml 文件的位置
-
+
(3)创建 Maven 工程
@@ -301,7 +301,7 @@ File -> New -> Maven Project -> Next,在接下来的窗口中会看到一大
接下来设置项目的参数,如下:
-
+
**groupId**是项目组织唯一的标识符,实际对应 JAVA 的包的结构,是 main 目录里 java 的目录结构。
@@ -315,11 +315,11 @@ Eclipse 中构建方式:
在 Elipse 项目上右击 -> Run As 就能看到很多 Maven 操作。这些操作和 maven 命令是等效的。例如 Maven clean,等同于 mvn clean 命令。
-
+
你也可以点击 Maven build,输入组合命令,并保存下来。如下图:
-
+
Maven 命令构建方式:
@@ -327,7 +327,7 @@ Maven 命令构建方式:
进入工程所在目录,输入 maven 命令就可以了。
-
+
## 使用说明
diff --git "a/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/04.Maven\345\256\236\346\210\230\351\227\256\351\242\230\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/04.Maven\345\256\236\346\210\230\351\227\256\351\242\230\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md"
index ee3b4efd..096d3ef7 100644
--- "a/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/04.Maven\345\256\236\346\210\230\351\227\256\351\242\230\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md"
+++ "b/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/04.Maven\345\256\236\346\210\230\351\227\256\351\242\230\345\222\214\346\234\200\344\275\263\345\256\236\350\267\265.md"
@@ -48,21 +48,21 @@ maven 的 JDK 源与指定的 JDK 编译版本不符。
Project SDK 是否正确
-
+
SDK 路径是否正确
-
+
- **查看 Settings > Maven 的配置**
JDK for importer 是否正确
-
+
Runner 是否正确
-
+
### 重复引入依赖
diff --git "a/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/05.Maven\346\225\231\347\250\213\344\271\213\345\217\221\345\270\203jar\345\210\260\347\247\201\346\234\215\346\210\226\344\270\255\345\244\256\344\273\223\345\272\223.md" "b/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/05.Maven\346\225\231\347\250\213\344\271\213\345\217\221\345\270\203jar\345\210\260\347\247\201\346\234\215\346\210\226\344\270\255\345\244\256\344\273\223\345\272\223.md"
index 32416c41..d6dfddaa 100644
--- "a/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/05.Maven\346\225\231\347\250\213\344\271\213\345\217\221\345\270\203jar\345\210\260\347\247\201\346\234\215\346\210\226\344\270\255\345\244\256\344\273\223\345\272\223.md"
+++ "b/docs/11.\350\275\257\344\273\266/01.\346\236\204\345\273\272/01.Maven/05.Maven\346\225\231\347\250\213\344\271\213\345\217\221\345\270\203jar\345\210\260\347\247\201\346\234\215\346\210\226\344\270\255\345\244\256\344\273\223\345\272\223.md"
@@ -32,7 +32,7 @@ permalink: /pages/2ddf04/
注册账号成功后,根据你 Java 包的功能分别写上`Summary`、`Description`、`Group Id`、`SCM url`以及`Project URL`等必要信息,可以参见我之前创建的 Issue:[OSSRH-36187](https://issues.sonatype.org/browse/OSSRH-36187)。
-
+
创建完之后需要等待 Sonatype 的工作人员审核处理,审核时间还是很快的,我的审核差不多等待了两小时。当 Issue 的 Status 变为`RESOLVED`后,就可以进行下一步操作了。
@@ -308,7 +308,7 @@ gpg: unchanged: 1
进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。
-
+
本人希望将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。
@@ -340,13 +340,13 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload}
启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示:
-
+
点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。
有必要提一下的是,在 Nexus 的 Repositories 管理页面,展示了可用的 maven 仓库,如下图所示:
-
+
> 说明:
>
diff --git "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/01.\347\233\221\346\216\247\345\267\245\345\205\267\345\257\271\346\257\224.md" "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/01.\347\233\221\346\216\247\345\267\245\345\205\267\345\257\271\346\257\224.md"
index 2c1d9e94..a4864ab0 100644
--- "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/01.\347\233\221\346\216\247\345\267\245\345\205\267\345\257\271\346\257\224.md"
+++ "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/01.\347\233\221\346\216\247\345\267\245\345\205\267\345\257\271\346\257\224.md"
@@ -17,17 +17,17 @@ permalink: /pages/c7c5ec/
## 监控工具发展史
-
+
## 监控工具比对
### 特性对比
-
+
### 生态对比
-
+
## 技术选型
diff --git "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/02.CAT.md" "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/02.CAT.md"
index 639d3a9f..6d559c2d 100644
--- "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/02.CAT.md"
+++ "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/02.CAT.md"
@@ -38,7 +38,7 @@ CAT 监控系统将每次 URL、Service 的请求内部执行情况都封装为
- **Heartbeat** 表示程序内定期产生的统计信息, 如 CPU 利用率, 内存利用率, 连接池状态, 系统负载等
- **Metric** 用于记录业务指标、指标可能包含对一个指标记录次数、记录平均值、记录总和,业务指标最低统计粒度为 1 分钟
-
+
## CAT 部署
@@ -73,7 +73,7 @@ CAT 主要分为三个模块:
在实际开发和部署中,cat-consumer 和 cat-home 是部署在一个 jvm 内部,每个 CAT 服务端都可以作为 consumer 也可以作为 home,这样既能减少整个 CAT 层级结构,也可以增加整个系统稳定性。
-
+
上图是 CAT 目前多机房的整体结构图:
diff --git "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/03.Zipkin.md" "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/03.Zipkin.md"
index a36e4d17..f918b6e9 100644
--- "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/03.Zipkin.md"
+++ "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/03.Zipkin.md"
@@ -30,7 +30,7 @@ Zipkin 基于 Google Dapper 的论文设计而来,由 Twitter 公司开发贡
Zipkin UI 还提供了一个依赖关系图,该关系图显示了每个应用程序中跟踪了多少个请求。这对于识别聚合行为(包括错误路径或对不赞成使用的服务的调用)很有帮助。
-
+
### 多平台
@@ -48,7 +48,7 @@ Zipkin 服务器捆绑了用于采集和存储数据的扩展。
数据以 json 形式存储,可以参考:[Zipkin 官方的 Swagger API](https://zipkin.io/zipkin-api/#/default/post_spans)
-
+
## 二、Zipkin 安装
@@ -94,7 +94,7 @@ ZipKin 可以分为两部分,
架构如下:
-
+
### Zipkin Server
diff --git "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/04.Skywalking.md" "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/04.Skywalking.md"
index e0c84c3e..60113012 100644
--- "a/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/04.Skywalking.md"
+++ "b/docs/11.\350\275\257\344\273\266/03.\347\233\221\346\216\247\350\257\212\346\226\255/04.Skywalking.md"
@@ -24,7 +24,7 @@ SkyWalking 是观察性分析平台和应用性能管理系统。
提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。
-
+
### SkyWalking 特性
@@ -45,7 +45,7 @@ SkyWalking 是观察性分析平台和应用性能管理系统。
从逻辑上讲,SkyWalking 分为四个部分:探针(Probes),平台后端,存储和 UI。
-
+
- **探针(Probes)** - 探针是指集成到目标系统中的代理或 SDK 库。它们负责收集数据(包括跟踪数据和统计数据)并将其按照 SkyWalking 的要求重新格式化为。
- **平台后端** - 平台后端是一个提供后端服务的集群。它用于聚合、分析和驱动从探针到 UI 的流程。它还为传入格式(如 Zipkin 的格式),存储实现程序和集群管理提供可插入功能。 您甚至可以使用 Observability Analysis Language 自定义聚合和分析。
@@ -56,7 +56,7 @@ SkyWalking 是观察性分析平台和应用性能管理系统。
进入 [Apache SkyWalking 官方下载页面](http://skywalking.apache.org/downloads/),选择安装版本,下载解压到本地。
-
+
安装分为三个部分:
diff --git "a/docs/12.\345\267\245\345\205\267/03.\346\250\241\346\235\277\345\274\225\346\223\216/01.Freemark.md" "b/docs/12.\345\267\245\345\205\267/03.\346\250\241\346\235\277\345\274\225\346\223\216/01.Freemark.md"
index a36c7028..ad660158 100644
--- "a/docs/12.\345\267\245\345\205\267/03.\346\250\241\346\235\277\345\274\225\346\223\216/01.Freemark.md"
+++ "b/docs/12.\345\267\245\345\205\267/03.\346\250\241\346\235\277\345\274\225\346\223\216/01.Freemark.md"
@@ -35,7 +35,7 @@ Freemark 模板一句话概括就是:**_`模板 + 数据模型 = 输出`_**
- **FTL 标签**:FTL 标签和 HTML 标签很相似,但是它们却是给 FreeMarker 的指示, 而且不会打印在输出内容中。
- **注释**:注释和 HTML 的注释也很相似,但它们是由 `<#--` 和 `-->`来分隔的。注释会被 FreeMarker 直接忽略, 更不会在输出内容中显示。
-
+
> 🔔 注意:
>
diff --git "a/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/01.Junit.md" "b/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/01.Junit.md"
index defa29af..a130247c 100644
--- "a/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/01.Junit.md"
+++ "b/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/01.Junit.md"
@@ -51,7 +51,7 @@ permalink: /pages/06533c/
组件间依赖关系:
-
+
## JUnit 注解
diff --git "a/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/03.Jmeter.md" "b/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/03.Jmeter.md"
index e1649bff..0940d7f5 100644
--- "a/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/03.Jmeter.md"
+++ "b/docs/12.\345\267\245\345\205\267/04.\346\265\213\350\257\225/03.Jmeter.md"
@@ -45,7 +45,7 @@ Jmeter 的工作原理是仿真用户向服务器发送请求,并收集服务
Jmeter 的工作流如下图所示:
-
+
### 主要元素
@@ -65,7 +65,7 @@ Jmeter 的主要元素如下:
- **`预处理器元素(Pre-Processor Elements)`** - 预处理器元素在采样器发出请求之前执行,如果预处理器附加到采样器元素,那么它将在该采样器元素运行之前执行。预处理器元素用于在运行之前准备环境及参数。
- **`后处理器元素(Post-Processor Elements)`** - 后处理器元素是在发送采样器请求之后执行的元素,常用于处理响应数据。
-
+
> 📌 提示:
>
@@ -99,7 +99,7 @@ Jmeter 的主要元素如下:
Unix 类系统运行 `jmeter` ;Windows 系统运行 `jmeter.bat`
-
+
## 使用
@@ -117,7 +117,7 @@ Unix 类系统运行 `jmeter` ;Windows 系统运行 `jmeter.bat`
- 设置线程数和循环次数
-
+
#### 配置原件
@@ -125,7 +125,7 @@ Unix 类系统运行 `jmeter` ;Windows 系统运行 `jmeter.bat`
- 填写协议、服务器名称或 IP、端口号
-
+
#### 构造 HTTP 请求
@@ -135,35 +135,35 @@ Unix 类系统运行 `jmeter` ;Windows 系统运行 `jmeter.bat`
- 填写方法、路径
- 填写参数、消息体数据、文件上传
-
+
#### 添加 HTTP 请求头
- 在“线程组”上右键 【添加】=>【配置元件】=>【HTTP 信息头管理器】
- 由于我的测试例中传输的数据为 json 形式,所以设置键值对 `Content-Type`:`application/json`
-
+
#### 添加断言
- 在“线程组”上右键 【添加】=>【断言】=>【 响应断言 】
- 在我的案例中,以 HTTP 应答状态码为 200 来判断请求是否成功
-
+
#### 添加察看结果树
- 在“线程组”上右键 【添加】=>【监听器】=>【察看结果树】
- 直接点击运行,就可以查看测试结果
-
+
#### 添加汇总报告
- 在“线程组”上右键 【添加】=>【监听器】=>【汇总报告】
- 直接点击运行,就可以查看测试结果
-
+
#### 保存测试计划
@@ -181,7 +181,7 @@ jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
执行测试计划后,在 `-e -o` 参数后指定的 web 报告目录下,可以找到测试报告内容。在浏览器中打开 `index.html` 文件,可以看到如下报告:
-
+
## 问题
@@ -193,7 +193,7 @@ jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
配置如下所示:
-
+
重要配置说明(其他配置根据实际情况填):
diff --git "a/docs/12.\345\267\245\345\205\267/99.\345\205\266\344\273\226/01.Java\346\227\245\345\277\227.md" "b/docs/12.\345\267\245\345\205\267/99.\345\205\266\344\273\226/01.Java\346\227\245\345\277\227.md"
index c37498e5..e1ce2a35 100644
--- "a/docs/12.\345\267\245\345\205\267/99.\345\205\266\344\273\226/01.Java\346\227\245\345\277\227.md"
+++ "b/docs/12.\345\267\245\345\205\267/99.\345\205\266\344\273\226/01.Java\346\227\245\345\277\227.md"
@@ -64,7 +64,7 @@ logback 当前分成三个模块:`logback-core`、`logback-classic` 和 `logba
Log4j2 架构:
-
+
### Log4j vs Logback vs Log4j2
@@ -108,7 +108,7 @@ common-logging 的功能是提供日志功能的 API 接口,本身并不提供
[官网地址](http://www.slf4j.org/)
-
+
### common-logging vs slf4j
@@ -202,7 +202,7 @@ _slf4j-jdk14-1.7.21.jar_ 会自动将 _slf4j-api-1.7.21.jar_ 也添加到你的
假如你正在开发应用程序所调用的组件当中已经使用了 common-logging,这时你需要 jcl-over-slf4j.jar 把日志信息输出重定向到 slf4j-api,slf4j-api 再去调用 slf4j 实际依赖的日志组件。这个过程称为桥接。下图是官方的 slf4j 桥接策略图:
-
+
从图中应该可以看出,无论你的老项目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用对应的桥接 jar 包来解决兼容问题。
@@ -418,7 +418,7 @@ log4j2 基本配置形式如下:
- 要点
- 它有 ``、``、`` 三个子元素。
-
+
### ``
@@ -456,7 +456,7 @@ log4j2 基本配置形式如下:
- 属性
- class:设置具体的实例化类。
-
+
### ``
diff --git "a/docs/13.\346\241\206\346\236\266/11.ORM/01.Mybatis\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/docs/13.\346\241\206\346\236\266/11.ORM/01.Mybatis\345\277\253\351\200\237\345\205\245\351\227\250.md"
index 704af1b1..d313862f 100644
--- "a/docs/13.\346\241\206\346\236\266/11.ORM/01.Mybatis\345\277\253\351\200\237\345\205\245\351\227\250.md"
+++ "b/docs/13.\346\241\206\346\236\266/11.ORM/01.Mybatis\345\277\253\351\200\237\345\205\245\351\227\250.md"
@@ -19,11 +19,11 @@ permalink: /pages/538358/
> MyBatis 的前身就是 iBatis ,是一个作用在数据持久层的对象关系映射(Object Relational Mapping,简称 ORM)框架。
-
+
## Mybatis 简介
-
+
### 什么是 MyBatis
diff --git "a/docs/13.\346\241\206\346\236\266/11.ORM/02.Mybatis\345\216\237\347\220\206.md" "b/docs/13.\346\241\206\346\236\266/11.ORM/02.Mybatis\345\216\237\347\220\206.md"
index d36c074d..0f4e5bb8 100644
--- "a/docs/13.\346\241\206\346\236\266/11.ORM/02.Mybatis\345\216\237\347\220\206.md"
+++ "b/docs/13.\346\241\206\346\236\266/11.ORM/02.Mybatis\345\216\237\347\220\206.md"
@@ -229,7 +229,7 @@ public class MybatisDemo {
## Mybatis 生命周期
-
+
### SqlSessionFactoryBuilder
@@ -239,7 +239,7 @@ public class MybatisDemo {
`Configuration` 类包含了对一个 `SqlSessionFactory` 实例你可能关心的所有内容。
-
+
`SqlSessionFactoryBuilder` 应用了建造者设计模式,它有五个 `build` 方法,允许你通过不同的资源创建 `SqlSessionFactory` 实例。
@@ -261,7 +261,7 @@ SqlSessionFactory build(Configuration config)
**`SqlSessionFactory` 负责创建 `SqlSession` 实例。**
-
+
`SqlSessionFactory` 应用了工厂设计模式,它提供了一组方法,用于创建 SqlSession 实例。
@@ -304,7 +304,7 @@ Configuration getConfiguration();
SqlSession 类的方法可以按照下图进行大致分类:
-
+
#### SqlSession 生命周期
@@ -338,7 +338,7 @@ Mybatis 会根据相应的接口声明的方法信息,通过动态代理机制
下面的示例展示了一些方法签名以及它们是如何映射到 `SqlSession` 上的。
-
+
> **注意**
>
@@ -383,7 +383,7 @@ Mybatis 支持诸如 `@Insert`、`@Update`、`@Delete`、`@Select`、`@Result`
这些组件的架构层次如下:
-
+
### 配置层
@@ -437,13 +437,13 @@ Mybatis 和数据库的交互有两种方式:
- 如果开启了二级缓存,`SqlSession` 会先使用 `CachingExecutor` 对象来处理查询请求。`CachingExecutor` 会在二级缓存中查看是否有匹配的数据,如果匹配,则直接返回缓存结果;如果缓存中没有,再交给真正的 `Executor` 对象来完成查询,之后 `CachingExecutor` 会将真正 `Executor` 返回的查询结果放置到缓存中,然后在返回给用户。
- 二级缓存的生命周期是应用级别的。
-
+
## SqlSession 内部工作机制
从前文,我们已经了解了,Mybatis 封装了对数据库的访问,把对数据库的会话和事务控制放到了 SqlSession 对象中。那么具体是如何工作的呢?接下来,我们通过源码解读来进行分析。
-
+
`SqlSession` 对于 insert、update、delete、select 的内部处理机制基本上大同小异。所以,接下来,我会以一次完整的 select 查询流程为例讲解 `SqlSession` 内部的工作机制。相信读者如果理解了 select 的处理流程,对于其他 CRUD 操作也能做到一通百通。
@@ -459,7 +459,7 @@ Mybatis 和数据库的交互有两种方式:
Executor 即执行器,它负责生成动态 SQL 以及管理缓存。
-
+
- `Executor` 即执行器接口。
- `BaseExecutor` 是 `Executor` 的抽象类,它采用了模板方法设计模式,内置了一些共性方法,而将定制化方法留给子类去实现。
@@ -474,7 +474,7 @@ Executor 即执行器,它负责生成动态 SQL 以及管理缓存。
`StatementHandler` 的家族成员:
-
+
- `StatementHandler` 是接口;
- `BaseStatementHandler` 是实现 `StatementHandler` 的抽象类,内置一些共性方法;
@@ -572,7 +572,7 @@ Mybatis 所有的配置信息都维持在 `Configuration` 对象之中。中维
`MappedStatement` 维护了一个 Mapper 方法的元数据信息,其数据组织可以参考下面的 debug 截图:
-
+
> 小结:
>
diff --git "a/docs/13.\346\241\206\346\236\266/11.ORM/README.md" "b/docs/13.\346\241\206\346\236\266/11.ORM/README.md"
index fe20c394..58fe194b 100644
--- "a/docs/13.\346\241\206\346\236\266/11.ORM/README.md"
+++ "b/docs/13.\346\241\206\346\236\266/11.ORM/README.md"
@@ -21,7 +21,7 @@ permalink: /pages/e873e1/
> Mybatis 的前身就是 iBatis ,是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。本文以一个 Mybatis 完整示例为切入点,结合 Mybatis 底层源码分析,图文并茂的讲解 Mybatis 的核心工作机制。
-
+
### [Mybatis 快速入门](01.Mybatis快速入门.md)
diff --git "a/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/01.Shiro.md" "b/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/01.Shiro.md"
index e2b4f31e..56d32447 100644
--- "a/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/01.Shiro.md"
+++ "b/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/01.Shiro.md"
@@ -24,7 +24,7 @@ permalink: /pages/cd25bf/
### Shiro 特性
-
+
核心功能:
@@ -48,7 +48,7 @@ permalink: /pages/cd25bf/
### Shiro 架构概述
-
+
- **Subject** - **主题**。它代表当前用户,`Subject` 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它——当前和软件交互的任何事件。`Subject` 是 Shiro 的入口。
@@ -63,7 +63,7 @@ permalink: /pages/cd25bf/
`SecurityManager` 是 Shiro 框架核心中的核心,它相当于 Shiro 的总指挥,负责调度所有行为,包括:认证、授权、获取安全数据(调用 `Realm`)、会话管理等。
-
+
`SecurityManager` 聚合了以下组件:
@@ -128,7 +128,7 @@ currentUser.logout();
### 认证流程
-
+
1. 应用程序代码调用 `Subject.login` 方法,传入构造的 `AuthenticationToken` 实例,该实例代表最终用户的 `Principals` 和 `Credentials`。
@@ -282,7 +282,7 @@ public void updateAccount(Account userAccount) {
### 授权流程
-
+
1. 应用程序或框架代码调用任何 `Subject` 的 `hasRole*`,`checkRole*`,`isPermitted*` 或 `checkPermission*` 方法,并传入所需的权限或角色。
diff --git "a/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/02.SpringSecurity.md" "b/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/02.SpringSecurity.md"
index 4c136eb7..179d7c88 100644
--- "a/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/02.SpringSecurity.md"
+++ "b/docs/13.\346\241\206\346\236\266/12.\345\256\211\345\205\250/02.SpringSecurity.md"
@@ -70,7 +70,7 @@ try {
Spring Security 框架中的认证数据模型如下:
-
+
- `Authentication` - 认证信息实体。
- `principal` - 用户标识。如:用户名、账户名等。通常是 `UserDetails` 的实例(后面详细讲解)。
diff --git "a/docs/13.\346\241\206\346\236\266/14.\345\276\256\346\234\215\345\212\241/01.Dubbo.md" "b/docs/13.\346\241\206\346\236\266/14.\345\276\256\346\234\215\345\212\241/01.Dubbo.md"
index ec3281ff..cdf7b861 100644
--- "a/docs/13.\346\241\206\346\236\266/14.\345\276\256\346\234\215\345\212\241/01.Dubbo.md"
+++ "b/docs/13.\346\241\206\346\236\266/14.\345\276\256\346\234\215\345\212\241/01.Dubbo.md"
@@ -37,7 +37,7 @@ RPC(Remote Procedure Call),即远程过程调用,它是一种通过网
#### RPC 工作流程
-
+
1. 服务消费方(client)调用以本地调用方式调用服务;
2. client stub 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
@@ -188,7 +188,7 @@ Dubbo 支持多种配置方式:
- Properties 最后,相当于缺省值,只有 XML 没有配置时,dubbo.properties 的相应配置项才会生效,通常用于共享公共配置,比如应用名。
-

+
#### xml 配置
@@ -264,7 +264,7 @@ dubbo.registry.address=10.20.153.10:9090
#### 配置之间的关系
-

+
#### 配置覆盖关系
@@ -277,7 +277,7 @@ dubbo.registry.address=10.20.153.10:9090
其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。
-

+
### 动态配置中心
@@ -310,7 +310,7 @@ configCenter.setAddress("zookeeper://127.0.0.1:2181");
### Dubbo 核心组件
-

+
节点角色:
@@ -350,7 +350,7 @@ configCenter.setAddress("zookeeper://127.0.0.1:2181");
### Dubbo 架构层次
-

+
图例说明:
@@ -487,7 +487,7 @@ Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
-

+
- **Failover** - **失败自动切换**,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
@@ -702,7 +702,7 @@ public class HelloServiceMock implements HelloService {
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
-

+
配置方式:
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/02.\346\266\210\346\201\257\351\230\237\345\210\227\345\237\272\346\234\254\345\216\237\347\220\206.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/02.\346\266\210\346\201\257\351\230\237\345\210\227\345\237\272\346\234\254\345\216\237\347\220\206.md"
index fa5d6f9c..bd6cd7ee 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/02.\346\266\210\346\201\257\351\230\237\345\210\227\345\237\272\346\234\254\345\216\237\347\220\206.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/02.\346\266\210\346\201\257\351\230\237\345\210\227\345\237\272\346\234\254\345\216\237\347\220\206.md"
@@ -57,11 +57,11 @@ MQ 通信模型大致有以下类型:
假设这样一个场景,用户向系统 A 发起请求,系统 A 处理计算只需要 10 ms,然后通知系统 BCD 写库,系统 BCD 写库耗时分别为:100ms、200ms、300ms。最终总耗时为: 10+100ms+200ms+300ms=610ms。此外,加上请求和响应的网络传输时间,从用户角度看,可能要等待将近 1s 才能得到结果。
-
+
如果使用 MQ,系统 A 接到请求后,耗时 10ms 处理计算,然后向系统 BCD 连续发送消息,假设耗时 5ms。那么 这一过程的总耗时为 3ms + 5ms = 8ms,这相比于 610 ms,大大缩短了响应时间。至于系统 BCD 的写库操作,只要自行消费 MQ 后处理即可,用户无需关注。
-
+
### 系统解耦
@@ -76,11 +76,11 @@ MQ 通信模型大致有以下类型:
如果需要和新的系统建立通信或删除已建立的通信,都需要修改代码,这种方案显然耦合度很高。
-
+
如果使用 MQ,系统间的通信只需要通过发布/订阅(Pub/Sub)模型即可,彼此没有直接联系,也就不需要相互感知,从而达到 **解耦**。
-
+
### 流量削峰
@@ -92,11 +92,11 @@ MQ 通信模型大致有以下类型:
假设某个系统读写数据库的稳定性能为每秒处理 1000 条数据。平常情况下,远远达不到这么大的处理量。假设,因为因为做活动,系统的瞬时请求量剧增,达到每秒 10000 个并发请求,数据库根本承受不了,可能直接就把数据库给整崩溃了,这样系统服务就不可用了。
-
+
如果使用 MQ,每秒写入 10000 条请求,但是系统 A 每秒只从 MQ 中消费 1000 条请求,然后写入数据库。这样,就不会超过数据库的承受能力,而是把请求积压在 MQ 中。只要高峰期一过,系统 A 就会很快把积压的消息给处理掉。
-
+
### 传输缓冲
@@ -104,7 +104,7 @@ MQ 通信模型大致有以下类型:
例如,Kafka 常被用于做为各种日志数据、采集数据的数据中转。然后,Kafka 将数据转发给 Logstash、Elasticsearch 中,然后基于 Elasticsearch 来做日志中心,提供检索、聚合、分析日志的能力。开发者可以通过 Kibana 集成 Elasticsearch 数据进行可视化展示,或自行进行定制化开发。
-
+
(2)MQ 也可以被用于流式处理。
@@ -155,7 +155,7 @@ MQ 主要引入了以下问题:
Kafka 的客户端和 Broker 都会保存 Offset。客户端消费消息后,每隔一段时间,就把已消费的 Offset 提交给 Kafka Broker,表示已消费。
-
+
在这个过程中,如果客户端应用消费消息后,因为宕机、重启等情况而没有提交已消费的 Offset 。当系统恢复后,会继续消费消息,由于 Offset 未提交,就会出现重复消费的问题。
@@ -217,7 +217,7 @@ MQ 重复消费不可怕,可怕的是没有应对机制,可以借鉴的思
- 消费方维护 N 个缓存队列,具有相同 ID 的数据都写入同一个队列中;
- 创建 N 个线程,每个线程只负责从指定的一个队列中取数据。
-
+
### 消息积压
@@ -249,7 +249,7 @@ MQ 重复消费不可怕,可怕的是没有应对机制,可以借鉴的思
- Kafka 日志的分区(Partition)分布在 Kafka 集群的节点上。每个节点在处理数据和请求时,共享这些分区。每一个分区都会在已配置的节点上进行备份,确保容错性。
-
+
#### Kafka 的副本机制
@@ -268,7 +268,7 @@ Kafka 在 0.8 以前的版本中,如果一个 Broker 宕机了,其上面的
**同一个 Topic 的不同 Partition 会分布在多个 Broker 上,而且一个 Partition 还会在其他的 Broker 上面进行备份**,Producer 在发布消息到某个 Partition 时,先找到该 Partition 的 Leader,然后向这个 Leader 推送消息;每个 Follower 都从 Leader 拉取消息,拉取消息成功之后,向 Leader 发送一个 ACK 确认。
-
+
> FAQ
>
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/03.RocketMQ.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/03.RocketMQ.md"
index c1cf2288..13529916 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/03.RocketMQ.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/03.RocketMQ.md"
@@ -23,7 +23,7 @@ RocketMQ 是一款开源的分布式消息队列,基于高可用分布式集
RocketMQ 被阿里巴巴捐赠给 Apache,成为 Apache 的孵化项目。
-
+
RocketMQ 有以下核心概念:
@@ -310,7 +310,7 @@ nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &
## 架构
-
+
RocketMQ 由四部分组成:NameServer、Broker、Producer、Consumer。其中任意一个组成都可以水平扩展为集群模式,以避免单点故障问题。
@@ -359,7 +359,7 @@ Broker 有几个重要的子模块:
- **HA Service**:高可用服务,提供 Master Broker 和 Slave Broker 之间的数据同步功能。
- **Index Service**:根据特定的 Message key 对投递到 Broker 的消息进行索引服务,以提供消息的快速查询。
-
+
## 原理
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/04.ActiveMQ.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/04.ActiveMQ.md"
index 04890480..169d8c8a 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/04.ActiveMQ.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/01.MQ/04.ActiveMQ.md"
@@ -30,7 +30,7 @@ JMS 有两种消息模型:
#### P2P 的特点
-
+
在点对点的消息系统中,消息分发给一个单独的使用者。点对点消息往往与队列 `javax.jms.Queue` 相关联。
@@ -44,7 +44,7 @@ JMS 有两种消息模型:
#### Pub/Sub 的特点
-
+
发布/订阅消息系统支持一个事件驱动模型,消息生产者和消费者都参与消息的传递。生产者发布事件,而使用者订阅感兴趣的事件,并使用事件。该类型消息一般与特定的主题 `javax.jms.Topic` 关联。
@@ -58,7 +58,7 @@ JMS 有两种消息模型:
### JMS 编程模型
-
+
#### ConnectionFactory
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/02.Java\347\274\223\345\255\230\344\270\255\351\227\264\344\273\266.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/02.Java\347\274\223\345\255\230\344\270\255\351\227\264\344\273\266.md"
index 84a962ca..6a13d7b4 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/02.Java\347\274\223\345\255\230\344\270\255\351\227\264\344\273\266.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/02.Java\347\274\223\345\255\230\344\270\255\351\227\264\344\273\266.md"
@@ -24,7 +24,7 @@ permalink: /pages/970fa6/
因此,在很多缓存框架、缓存库中,其 API 都参考了 JSR 107 规范。
-
+
Java Caching 定义了 5 个核心接口
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/04.Ehcache.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/04.Ehcache.md"
index 5ffa73cd..5b4713d2 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/04.Ehcache.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/04.Ehcache.md"
@@ -19,7 +19,7 @@ permalink: /pages/c4647d/
> EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。
-
+
## 一、简介
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/README.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/README.md"
index 758a8e1d..e5beea8e 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/README.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/02.\347\274\223\345\255\230/README.md"
@@ -21,7 +21,7 @@ permalink: /pages/71104f/
>
> 如果想深入学习缓存,建议先了解一下 [缓存基本原理](https://dunwu.github.io/design/distributed/分布式缓存.html),有助于理解缓存的特性、原理,使用缓存常见的问题及解决方案。
-
+
## 📖 内容
diff --git "a/docs/14.\344\270\255\351\227\264\344\273\266/03.\346\265\201\351\207\217\346\216\247\345\210\266/01.Hystrix.md" "b/docs/14.\344\270\255\351\227\264\344\273\266/03.\346\265\201\351\207\217\346\216\247\345\210\266/01.Hystrix.md"
index 6d7d8e2a..20a8fca3 100644
--- "a/docs/14.\344\270\255\351\227\264\344\273\266/03.\346\265\201\351\207\217\346\216\247\345\210\266/01.Hystrix.md"
+++ "b/docs/14.\344\270\255\351\227\264\344\273\266/03.\346\265\201\351\207\217\346\216\247\345\210\266/01.Hystrix.md"
@@ -45,15 +45,15 @@ Hystrix 官方宣布**不再发布新版本**。
当一切正常时,整体系统如下所示:
-
+
在高并发场景,这些依赖的稳定性与否对系统的影响非常大,但是依赖有很多不可控问题:如网络连接、资源繁忙、服务宕机等。例如:下图中有一个 QPS 为 50 的依赖 I 出现不可用,但是其他依赖服务是可用的。
-
+
但是,在高并发场景下,当依赖 I 阻塞时,大多数服务器的线程池就出现阻塞(BLOCK)。当这种级联故障愈演愈烈,就可能造成整个线上服务不可用的雪崩效应,如下图:
-
+
Hystrix 就是为了解决这类问题而应运而生。
@@ -71,7 +71,7 @@ Hystrix 具有以下功能:
如果使用 Hystrix 对每个基础依赖服务进行过载保护,则整个系统架构将会类似下图所示,每个依赖项彼此隔离,受到延迟时发生饱和的资源的被限制访问,并包含 fallback 逻辑(用于降级处理),该逻辑决定了在依赖项中发生任何类型的故障时做出对应的处理。
-
+
## Hystrix 核心概念
@@ -79,7 +79,7 @@ Hystrix 具有以下功能:
如下图所示,Hystrix 的工作流程大致可以分为 9 个步骤。
-
+
### (一)包装命令