Skip to content

Commit 63e9b72

Browse files
committed
docs: 更新文档
1 parent 26da14a commit 63e9b72

File tree

10 files changed

+35
-35
lines changed

10 files changed

+35
-35
lines changed

assets/framework/Dubbo.xmind

214 KB
Binary file not shown.

docs/framework/mybatis/Mybatis原理.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ public class MybatisDemo {
243243
244244
## 2. Mybatis 生命周期
245245

246-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510113446.png)
246+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510113446.png)
247247

248248
### 2.1. SqlSessionFactoryBuilder
249249

@@ -253,7 +253,7 @@ public class MybatisDemo {
253253

254254
`Configuration` 类包含了对一个 `SqlSessionFactory` 实例你可能关心的所有内容。
255255

256-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210508173040.png)
256+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210508173040.png)
257257

258258
`SqlSessionFactoryBuilder` 应用了建造者设计模式,它有五个 `build` 方法,允许你通过不同的资源创建 `SqlSessionFactory` 实例。
259259

@@ -275,7 +275,7 @@ SqlSessionFactory build(Configuration config)
275275

276276
**`SqlSessionFactory` 负责创建 `SqlSession` 实例。**
277277

278-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510105641.png)
278+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510105641.png)
279279

280280
`SqlSessionFactory` 应用了工厂设计模式,它提供了一组方法,用于创建 SqlSession 实例。
281281

@@ -318,7 +318,7 @@ Configuration getConfiguration();
318318
319319
SqlSession 类的方法可以按照下图进行大致分类:
320320

321-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510110638.png)
321+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510110638.png)
322322

323323
#### SqlSession 生命周期
324324

@@ -352,7 +352,7 @@ Mybatis 会根据相应的接口声明的方法信息,通过动态代理机制
352352

353353
下面的示例展示了一些方法签名以及它们是如何映射到 `SqlSession` 上的。
354354

355-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512111723.png)
355+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512111723.png)
356356

357357
> **注意**
358358
>
@@ -397,7 +397,7 @@ Mybatis 支持诸如 `@Insert`、`@Update`、`@Delete`、`@Select`、`@Result`
397397

398398
这些组件的架构层次如下:
399399

400-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512114852.png)
400+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512114852.png)
401401

402402
### 3.1. 配置层
403403

@@ -451,13 +451,13 @@ Mybatis 和数据库的交互有两种方式:
451451
- 如果开启了二级缓存,`SqlSession` 会先使用 `CachingExecutor` 对象来处理查询请求。`CachingExecutor` 会在二级缓存中查看是否有匹配的数据,如果匹配,则直接返回缓存结果;如果缓存中没有,再交给真正的 `Executor` 对象来完成查询,之后 `CachingExecutor` 会将真正 `Executor` 返回的查询结果放置到缓存中,然后在返回给用户。
452452
- 二级缓存的生命周期是应用级别的。
453453

454-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512185709.png)
454+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512185709.png)
455455

456456
## 4. SqlSession 内部工作机制
457457

458458
从前文,我们已经了解了,Mybatis 封装了对数据库的访问,把对数据库的会话和事务控制放到了 SqlSession 对象中。那么具体是如何工作的呢?接下来,我们通过源码解读来进行分析。
459459

460-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512173437.png)
460+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512173437.png)
461461

462462
`SqlSession` 对于 insert、update、delete、select 的内部处理机制基本上大同小异。所以,接下来,我会以一次完整的 select 查询流程为例讲解 `SqlSession` 内部的工作机制。相信读者如果理解了 select 的处理流程,对于其他 CRUD 操作也能做到一通百通。
463463

@@ -473,7 +473,7 @@ Mybatis 和数据库的交互有两种方式:
473473

474474
Executor 即执行器,它负责生成动态 SQL 以及管理缓存。
475475

476-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512150000.png)
476+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512150000.png)
477477

478478
- `Executor` 即执行器接口。
479479
- `BaseExecutor``Executor` 的抽象类,它采用了模板方法设计模式,内置了一些共性方法,而将定制化方法留给子类去实现。
@@ -488,7 +488,7 @@ Executor 即执行器,它负责生成动态 SQL 以及管理缓存。
488488

489489
`StatementHandler` 的家族成员:
490490

491-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512160243.png)
491+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210512160243.png)
492492

493493
- `StatementHandler` 是接口;
494494
- `BaseStatementHandler` 是实现 `StatementHandler` 的抽象类,内置一些共性方法;
@@ -586,7 +586,7 @@ Mybatis 所有的配置信息都维持在 `Configuration` 对象之中。中维
586586

587587
`MappedStatement` 维护了一个 Mapper 方法的元数据信息,其数据组织可以参考下面的 debug 截图:
588588

589-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210511150650.png)
589+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210511150650.png)
590590

591591
> 小结:
592592
>

docs/framework/mybatis/Mybatis应用指南.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
## 1. Mybatis 简介
2828

29-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510164925.png)
29+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210510164925.png)
3030

3131
### 1.1. 什么是 MyBatis
3232

docs/framework/mybatis/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> Mybatis 的前身就是 iBatis ,是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。本文以一个 Mybatis 完整示例为切入点,结合 Mybatis 底层源码分析,图文并茂的讲解 Mybatis 的核心工作机制。
44
5-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210522101005.png)
5+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210522101005.png)
66

77
## 📖 内容
88

docs/mq/消息队列基本原理.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ MQ 主要引入了以下问题:
181181

182182
Kafka 的客户端和 Broker 都会保存 Offset。客户端消费消息后,每隔一段时间,就把已消费的 Offset 提交给 Kafka Broker,表示已消费。
183183

184-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210427194009.png)
184+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210427194009.png)
185185

186186
在这个过程中,如果客户端应用消费消息后,因为宕机、重启等情况而没有提交已消费的 Offset 。当系统恢复后,会继续消费消息,由于 Offset 未提交,就会出现重复消费的问题。
187187

@@ -243,7 +243,7 @@ MQ 重复消费不可怕,可怕的是没有应对机制,可以借鉴的思
243243
- 消费方维护 N 个缓存队列,具有相同 ID 的数据都写入同一个队列中;
244244
- 创建 N 个线程,每个线程只负责从指定的一个队列中取数据。
245245

246-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210427194215.png)
246+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20210427194215.png)
247247

248248
### 3.4. 消息积压
249249

docs/server/Tomcat容器.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ Tomcat 作为 Web 容器,需要解决以下问题:
341341
2. 两个 Web 应用都依赖同一个第三方的 JAR 包,比如 Spring,那 SpringJAR 包被加载到内存后,Tomcat 要保证这两个 Web 应用能够共享,也就是说 SpringJAR 包只被加载一次,否则随着依赖的第三方 JAR 包增多,JVM 的内存会膨胀。
342342
3. 需要隔离 Tomcat 本身的类和 Web 应用的类。
343343

344-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201130141536.png)
344+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201130141536.png)
345345

346346
#### WebAppClassLoader
347347

docs/server/Tomcat应用指南.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ public class SimpleTomcatServer {
384384

385385
## 3. Tomcat 架构
386386

387-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201113193431.png)
387+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201113193431.png)
388388

389389
Tomcat 要实现 2 个核心功能:
390390

@@ -412,7 +412,7 @@ Tomcat 支持的应用层协议有:
412412

413413
Tomcat 支持多种 I/O 模型和应用层协议。为了实现这点,一个容器可能对接多个连接器。但是,单独的连接器或容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作 Service 组件。Tomcat 内可能有多个 Service,通过在 Tomcat 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。
414414

415-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201111093124.png)
415+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201111093124.png)
416416

417417
**一个 Tomcat 实例有一个或多个 Service;一个 Service 有多个 Connector 和 Container**。Connector 和 Container 之间通过标准的 ServletRequest 和 ServletResponse 通信。
418418

@@ -428,13 +428,13 @@ Tomcat 支持多种 I/O 模型和应用层协议。为了实现这点,一个
428428

429429
Tomcat 设计了 3 个组件来实现这 3 个功能,分别是 **`EndPoint`****`Processor`****`Adapter`**
430430

431-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201111101440.png)
431+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201111101440.png)
432432

433433
组件间通过抽象接口交互。这样做还有一个好处是**封装变化。**这是面向对象设计的精髓,将系统中经常变化的部分和稳定的部分隔离,有助于增加复用性,并降低系统耦合度。网络通信的 I/O 模型是变化的,可能是非阻塞 I/O、异步 I/O 或者 APR。应用层协议也是变化的,可能是 HTTP、HTTPS、AJP。浏览器端发送的请求信息也是变化的。但是整体的处理逻辑是不变的,EndPoint 负责提供字节流给 Processor,Processor 负责提供 Tomcat Request 对象给 Adapter,Adapter 负责提供 ServletRequest 对象给容器。
434434

435435
如果要支持新的 I/O 方案、新的应用层协议,只需要实现相关的具体子类,上层通用的处理逻辑是不变的。由于 I/O 模型和应用层协议可以自由组合,比如 NIO + HTTP 或者 NIO2 + AJP。Tomcat 的设计者将网络通信和应用层协议解析放在一起考虑,设计了一个叫 ProtocolHandler 的接口来封装这两种变化点。各种协议和通信模型的组合有相应的具体实现类。比如:Http11NioProtocol 和 AjpNioProtocol。
436436

437-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201027091819.png)
437+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201027091819.png)
438438

439439
#### ProtocolHandler 组件
440440

@@ -454,7 +454,7 @@ EndPoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而 Ab
454454

455455
Processor 是一个接口,定义了请求的处理等方法。它的抽象实现类 AbstractProcessor 对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有 AJPProcessor、HTTP11Processor 等,这些具体实现类实现了特定协议的解析方法和请求处理方式。
456456

457-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201113185929.png)
457+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201113185929.png)
458458

459459
从图中我们看到,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。
460460

@@ -481,7 +481,7 @@ Tomcat 是怎么确定请求是由哪个 Wrapper 容器里的 Servlet 来处理
481481

482482
举例来说,假如有一个网购系统,有面向网站管理人员的后台管理系统,还有面向终端客户的在线购物系统。这两个系统跑在同一个 Tomcat 上,为了隔离它们的访问域名,配置了两个虚拟域名:`manage.shopping.com``user.shopping.com`,网站管理人员通过`manage.shopping.com`域名访问 Tomcat 去管理用户和商品,而用户管理和商品管理是两个单独的 Web 应用。终端客户通过`user.shopping.com`域名去搜索商品和下订单,搜索功能和订单管理也是两个独立的 Web 应用。如下所示,演示了 url 应声 Servlet 的处理流程。
483483

484-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201113192022.jpg)
484+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201113192022.jpg)
485485

486486
假如有用户访问一个 URL,比如图中的`http://user.shopping.com:8080/order/buy`,Tomcat 如何将这个 URL 定位到一个 Servlet 呢?
487487

@@ -509,7 +509,7 @@ Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理
509509
- 各层容器对应的 basic valve 分别是 `StandardEngineValve``StandardHostValve``StandardContextValve``StandardWrapperValve`
510510
- 由于 Valve 是一个处理点,因此 invoke 方法就是来处理请求的。注意到 Valve 中有 getNext 和 setNext 方法,因此我们大概可以猜到有一个链表将 Valve 链起来了。
511511

512-
![](https://raw.githubusercontent.com/dunwu/images/dev/cs/java/javaweb/tools/tomcat/请求处理过程.png)
512+
![img](https://raw.githubusercontent.com/dunwu/images/dev/cs/java/javaweb/tools/tomcat/请求处理过程.png)
513513

514514
整个调用过程由连接器中的 Adapter 触发的,它会调用 Engine 的第一个 Valve:
515515

@@ -521,7 +521,7 @@ connector.getService().getContainer().getPipeline().getFirst().invoke(request, r
521521

522522
### 4.1. Tomcat 的启动过程
523523

524-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201118145455.png)
524+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201118145455.png)
525525

526526
1. Tomcat 是一个 Java 程序,它的运行从执行 `startup.sh` 脚本开始。`startup.sh` 会启动一个 JVM 来运行 Tomcat 的启动类 `Bootstrap`
527527
2. `Bootstrap` 会初始化 Tomcat 的类加载器并实例化 `Catalina`
@@ -741,7 +741,7 @@ ContextConfig 解析 web.xml 顺序:
741741

742742
### 4.3. LifeCycle
743743

744-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201118105012.png)
744+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201118105012.png)
745745

746746
#### 4.2.3. 请求处理过程
747747

docs/server/Tomcat连接器.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
Tomcat 的 NioEndPoint 组件利用 Java NIO 实现了 I/O 多路复用模型。
2929

30-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127094302.jpg)
30+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127094302.jpg)
3131

3232
NioEndPoint 子组件功能简介:
3333

@@ -130,7 +130,7 @@ private final SynchronizedQueue<PollerEvent> events = new SynchronizedQueue<>();
130130

131131
Nio2Endpoint 工作流程跟 NioEndpoint 较为相似。
132132

133-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127143839.jpg)
133+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127143839.jpg)
134134

135135
Nio2Endpoint 子组件功能说明:
136136

@@ -225,7 +225,7 @@ Tomcat 本身是 Java 编写的,为了调用 C 语言编写的 APR,需要通
225225

226226
### 3.1. AprEndpoint 工作流程
227227

228-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127145740.jpg)
228+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127145740.jpg)
229229

230230
#### Acceptor
231231

@@ -289,7 +289,7 @@ java my.class
289289

290290
这个命令行中的`java`其实是**一个可执行程序,这个程序会创建 JVM 来加载和运行你的 Java**。操作系统会创建一个进程来执行这个`java`可执行程序,而每个进程都有自己的虚拟地址空间,JVM 用到的内存(包括堆、栈和方法区)就是从进程的虚拟地址空间上分配的。请你注意的是,JVM 内存只是进程空间的一部分,除此之外进程空间内还有代码段、数据段、内存映射区、内核空间等。从 JVM 的角度看,JVM 内存之外的部分叫作本地内存,C 程序代码在运行过程中用到的内存就是本地内存中分配的。下面我们通过一张图来理解一下。
291291

292-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127150729.jpg)
292+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127150729.jpg)
293293

294294
TomcatEndpoint 组件在接收网络数据时需要预先分配好一块 Buffer,所谓的 Buffer 就是字节数组`byte[]`,Java 通过 JNI 调用把这块 Buffer 的地址传给 C 代码,C 代码通过操作系统 API 读取 Socket 并把数据填充到这块 BufferJava NIO API 提供了两种 Buffer 来接收数据:HeapByteBufferDirectByteBuffer,下面的代码演示了如何创建两种 Buffer
295295

@@ -330,7 +330,7 @@ Tomcat 中的 AprEndpoint 就是通过 DirectByteBuffer 来接收数据的,而
330330

331331
从下面的图你会发现这个过程有 6 次内存拷贝,并且 read 和 write 等系统调用将导致进程从用户态到内核态的切换,会耗费大量的 CPU 和内存资源。
332332

333-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127151041.jpg)
333+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127151041.jpg)
334334

335335
TomcatAprEndpoint 通过操作系统层面的 sendfile 特性解决了这个问题,sendfile 系统调用方式非常简洁。
336336

@@ -344,7 +344,7 @@ sendfile(socket, file, len);
344344

345345
第二步:数据并没有从内核缓冲区复制到 Socket 关联的缓冲区,只有记录数据位置和长度的描述符被添加到 Socket 缓冲区中;接着把数据直接从内核缓冲区传递给网卡。这个过程你可以看下面的图。
346346

347-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127151155.jpg)
347+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127151155.jpg)
348348

349349
## 4. Executor 组件
350350

@@ -515,7 +515,7 @@ Tomcat 用 ProtocolHandler 组件屏蔽应用层协议的差异,其中 Protoco
515515

516516
WebSocket 是通过 HTTP 协议来进行握手的,因此当 WebSocket 的握手请求到来时,HttpProtocolHandler 首先接收到这个请求,在处理这个 HTTP 请求时,Tomcat 通过一个特殊的 Filter 判断该当前 HTTP 请求是否是一个 WebSocket Upgrade 请求(即包含`Upgrade: websocket`的 HTTP 头信息),如果是,则在 HTTP 响应里添加 WebSocket 相关的响应头信息,并进行协议升级。具体来说就是用 UpgradeProtocolHandler 替换当前的 HttpProtocolHandler,相应的,把当前 SocketProcessor 替换成 UpgradeProcessor,同时 Tomcat 会创建 WebSocket Session 实例和 Endpoint 实例,并跟当前的 WebSocket 连接一一对应起来。这个 WebSocket 连接不会立即关闭,并且在请求处理中,不再使用原有的 HttpProcessor,而是用专门的 UpgradeProcessorUpgradeProcessor 最终会调用相应的 Endpoint 实例来处理请求。
517517

518-
![](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127153521.jpg)
518+
![img](https://raw.githubusercontent.com/dunwu/images/dev/snap/20201127153521.jpg)
519519

520520
你可以看到,Tomcat 对 WebSocket 请求的处理没有经过 Servlet 容器,而是通过 UpgradeProcessor 组件直接把请求发到 ServerEndpoint 实例,并且 Tomcat 的 WebSocket 实现不需要关注具体 I/O 模型的细节,从而实现了与具体 I/O 方式的解耦。
521521

0 commit comments

Comments
 (0)