跟换图片源
This commit is contained in:
parent
f039b42ea7
commit
57ce004275
@ -1,6 +1,6 @@
|
|||||||
# Full-Stack-Notes
|
# Full-Stack-Notes
|
||||||
|
|
||||||
<div align="center"> <img width="380px" src="pictures/full-stack-notes-logo.png"/> </div>
|
<div align="center"> <img width="380px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/full-stack-notes-logo.png"/> </div>
|
||||||
<br/>
|
<br/>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="pictures/芽.png"/>
|
<img src="pictures/芽.png"/>
|
||||||
@ -218,6 +218,6 @@ TODO
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div align="center"> <img width="200px" src="pictures/blog-logo.png"/> </div>
|
<div align="center"> <img width="200px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/blog-logo.png"/> </div>
|
||||||
|
|
||||||
<div align="center"> <a href = "https://blog.csdn.net/m0_37809146"> 欢迎关注我的博客:https://blog.csdn.net/m0_37809146</a> </div>
|
<div align="center"> <a href = "https://blog.csdn.net/m0_37809146"> 欢迎关注我的博客:https://blog.csdn.net/m0_37809146</a> </div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Full-Stack-Notes
|
# Full-Stack-Notes
|
||||||
|
|
||||||
<div align="center"> <img width="380px" src="pictures/full-stack-notes-logo.png"/> </div>
|
<div align="center"> <img width="380px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/full-stack-notes-logo.png"/> </div>
|
||||||
<br/>
|
<br/>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="pictures/芽.png"/>
|
<img src="pictures/芽.png"/>
|
||||||
@ -145,6 +145,6 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div align="center"> <img width="200px" src="pictures/blog-logo.png"/> </div>
|
<div align="center"> <img width="200px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/blog-logo.png"/> </div>
|
||||||
|
|
||||||
<div align="center"> <a href = "https://blog.csdn.net/m0_37809146"> 欢迎关注我的博客:https://blog.csdn.net/m0_37809146</a> </div>
|
<div align="center"> <a href = "https://blog.csdn.net/m0_37809146"> 欢迎关注我的博客:https://blog.csdn.net/m0_37809146</a> </div>
|
||||||
|
@ -31,7 +31,7 @@ CAP 理论强调:一个分布式系统不可能同时满足一致性(C:Con
|
|||||||
|
|
||||||
首先对于一个分布式系统而言,网络分区是不可避免的,不可能永远不出现网络故障,所以分区容错性 P 必须要保证。假设一个分布式系统中出现了网络分区,如下:
|
首先对于一个分布式系统而言,网络分区是不可避免的,不可能永远不出现网络故障,所以分区容错性 P 必须要保证。假设一个分布式系统中出现了网络分区,如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/cap_示例.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/cap_示例.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
假设用户 1 向节点 1 上增加了 10 个数据,但节点 1 和节点 2 之间因为网络分区而无法进行数据同步,碰巧用户 2 此时发起了查询请求,此时有两种处理方案:
|
假设用户 1 向节点 1 上增加了 10 个数据,但节点 1 和节点 2 之间因为网络分区而无法进行数据同步,碰巧用户 2 此时发起了查询请求,此时有两种处理方案:
|
||||||
@ -51,7 +51,7 @@ CAP 理论强调:一个分布式系统不可能同时满足一致性(C:Con
|
|||||||
+ **保证 CP ,放弃 A**:这种情况下如果发生了网络分区故障,此时节点间的数据就无法同步。因此在故障修复前都需要放弃对外提供服务,直至网络恢复,数据到达一致为止。
|
+ **保证 CP ,放弃 A**:这种情况下如果发生了网络分区故障,此时节点间的数据就无法同步。因此在故障修复前都需要放弃对外提供服务,直至网络恢复,数据到达一致为止。
|
||||||
+ **保证 AP ,放弃 C**:这种情况相当于放弃一致性。具体而言,是放弃数据的强一致性,但保证数据的最终一致性。因为不论是什么系统,数据最终都需要保持一致,否则整个系统就无法使用。在这种策略下,在某个短暂的时间窗口内会存在数据不一致的情况。
|
+ **保证 AP ,放弃 C**:这种情况相当于放弃一致性。具体而言,是放弃数据的强一致性,但保证数据的最终一致性。因为不论是什么系统,数据最终都需要保持一致,否则整个系统就无法使用。在这种策略下,在某个短暂的时间窗口内会存在数据不一致的情况。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/cap理论.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/cap理论.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
## 二、BASE 理论
|
## 二、BASE 理论
|
||||||
|
@ -19,13 +19,13 @@ Docker 使用 Go 语言进行开发,基于 Linux 内核的 cgroup,namespace
|
|||||||
|
|
||||||
下图体现了 Docker 和传统虚拟化方式的差异:传统虚拟机技术是虚拟出一套硬件后,再在其上运行一个完整操作系统,之后可以在该系统上运行所需的应用进程;而 Docker 容器内的应用进程则是直接运行于宿主的内核,容器没有自己的内核,也没有进行硬件虚拟,因此要比传统虚拟机更为轻便。
|
下图体现了 Docker 和传统虚拟化方式的差异:传统虚拟机技术是虚拟出一套硬件后,再在其上运行一个完整操作系统,之后可以在该系统上运行所需的应用进程;而 Docker 容器内的应用进程则是直接运行于宿主的内核,容器没有自己的内核,也没有进行硬件虚拟,因此要比传统虚拟机更为轻便。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/docker与虚拟机.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/docker与虚拟机.png"/> </div>
|
||||||
|
|
||||||
## 二、Docker 架构与核心概念
|
## 二、Docker 架构与核心概念
|
||||||
|
|
||||||
Docker 使用 client-server 架构, Docker 客户端将命令发送给 Docker 守护进程,后者负责构建,运行和分发 Docker 容器。 Docker 客户端和守护程序使用 REST API,通过 UNIX 套接字或网络接口进行通信。核心概念如下:
|
Docker 使用 client-server 架构, Docker 客户端将命令发送给 Docker 守护进程,后者负责构建,运行和分发 Docker 容器。 Docker 客户端和守护程序使用 REST API,通过 UNIX 套接字或网络接口进行通信。核心概念如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/docker架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/docker架构.png"/> </div>
|
||||||
|
|
||||||
### 2.1 镜像
|
### 2.1 镜像
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ Docker 客户端(docker)是用户与 Docker 交互的主要方式。当你
|
|||||||
|
|
||||||
Docker 提供了大量命令用于管理镜像、容器和服务,命令的统一使用格式为:`docker [OPTIONS] COMMAND` ,其中 OPTIONS 代表可选参数。需要注意的是 Docker 命令的执行一般都需要获取 root 权限,这是因为 Docker 的命令行工具 docker 与 docker daemon 是同一个二进制文件,docker daemon 负责接收并执行来自 docker 的命令,它的运行需要 root 权限。所有常用命令及其使用场景如下:
|
Docker 提供了大量命令用于管理镜像、容器和服务,命令的统一使用格式为:`docker [OPTIONS] COMMAND` ,其中 OPTIONS 代表可选参数。需要注意的是 Docker 命令的执行一般都需要获取 root 权限,这是因为 Docker 的命令行工具 docker 与 docker daemon 是同一个二进制文件,docker daemon 负责接收并执行来自 docker 的命令,它的运行需要 root 权限。所有常用命令及其使用场景如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/docker常用命令.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/docker常用命令.jpg"/> </div>
|
||||||
|
|
||||||
### 3.1 基础命令
|
### 3.1 基础命令
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ docker run -it -p 8080:8080 spring-boot-base-java
|
|||||||
|
|
||||||
这里为了观察到启动效果,所以使用交互的方式启动,实际部署时可以使用`-d`参数来后台启动,输出如下:
|
这里为了观察到启动效果,所以使用交互的方式启动,实际部署时可以使用`-d`参数来后台启动,输出如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/dockerfile01.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/dockerfile01.png"/> </div>
|
||||||
|
|
||||||
### 5.2 基于 JDK 镜像部署 Spring Boot 项目
|
### 5.2 基于 JDK 镜像部署 Spring Boot 项目
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ OpenSSL 是一个开源的底层密码库,封装了所有的密码学算法,
|
|||||||
|
|
||||||
HTTPS (Hyper Text Transfer Protocol over SecureSocket Layer,超文本传输安全协议)是在 HTTP 的基础上通过 SSL/TLS 层来进行传输加密和身份认证,从而保证通讯的安全性。除此之外它的报文结构、请求方法等都完全沿用 HTTP 原有的模式,因此可以很方便地将原有 HTTP 服务转换为 HTTPS 服务。
|
HTTPS (Hyper Text Transfer Protocol over SecureSocket Layer,超文本传输安全协议)是在 HTTP 的基础上通过 SSL/TLS 层来进行传输加密和身份认证,从而保证通讯的安全性。除此之外它的报文结构、请求方法等都完全沿用 HTTP 原有的模式,因此可以很方便地将原有 HTTP 服务转换为 HTTPS 服务。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 二、数据安全
|
## 二、数据安全
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ HTTPS (Hyper Text Transfer Protocol over SecureSocket Layer,超文本传输
|
|||||||
|
|
||||||
HTTPS 的公钥可以由任意的客户端持有,但私钥只能由服务器持有,因此所有经过公钥加密的数据只能由对应的服务器解密。
|
HTTPS 的公钥可以由任意的客户端持有,但私钥只能由服务器持有,因此所有经过公钥加密的数据只能由对应的服务器解密。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_非对称加密.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_非对称加密.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.2 对称加密
|
### 2.2 对称加密
|
||||||
@ -60,7 +60,7 @@ HTTPS 的公钥可以由任意的客户端持有,但私钥只能由服务器
|
|||||||
|
|
||||||
对称加密算法采用同一个密钥来进行信息的加密和解密,因此也被称为单密钥加密算法,其加解密速度都比较快。HTTPS 会在握手阶段采用非对称加密算法来生成对称加密所需的密钥,而之后的过程,因为要涉及到大量业务信息的传输,故都采用对称加密来提升性能。
|
对称加密算法采用同一个密钥来进行信息的加密和解密,因此也被称为单密钥加密算法,其加解密速度都比较快。HTTPS 会在握手阶段采用非对称加密算法来生成对称加密所需的密钥,而之后的过程,因为要涉及到大量业务信息的传输,故都采用对称加密来提升性能。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_对称加密.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_对称加密.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.3 摘要算法
|
### 2.3 摘要算法
|
||||||
@ -71,7 +71,7 @@ HTTPS 的公钥可以由任意的客户端持有,但私钥只能由服务器
|
|||||||
|
|
||||||
HTTPS 会为需要传输的数据生成摘要信息,然后将经过会话密钥加密后的摘要和数据传送给服务器。服务器进行解密后,首先需要使用同样的摘要算法为数据生成摘要,并与客户端传送过来的摘要进行比较;如果一致,则证明数据没有被篡改。
|
HTTPS 会为需要传输的数据生成摘要信息,然后将经过会话密钥加密后的摘要和数据传送给服务器。服务器进行解密后,首先需要使用同样的摘要算法为数据生成摘要,并与客户端传送过来的摘要进行比较;如果一致,则证明数据没有被篡改。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_摘要会话密钥加密.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_摘要会话密钥加密.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.4 数字签名
|
### 2.4 数字签名
|
||||||
@ -80,14 +80,14 @@ HTTPS 会为需要传输的数据生成摘要信息,然后将经过会话密
|
|||||||
|
|
||||||
由于非对称性加密比较耗时,因此只会对返回数据的摘要进行加密。当客户端收到服务器的响应时,首先尝试使用公钥对加密后的摘要进行解密,如果解密失败,则表示是其他伪装服务器返回的;如果解密成功,则继续对数据的完整性进行校验。
|
由于非对称性加密比较耗时,因此只会对返回数据的摘要进行加密。当客户端收到服务器的响应时,首先尝试使用公钥对加密后的摘要进行解密,如果解密失败,则表示是其他伪装服务器返回的;如果解密成功,则继续对数据的完整性进行校验。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_摘要私钥加密.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_摘要私钥加密.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.5 中间人攻击
|
### 2.5 中间人攻击
|
||||||
|
|
||||||
经过以上步骤,数据的传输已经相当安全了,但此时还是无法抵御中间人攻击:
|
经过以上步骤,数据的传输已经相当安全了,但此时还是无法抵御中间人攻击:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_中间人攻击.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_中间人攻击.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
简单来说,中间人攻击就是服务器传递给客户端的公钥可能被攻击者替换,进而丧失数据安全的一种攻击方式:
|
简单来说,中间人攻击就是服务器传递给客户端的公钥可能被攻击者替换,进而丧失数据安全的一种攻击方式:
|
||||||
@ -104,7 +104,7 @@ CA(Certification Authority)是证书的签发机构,它是公钥基础设
|
|||||||
|
|
||||||
顶级的 CA 机构有 Symantec、Comodo、GeoTrust、DigiCert,但是通常它们并不支持给厂商颁发证书,而是授权给中间厂商,由中间厂商再颁发给应用厂商。从而形成 CA 认证链:
|
顶级的 CA 机构有 Symantec、Comodo、GeoTrust、DigiCert,但是通常它们并不支持给厂商颁发证书,而是授权给中间厂商,由中间厂商再颁发给应用厂商。从而形成 CA 认证链:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_证书链.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_证书链.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
就 CA 证书类型而言,其分为以下三类:
|
就 CA 证书类型而言,其分为以下三类:
|
||||||
@ -115,15 +115,15 @@ CA(Certification Authority)是证书的签发机构,它是公钥基础设
|
|||||||
|
|
||||||
通过点击浏览器上锁图标可以查看对应网站的证书状态,示例如下:
|
通过点击浏览器上锁图标可以查看对应网站的证书状态,示例如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/http_baidu_ov.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/http_baidu_ov.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/http_baidu_ov_证书.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/http_baidu_ov_证书.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
除了以上三种证书外,你还可以使用 Openssl 生成自签名证书,但由于自签名证书并不在整个认证链上,所以其通常不被浏览器所信任,访问时会出现以下警告:
|
除了以上三种证书外,你还可以使用 Openssl 生成自签名证书,但由于自签名证书并不在整个认证链上,所以其通常不被浏览器所信任,访问时会出现以下警告:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_非安全的连接.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_非安全的连接.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.7 PKI 体系
|
### 2.7 PKI 体系
|
||||||
@ -138,7 +138,7 @@ PKI(Public Key Infrastructure,公开密钥基础建设系统)由数字证
|
|||||||
|
|
||||||
以 `TLS v1.2` 版本中采用 ECDHE 算法的握手过程为例,其具体流程如下:
|
以 `TLS v1.2` 版本中采用 ECDHE 算法的握手过程为例,其具体流程如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_tsl_v1.2_ECDHE.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_tsl_v1.2_ECDHE.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
1. TLS/SSL 协议是基于 TCP/IP 协议的,因此需要先等待 TCP 三次握手完成;
|
1. TLS/SSL 协议是基于 TCP/IP 协议的,因此需要先等待 TCP 三次握手完成;
|
||||||
@ -151,14 +151,14 @@ PKI(Public Key Infrastructure,公开密钥基础建设系统)由数字证
|
|||||||
|
|
||||||
5. 之后服务器还会发送一个 `Server Key Exchange` 消息,里面包含了进行 ECDHE 算法所需的各种参数 `Server Params` :
|
5. 之后服务器还会发送一个 `Server Key Exchange` 消息,里面包含了进行 ECDHE 算法所需的各种参数 `Server Params` :
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_server_key_exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_server_key_exchange.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
6. 客户端按照密码套件的要求,也生成一个 `Client Params`,并通过 `Client Key Exchange` 消息发送给服务器;
|
6. 客户端按照密码套件的要求,也生成一个 `Client Params`,并通过 `Client Key Exchange` 消息发送给服务器;
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_client_key_exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_client_key_exchange.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ PKI(Public Key Infrastructure,公开密钥基础建设系统)由数字证
|
|||||||
|
|
||||||
12. 除此之外,服务器还会返回一个 `New Session Ticket` 消息,其内容如下:
|
12. 除此之外,服务器还会返回一个 `New Session Ticket` 消息,其内容如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_new_session_ticket.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_new_session_ticket.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,12 +31,12 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
|||||||
|
|
||||||
打开位于 bin 目录下的 `jconsole` 程序后,它会自动扫描当前主机上的所有 JVM 进程:
|
打开位于 bin 目录下的 `jconsole` 程序后,它会自动扫描当前主机上的所有 JVM 进程:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jconsole-start.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jconsole-start.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
选中需要监控的进程后,点击连接,即可进入监控界面。监控界面包含了 *概览*、*内存*、*线程*、*类*、*VM 概要*、*MBean* 六个选项卡。其中概览界面显示的是 *内存*、*线程*、*类* 等三个选项卡界面的概览信息,如下所示:
|
选中需要监控的进程后,点击连接,即可进入监控界面。监控界面包含了 *概览*、*内存*、*线程*、*类*、*VM 概要*、*MBean* 六个选项卡。其中概览界面显示的是 *内存*、*线程*、*类* 等三个选项卡界面的概览信息,如下所示:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jconsole-概览.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jconsole-概览.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jconsole-内存.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jconsole-内存.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -54,14 +54,14 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jconsole-检测死锁.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jconsole-检测死锁.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
点击死锁选项卡则可以看到造成死锁的线程:
|
点击死锁选项卡则可以看到造成死锁的线程:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jconsole-死锁.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jconsole-死锁.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jconsole-概要.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jconsole-概要.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
打开位于 bin 目录下的 `jvisualvm` 程序, 它会自动扫描当前主机上的所有 JVM 进程:
|
打开位于 bin 目录下的 `jvisualvm` 程序, 它会自动扫描当前主机上的所有 JVM 进程:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-监视.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-监视.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-堆dump.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-堆dump.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
在线程界面可以查看所有线程的状态,如果出现死锁,该界面还会进行提示:
|
在线程界面可以查看所有线程的状态,如果出现死锁,该界面还会进行提示:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-线程.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-线程.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-dump.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-dump.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
在 Profiler 界面,可以进行 CPU 和 内存的性能分析。要开始性能分析,需要先选择 **CPU** 或 **内存** 按钮中的一个,VisualVM 将会开始记录应用程序执行过的所有方法:如果是进行的是 CPU 执行时间分析,将会统计每个方法的执行次数、执行耗时;如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。想要结束性能分析,点击停止按钮即可:
|
在 Profiler 界面,可以进行 CPU 和 内存的性能分析。要开始性能分析,需要先选择 **CPU** 或 **内存** 按钮中的一个,VisualVM 将会开始记录应用程序执行过的所有方法:如果是进行的是 CPU 执行时间分析,将会统计每个方法的执行次数、执行耗时;如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。想要结束性能分析,点击停止按钮即可:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-性能分析.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-性能分析.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
|||||||
|
|
||||||
Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会实时监控虚拟机的状态,在功能上类似于 jstat 命令:
|
Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会实时监控虚拟机的状态,在功能上类似于 jstat 命令:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-gc.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-gc.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会
|
|||||||
|
|
||||||
在主界面,点击 **工具 => 插件** ,可以打开插件面板。右击插件选项或者点击安装按钮即可完成对应插件的安装:
|
在主界面,点击 **工具 => 插件** ,可以打开插件面板。右击插件选项或者点击安装按钮即可完成对应插件的安装:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-插件安装.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-插件安装.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-插件中心.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-插件中心.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-配置插件中心.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-配置插件中心.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ chown root:root /usr/local/jmxremote.password
|
|||||||
|
|
||||||
之后在使用 VisualVM 进行远程连接时,配置如下:
|
之后在使用 VisualVM 进行远程连接时,配置如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jvisual-连接远程主机.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jvisual-连接远程主机.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
需要注意的是这里的端口号是配置的 `Dcom.sun.management.jmxremote.port` 的值,而不是 Java 程序的端口号。连接完成后,即可查看到对应进程的监控状态。
|
需要注意的是这里的端口号是配置的 `Dcom.sun.management.jmxremote.port` 的值,而不是 Java 程序的端口号。连接完成后,即可查看到对应进程的监控状态。
|
||||||
|
@ -65,7 +65,7 @@ jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
|
|||||||
jstat -gc 9492 3s 5 # 每3s输出一次,一共输出5次
|
jstat -gc 9492 3s 5 # 每3s输出一次,一共输出5次
|
||||||
```
|
```
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jstat_gc.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jstat_gc.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
输出信息中各个参数含义分别如下:
|
输出信息中各个参数含义分别如下:
|
||||||
@ -115,7 +115,7 @@ jinfo -flags 13604
|
|||||||
jinfo -flag CMSInitiatingOccupancyFraction 13604
|
jinfo -flag CMSInitiatingOccupancyFraction 13604
|
||||||
```
|
```
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jinfo.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jinfo.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ jmap [option] <pid>
|
|||||||
jmap -dump:format=b,file=test.bin 3260
|
jmap -dump:format=b,file=test.bin 3260
|
||||||
```
|
```
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jmap.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jmap.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -170,22 +170,22 @@ public class StackOverFlowTest {
|
|||||||
|
|
||||||
其最终会抛出 `java.lang.OutOfMemoryError: Java heap space` 异常,意味着在 JVM 堆上发生了内存溢出。在程序运行期间,我们可以使用上面的 jmap 命令生成堆转储快照,并使用 jhat 命令进行分析:
|
其最终会抛出 `java.lang.OutOfMemoryError: Java heap space` 异常,意味着在 JVM 堆上发生了内存溢出。在程序运行期间,我们可以使用上面的 jmap 命令生成堆转储快照,并使用 jhat 命令进行分析:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jhat.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jhat.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
jhat 命令最终的分析结果会以网页的方式进行提供,端口为 7000,界面如下:
|
jhat 命令最终的分析结果会以网页的方式进行提供,端口为 7000,界面如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jhat_web.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jhat_web.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
jhat 分析的结果并不够直观,因此我们还可以借助第三方工具来分析堆转储快照,这里以 JProfiler 为例,该软件可以直接从[官网](https://www.ej-technologies.com/products/jprofiler/overview.htm)下载并安装,安装完成后,点击 `session` 选项卡,并使用 `Open Snapshot` 打开 jmap 命令生成的堆转储快照:
|
jhat 分析的结果并不够直观,因此我们还可以借助第三方工具来分析堆转储快照,这里以 JProfiler 为例,该软件可以直接从[官网](https://www.ej-technologies.com/products/jprofiler/overview.htm)下载并安装,安装完成后,点击 `session` 选项卡,并使用 `Open Snapshot` 打开 jmap 命令生成的堆转储快照:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jprofiler-1.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jprofiler-1.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
之后程序会自动进行分析,分析结果如下:
|
之后程序会自动进行分析,分析结果如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jprofiler-2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jprofiler-2.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
通过以上可视化的统计结果,我们就可以很快定位到导致内存溢出的原因。
|
通过以上可视化的统计结果,我们就可以很快定位到导致内存溢出的原因。
|
||||||
@ -250,7 +250,7 @@ jstack 8112
|
|||||||
|
|
||||||
输出结果如下:
|
输出结果如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\jstack.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\jstack.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
从输出中结果中可以看出,出现了一个死锁,该死锁由线程 Thread-0 和 Thread-1 导致,原因是 Thread-0 锁住了对象 `<0x00000000d6d8d610>` ,并尝试获取 `<0x00000000d6d8d640>` 对象的锁;但是 Thread-0 却恰恰相反,锁住了对象 `<0x00000000d6d8d640>` ,并尝试获取 `<0x00000000d6d8d610>` 对象的锁,由此导致死锁。
|
从输出中结果中可以看出,出现了一个死锁,该死锁由线程 Thread-0 和 Thread-1 导致,原因是 Thread-0 锁住了对象 `<0x00000000d6d8d610>` ,并尝试获取 `<0x00000000d6d8d640>` 对象的锁;但是 Thread-0 却恰恰相反,锁住了对象 `<0x00000000d6d8d640>` ,并尝试获取 `<0x00000000d6d8d610>` 对象的锁,由此导致死锁。
|
||||||
|
@ -58,7 +58,7 @@ Java NIO 是 JDK 1.4 中引入的新的 IO 方式,它主要包含 Buffer、Cha
|
|||||||
|
|
||||||
传统 IO 的流都是单向的,因此它们需要分为 Input Stream 和 Output Stream。而 NIO 中的 Channel 则是双向的,数据可以从 Channel 读到 Buffer 中,也可以从 Buffer 写到 Channel:
|
传统 IO 的流都是单向的,因此它们需要分为 Input Stream 和 Output Stream。而 NIO 中的 Channel 则是双向的,数据可以从 Channel 读到 Buffer 中,也可以从 Buffer 写到 Channel:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_channel_buffer.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_channel_buffer.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
> 注意:从 Channel 写入到 Buffer 执行的是 read 方法,而从 Buffer 写出到 Channel 执行的是 write 方法。
|
> 注意:从 Channel 写入到 Buffer 执行的是 read 方法,而从 Buffer 写出到 Channel 执行的是 write 方法。
|
||||||
@ -69,7 +69,7 @@ Channel 可以设置为非阻塞模式,此时当 Channel 从 Buffer 中读取
|
|||||||
|
|
||||||
Java NIO 通过 Reactor 模型实现了 IO 的多路复用,可以在一个线程上通过一个选择(Selector)使用轮询的方式去监听多个通道 Channel 上注册的事件,从而在一个线程上就能实现对多个 Channel 的处理:
|
Java NIO 通过 Reactor 模型实现了 IO 的多路复用,可以在一个线程上通过一个选择(Selector)使用轮询的方式去监听多个通道 Channel 上注册的事件,从而在一个线程上就能实现对多个 Channel 的处理:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_selector.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_selector.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ Java NIO 通过 Reactor 模型实现了 IO 的多路复用,可以在一个线
|
|||||||
|
|
||||||
### 2.1 缓冲区属性
|
### 2.1 缓冲区属性
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_buffer.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_buffer.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
所有缓冲区(ByteBuffer、FloatBuffer、IntBuffer、DoubleBuffer、ShortBuffer、LongBuffer、CharBuffer、MappedByteBuffer)都直接或间接继承自 Buffer 抽象类,Buffer 中定义了缓冲区的四个基本属性:
|
所有缓冲区(ByteBuffer、FloatBuffer、IntBuffer、DoubleBuffer、ShortBuffer、LongBuffer、CharBuffer、MappedByteBuffer)都直接或间接继承自 Buffer 抽象类,Buffer 中定义了缓冲区的四个基本属性:
|
||||||
@ -128,7 +128,7 @@ public abstract class CharBuffer extends Buffer implements Comparable<CharBuffer
|
|||||||
|
|
||||||
缓冲区创建完成后,它处于以下初始状态:
|
缓冲区创建完成后,它处于以下初始状态:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_init.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_init.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.3 操作缓冲区
|
### 2.3 操作缓冲区
|
||||||
@ -150,7 +150,7 @@ put(char[] src, int offset, int length)
|
|||||||
|
|
||||||
当我们向 Buffer 中添加数据后,position 属性也会随之变动:
|
当我们向 Buffer 中添加数据后,position 属性也会随之变动:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_put.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_put.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**2. get()**
|
**2. get()**
|
||||||
@ -184,12 +184,12 @@ buffer.clear();
|
|||||||
|
|
||||||
当使用 `filp()` 将 Buffer 由写模式切换到读模式后:position 属性会恢复到初始位置,代表从此处开始读取数据;limit 属性也会随之变动,代表我们所能读取数据的上界:
|
当使用 `filp()` 将 Buffer 由写模式切换到读模式后:position 属性会恢复到初始位置,代表从此处开始读取数据;limit 属性也会随之变动,代表我们所能读取数据的上界:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_flip.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_flip.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
当我们再通过 `get()` 方法进行读取时,position 属性会随之移动,position 和 limit 之间就是待处理的数据:
|
当我们再通过 `get()` 方法进行读取时,position 属性会随之移动,position 和 limit 之间就是待处理的数据:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_get.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_get.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**4. hasRemaining()**
|
**4. hasRemaining()**
|
||||||
@ -200,7 +200,7 @@ buffer.clear();
|
|||||||
|
|
||||||
`clear()` 并不会真的清除缓冲区中的数据,它只是将 position 设置为 0,并将 limit 设置为 capacity 的大小,从而让缓冲区恢复到初始状态:
|
`clear()` 并不会真的清除缓冲区中的数据,它只是将 position 设置为 0,并将 limit 设置为 capacity 的大小,从而让缓冲区恢复到初始状态:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_clear.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_clear.png"/> </div>
|
||||||
|
|
||||||
当有新的数据写入时,新的数据会覆盖原有位置上的数据。
|
当有新的数据写入时,新的数据会覆盖原有位置上的数据。
|
||||||
|
|
||||||
@ -208,12 +208,12 @@ buffer.clear();
|
|||||||
|
|
||||||
用于压缩缓冲区,即将数组中待处理的数据复制到头部。如下所示,会将未读取的 `LL0` 复制到头部:
|
用于压缩缓冲区,即将数组中待处理的数据复制到头部。如下所示,会将未读取的 `LL0` 复制到头部:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_compact.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_compact.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
需要注意的是这里执行的是复制操作,而不是移动操作,底层调用的是 `System.arraycopy` 方法,因此原有位置上的数据依然存在。但由于 position 会移动到未处理数据的下一个位置上,所以不用担心原有位置上的数据会被读取到,原因是你切换到读模式时,原有的 `LO` 数据仍处于 limit 之后:
|
需要注意的是这里执行的是复制操作,而不是移动操作,底层调用的是 `System.arraycopy` 方法,因此原有位置上的数据依然存在。但由于 position 会移动到未处理数据的下一个位置上,所以不用担心原有位置上的数据会被读取到,原因是你切换到读模式时,原有的 `LO` 数据仍处于 limit 之后:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_compact_flip.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_compact_flip.png"/> </div>
|
||||||
|
|
||||||
**7. mark()**
|
**7. mark()**
|
||||||
|
|
||||||
@ -239,14 +239,14 @@ public abstract CharBuffer slice();
|
|||||||
- 复制缓冲区的 mark、position、limit、capcaity 属性的初始值与复制时原缓冲区的 mark、position、limit、capcaity 的值相同,但这些属性与原缓冲区的属性相互独立,创建后就不再受原有缓冲区的影响;
|
- 复制缓冲区的 mark、position、limit、capcaity 属性的初始值与复制时原缓冲区的 mark、position、limit、capcaity 的值相同,但这些属性与原缓冲区的属性相互独立,创建后就不再受原有缓冲区的影响;
|
||||||
- 如果原缓冲区是只读缓冲区或直接缓冲区,则复制缓冲区也将继承这些属性。
|
- 如果原缓冲区是只读缓冲区或直接缓冲区,则复制缓冲区也将继承这些属性。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_duplicate.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_duplicate.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
`asReadOnlyBuffer()` 与 `duplicate()` 类似,但创建的复制缓冲区为只读缓冲区。
|
`asReadOnlyBuffer()` 与 `duplicate()` 类似,但创建的复制缓冲区为只读缓冲区。
|
||||||
|
|
||||||
`slice()` 也与 `duplicate()` 类似,但创建的复制缓冲区与原缓冲区只共享部分数据元素,并且所有标志位都处于原始状态:
|
`slice()` 也与 `duplicate()` 类似,但创建的复制缓冲区与原缓冲区只共享部分数据元素,并且所有标志位都处于原始状态:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/buffer_slice.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/buffer_slice.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
使用示例如下:
|
使用示例如下:
|
||||||
@ -405,7 +405,7 @@ fileInputStream.getChannel().read(buffers);
|
|||||||
|
|
||||||
此时 Channel 中的数据会依次写入到 Buffer01, Buffer02, Buffer03 上。Scatter 通常用于固定长度数据的处理,假设一个数据单元由 header,body,footer 三部分组成,并且每部分的长度都是固定的,此时通过 Scatter 操作,每一组数据的 header,body,footer 都会分别固定地写到 Buffer01, Buffer02, Buffer03 上,此时就可以对每个 Buffer 应用不同的处理逻辑:
|
此时 Channel 中的数据会依次写入到 Buffer01, Buffer02, Buffer03 上。Scatter 通常用于固定长度数据的处理,假设一个数据单元由 header,body,footer 三部分组成,并且每部分的长度都是固定的,此时通过 Scatter 操作,每一组数据的 header,body,footer 都会分别固定地写到 Buffer01, Buffer02, Buffer03 上,此时就可以对每个 Buffer 应用不同的处理逻辑:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_scatter.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_scatter.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
+ **聚集 (gather)**:将多个 Buffer 中的数据依次写入到同一个 Channel 上。示例如下:
|
+ **聚集 (gather)**:将多个 Buffer 中的数据依次写入到同一个 Channel 上。示例如下:
|
||||||
@ -419,14 +419,14 @@ ByteBuffer[] buffers = new ByteBuffer[]{buffer01, buffer02, buffer03};
|
|||||||
fileInputStream.getChannel().read(buffers);
|
fileInputStream.getChannel().read(buffers);
|
||||||
```
|
```
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_gather.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_gather.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.5 Pipe
|
### 3.5 Pipe
|
||||||
|
|
||||||
Java NIO 还提供了 Pipe 管道用于在不同线程之间传递数据:
|
Java NIO 还提供了 Pipe 管道用于在不同线程之间传递数据:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_pipe.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_pipe.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
Pipe 管道可以通过 Pipe 类的静态方法 `open()` 来创建:
|
Pipe 管道可以通过 Pipe 类的静态方法 `open()` 来创建:
|
||||||
@ -618,7 +618,7 @@ public abstract class SelectionKey {
|
|||||||
|
|
||||||
下面以一个群聊的聊天室为例,来展示 Java NIO 三大组件的综合使用,效果如下:
|
下面以一个群聊的聊天室为例,来展示 Java NIO 三大组件的综合使用,效果如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nio_chat_group.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nio_chat_group.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ Java 线程的生命周期分为以下五类状态:
|
|||||||
|
|
||||||
各个状态之间的转换关系如下图:
|
各个状态之间的转换关系如下图:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/线程完整生命周期.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/线程完整生命周期.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 1.4 线程终止
|
### 1.4 线程终止
|
||||||
@ -322,7 +322,7 @@ public class J1_ThreadUnsafe {
|
|||||||
|
|
||||||
现代处理器一般具有多个层次的高速缓存,如:一级缓存(L1 Cache)、二级缓存(L2 Cache)、三级缓存(L3 Cache)等。其中一级缓存通常包含两部分,其中一部分用于存储指令(L1i),另外一部分用于存储数据(L1d)。距离处理器越近的高速缓存,其存储速率越快,制造成本越高,因此其容量也越小。在 Linux 系统中,可以使用 `lscpu` 命令查看其高速缓存的情况:
|
现代处理器一般具有多个层次的高速缓存,如:一级缓存(L1 Cache)、二级缓存(L2 Cache)、三级缓存(L3 Cache)等。其中一级缓存通常包含两部分,其中一部分用于存储指令(L1i),另外一部分用于存储数据(L1d)。距离处理器越近的高速缓存,其存储速率越快,制造成本越高,因此其容量也越小。在 Linux 系统中,可以使用 `lscpu` 命令查看其高速缓存的情况:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/cahce.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/cahce.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 4.3 缓存一致性协议
|
### 4.3 缓存一致性协议
|
||||||
@ -383,7 +383,7 @@ public class J1_ThreadUnsafe {
|
|||||||
|
|
||||||
线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量;不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
|
线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量;不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/java内存模型.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/java内存模型.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode) # 使用的是H
|
|||||||
|
|
||||||
## 二、Java 内存区域
|
## 二、Java 内存区域
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/java虚拟机运行时数据区.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/java虚拟机运行时数据区.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.1 程序计数器
|
### 2.1 程序计数器
|
||||||
@ -119,7 +119,7 @@ Java 堆(Java Heap)是虚拟机所管理的最大一块的内存空间,它
|
|||||||
|
|
||||||
+ **指针碰撞**:假设 Java 堆中内存是绝对规整的,所有使用的内存放在一边,所有未被使用的内存放在另外一边,中间以指针作为分界点指示器。此时内存分配只是将指针向空闲方向偏移出对象大小的空间即可,这种方式被称为指针碰撞。
|
+ **指针碰撞**:假设 Java 堆中内存是绝对规整的,所有使用的内存放在一边,所有未被使用的内存放在另外一边,中间以指针作为分界点指示器。此时内存分配只是将指针向空闲方向偏移出对象大小的空间即可,这种方式被称为指针碰撞。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_指针碰撞.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_指针碰撞.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
+ **空闲列表**:如果 Java 堆不是规整的,此时虚拟机需要维护一个列表,记录哪些内存块是可用的,哪些是不可用的。在进行内存分配时,只需要从该列表中选取出一块足够的内存空间划分给对象实例即可。
|
+ **空闲列表**:如果 Java 堆不是规整的,此时虚拟机需要维护一个列表,记录哪些内存块是可用的,哪些是不可用的。在进行内存分配时,只需要从该列表中选取出一块足够的内存空间划分给对象实例即可。
|
||||||
@ -167,12 +167,12 @@ Java 堆(Java Heap)是虚拟机所管理的最大一块的内存空间,它
|
|||||||
|
|
||||||
通过句柄访问对象:
|
通过句柄访问对象:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_通过句柄访问对象.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_通过句柄访问对象.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
通过直接指针访问对象:
|
通过直接指针访问对象:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_通过直接指针访问对象.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_通过直接指针访问对象.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
句柄访问的优点在于对象移动时(垃圾收集时移动对象是非常普遍的行为)只需要改变句柄中实例数据的指针,而 `reference` 本生并不需要修改;指针访问则反之,由于其 `reference` 中存储的直接就是对象地址,所以当对象移动时, `reference` 需要被修改。但针对只需要访问对象本身的场景,指针访问则可以减少一次定位开销。由于对象访问是一项非常频繁的操作,所以这类减少的效果会非常显著,基于这个原因,HotSpot 主要使用的是指针访问的方式。
|
句柄访问的优点在于对象移动时(垃圾收集时移动对象是非常普遍的行为)只需要改变句柄中实例数据的指针,而 `reference` 本生并不需要修改;指针访问则反之,由于其 `reference` 中存储的直接就是对象地址,所以当对象移动时, `reference` 需要被修改。但针对只需要访问对象本身的场景,指针访问则可以减少一次定位开销。由于对象访问是一项非常频繁的操作,所以这类减少的效果会非常显著,基于这个原因,HotSpot 主要使用的是指针访问的方式。
|
||||||
@ -203,7 +203,7 @@ System.gc();
|
|||||||
|
|
||||||
上面的代码在大多数虚拟机中都能被正确的回收,因为大多数主流的虚拟机都是采用的可达性分析方法来判断对象是否死亡。可达性分析是通过一系列被称为 `GC Roots` 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为引用链(Reference Chain),如果某个对象到 `GC Roots` 间没有任何引用链相连,这代表 `GC Roots` 到该对象不可达, 此时证明此该对象不可能再被使用。
|
上面的代码在大多数虚拟机中都能被正确的回收,因为大多数主流的虚拟机都是采用的可达性分析方法来判断对象是否死亡。可达性分析是通过一系列被称为 `GC Roots` 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为引用链(Reference Chain),如果某个对象到 `GC Roots` 间没有任何引用链相连,这代表 `GC Roots` 到该对象不可达, 此时证明此该对象不可能再被使用。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_可达性分析.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_可达性分析.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
在 Java 语言中,固定可作为 `GC Roots` 的对象包括以下几种:
|
在 Java 语言中,固定可作为 `GC Roots` 的对象包括以下几种:
|
||||||
@ -264,7 +264,7 @@ System.gc();
|
|||||||
|
|
||||||
它是最基础的垃圾收集算法,收集过程分为两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象;也可以反过来,标记存活对象,统一回收所有未被标记的对象。
|
它是最基础的垃圾收集算法,收集过程分为两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象;也可以反过来,标记存活对象,统一回收所有未被标记的对象。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_标记清除算法.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_标记清除算法.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
它主要有以下两个缺点:
|
它主要有以下两个缺点:
|
||||||
@ -279,7 +279,7 @@ System.gc();
|
|||||||
+ 如果内存中多数对象都是存活的,这种算法将产生大量的复制开销;
|
+ 如果内存中多数对象都是存活的,这种算法将产生大量的复制开销;
|
||||||
+ 浪费内存空间,内存空间变为了原有的一半。
|
+ 浪费内存空间,内存空间变为了原有的一半。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_标记复制算法.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_标记复制算法.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
基于新生代 “朝生夕灭” 的特点,大多数虚拟机都不会按照 1:1 的比例来进行内存划分,例如 HotSpot 虚拟机会将内存空间划分为一块较大的 `Eden` 和 两块较小的 `Survivor` 空间,它们之间的比例是 8:1:1 。 每次分配时只会使用 `Eden` 和其中的一块 `Survivor` ,发生垃圾回收时,只需要将存活的对象一次性复制到另外一块 `Survivor` 上,这样只有 10% 的内存空间会被浪费掉。当 `Survivor` 空间不足以容纳一次 `Minor GC` 时,此时由其他内存区域(通常是老年代)来进行分配担保。
|
基于新生代 “朝生夕灭” 的特点,大多数虚拟机都不会按照 1:1 的比例来进行内存划分,例如 HotSpot 虚拟机会将内存空间划分为一块较大的 `Eden` 和 两块较小的 `Survivor` 空间,它们之间的比例是 8:1:1 。 每次分配时只会使用 `Eden` 和其中的一块 `Survivor` ,发生垃圾回收时,只需要将存活的对象一次性复制到另外一块 `Survivor` 上,这样只有 10% 的内存空间会被浪费掉。当 `Survivor` 空间不足以容纳一次 `Minor GC` 时,此时由其他内存区域(通常是老年代)来进行分配担保。
|
||||||
@ -288,7 +288,7 @@ System.gc();
|
|||||||
|
|
||||||
标记-整理算法是在标记完成后,让所有存活对象都向内存的一端移动,然后直接清理掉边界以外的内存。其优点在于可以避免内存空间碎片化的问题,也可以充分利用内存空间;其缺点在于根据所使用的收集器的不同,在移动存活对象时可能要全程暂停用户程序:
|
标记-整理算法是在标记完成后,让所有存活对象都向内存的一端移动,然后直接清理掉边界以外的内存。其优点在于可以避免内存空间碎片化的问题,也可以充分利用内存空间;其缺点在于根据所使用的收集器的不同,在移动存活对象时可能要全程暂停用户程序:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_标记移动算法.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_标记移动算法.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
## 五、经典垃圾收集器
|
## 五、经典垃圾收集器
|
||||||
@ -301,7 +301,7 @@ System.gc();
|
|||||||
|
|
||||||
HotSpot 虚拟机中一共存在七款经典的垃圾收集器:
|
HotSpot 虚拟机中一共存在七款经典的垃圾收集器:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_hotspot_垃圾收集器.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_hotspot_垃圾收集器.png"/> </div>
|
||||||
|
|
||||||
> 注:收集器之间存在连线,则代表它们可以搭配使用。
|
> 注:收集器之间存在连线,则代表它们可以搭配使用。
|
||||||
|
|
||||||
@ -309,14 +309,14 @@ HotSpot 虚拟机中一共存在七款经典的垃圾收集器:
|
|||||||
|
|
||||||
Serial 收集器是最基础、历史最悠久的收集器,它是一个单线程收集器,在进行垃圾回收时,必须暂停其他所有的工作线程,直到收集结束,这是其主要缺点。它的优点在于单线程避免了多线程复杂的上下文切换,因此在单线程环境下收集效率非常高,由于这个优点,迄今为止,其仍然是 HotSpot 虚拟机在客户端模式下默认的新生代收集器:
|
Serial 收集器是最基础、历史最悠久的收集器,它是一个单线程收集器,在进行垃圾回收时,必须暂停其他所有的工作线程,直到收集结束,这是其主要缺点。它的优点在于单线程避免了多线程复杂的上下文切换,因此在单线程环境下收集效率非常高,由于这个优点,迄今为止,其仍然是 HotSpot 虚拟机在客户端模式下默认的新生代收集器:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_收集器1.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_收集器1.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 5.2 ParNew 收集器
|
### 5.2 ParNew 收集器
|
||||||
|
|
||||||
他是 Serial 收集器的多线程版本,可以使用多条线程进行垃圾回收:
|
他是 Serial 收集器的多线程版本,可以使用多条线程进行垃圾回收:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_收集器2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_收集器2.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 5.3 Parallel Scavenge 收集器
|
### 5.3 Parallel Scavenge 收集器
|
||||||
@ -335,14 +335,14 @@ Parallel Scavenge 收集器提供两个参数用于精确控制吞吐量:
|
|||||||
|
|
||||||
从名字也可以看出来,它是 Serial 收集器的老年代版本,同样是一个单线程收集器,采用 标记-整理 算法,主要用于给客户端模式下的 HotSpot 虚拟机使用:
|
从名字也可以看出来,它是 Serial 收集器的老年代版本,同样是一个单线程收集器,采用 标记-整理 算法,主要用于给客户端模式下的 HotSpot 虚拟机使用:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_收集器1.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_收集器1.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 5.5 Paralled Old 收集器
|
### 5.5 Paralled Old 收集器
|
||||||
|
|
||||||
Paralled Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,采用 标记-整理 算法实现:
|
Paralled Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,采用 标记-整理 算法实现:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_收集器3.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_收集器3.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 5.6 CMS 收集器
|
### 5.6 CMS 收集器
|
||||||
@ -354,7 +354,7 @@ CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时
|
|||||||
3. **重新标记 (remark)**:采用增量更新算法,对并发标记阶段因为用户线程运行而产生变动的那部分对象进行重新标记,耗时比初始标记稍长且需要暂停用户线程;
|
3. **重新标记 (remark)**:采用增量更新算法,对并发标记阶段因为用户线程运行而产生变动的那部分对象进行重新标记,耗时比初始标记稍长且需要暂停用户线程;
|
||||||
4. **并发清除 (inital sweep)**:并发清除掉已经死亡的对象,耗时长但不需要暂停用户线程。
|
4. **并发清除 (inital sweep)**:并发清除掉已经死亡的对象,耗时长但不需要暂停用户线程。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_收集器4.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_收集器4.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
其优点在于耗时长的 并发标记 和 并发清除 阶段都不需要暂停用户线程,因此其停顿时间较短,其主要缺点如下:
|
其优点在于耗时长的 并发标记 和 并发清除 阶段都不需要暂停用户线程,因此其停顿时间较短,其主要缺点如下:
|
||||||
@ -367,7 +367,7 @@ CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时
|
|||||||
|
|
||||||
Garbage First(简称 G1)是一款面向服务端的垃圾收集器,也是 JDK 9 服务端模式下默认的垃圾收集器,它的诞生具有里程碑式的意义。G1 虽然也遵循分代收集理论,但不再以固定大小和固定数量来划分分代区域,而是把连续的 Java 堆划分为多个大小相等的独立区域(Region)。每一个 Region 都可以根据不同的需求来扮演新生代的 `Eden` 空间、`Survivor` 空间或者老年代空间,收集器会根据其扮演角色的不同而采用不同的收集策略。
|
Garbage First(简称 G1)是一款面向服务端的垃圾收集器,也是 JDK 9 服务端模式下默认的垃圾收集器,它的诞生具有里程碑式的意义。G1 虽然也遵循分代收集理论,但不再以固定大小和固定数量来划分分代区域,而是把连续的 Java 堆划分为多个大小相等的独立区域(Region)。每一个 Region 都可以根据不同的需求来扮演新生代的 `Eden` 空间、`Survivor` 空间或者老年代空间,收集器会根据其扮演角色的不同而采用不同的收集策略。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/G1_内存布局.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/G1_内存布局.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
上面还有一些 Region 使用 H 进行标注,它代表 Humongous,表示这些 Region 用于存储大对象(humongous object,H-obj),即大小大于等于 region 一半的对象。G1 收集器的运行大致可以分为以下四个步骤:
|
上面还有一些 Region 使用 H 进行标注,它代表 Humongous,表示这些 Region 用于存储大对象(humongous object,H-obj),即大小大于等于 region 一半的对象。G1 收集器的运行大致可以分为以下四个步骤:
|
||||||
@ -377,7 +377,7 @@ Garbage First(简称 G1)是一款面向服务端的垃圾收集器,也是
|
|||||||
3. **最终标记 (Final Marking)**:对用户线程做一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的少量的 STAB 记录。虽然并发标记阶段会处理 SATB 记录,但由于处理时用户线程依然是运行中的,因此依然会有少量的变动,所以需要最终标记来处理;
|
3. **最终标记 (Final Marking)**:对用户线程做一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的少量的 STAB 记录。虽然并发标记阶段会处理 SATB 记录,但由于处理时用户线程依然是运行中的,因此依然会有少量的变动,所以需要最终标记来处理;
|
||||||
4. **筛选回收 (Live Data Counting and Evacuation)**:负责更新 Regin 统计数据,按照各个 Regin 的回收价值和成本进行排序,在根据用户期望的停顿时间进行来指定回收计划,可以选择任意多个 Regin 构成回收集。然后将回收集中 Regin 的存活对象复制到空的 Regin 中,再清理掉整个旧的 Regin 。此时因为涉及到存活对象的移动,所以需要暂停用户线程,并由多个收集线程并行执行。
|
4. **筛选回收 (Live Data Counting and Evacuation)**:负责更新 Regin 统计数据,按照各个 Regin 的回收价值和成本进行排序,在根据用户期望的停顿时间进行来指定回收计划,可以选择任意多个 Regin 构成回收集。然后将回收集中 Regin 的存活对象复制到空的 Regin 中,再清理掉整个旧的 Regin 。此时因为涉及到存活对象的移动,所以需要暂停用户线程,并由多个收集线程并行执行。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_收集器5.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_收集器5.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 5.8 内存分配原则
|
### 5.8 内存分配原则
|
||||||
@ -410,7 +410,7 @@ Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数
|
|||||||
|
|
||||||
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、卸载、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个部分统称为连接:
|
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、卸载、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个部分统称为连接:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_类的生命周期.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_类的生命周期.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
《Java 虚拟机规范》严格规定了有且只有六种情况必须立即对类进行初始化:
|
《Java 虚拟机规范》严格规定了有且只有六种情况必须立即对类进行初始化:
|
||||||
@ -489,7 +489,7 @@ Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数
|
|||||||
|
|
||||||
JDK 9 之前的 Java 应用都是由这三种类加载器相互配合来完成加载:
|
JDK 9 之前的 Java 应用都是由这三种类加载器相互配合来完成加载:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_双亲委派模型.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_双亲委派模型.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
上图所示的各种类加载器之间的层次关系被称为类加载器的 “双亲委派模型”,“双亲委派模型” 要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,需要注意的是这里的加载器之间的父子关系一般不是以继承关系来实现的,而是使用组合关系来复用父类加载器的代码。
|
上图所示的各种类加载器之间的层次关系被称为类加载器的 “双亲委派模型”,“双亲委派模型” 要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,需要注意的是这里的加载器之间的父子关系一般不是以继承关系来实现的,而是使用组合关系来复用父类加载器的代码。
|
||||||
@ -504,7 +504,7 @@ JDK 9 之后为了适应模块化的发展,类加载器做了如下变化:
|
|||||||
+ 当平台及应用程序类加载器收到类加载请求时,要首先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载;
|
+ 当平台及应用程序类加载器收到类加载请求时,要首先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载;
|
||||||
+ 启动类加载器、平台类加载器、应用程序类加载器全部继承自 `java.internal.loader.BuiltinClassLoader` ,BuiltinClassLoader 中实现了新的模块化架构下类如何从模块中加载的逻辑,以及模块中资源可访问性的处理。
|
+ 启动类加载器、平台类加载器、应用程序类加载器全部继承自 `java.internal.loader.BuiltinClassLoader` ,BuiltinClassLoader 中实现了新的模块化架构下类如何从模块中加载的逻辑,以及模块中资源可访问性的处理。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/jvm_jdk9_双亲委派模型.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/jvm_jdk9_双亲委派模型.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
## 七、程序编译
|
## 七、程序编译
|
||||||
@ -537,7 +537,7 @@ JDK 9 之后为了适应模块化的发展,类加载器做了如下变化:
|
|||||||
|
|
||||||
以上层次并不是固定不变的,根据不同的运行参数和版本,虚拟机可以调整分层的数量。各层次编译之间的交互转换关系如下图所示:
|
以上层次并不是固定不变的,根据不同的运行参数和版本,虚拟机可以调整分层的数量。各层次编译之间的交互转换关系如下图所示:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="../pictures/分层编译的交互关系.png"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/分层编译的交互关系.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
实施分层编译后,解释器、客户端编译器和服务端编译器就会同时工作,可以用客户端编译器获取更高的编译速度、用服务端编译器来获取更好的编译质量。
|
实施分层编译后,解释器、客户端编译器和服务端编译器就会同时工作,可以用客户端编译器获取更高的编译速度、用服务端编译器来获取更好的编译质量。
|
||||||
|
@ -433,7 +433,7 @@ Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflective
|
|||||||
|
|
||||||
### 2.2 示例
|
### 2.2 示例
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/23_simple_factory.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/23_simple_factory.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -514,7 +514,7 @@ public Phone getPhone(Class<? extends Phone> phoneClass) {
|
|||||||
|
|
||||||
### 3.2 示例
|
### 3.2 示例
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/23_factory_method.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/23_factory_method.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -597,7 +597,7 @@ public class ZTest {
|
|||||||
|
|
||||||
### 4.2 示例
|
### 4.2 示例
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/23_abstract_factory.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/23_abstract_factory.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -691,7 +691,7 @@ public class ZTest {
|
|||||||
|
|
||||||
### 5.2 示例
|
### 5.2 示例
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/23_builder.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/23_builder.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1035,7 +1035,7 @@ service.compute();
|
|||||||
|
|
||||||
将 220V 的电流通过适配器转换为对应规格的电流给手机充电:
|
将 220V 的电流通过适配器转换为对应规格的电流给手机充电:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_adapter.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_adapter.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1110,7 +1110,7 @@ public class ZTest {
|
|||||||
|
|
||||||
将一个图形的形状和颜色进行分离,从而可以通过组合来实现的不同的效果:
|
将一个图形的形状和颜色进行分离,从而可以通过组合来实现的不同的效果:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_bridge.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_bridge.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1205,7 +1205,7 @@ new Round().setColor(new Yellow()).getDesc();
|
|||||||
|
|
||||||
模拟 Linux 文件系统:
|
模拟 Linux 文件系统:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_composite.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_composite.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1334,7 +1334,7 @@ nginx.cat();
|
|||||||
|
|
||||||
在购买手机后,你可能还会购买屏幕保护膜,手机壳等来进行装饰:
|
在购买手机后,你可能还会购买屏幕保护膜,手机壳等来进行装饰:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_decorator.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_decorator.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1455,7 +1455,7 @@ public class ZTest {
|
|||||||
|
|
||||||
模仿电商购物下单,此时内部需要调用支付子系统,仓储子系统,物流子系统,而这些细节对用户都是屏蔽的:
|
模仿电商购物下单,此时内部需要调用支付子系统,仓储子系统,物流子系统,而这些细节对用户都是屏蔽的:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_facade.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_facade.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1542,7 +1542,7 @@ XXX手机已经发货,请注意查收...
|
|||||||
|
|
||||||
这里以创建 PPT 模板为例,相同类型的 PPT 模板不再重复创建:
|
这里以创建 PPT 模板为例,相同类型的 PPT 模板不再重复创建:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_flyweight.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_flyweight.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1677,7 +1677,7 @@ public class ZTest {
|
|||||||
|
|
||||||
假设多个用户都关注某一个商家,当商家发出降价等通知时,所有用户都应该收到:
|
假设多个用户都关注某一个商家,当商家发出降价等通知时,所有用户都应该收到:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/23_observer.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/23_observer.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1780,7 +1780,7 @@ business.notifyObservers("商品促销通知");
|
|||||||
|
|
||||||
假设一个正常的流程,根据请假天数的不同,需要不同的领导共同审批:
|
假设一个正常的流程,根据请假天数的不同,需要不同的领导共同审批:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_chain_of_responsibility.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_chain_of_responsibility.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1894,7 +1894,7 @@ groupLeader.approval(new Application("婚假单", 10));
|
|||||||
|
|
||||||
手机一般都有电池,摄像头等模块,但不是所有手机都有 NFC 模块,如果采用模板模式构建,则相关代码如下:
|
手机一般都有电池,摄像头等模块,但不是所有手机都有 NFC 模块,如果采用模板模式构建,则相关代码如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_template.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_template.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1997,7 +1997,7 @@ smartPhone.assembling();
|
|||||||
|
|
||||||
假设公司需要根据营业额的不同来选择不同的员工激励策略:
|
假设公司需要根据营业额的不同来选择不同的员工激励策略:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_strategy.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_strategy.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2083,7 +2083,7 @@ public static void main(String[] args) {
|
|||||||
|
|
||||||
假设我们正在开发一个播放器,它有如下图所示四种基本的状态:播放状态,关闭状态,暂停状态,加速播放状态。这四种状态间可以相互转换,但存在一定的限制,比如在关闭或者暂停状态下,都不能加速视频,采用状态模式来实现该播放器的相关代码如下:
|
假设我们正在开发一个播放器,它有如下图所示四种基本的状态:播放状态,关闭状态,暂停状态,加速播放状态。这四种状态间可以相互转换,但存在一定的限制,比如在关闭或者暂停状态下,都不能加速视频,采用状态模式来实现该播放器的相关代码如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_state.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_state.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2449,7 +2449,7 @@ while (iterator.hasNext()) {
|
|||||||
|
|
||||||
通常不同级别的员工对于公司档案的访问权限是不同的,为方便理解,如下图所示假设只有公开和加密两种类型的档案,并且只有总经理和部门经理才能进入档案室:
|
通常不同级别的员工对于公司档案的访问权限是不同的,为方便理解,如下图所示假设只有公开和加密两种类型的档案,并且只有总经理和部门经理才能进入档案室:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\23_visitor.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\23_visitor.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -598,7 +598,7 @@ userdel 命令用于删除用户,格式为:userdel [选项] 用户名。常
|
|||||||
|
|
||||||
使用 `ll` 命令可以查看到文件的详细属性,各个属性的含义如下:
|
使用 `ll` 命令可以查看到文件的详细属性,各个属性的含义如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/文件属性信息.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/文件属性信息.png"/> </div>
|
||||||
|
|
||||||
文件类型可以分为以下几类:
|
文件类型可以分为以下几类:
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ userdel 命令用于删除用户,格式为:userdel [选项] 用户名。常
|
|||||||
|
|
||||||
chmod 用于设置文件或目录的权限,格式为:chmod [参数] 权限 文件或目录名称。 权限支持字符表示和数字表示两种形式,其对应关系如下:
|
chmod 用于设置文件或目录的权限,格式为:chmod [参数] 权限 文件或目录名称。 权限支持字符表示和数字表示两种形式,其对应关系如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/文件权限的字符与数字表示.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/文件权限的字符与数字表示.png"/> </div>
|
||||||
示例如下:
|
示例如下:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -664,7 +664,7 @@ Vim编辑器有三种状态模式:
|
|||||||
- 输入模式:正常的文本录入。
|
- 输入模式:正常的文本录入。
|
||||||
- 末行模式:保存或退出文档,以及设置编辑环境。
|
- 末行模式:保存或退出文档,以及设置编辑环境。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/vim不同模式间的切换.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/vim不同模式间的切换.png"/> </div>
|
||||||
命令模式中常用的命令如下:
|
命令模式中常用的命令如下:
|
||||||
|
|
||||||
| 命令 | 作用 |
|
| 命令 | 作用 |
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
+ **config servers** :配置服务器,它是整个集群的核心,用于存储集群的元数据和配置信息 (如分片服务器上存储了哪些数据块以及这些块的数据范围) 。从 MongoDB 3.4 开始,必须将配置服务器部署为副本集。
|
+ **config servers** :配置服务器,它是整个集群的核心,用于存储集群的元数据和配置信息 (如分片服务器上存储了哪些数据块以及这些块的数据范围) 。从 MongoDB 3.4 开始,必须将配置服务器部署为副本集。
|
||||||
+ **mongos** :查询服务的路由器,它是集群对外提供服务的门户。mongos 从配置服务器获取数据块的相关信息,并将客户端请求路由到对应的分片服务器上。
|
+ **mongos** :查询服务的路由器,它是集群对外提供服务的门户。mongos 从配置服务器获取数据块的相关信息,并将客户端请求路由到对应的分片服务器上。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-分片模式.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-分片模式.png"/> </div>
|
||||||
|
|
||||||
### 1.2 分片键
|
### 1.2 分片键
|
||||||
|
|
||||||
@ -55,11 +55,11 @@
|
|||||||
|
|
||||||
无论采用何种分片策略,数据最终都被存储到对应范围的数据块 (chunk) 上,每个块默认的大小都是 64 M。由于数据源源不断的加入,当块超过指定大小时,就会进行块拆分。需要强调的是块拆分是一个轻量级的操作,因为在本质上并没有任何数据的移动,只是由 config servers 更新关于块的元数据信息。
|
无论采用何种分片策略,数据最终都被存储到对应范围的数据块 (chunk) 上,每个块默认的大小都是 64 M。由于数据源源不断的加入,当块超过指定大小时,就会进行块拆分。需要强调的是块拆分是一个轻量级的操作,因为在本质上并没有任何数据的移动,只是由 config servers 更新关于块的元数据信息。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-块拆分.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-块拆分.png"/> </div>
|
||||||
|
|
||||||
当某个分片服务器上的数据过多时候,此时为了避免单服务器上 CPU 和 IO 操作的性能问题,就会发生块迁移,将块从一个分片迁移到另外一个分片,同时 config servers 也会更新相关块的元数据信息。 块迁移是由在后台运行的平衡器 (balancer) 所负责的,它在后台进行持续监控,如果最大和最小分片之间的块数量差异超过迁移阈值,平衡器则开始在群集中迁移块以确保数据的均匀分布。
|
当某个分片服务器上的数据过多时候,此时为了避免单服务器上 CPU 和 IO 操作的性能问题,就会发生块迁移,将块从一个分片迁移到另外一个分片,同时 config servers 也会更新相关块的元数据信息。 块迁移是由在后台运行的平衡器 (balancer) 所负责的,它在后台进行持续监控,如果最大和最小分片之间的块数量差异超过迁移阈值,平衡器则开始在群集中迁移块以确保数据的均匀分布。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-块迁移.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-块迁移.png"/> </div>
|
||||||
|
|
||||||
块的大小是可以手动进行配置修改,但需要注意权衡利弊:
|
块的大小是可以手动进行配置修改,但需要注意权衡利弊:
|
||||||
|
|
||||||
@ -70,19 +70,19 @@
|
|||||||
|
|
||||||
这里需要注意块的迁移不会对查询造成任何影响,MongoDB 集群和 Redis 集群的查询原理不同。对于 Redis Cluster 而言,数据的散列规则同时也是查询时的路由规则。但是对于 MongoDB 分片集群而言,查询需要先经过 mongos ,mongos 会从 config servers 上获取块的位置信息和数据范围,然后按照这些信息进行匹配后再路由到正确的分片上。因此,从本质上而言 MongoDB 的分片策略和路由规则没有任何关系,假设按照分片策略将某文档分发到 Shard A 的 Chunk01 上,之后 Chunk01 迁移到 Shard B , 由于配置服务器会更新 Chunk01 块的位置信息,所以仍然能够正确路由到。
|
这里需要注意块的迁移不会对查询造成任何影响,MongoDB 集群和 Redis 集群的查询原理不同。对于 Redis Cluster 而言,数据的散列规则同时也是查询时的路由规则。但是对于 MongoDB 分片集群而言,查询需要先经过 mongos ,mongos 会从 config servers 上获取块的位置信息和数据范围,然后按照这些信息进行匹配后再路由到正确的分片上。因此,从本质上而言 MongoDB 的分片策略和路由规则没有任何关系,假设按照分片策略将某文档分发到 Shard A 的 Chunk01 上,之后 Chunk01 迁移到 Shard B , 由于配置服务器会更新 Chunk01 块的位置信息,所以仍然能够正确路由到。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-路由查询.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-路由查询.png"/> </div>
|
||||||
|
|
||||||
### 1.6 非分片集合
|
### 1.6 非分片集合
|
||||||
|
|
||||||
以上的所有讲解都是针对分片集合的,而在实际开发中并非每个集合都需要进行分片,MongoDB 允许在同一数据库下混合使用分片和非分片集合。每个数据库都有自己的主分片,所有非分片集合统一存储在主分片上。需要特别说明的是主分片和复本集中的主节点没有任何关系,在新数据库创建时程序会自动选择当前集群中最少数据量的分片作为主分片。如下图所示: Shard A 为主分片,集合 Collection1 是分片集合,而集合 Collection2 是非分片集合。
|
以上的所有讲解都是针对分片集合的,而在实际开发中并非每个集合都需要进行分片,MongoDB 允许在同一数据库下混合使用分片和非分片集合。每个数据库都有自己的主分片,所有非分片集合统一存储在主分片上。需要特别说明的是主分片和复本集中的主节点没有任何关系,在新数据库创建时程序会自动选择当前集群中最少数据量的分片作为主分片。如下图所示: Shard A 为主分片,集合 Collection1 是分片集合,而集合 Collection2 是非分片集合。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-非分片集合.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-非分片集合.png"/> </div>
|
||||||
|
|
||||||
## 二、集群搭建
|
## 二、集群搭建
|
||||||
|
|
||||||
这里我只有三台服务器,为保证高可用,三台服务器上均部署 mongod 服务(每个服务器上使用不同端口部署两个 mongod 服务),形成两个分片副本集;同时三台服务器上均部署 config servers 服务,形成一个配置副本集:
|
这里我只有三台服务器,为保证高可用,三台服务器上均部署 mongod 服务(每个服务器上使用不同端口部署两个 mongod 服务),形成两个分片副本集;同时三台服务器上均部署 config servers 服务,形成一个配置副本集:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-分片集群.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-分片集群.png"/> </div>
|
||||||
|
|
||||||
### 2.1 分片副本集配置
|
### 2.1 分片副本集配置
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ db.runCommand({ addshard : "rs1/hadoop001:37018,hadoop002:37018,hadoop003:37018"
|
|||||||
|
|
||||||
务必注意,在添加分片副本集之前一定要切换到管理员角色,否则后面的添加操作会返回 `"ok" : 0` 的失败状态码,同时会提示 `addShard may only be run against the admin database.` 添加成功后,可以使用 `sh.status()` 查看集群状态,此时输出如下,可以看到两个分片副本集已经被成功添加。
|
务必注意,在添加分片副本集之前一定要切换到管理员角色,否则后面的添加操作会返回 `"ok" : 0` 的失败状态码,同时会提示 `addShard may only be run against the admin database.` 添加成功后,可以使用 `sh.status()` 查看集群状态,此时输出如下,可以看到两个分片副本集已经被成功添加。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-分片集群状态.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-分片集群状态.png"/> </div>
|
||||||
|
|
||||||
### 2.8 测试分片
|
### 2.8 测试分片
|
||||||
|
|
||||||
@ -322,7 +322,7 @@ db.users.insertMany(arr);
|
|||||||
|
|
||||||
插入数据完成后,执行 `sh.status()` 命令可以查看分片情况,以及块的数据信息,部分输出如下:
|
插入数据完成后,执行 `sh.status()` 命令可以查看分片情况,以及块的数据信息,部分输出如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-分片测试.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-分片测试.png"/> </div>
|
||||||
|
|
||||||
## 参考资料
|
## 参考资料
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
为保证数据安全,实现高可用,MongoDB 提供了复制功能,可以将主节点上的数据复制到多个从节点上,这样即便主节点出现异常,由于数据是以多副本的方式进行存储,仍然可以保证数据安全。一个标准的三节点副本集架构如下:
|
为保证数据安全,实现高可用,MongoDB 提供了复制功能,可以将主节点上的数据复制到多个从节点上,这样即便主节点出现异常,由于数据是以多副本的方式进行存储,仍然可以保证数据安全。一个标准的三节点副本集架构如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-复制.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-复制.png"/> </div>
|
||||||
|
|
||||||
复制过程如下:
|
复制过程如下:
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ MongoDB 按 namespace 或 document id 对每批操作进行分组,并使用不
|
|||||||
|
|
||||||
虽然仲裁者可以占用更少的服务器资源,但是由于其并不存储数据,所以对数据的安全性并不能起到帮助作用。因此应该尽量避免使用仲裁者,同时尽量保证最多只使用一个仲裁者,即如果节点数量恰好是偶数,则添加一个仲裁者,如果节点数量是奇数,那就不需要仲裁者。
|
虽然仲裁者可以占用更少的服务器资源,但是由于其并不存储数据,所以对数据的安全性并不能起到帮助作用。因此应该尽量避免使用仲裁者,同时尽量保证最多只使用一个仲裁者,即如果节点数量恰好是偶数,则添加一个仲裁者,如果节点数量是奇数,那就不需要仲裁者。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-仲裁者.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-仲裁者.png"/> </div>
|
||||||
|
|
||||||
## 二、故障发现与恢复
|
## 二、故障发现与恢复
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ MongoDB 按 namespace 或 document id 对每批操作进行分组,并使用不
|
|||||||
|
|
||||||
MongoDB 的选举算法会尝试让高优先级的节点优先发起选举,从而更容易在选举中胜出。如果某一个优先级较低的节点在短时间内被选举为新的主节点,这时副本集仍然会继续发起选举,直至可用的最高优先级的节点成为新的主节点。需要特别注意的是在这个过程当中,优先级为 0 的成员不能寻求选举,也不能成为主节点。
|
MongoDB 的选举算法会尝试让高优先级的节点优先发起选举,从而更容易在选举中胜出。如果某一个优先级较低的节点在短时间内被选举为新的主节点,这时副本集仍然会继续发起选举,直至可用的最高优先级的节点成为新的主节点。需要特别注意的是在这个过程当中,优先级为 0 的成员不能寻求选举,也不能成为主节点。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-故障恢复.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-故障恢复.png"/> </div>
|
||||||
|
|
||||||
### 2.3 投票成员
|
### 2.3 投票成员
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ MongoDB 的选举算法会尝试让高优先级的节点优先发起选举,从
|
|||||||
|
|
||||||
如下示例是一个 9 个成员的副本集,包含 7 个投票成员和 2 个无投票成员:
|
如下示例是一个 9 个成员的副本集,包含 7 个投票成员和 2 个无投票成员:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongdb-vote.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongdb-vote.png"/> </div>
|
||||||
|
|
||||||
## 三、搭建副本集
|
## 三、搭建副本集
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ MongoDB 的选举算法会尝试让高优先级的节点优先发起选举,从
|
|||||||
|
|
||||||
选择所需版本的 MongoDB 进行下载,下载地址为: https://www.mongodb.com/download-center/community
|
选择所需版本的 MongoDB 进行下载,下载地址为: https://www.mongodb.com/download-center/community
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-version-select.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-version-select.png"/> </div>
|
||||||
|
|
||||||
这里我下载的版本为 `4.0.10` , 安装环境为 `RHEL 7.0`,下载后进行解压:
|
这里我下载的版本为 `4.0.10` , 安装环境为 `RHEL 7.0`,下载后进行解压:
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ rs.initiate( {
|
|||||||
|
|
||||||
使用 `rs.status()` 命令查看副本集状态,部分输出如下。从输出中可以看到 hadoop001 为 PRIMARY 节点,而 hadoop002 和 hadoop003 均为 SECONDARY 节点,此时代表副本集已经搭建成功。
|
使用 `rs.status()` 命令查看副本集状态,部分输出如下。从输出中可以看到 hadoop001 为 PRIMARY 节点,而 hadoop002 和 hadoop003 均为 SECONDARY 节点,此时代表副本集已经搭建成功。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mongodb-副本集状态.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-副本集状态.png"/> </div>
|
||||||
|
|
||||||
## 参考资料
|
## 参考资料
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ db.user.find({}).sort({name:1})
|
|||||||
|
|
||||||
当前大多数数据库都支持双向遍历索引,这和存储结构有关 (如下图)。在 B-Tree 结构的叶子节点上,存储了索引键的值及其对应文档的位置信息,而每个叶子节点间则类似于双向链表,既可以从前往后遍历,也可以从后往前遍历:
|
当前大多数数据库都支持双向遍历索引,这和存储结构有关 (如下图)。在 B-Tree 结构的叶子节点上,存储了索引键的值及其对应文档的位置信息,而每个叶子节点间则类似于双向链表,既可以从前往后遍历,也可以从后往前遍历:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/b-tree.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/b-tree.png"/> </div>
|
||||||
|
|
||||||
### 2.2 复合索引
|
### 2.2 复合索引
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ mysql> EXPLAIN SELECT * FROM employees;
|
|||||||
注:本篇文章的测试数据来源于 MySQL 官方提供的 [Employees Sample Database](https://dev.mysql.com/doc/employee/en/),其数据库结构如下:
|
注:本篇文章的测试数据来源于 MySQL 官方提供的 [Employees Sample Database](https://dev.mysql.com/doc/employee/en/),其数据库结构如下:
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/employees-schema.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/employees-schema.png"/> </div>
|
||||||
以下分别介绍 EXPLAIN 输出结果中各个字段的含义:
|
以下分别介绍 EXPLAIN 输出结果中各个字段的含义:
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ Mycat 读写分离的配置非常简单,只需要通过配置 balance,writeH
|
|||||||
|
|
||||||
综合以上全部内容,这里给出一个分库分表的示例,其架构如下:
|
综合以上全部内容,这里给出一个分库分表的示例,其架构如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-mycat-分库分表实战.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-mycat-分库分表实战.png"/> </div>
|
||||||
如上图所示,这里模拟的是一个电商数据库,并对其执行分库分表操作:
|
如上图所示,这里模拟的是一个电商数据库,并对其执行分库分表操作:
|
||||||
|
|
||||||
- 将用户相关表,订单相关表,商品相关表分表拆分到单独的数据库中;
|
- 将用户相关表,订单相关表,商品相关表分表拆分到单独的数据库中;
|
||||||
|
@ -24,7 +24,7 @@ Percona XtraDB Cluster (简称 PXC) 是 Percona 公司开源的实现 MySQL 高
|
|||||||
+ 允许的最大事务大小由 wsrep_max_ws_rows 和 wsrep_max_ws_size 参数共同定义,因此超大型事务会被拆分为一系列小型事务,如加载大数据集 LOAD DATA INFILE。
|
+ 允许的最大事务大小由 wsrep_max_ws_rows 和 wsrep_max_ws_size 参数共同定义,因此超大型事务会被拆分为一系列小型事务,如加载大数据集 LOAD DATA INFILE。
|
||||||
+ 由于在集群级别采用乐观锁进行并发控制,所以事务在 COMMIT 阶段仍然有被中止的可能。如两个事务在不同的集群节点上提交对相同的行的写入,此时只有其中一个可以成功提交,另一个将被中止。
|
+ 由于在集群级别采用乐观锁进行并发控制,所以事务在 COMMIT 阶段仍然有被中止的可能。如两个事务在不同的集群节点上提交对相同的行的写入,此时只有其中一个可以成功提交,另一个将被中止。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/pxc-cluster.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/pxc-cluster.png"/> </div>
|
||||||
虽然 PXC 集群存在以上限制,但就目前而言,它仍然是解决数据一致性和高可用性的最好方案,其搭建步骤如下:
|
虽然 PXC 集群存在以上限制,但就目前而言,它仍然是解决数据一致性和高可用性的最好方案,其搭建步骤如下:
|
||||||
|
|
||||||
## 二、集群搭建
|
## 二、集群搭建
|
||||||
|
@ -44,7 +44,7 @@ InnoDB 是 MySQL 5.5 之后默认的存储引擎,它具有高可靠、高性
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/innodb-architecture.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/innodb-architecture.png"/> </div>
|
||||||
|
|
||||||
### 1.2 MyISAM
|
### 1.2 MyISAM
|
||||||
|
|
||||||
@ -119,19 +119,19 @@ mysql> SELECT * FROM total;
|
|||||||
|
|
||||||
**平衡二叉树数据结构**:
|
**平衡二叉树数据结构**:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/avl-tree.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/avl-tree.png"/> </div>
|
||||||
|
|
||||||
**红黑树数据结构**:
|
**红黑树数据结构**:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/red-black-tree.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/red-black-tree.png"/> </div>
|
||||||
|
|
||||||
**Btree 数据结构**:
|
**Btree 数据结构**:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/btree.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/btree.png"/> </div>
|
||||||
|
|
||||||
**B+ Tree 数据结构**
|
**B+ Tree 数据结构**
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/B+Trees.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/B+Trees.png"/> </div>
|
||||||
|
|
||||||
> 以上图片均通过数据结构可视化网站 [Data Structure Visualizations](https://www.cs.usfca.edu/~galles/visualization/Algorithms.html) 自动生成,感兴趣的小伙伴也可自行尝试。
|
> 以上图片均通过数据结构可视化网站 [Data Structure Visualizations](https://www.cs.usfca.edu/~galles/visualization/Algorithms.html) 自动生成,感兴趣的小伙伴也可自行尝试。
|
||||||
|
|
||||||
@ -146,10 +146,10 @@ mysql> SELECT * FROM total;
|
|||||||
|
|
||||||
对于 InnoDB ,因为主键索引是聚集索引,所以其叶子节点存储的就是实际的数据。而非主键索引存储的则是主键的值 :
|
对于 InnoDB ,因为主键索引是聚集索引,所以其叶子节点存储的就是实际的数据。而非主键索引存储的则是主键的值 :
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-innodb-索引.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-innodb-索引.png"/> </div>
|
||||||
对于 MyISAM,因为主键索引是非聚集索引,所以其叶子节点存储的只是指向数据位置的指针:
|
对于 MyISAM,因为主键索引是非聚集索引,所以其叶子节点存储的只是指向数据位置的指针:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-myisam-索引.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-myisam-索引.png"/> </div>
|
||||||
综上所述,B+ tree 结构普遍适用于范围查找,优化排序和分组等操作。B+ tree 是基于字典序进行构建的,因此其适用于以下查询:
|
综上所述,B+ tree 结构普遍适用于范围查找,优化排序和分组等操作。B+ tree 是基于字典序进行构建的,因此其适用于以下查询:
|
||||||
|
|
||||||
+ **全值匹配**:以索引为条件进行精确查找。如 `emp_no` 字段为索引,查询条件为 `emp_no = 10008`。
|
+ **全值匹配**:以索引为条件进行精确查找。如 `emp_no` 字段为索引,查询条件为 `emp_no = 10008`。
|
||||||
@ -287,25 +287,25 @@ InnoDB 存储引擎完全支持 ACID 模型:
|
|||||||
|
|
||||||
一个事务的更新操作被另外一个事务的更新操作锁覆盖,从而导致数据不一致:
|
一个事务的更新操作被另外一个事务的更新操作锁覆盖,从而导致数据不一致:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-修改丢失.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-修改丢失.png"/> </div>
|
||||||
|
|
||||||
**2. 脏读**
|
**2. 脏读**
|
||||||
|
|
||||||
在不同的事务下,一个事务读取到其他事务未提交的数据:
|
在不同的事务下,一个事务读取到其他事务未提交的数据:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-脏读.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-脏读.png"/> </div>
|
||||||
|
|
||||||
**3. 不可重复读**
|
**3. 不可重复读**
|
||||||
|
|
||||||
在同一个事务的两次读取之间,由于其他事务对数据进行了修改,导致对同一条数据两次读到的结果不一致:
|
在同一个事务的两次读取之间,由于其他事务对数据进行了修改,导致对同一条数据两次读到的结果不一致:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-不可重复读.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-不可重复读.png"/> </div>
|
||||||
|
|
||||||
**4.幻读**
|
**4.幻读**
|
||||||
|
|
||||||
在同一个事务的两次读取之间,由于其他事务对数据进行了修改,导致第二次读取到第一次不存在数据,或第一次原本存在的数据,第二次却读取不到,就好像之前的读取是 “幻觉” 一样:
|
在同一个事务的两次读取之间,由于其他事务对数据进行了修改,导致第二次读取到第一次不存在数据,或第一次原本存在的数据,第二次却读取不到,就好像之前的读取是 “幻觉” 一样:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-幻读.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-幻读.png"/> </div>
|
||||||
|
|
||||||
### 4.4 隔离级别
|
### 4.4 隔离级别
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ MySQL 的复制原理如下图所示:
|
|||||||
|
|
||||||
如果没有进行任何配置,主库在将变更写入到二进制日志后,就会返回对客户端的响应,因此默认情况下的复制是完全异步进行的,主备之间可能会短暂存在数据不一致的情况。
|
如果没有进行任何配置,主库在将变更写入到二进制日志后,就会返回对客户端的响应,因此默认情况下的复制是完全异步进行的,主备之间可能会短暂存在数据不一致的情况。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-replication.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-replication.jpg"/> </div>
|
||||||
|
|
||||||
### 2.2 配置步骤
|
### 2.2 配置步骤
|
||||||
|
|
||||||
@ -355,14 +355,14 @@ MMM (Master-Master replication manager for MySQL) 是由 Perl 语言开发的一
|
|||||||
|
|
||||||
除了管理双主节点,MMM 也负责管理所有 Slave节点,在出现宕机、复制延迟或复制错误,MMM 会移除该节点的 VIP,直至节点恢复正常。MMM 高可用的架构示例图如下:
|
除了管理双主节点,MMM 也负责管理所有 Slave节点,在出现宕机、复制延迟或复制错误,MMM 会移除该节点的 VIP,直至节点恢复正常。MMM 高可用的架构示例图如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-MMM架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-MMM架构.png"/> </div>
|
||||||
MMM 架构的缺点在于虽然其能实现自动切换,但却不会主动补齐丢失的数据,所以会存在数据不一致性的风险。另外 MMM 的发布时间比较早,所以其也不支持 MySQL 最新的基于 GTID 的复制,如果你使用的是基于 GTID 的复制,则只能采用 MHA。
|
MMM 架构的缺点在于虽然其能实现自动切换,但却不会主动补齐丢失的数据,所以会存在数据不一致性的风险。另外 MMM 的发布时间比较早,所以其也不支持 MySQL 最新的基于 GTID 的复制,如果你使用的是基于 GTID 的复制,则只能采用 MHA。
|
||||||
|
|
||||||
### 5.2 MHA
|
### 5.2 MHA
|
||||||
|
|
||||||
MHA (Master High Availability) 是由 Perl 实现的一款高可用程序,相对于 MMM ,它能尽量避免数据不一致的问题。它监控的是一主多从的复制架构,架构如下图所示:
|
MHA (Master High Availability) 是由 Perl 实现的一款高可用程序,相对于 MMM ,它能尽量避免数据不一致的问题。它监控的是一主多从的复制架构,架构如下图所示:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/mysql-MHA架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-MHA架构.png"/> </div>
|
||||||
在 Master 节点宕机后,其处理流程如下:
|
在 Master 节点宕机后,其处理流程如下:
|
||||||
|
|
||||||
1. 尝试从宕机 Master 中保存二进制日志;
|
1. 尝试从宕机 Master 中保存二进制日志;
|
||||||
|
@ -45,7 +45,7 @@ Nginx 能够同时支持正向代理和反向代理,这两种代理模式的
|
|||||||
+ 正向代理发生在客户端,是客户端主动发起的代理。如我们不能直接访问某个服务器,但可以间接通过中间的代理服务器去进行访问,然后将访问结果再返回给我们。
|
+ 正向代理发生在客户端,是客户端主动发起的代理。如我们不能直接访问某个服务器,但可以间接通过中间的代理服务器去进行访问,然后将访问结果再返回给我们。
|
||||||
+ 反向代理发生在服务端,客户端并不知道发生了代理,示例如下。用户只知道将请求发送给 Nginx,但是并不知道请求被转发了,也不知道被转发给了哪一台应用服务器。实际上对于用户来说,他也没必要知道,因为请求结果都是相同的。
|
+ 反向代理发生在服务端,客户端并不知道发生了代理,示例如下。用户只知道将请求发送给 Nginx,但是并不知道请求被转发了,也不知道被转发给了哪一台应用服务器。实际上对于用户来说,他也没必要知道,因为请求结果都是相同的。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/nginx-plus.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/nginx-plus.png"/> </div>
|
||||||
|
|
||||||
## 二、基本命令
|
## 二、基本命令
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ RabbitMQ 完全实现了 AMQP 协议,并基于相同的模型架构。RabbitMQ
|
|||||||
|
|
||||||
RabbitMQ 与 AMQP 遵循相同的模型架构,其架构示例图如下:
|
RabbitMQ 与 AMQP 遵循相同的模型架构,其架构示例图如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-模型架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-模型架构.png"/> </div>
|
||||||
|
|
||||||
### 1. Publisher(发布者)
|
### 1. Publisher(发布者)
|
||||||
|
|
||||||
@ -109,17 +109,17 @@ RabbitMQ 支持多种交换器类型,常用的有以下四种:
|
|||||||
|
|
||||||
这是最简单的一种交换器模型,此时会把消息路由到与该交换器绑定的所有队列中。如下图,任何发送到 X 交换器上的消息,都会被路由到 Q1 和 Q2 两个队列上。
|
这是最简单的一种交换器模型,此时会把消息路由到与该交换器绑定的所有队列中。如下图,任何发送到 X 交换器上的消息,都会被路由到 Q1 和 Q2 两个队列上。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-fanout-exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-fanout-exchange.png"/> </div>
|
||||||
|
|
||||||
### 5.2 direct
|
### 5.2 direct
|
||||||
|
|
||||||
把消息路由到 BindingKey 和 RountingKey 完全一样的队列中。如下图,当消息的 RountingKey 为 orange 时,消息会被路由到 Q1 队列;当消息的 RountingKey 为 black 或 green 时,消息会被路由到 Q2 队列。
|
把消息路由到 BindingKey 和 RountingKey 完全一样的队列中。如下图,当消息的 RountingKey 为 orange 时,消息会被路由到 Q1 队列;当消息的 RountingKey 为 black 或 green 时,消息会被路由到 Q2 队列。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-direct-exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-direct-exchange.png"/> </div>
|
||||||
|
|
||||||
需要特别说明的是一个交换器绑定多个队列时,它们的 BindingKey 是可以相同的,如下图。此时当消息的 RountingKey 为 black 时,消息会同时被路由到 Q1 和 Q2 队列。
|
需要特别说明的是一个交换器绑定多个队列时,它们的 BindingKey 是可以相同的,如下图。此时当消息的 RountingKey 为 black 时,消息会同时被路由到 Q1 和 Q2 队列。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-direct-exchange-2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-direct-exchange-2.png"/> </div>
|
||||||
|
|
||||||
### 5.3 topic
|
### 5.3 topic
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ RabbitMQ 支持多种交换器类型,常用的有以下四种:
|
|||||||
|
|
||||||
以下是官方文档中的示例,交换器与队列的绑定情况如图所示,此时的路由情况如下:
|
以下是官方文档中的示例,交换器与队列的绑定情况如图所示,此时的路由情况如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-topic-exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-topic-exchange.png"/> </div>
|
||||||
|
|
||||||
+ 路由键为 `lazy.orange.elephant` 的消息会发送给所有队列;
|
+ 路由键为 `lazy.orange.elephant` 的消息会发送给所有队列;
|
||||||
+ 路由键为 `quick.orange.fox` 的消息只会发送给 Q1 队列;
|
+ 路由键为 `quick.orange.fox` 的消息只会发送给 Q1 队列;
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-集群架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-集群架构.png"/> </div>
|
||||||
这里对上面的集群架构做一下解释说明:
|
这里对上面的集群架构做一下解释说明:
|
||||||
|
|
||||||
首先一个基本的 RabbitMQ 集群不是高可用的,虽然集群共享队列,但在默认情况下,消息只会被路由到某一个节点的符合条件的队列上,并不会同步到其他节点的相同队列上。假设消息路由到 node1 的 my-queue 队列上,但是 node1 突然宕机了,那么消息就会丢失,想要解决这个问题,需要开启队列镜像,将集群中的队列彼此之间进行镜像,此时消息就会被拷贝到处于同一个镜像分组中的所有队列上。
|
首先一个基本的 RabbitMQ 集群不是高可用的,虽然集群共享队列,但在默认情况下,消息只会被路由到某一个节点的符合条件的队列上,并不会同步到其他节点的相同队列上。假设消息路由到 node1 的 my-queue 队列上,但是 node1 突然宕机了,那么消息就会丢失,想要解决这个问题,需要开启队列镜像,将集群中的队列彼此之间进行镜像,此时消息就会被拷贝到处于同一个镜像分组中的所有队列上。
|
||||||
@ -142,7 +142,7 @@ rabbitmqctl set_cluster_name my_rabbitmq_cluster
|
|||||||
|
|
||||||
除了可以使用命令行外,还可以使用打开任意节点的 UI 界面进行查看,情况如下:
|
除了可以使用命令行外,还可以使用打开任意节点的 UI 界面进行查看,情况如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-集群模式.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-集群模式.png"/> </div>
|
||||||
|
|
||||||
### 2.5 配置镜像队列
|
### 2.5 配置镜像队列
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}'
|
|||||||
|
|
||||||
配置完成后,可以通过 Web UI 界面查看任意队列的镜像状态,情况如下:
|
配置完成后,可以通过 Web UI 界面查看任意队列的镜像状态,情况如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-镜像复制策略.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-镜像复制策略.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 2.6 节点下线
|
### 2.6 节点下线
|
||||||
@ -324,7 +324,7 @@ haproxy -f /etc/haproxy/haproxy.cfg
|
|||||||
|
|
||||||
启动后可以在监控页面进行查看,端口为设置的 8100,完整地址为:http://hadoop001:8100/stats ,页面情况如下:
|
启动后可以在监控页面进行查看,端口为设置的 8100,完整地址为:http://hadoop001:8100/stats ,页面情况如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-haproxy.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-haproxy.png"/> </div>
|
||||||
所有节点都为绿色,代表节点健康。此时证明 HAProxy 搭建成功,并已经对 RabbitMQ 集群进行监控。
|
所有节点都为绿色,代表节点健康。此时证明 HAProxy 搭建成功,并已经对 RabbitMQ 集群进行监控。
|
||||||
|
|
||||||
|
|
||||||
@ -505,10 +505,10 @@ systemctl start keepalived
|
|||||||
|
|
||||||
启动后此时 hadoop001 为主节点,可以在 hadoop001 上使用 `ip a` 命令查看到虚拟 IP 的情况:
|
启动后此时 hadoop001 为主节点,可以在 hadoop001 上使用 `ip a` 命令查看到虚拟 IP 的情况:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-keepalived-vip.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-keepalived-vip.png"/> </div>
|
||||||
此时只有 hadoop001 上是存在虚拟 IP 的,而 hadoop002 上是没有的。
|
此时只有 hadoop001 上是存在虚拟 IP 的,而 hadoop002 上是没有的。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-keepalived-vip2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-keepalived-vip2.png"/> </div>
|
||||||
|
|
||||||
### 4.6 验证故障转移
|
### 4.6 验证故障转移
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ systemctl stop keepalived
|
|||||||
|
|
||||||
此时再次使用 `ip a` 分别查看,可以发现 hadoop001 上的 VIP 已经漂移到 hadoop002 上,情况如下:
|
此时再次使用 `ip a` 分别查看,可以发现 hadoop001 上的 VIP 已经漂移到 hadoop002 上,情况如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/rabbitmq-keepalived-vip3.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-keepalived-vip3.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
此时对外服务的 VIP 依然可用,代表已经成功地进行了故障转移。至此集群已经搭建成功,任何需要发送或者接受消息的客户端服务只需要连接到该 VIP 即可,示例如下:
|
此时对外服务的 VIP 依然可用,代表已经成功地进行了故障转移。至此集群已经搭建成功,任何需要发送或者接受消息的客户端服务只需要连接到该 VIP 即可,示例如下:
|
||||||
|
@ -73,7 +73,7 @@ jedis.set("lockKey", "lockValue", SetParams.setParams().nx().ex(3));
|
|||||||
|
|
||||||
此时一条命令就可以完成值和超时时间的设置,并且因为只有一条命令,因此其原子性也得到了保证。但因为引入了超时时间来避免死锁,同时也引出了其它两个问题:
|
此时一条命令就可以完成值和超时时间的设置,并且因为只有一条命令,因此其原子性也得到了保证。但因为引入了超时时间来避免死锁,同时也引出了其它两个问题:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis_分布式锁原理.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis_分布式锁原理.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
+ **问题一**:当业务处理的时间超过过期时间后(图中进程 A),由于锁已经被释放,此时其他进程就可以获得该锁(图中进程 B),这意味着有两个进程(A 和 B)同时进入了临界区,此时分布式锁就失效了;
|
+ **问题一**:当业务处理的时间超过过期时间后(图中进程 A),由于锁已经被释放,此时其他进程就可以获得该锁(图中进程 B),这意味着有两个进程(A 和 B)同时进入了临界区,此时分布式锁就失效了;
|
||||||
@ -206,7 +206,7 @@ redissonClient.shutdown();
|
|||||||
|
|
||||||
此时对应在 Redis 中的数据结构如下:
|
此时对应在 Redis 中的数据结构如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis_分布式锁_cli1.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis_分布式锁_cli1.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
可以看到 key 就是代码中设置的锁名,而 value 值的类型是 hash,其中键 `9280e909-c86b-43ec-b11d-6e5a7745e2e9:13` 的格式为 `UUID + 线程ID` ;键对应的值为 1,代表加锁的次数。之所以要采用 hash 这种格式,主要是因为 Redisson 创建的锁是具有重入性的,即你可以多次进行加锁:
|
可以看到 key 就是代码中设置的锁名,而 value 值的类型是 hash,其中键 `9280e909-c86b-43ec-b11d-6e5a7745e2e9:13` 的格式为 `UUID + 线程ID` ;键对应的值为 1,代表加锁的次数。之所以要采用 hash 这种格式,主要是因为 Redisson 创建的锁是具有重入性的,即你可以多次进行加锁:
|
||||||
@ -218,7 +218,7 @@ boolean isLock2 = lock.tryLock(0, 30, TimeUnit.SECONDS);
|
|||||||
|
|
||||||
此时对应的值就会变成 2,代表加了两次锁:
|
此时对应的值就会变成 2,代表加了两次锁:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis_分布式锁_cli2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis_分布式锁_cli2.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
当然和其他重入锁一样,需要保证解锁的次数和加锁的次数一样,才能完全解锁:
|
当然和其他重入锁一样,需要保证解锁的次数和加锁的次数一样,才能完全解锁:
|
||||||
@ -284,7 +284,7 @@ redissonClient03.shutdown();
|
|||||||
|
|
||||||
此时每个 Redis 实例上锁的情况如下:
|
此时每个 Redis 实例上锁的情况如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis_分布式锁_cli3.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis_分布式锁_cli3.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
可以看到每个实例上都获得了锁。
|
可以看到每个实例上都获得了锁。
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
- 在从节点启动时候加入 `--slaveof {masterHost} {masterPort}` 参数;
|
- 在从节点启动时候加入 `--slaveof {masterHost} {masterPort}` 参数;
|
||||||
- 直接在从节点上执行 `slaveof {masterHost} {masterPort}` 命令。
|
- 直接在从节点上执行 `slaveof {masterHost} {masterPort}` 命令。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis主从复制.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis主从复制.png"/> </div>
|
||||||
|
|
||||||
主从节点复制关系建立后,可以使用 `info replication` 命令查看相关的复制状态。需要注意的是每个从节点只能有一个主节点,但主节点可以同时拥有多个从节点,复制行为是单向的,只能由主节点复制到从节点,因此从节点默认都是只读模式,即 `slave-read-only` 的值默认为 `yes`。
|
主从节点复制关系建立后,可以使用 `info replication` 命令查看相关的复制状态。需要注意的是每个从节点只能有一个主节点,但主节点可以同时拥有多个从节点,复制行为是单向的,只能由主节点复制到从节点,因此从节点默认都是只读模式,即 `slave-read-only` 的值默认为 `yes`。
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ slaveof {newMasterIp} {newMasterPort}
|
|||||||
|
|
||||||
哨兵模式的主要作用在于它能够自动完成故障发现和故障转移,并通知客户端,从而实现高可用。哨兵模式通常由一组 Sentinel 节点和一组(或多组)主从复制节点组成,架构如下:
|
哨兵模式的主要作用在于它能够自动完成故障发现和故障转移,并通知客户端,从而实现高可用。哨兵模式通常由一组 Sentinel 节点和一组(或多组)主从复制节点组成,架构如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis哨兵模式.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis哨兵模式.png"/> </div>
|
||||||
|
|
||||||
### 2.1 架构说明
|
### 2.1 架构说明
|
||||||
|
|
||||||
@ -178,15 +178,15 @@ redis-sentinel sentinel-26381.conf
|
|||||||
|
|
||||||
使用 `ps -ef | grep redis` 命令查看进程,此时输出应该如下:
|
使用 `ps -ef | grep redis` 命令查看进程,此时输出应该如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-sentinel-ps-ef.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-sentinel-ps-ef.png"/> </div>
|
||||||
|
|
||||||
可以使用 `info replication` 命令查看 Redis 复制集的状态,此时输出如下。可以看到 6379 节点为 master 节点,并且有两个从节点,分别为 slave0 和 slave1,对应的端口为 6380 和 6381:
|
可以使用 `info replication` 命令查看 Redis 复制集的状态,此时输出如下。可以看到 6379 节点为 master 节点,并且有两个从节点,分别为 slave0 和 slave1,对应的端口为 6380 和 6381:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-info-replication.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-info-replication.png"/> </div>
|
||||||
|
|
||||||
可以使用 `info Sentinel` 命令查看任意 Sentinel 节点的状态,从最后一句输出可以看到 Sentinel 节点已经感知到 6379 的 master 节点,并且也知道它有两个 slaves 节点;同时 Sentinel 节点彼此之间也感知到,共有 3 个 Sentinel 节点:
|
可以使用 `info Sentinel` 命令查看任意 Sentinel 节点的状态,从最后一句输出可以看到 Sentinel 节点已经感知到 6379 的 master 节点,并且也知道它有两个 slaves 节点;同时 Sentinel 节点彼此之间也感知到,共有 3 个 Sentinel 节点:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-sentinel-infomation.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-sentinel-infomation.png"/> </div>
|
||||||
|
|
||||||
## 参考资料
|
## 参考资料
|
||||||
|
|
||||||
|
@ -31,13 +31,13 @@ HASH_SLOT = CRC16(key) mod 16384
|
|||||||
|
|
||||||
假设现在有一个 6 个节点的集群,分别有 3 个 Master 点和 3 个 Slave 节点,槽会尽量均匀的分布在所有 Master 节点上。数据经过散列后存储在指定的 Master 节点上,之后 Slave 节点会进行对应的复制操作。这里再次说明一下槽只是一个虚拟的概念,并不是数据存放的实际载体。
|
假设现在有一个 6 个节点的集群,分别有 3 个 Master 点和 3 个 Slave 节点,槽会尽量均匀的分布在所有 Master 节点上。数据经过散列后存储在指定的 Master 节点上,之后 Slave 节点会进行对应的复制操作。这里再次说明一下槽只是一个虚拟的概念,并不是数据存放的实际载体。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-集群架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-集群架构.png"/> </div>
|
||||||
|
|
||||||
### 1.2 节点通讯
|
### 1.2 节点通讯
|
||||||
|
|
||||||
在 Redis 分布式架构中,每个节点都存储有整个集群所有节点的元数据信息,这是通过 P2P 的 Gossip 协议来实现的。集群中的每个节点都会单独开辟一个 TCP 通道,用于节点之间彼此通信,通信端口号在基础端口上加 10000;每个节点定期通过特定的规则选择部分节点发送 ping 消息,接收到 ping 信息的节点用 pong 消息作为响应,通过一段时间的彼此通信,最终所有节点都会达到一致的状态,每个节点都会知道整个集群全部节点的状态信息,从而到达集群状态同步的目的。
|
在 Redis 分布式架构中,每个节点都存储有整个集群所有节点的元数据信息,这是通过 P2P 的 Gossip 协议来实现的。集群中的每个节点都会单独开辟一个 TCP 通道,用于节点之间彼此通信,通信端口号在基础端口上加 10000;每个节点定期通过特定的规则选择部分节点发送 ping 消息,接收到 ping 信息的节点用 pong 消息作为响应,通过一段时间的彼此通信,最终所有节点都会达到一致的状态,每个节点都会知道整个集群全部节点的状态信息,从而到达集群状态同步的目的。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis节点通讯.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis节点通讯.png"/> </div>
|
||||||
|
|
||||||
### 1.3 请求路由
|
### 1.3 请求路由
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ cluster-config-file nodes-6480.conf
|
|||||||
|
|
||||||
启动所有 Redis 节点,启动后使用 `ps -ef | grep redis` 查看进程,输出应如下:
|
启动所有 Redis 节点,启动后使用 `ps -ef | grep redis` 查看进程,输出应如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-cluster-ps-ef.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-cluster-ps-ef.png"/> </div>
|
||||||
接着需要使用以下命令创建集群,集群节点之间会开始进行通讯,并完成槽的分配:
|
接着需要使用以下命令创建集群,集群节点之间会开始进行通讯,并完成槽的分配:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -135,7 +135,7 @@ redis-cli --cluster create 127.0.0.1:6479 127.0.0.1:6480 127.0.0.1:6481 \
|
|||||||
|
|
||||||
执行后输出如下:M 开头的表示持有槽的主节点,S 开头的表示从节点,每个节点都有一个唯一的 ID。最后一句输出表示所有的槽都已经分配到主节点上,此时代表集群搭建成功。
|
执行后输出如下:M 开头的表示持有槽的主节点,S 开头的表示从节点,每个节点都有一个唯一的 ID。最后一句输出表示所有的槽都已经分配到主节点上,此时代表集群搭建成功。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-cluster-create.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-cluster-create.png"/> </div>
|
||||||
|
|
||||||
### 2.3 集群完整性校验
|
### 2.3 集群完整性校验
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ redis-cli --cluster add-node 127.0.0.1:6485 127.0.0.1:6479
|
|||||||
redis-cli -h 127.0.0.1 -p 6479 cluster nodes
|
redis-cli -h 127.0.0.1 -p 6479 cluster nodes
|
||||||
```
|
```
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-cluster-nodes.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-cluster-nodes.png"/> </div>
|
||||||
|
|
||||||
想要让新加入的节点能够进行读写操作,可以使用 `reshard` 命令为其分配槽,这里我们将其他三个主节点上的槽迁移一部分到 6485 节点上,这里一共迁移 4096 个槽,即 16384 除以 4 。 `cluster-from` 用于指明槽的源节点,可以为多个,`cluster-to` 为槽的目标节点,`cluster-slots` 为需要迁移的槽的总数:
|
想要让新加入的节点能够进行读写操作,可以使用 `reshard` 命令为其分配槽,这里我们将其他三个主节点上的槽迁移一部分到 6485 节点上,这里一共迁移 4096 个槽,即 16384 除以 4 。 `cluster-from` 用于指明槽的源节点,可以为多个,`cluster-to` 为槽的目标节点,`cluster-slots` 为需要迁移的槽的总数:
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ redis-cli --cluster reshard 127.0.0.1:6479 \
|
|||||||
|
|
||||||
迁移后,再次使用 `cluster nodes` 命令可以查看到此时 6485 上已经有其他三个主节点上迁移过来的槽:
|
迁移后,再次使用 `cluster nodes` 命令可以查看到此时 6485 上已经有其他三个主节点上迁移过来的槽:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/redis-cluster-nodes2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/redis-cluster-nodes2.png"/> </div>
|
||||||
|
|
||||||
为保证高可用,可以为新加入的主节点添加从节点,命令如下。`add-node` 接收两个参数,第一个为需要添加的从节点,第二个参数为集群内任意节点,用于发现集群。`cluster-master-id` 参数用于指明作为哪个主节点的从节点,如果不加这个参数,则自动分配给从节点较少的主节点:
|
为保证高可用,可以为新加入的主节点添加从节点,命令如下。`add-node` 接收两个参数,第一个为需要添加的从节点,第二个参数为集群内任意节点,用于发现集群。`cluster-master-id` 参数用于指明作为哪个主节点的从节点,如果不加这个参数,则自动分配给从节点较少的主节点:
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ Tomcat 是目前主流的基于 Java 语言的轻量级应用服务器,它是
|
|||||||
|
|
||||||
Tomcat 的整体架构如下:
|
Tomcat 的整体架构如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat_架构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_架构.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
+ **Server**:表示整个 Servlet 容器,在整个 Tomcat 运行环境中只有唯一一个 Server 实例。一个 Server 包含多个 Service,每个 Service 互相独立,但共享一个 JVM 以及系统类库。
|
+ **Server**:表示整个 Servlet 容器,在整个 Tomcat 运行环境中只有唯一一个 Server 实例。一个 Server 包含多个 Service,每个 Service 互相独立,但共享一个 JVM 以及系统类库。
|
||||||
@ -56,7 +56,7 @@ Tomcat 的整体架构如下:
|
|||||||
|
|
||||||
连接器的主要功能是将 Socket 的输入转换为 Request 对象,并交由容器进行处理;之后再将容器处理完成的 Response 对象写到输出流。连接器的内部组件如下:
|
连接器的主要功能是将 Socket 的输入转换为 Request 对象,并交由容器进行处理;之后再将容器处理完成的 Response 对象写到输出流。连接器的内部组件如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat连接器组件.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat连接器组件.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.1 ProtocolHandler
|
### 3.1 ProtocolHandler
|
||||||
@ -82,7 +82,7 @@ EndPoint 会启动线程来监听服务器端口,并负责处理来自客户
|
|||||||
|
|
||||||
ProtocolHandler 通过组合不同类型的 Endpoint 和 Processor 来实现针对具体协议的处理功能。按照不同的协议(HTTP 和 AJP)和不同的 I/O 方式(NIO,NIO2,AJP)进行组合,其有以下六个具体的实现类:
|
ProtocolHandler 通过组合不同类型的 Endpoint 和 Processor 来实现针对具体协议的处理功能。按照不同的协议(HTTP 和 AJP)和不同的 I/O 方式(NIO,NIO2,AJP)进行组合,其有以下六个具体的实现类:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat_AbstractProtocol.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_AbstractProtocol.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**4. 协议升级**
|
**4. 协议升级**
|
||||||
@ -93,7 +93,7 @@ ProtocolHandler 通过组合不同类型的 Endpoint 和 Processor 来实现针
|
|||||||
|
|
||||||
Tomcat 设计者希望连接器是一个单独的组件,能够脱离 Servlet 规范而独立存在,以便增加其使用场景,因此 Process 对输入流封装后得到的 Request 不是一个 Servlet Request,该 Request 的全限定命名为:org.apache.coyote.Request 。因此在这里需要使用适配器模式(具体实现类是 CoyoteAdapter)将其转换为 org.apache.catalina.connector.Request,它才是标准的 ServletRequest 的实现:
|
Tomcat 设计者希望连接器是一个单独的组件,能够脱离 Servlet 规范而独立存在,以便增加其使用场景,因此 Process 对输入流封装后得到的 Request 不是一个 Servlet Request,该 Request 的全限定命名为:org.apache.coyote.Request 。因此在这里需要使用适配器模式(具体实现类是 CoyoteAdapter)将其转换为 org.apache.catalina.connector.Request,它才是标准的 ServletRequest 的实现:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat_request.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_request.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.3 Mapper 和 MapperListener
|
### 3.3 Mapper 和 MapperListener
|
||||||
@ -112,7 +112,7 @@ Tomcat 中的所有容器都实现了 Container 接口,它定义了容器共
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat_container.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_container.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ Tomcat 之所以采用分层的结构,主要是为了更好的灵活性和可
|
|||||||
+ **Context**:表示一个具体的 Web 应用程序,一个虚拟主机可以包含多个 Context;
|
+ **Context**:表示一个具体的 Web 应用程序,一个虚拟主机可以包含多个 Context;
|
||||||
+ **Wrapper**:是 Tomcat 对 Servlet 的包装,一个 Context 中可以有多个 Wrapper。
|
+ **Wrapper**:是 Tomcat 对 Servlet 的包装,一个 Context 中可以有多个 Wrapper。
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat_分层结构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_分层结构.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
Tomcat 容器的分层结构在其 conf 目录下的 `server.xml` 配置文件中也有体现:
|
Tomcat 容器的分层结构在其 conf 目录下的 `server.xml` 配置文件中也有体现:
|
||||||
@ -146,7 +146,7 @@ Tomcat 容器的分层结构在其 conf 目录下的 `server.xml` 配置文件
|
|||||||
|
|
||||||
### 4.3 Pipeline 和 Valve
|
### 4.3 Pipeline 和 Valve
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat_多层容器.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_多层容器.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
由连接器发过来的请求会最先发送到 Engine,最终逐层传递,直至我们编写的 Servlet,这种传递主要通过 Pipeline 和 Valve 来实现。每层容器都有自己的 Pipeline,Pipeline 相当于处理管道;每个 Pipeline 中有一个 Valve 链,每个 Valve 可以看做一个独立的处理单元,用于对请求进行处理。最基础的 Valve 叫做 Basic Valve,新增的 Valve 会位于已有的 Valve 之前。Pipeline 和 Valve 的接口定义如下:
|
由连接器发过来的请求会最先发送到 Engine,最终逐层传递,直至我们编写的 Servlet,这种传递主要通过 Pipeline 和 Valve 来实现。每层容器都有自己的 Pipeline,Pipeline 相当于处理管道;每个 Pipeline 中有一个 Valve 链,每个 Valve 可以看做一个独立的处理单元,用于对请求进行处理。最基础的 Valve 叫做 Basic Valve,新增的 Valve 会位于已有的 Valve 之前。Pipeline 和 Valve 的接口定义如下:
|
||||||
@ -284,14 +284,14 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws
|
|||||||
|
|
||||||
这里对前面的连接器和容器章节进行总结,Tomcat 对客户端请求的完整处理流程如下:
|
这里对前面的连接器和容器章节进行总结,Tomcat 对客户端请求的完整处理流程如下:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat启动请求处理流程.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat启动请求处理流程.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
## 六、启动流程
|
## 六、启动流程
|
||||||
|
|
||||||
Tomcat 整体的启动流程如下图所示:
|
Tomcat 整体的启动流程如下图所示:
|
||||||
|
|
||||||
<div align="center"> <img src="..\pictures\tomcat启动流程.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat启动流程.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ Catalina 通过 Digester 解析 server.xml 来创建所有的服务组件。Dige
|
|||||||
|
|
||||||
Tomcat 并没有完全沿用 JVM 默认的类加载机制,为了保证 Web 应用之间的隔离性和加载的灵活性,其采用了下图所示的类加载机制:
|
Tomcat 并没有完全沿用 JVM 默认的类加载机制,为了保证 Web 应用之间的隔离性和加载的灵活性,其采用了下图所示的类加载机制:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="..\pictures\tomcat_类加载器.jpg"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures\tomcat_类加载器.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
#### 1. Web App Class Loader
|
#### 1. Web App Class Loader
|
||||||
|
@ -26,19 +26,19 @@
|
|||||||
|
|
||||||
Wireshark 是一个网络抓包分析软件,下载地址为:https://www.wireshark.org/index.html#download ,下载后直接安装即可。启动后,进入主页面,需要先选择对应的抓包网络:
|
Wireshark 是一个网络抓包分析软件,下载地址为:https://www.wireshark.org/index.html#download ,下载后直接安装即可。启动后,进入主页面,需要先选择对应的抓包网络:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_主界面.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_主界面.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
这里我使用的是无线网络,因此选择 **WLAN**,双击后即可进入抓包页面。下面我们以 CSDN 的首页 `https://www.csdn.net` 为例,我们首先通过 ping 命令获取其 IP 地址:
|
这里我使用的是无线网络,因此选择 **WLAN**,双击后即可进入抓包页面。下面我们以 CSDN 的首页 `https://www.csdn.net` 为例,我们首先通过 ping 命令获取其 IP 地址:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_ping.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_ping.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
可以看到,其 IP 地址为 `47.95.164.112` 。为了避免浏览器上其他网站的干扰,我们先通过该 IP 地址进行过滤:
|
可以看到,其 IP 地址为 `47.95.164.112` 。为了避免浏览器上其他网站的干扰,我们先通过该 IP 地址进行过滤:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_捕获信息.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_捕获信息.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
输入过滤条件 `ip.addr == 47.95.164.112` 后,敲击回车键即可。可以看到 TCP 和 TLS 的握手消息都已经被过滤出来。
|
输入过滤条件 `ip.addr == 47.95.164.112` 后,敲击回车键即可。可以看到 TCP 和 TLS 的握手消息都已经被过滤出来。
|
||||||
@ -47,7 +47,7 @@ Wireshark 是一个网络抓包分析软件,下载地址为:https://www.wire
|
|||||||
|
|
||||||
这里首先以图片的方式回顾整个 HTTPS 的握手过程:
|
这里首先以图片的方式回顾整个 HTTPS 的握手过程:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/https_tsl_v1.2_ECDHE.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/https_tsl_v1.2_ECDHE.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
然后我们再逐步通过 WireShark 进行分析:
|
然后我们再逐步通过 WireShark 进行分析:
|
||||||
@ -56,12 +56,12 @@ Wireshark 是一个网络抓包分析软件,下载地址为:https://www.wire
|
|||||||
|
|
||||||
首先 TLS/SSL 协议是基于 TCP/IP 协议的,因此需要先等待 TCP 三次握手完成:
|
首先 TLS/SSL 协议是基于 TCP/IP 协议的,因此需要先等待 TCP 三次握手完成:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_三次握手.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_三次握手.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
从上图被被捕获的记录中,可以很直观的看出整个握手过程与下图是完全匹配的:
|
从上图被被捕获的记录中,可以很直观的看出整个握手过程与下图是完全匹配的:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/三次握手.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/三次握手.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
该过程比较简单,这里就不再进行赘述了。
|
该过程比较简单,这里就不再进行赘述了。
|
||||||
@ -70,28 +70,28 @@ Wireshark 是一个网络抓包分析软件,下载地址为:https://www.wire
|
|||||||
|
|
||||||
整个 TLS/ SSL 的握手过程如下:
|
整个 TLS/ SSL 的握手过程如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_密钥交换过程.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_密钥交换过程.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.1 Client Hello
|
### 3.1 Client Hello
|
||||||
|
|
||||||
TCP 握手完成后,客户端首先发起一个 `Client Hello` 请求,里面包含客户端版本号、所有支持的密码套件、以及一个随机数 `Client Random`:
|
TCP 握手完成后,客户端首先发起一个 `Client Hello` 请求,里面包含客户端版本号、所有支持的密码套件、以及一个随机数 `Client Random`:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_client_hello.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_client_hello.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.2 Server Hello
|
### 3.2 Server Hello
|
||||||
|
|
||||||
服务器收到该请求后,会返回一个 `Server Hello` 消息,里面包含选中的用于本次通信的密码套件,以及一个随机数 `Server Random`;
|
服务器收到该请求后,会返回一个 `Server Hello` 消息,里面包含选中的用于本次通信的密码套件,以及一个随机数 `Server Random`;
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_server_hello.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_server_hello.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.3 Server Hello 的详细过程
|
### 3.3 Server Hello 的详细过程
|
||||||
|
|
||||||
接着服务器会在一次返回里面返回多组消息:
|
接着服务器会在一次返回里面返回多组消息:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_server_hello_done.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_server_hello_done.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
这里分别对其进行解释:
|
这里分别对其进行解释:
|
||||||
@ -100,7 +100,7 @@ TCP 握手完成后,客户端首先发起一个 `Client Hello` 请求,里面
|
|||||||
|
|
||||||
这里面包含的是证书信息:
|
这里面包含的是证书信息:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_certificate.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_certificate.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
这里我将里面的部分内容复制了出来:
|
这里我将里面的部分内容复制了出来:
|
||||||
@ -111,7 +111,7 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
可以看到,这就是在浏览器中使用的证书的:
|
可以看到,这就是在浏览器中使用的证书的:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_csdn_证书.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_csdn_证书.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
这一步主要是依据在线证书状态协议(OCSP,Online Certificate Status Protocol)对当前证书状态进行查询:
|
这一步主要是依据在线证书状态协议(OCSP,Online Certificate Status Protocol)对当前证书状态进行查询:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_certifcate_status.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_certifcate_status.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -129,14 +129,14 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
这里面包含了进行 ECDHE 算法所需的各种参数 `Server Params` :
|
这里面包含了进行 ECDHE 算法所需的各种参数 `Server Params` :
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_server_key_exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_server_key_exchange.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
#### 4. Server Hello Done
|
#### 4. Server Hello Done
|
||||||
|
|
||||||
这个就是用于告知客户端服务器的整个 Hello 过程已经结束,并不包含任何内容:
|
这个就是用于告知客户端服务器的整个 Hello 过程已经结束,并不包含任何内容:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_server_hello_done_protocol.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_server_hello_done_protocol.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -145,12 +145,12 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
之后,客户端按照密码套件的要求,也生成一个 `Client Params`,并通过 `Client Key Exchange` 消息发送给服务器;
|
之后,客户端按照密码套件的要求,也生成一个 `Client Params`,并通过 `Client Key Exchange` 消息发送给服务器;
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_client_key_exchange.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_client_key_exchange.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
`Client Key Exchange` 具体内容如下:
|
`Client Key Exchange` 具体内容如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_client_key_exchange_protocol.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_client_key_exchange_protocol.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 3.5 计算预主密钥和主密钥
|
### 3.5 计算预主密钥和主密钥
|
||||||
@ -183,7 +183,7 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
有了会话密钥后,客户端会发送一个 `Change Cipher Spec` 请求,告知服务器将加密方式由非对称加密转换为对称加密;
|
有了会话密钥后,客户端会发送一个 `Change Cipher Spec` 请求,告知服务器将加密方式由非对称加密转换为对称加密;
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_change_cipher_spec.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_change_cipher_spec.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
紧接着,为了验证对称加密,客户端会将所有的握手消息(Handshake Message)进行加密,通过一个 `Encrypted Handshake Message` 请求发送给服务器;
|
紧接着,为了验证对称加密,客户端会将所有的握手消息(Handshake Message)进行加密,通过一个 `Encrypted Handshake Message` 请求发送给服务器;
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_handshake_message.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_handshake_message.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ Certificate: 308206873082056fa00302010202100b038a343b5b17ae92… (id-at-commonNa
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/wireshark_new_session_ticket.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/wireshark_new_session_ticket.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ JDK 原生的锁可以让不同**线程**之间以互斥的方式来访问共享
|
|||||||
|
|
||||||
### 1.1 临时节点方案
|
### 1.1 临时节点方案
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/zookeeper_分布式锁_临时节点方法.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/zookeeper_分布式锁_临时节点方法.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
临时节点方案的原理如下:
|
临时节点方案的原理如下:
|
||||||
@ -37,7 +37,7 @@ JDK 原生的锁可以让不同**线程**之间以互斥的方式来访问共享
|
|||||||
|
|
||||||
### 1.2 临时有序节点方案
|
### 1.2 临时有序节点方案
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/zookeeper_分布式锁_临时有序节点方案.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/zookeeper_分布式锁_临时有序节点方案.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
采用临时有序节点时,对应的流程如下:
|
采用临时有序节点时,对应的流程如下:
|
||||||
@ -69,7 +69,7 @@ JDK 原生的锁可以让不同**线程**之间以互斥的方式来访问共享
|
|||||||
+ 对于读锁节点而言,其只需要关心前一个写锁节点的释放。如果前一个写锁释放了,则多个读锁节点对应的线程可以并发地读取数据;
|
+ 对于读锁节点而言,其只需要关心前一个写锁节点的释放。如果前一个写锁释放了,则多个读锁节点对应的线程可以并发地读取数据;
|
||||||
+ 对于写锁节点而言,其只需要关心前一个节点的释放,而不需要关心前一个节点是写锁节点还是读锁节点。因为为了保证有序性,写操作必须要等待前面的读操作或者写操作执行完成。
|
+ 对于写锁节点而言,其只需要关心前一个节点的释放,而不需要关心前一个节点是写锁节点还是读锁节点。因为为了保证有序性,写操作必须要等待前面的读操作或者写操作执行完成。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/zookeeper_分布式读写锁.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/zookeeper_分布式读写锁.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ client.close();
|
|||||||
|
|
||||||
之后就可以启动多个程序进程来进行测试,此时 ZooKeeper 上的数据结构如下:
|
之后就可以启动多个程序进程来进行测试,此时 ZooKeeper 上的数据结构如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/zookeeper_分布式锁_cli.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/zookeeper_分布式锁_cli.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
在我们指定的路径下,会依次创建多个临时有序节点,而当业务逻辑处理完成后,这些节点就会被移除。这里我们使用的是单机版本的 ZooKeeper ,而集群环境下也是一样,和 Redis 主从模式下的延迟复制会导致数据不一致的情况不同,ZooKeeper 集群各个节点上的数据一致性可以由其自身来进行保证。
|
在我们指定的路径下,会依次创建多个临时有序节点,而当业务逻辑处理完成后,这些节点就会被移除。这里我们使用的是单机版本的 ZooKeeper ,而集群环境下也是一样,和 Redis 主从模式下的延迟复制会导致数据不一致的情况不同,ZooKeeper 集群各个节点上的数据一致性可以由其自身来进行保证。
|
||||||
|
@ -97,7 +97,7 @@ chown -R heibaiying:heibaiying /usr/app/elasticsearch-7.2.0/
|
|||||||
|
|
||||||
想要验证是否启动成功,可以使用 `jps` 命令查看 `Elasticsearch` 进程是否启动,也可以访问`9200`端口,出现如下页面则代表启动成功:
|
想要验证是否启动成功,可以使用 `jps` 命令查看 `Elasticsearch` 进程是否启动,也可以访问`9200`端口,出现如下页面则代表启动成功:
|
||||||
|
|
||||||
<div align="center"> <img src="../../pictures/elk-web-ui.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/elk-web-ui.png"/> </div>
|
||||||
|
|
||||||
## 二、Kibana 安装
|
## 二、Kibana 安装
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ nohup ./kibana &
|
|||||||
|
|
||||||
kibana Web UI 的访问端口号为`5601`,出现以下页面则代表启动成功:
|
kibana Web UI 的访问端口号为`5601`,出现以下页面则代表启动成功:
|
||||||
|
|
||||||
<div align="center"> <img src="../../pictures/kibana-web-ui.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/kibana-web-ui.png"/> </div>
|
||||||
|
|
||||||
### 2.4 界面汉化
|
### 2.4 界面汉化
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
下载地址为: https://www.mongodb.com/download-center/community ,选择所需版本的 MongoDB 后进行下载:
|
下载地址为: https://www.mongodb.com/download-center/community ,选择所需版本的 MongoDB 后进行下载:
|
||||||
|
|
||||||
<div align="center"> <img src="../../pictures/mongodb-version-select.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mongodb-version-select.png"/> </div>
|
||||||
这里我下载的版本为 `4.0.10` , 安装环境为 `RHEL 7.0`,下载后进行解压:
|
这里我下载的版本为 `4.0.10` , 安装环境为 `RHEL 7.0`,下载后进行解压:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
这里我采用的是二进制安装包的方式进行安装,安装包的下载地址为:https://dev.mysql.com/downloads/mysql/ ,按需选择对应的版本后进行下载:
|
这里我采用的是二进制安装包的方式进行安装,安装包的下载地址为:https://dev.mysql.com/downloads/mysql/ ,按需选择对应的版本后进行下载:
|
||||||
|
|
||||||
<div align="center"> <img src="../../pictures/mysql-version.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/mysql-version.png"/> </div>
|
||||||
下载后进行解压,并对解压后的文件夹进行重命名,以便在后面的配置中进行引用:
|
下载后进行解压,并对解压后的文件夹进行重命名,以便在后面的配置中进行引用:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -155,7 +155,7 @@ rabbitmq-plugins enable rabbitmq_management
|
|||||||
|
|
||||||
访问端口为 `15672`。默认的用户名和密码都是 `guest` 。如果你所用浏览器和 RabbitMQ 服务不在同一台主机上,此时应该无法登录,并出现下面的提示 :
|
访问端口为 `15672`。默认的用户名和密码都是 `guest` 。如果你所用浏览器和 RabbitMQ 服务不在同一台主机上,此时应该无法登录,并出现下面的提示 :
|
||||||
|
|
||||||
<div align="center"> <img src="../../pictures/RabbitMQ-访问限制.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/RabbitMQ-访问限制.png"/> </div>
|
||||||
之所以会出现这个提示,是因为出于安全考虑,RabbitMQ 只允许在本机使用默认的`guest`用户名登录。想要在其他主机上登录,需要使用自定义的账户。
|
之所以会出现这个提示,是因为出于安全考虑,RabbitMQ 只允许在本机使用默认的`guest`用户名登录。想要在其他主机上登录,需要使用自定义的账户。
|
||||||
|
|
||||||
### 4.2 新增账户
|
### 4.2 新增账户
|
||||||
@ -182,4 +182,4 @@ rabbitmqctl set_user_tags root administrator
|
|||||||
|
|
||||||
登录后可以查看到RabbitMQ 和 Erlang 的版本号,以及对应的账户信息:
|
登录后可以查看到RabbitMQ 和 Erlang 的版本号,以及对应的账户信息:
|
||||||
|
|
||||||
<div align="center"> <img src="../../pictures/rabbitmq-管控台.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/rabbitmq-管控台.png"/> </div>
|
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
OSI 七层网络模型由国际标准化组织进行制定,它是正统意义上的国际标准。但其实现过于复杂,且制定周期过长,在其整套标准推出之前,TCP/IP 模型已经在全球范围内被广泛使用,所以 TCP/IP 模型才是事实上的国际标准。TCP/IP 模型定义了应用层、传输层、网际层、网络接口层这四层网络结构,但并没有给出网络接口层的具体内容,因此在学习和开发中,通常将网络接口层替换为 OSI 七层模型中的数据链路层和物理层来进行理解,这就是五层网络模型:
|
OSI 七层网络模型由国际标准化组织进行制定,它是正统意义上的国际标准。但其实现过于复杂,且制定周期过长,在其整套标准推出之前,TCP/IP 模型已经在全球范围内被广泛使用,所以 TCP/IP 模型才是事实上的国际标准。TCP/IP 模型定义了应用层、传输层、网际层、网络接口层这四层网络结构,但并没有给出网络接口层的具体内容,因此在学习和开发中,通常将网络接口层替换为 OSI 七层模型中的数据链路层和物理层来进行理解,这就是五层网络模型:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/计算机网络体系结构.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/计算机网络体系结构.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
1. **应用层 (application layer)**:直接为应用进程提供服务。应用层协议定义的是应用进程间通讯和交互的规则,不同的应用有着不同的应用层协议,如 HTTP协议(万维网服务)、FTP协议(文件传输)、SMTP协议(电子邮件)、DNS(域名查询)等。
|
1. **应用层 (application layer)**:直接为应用进程提供服务。应用层协议定义的是应用进程间通讯和交互的规则,不同的应用有着不同的应用层协议,如 HTTP协议(万维网服务)、FTP协议(文件传输)、SMTP协议(电子邮件)、DNS(域名查询)等。
|
||||||
@ -102,19 +102,19 @@ OSI 七层网络模型由国际标准化组织进行制定,它是正统意义
|
|||||||
|
|
||||||
频分复用(FDM,Frequency Division Multiplexing)是将用于传输信道的总带宽划分成若干个子频带(或称子信道),每个子信道传输一路信号:
|
频分复用(FDM,Frequency Division Multiplexing)是将用于传输信道的总带宽划分成若干个子频带(或称子信道),每个子信道传输一路信号:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/频分复用.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/频分复用.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**2. 时分复用**
|
**2. 时分复用**
|
||||||
|
|
||||||
时分复用(TDM,Time Division Multiplexing) 是指采用同一物理连接的不同时段来传输不同的信号:
|
时分复用(TDM,Time Division Multiplexing) 是指采用同一物理连接的不同时段来传输不同的信号:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/时分复用.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/时分复用.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
如上图所示,在一个时分复用帧中,不同用户的信号周期性出现,如果某个用户处于闲置状态,则其对应的帧上也会出现空闲:
|
如上图所示,在一个时分复用帧中,不同用户的信号周期性出现,如果某个用户处于闲置状态,则其对应的帧上也会出现空闲:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/时分复用的缺陷.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/时分复用的缺陷.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
为了解决时分复用的这个缺点就产生了统计时分复用。
|
为了解决时分复用的这个缺点就产生了统计时分复用。
|
||||||
@ -123,14 +123,14 @@ OSI 七层网络模型由国际标准化组织进行制定,它是正统意义
|
|||||||
|
|
||||||
在统计时分复用(Statistic TDM)模式下,各用户将数据发送到集中器的输入缓存中,然后由集中器进行顺序扫描并放入到 STDM 帧中:
|
在统计时分复用(Statistic TDM)模式下,各用户将数据发送到集中器的输入缓存中,然后由集中器进行顺序扫描并放入到 STDM 帧中:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/统计时分复用.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/统计时分复用.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**4. 波分复用**
|
**4. 波分复用**
|
||||||
|
|
||||||
波分复用(WDM,Wavelength Division Multiplexing)是将两种或多种不同波长的光载波信号在发送端经复用器汇合在一起,并耦合到光线路的同一根光纤中进行传输;在接收端,经分用器将各种波长的光载波分离,然后由光解调器作进一步处理以恢复原信号:
|
波分复用(WDM,Wavelength Division Multiplexing)是将两种或多种不同波长的光载波信号在发送端经复用器汇合在一起,并耦合到光线路的同一根光纤中进行传输;在接收端,经分用器将各种波长的光载波分离,然后由光解调器作进一步处理以恢复原信号:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/波分复用.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/波分复用.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**5.码分复用**
|
**5.码分复用**
|
||||||
@ -145,14 +145,14 @@ OSI 七层网络模型由国际标准化组织进行制定,它是正统意义
|
|||||||
|
|
||||||
数据链路层会将网络层传递下来的数据拆分为多段,并在每段数据前后分别添加首部和尾部,以构成一个完成的帧,帧是链路层传输的基本数据单元。帧首部用控制字符 `SOH` 表示,帧尾部用控制字符 `EOT` 表示:
|
数据链路层会将网络层传递下来的数据拆分为多段,并在每段数据前后分别添加首部和尾部,以构成一个完成的帧,帧是链路层传输的基本数据单元。帧首部用控制字符 `SOH` 表示,帧尾部用控制字符 `EOT` 表示:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/封装成帧.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/封装成帧.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**2. 透明传输**
|
**2. 透明传输**
|
||||||
|
|
||||||
透明传输是指不论何种数据都应当能够在链路上进行安全地传输。由于我们采用控制字符来封装帧,当传输数据中出现了控制字符时,就会导致无法正确区分出帧头帧尾,此时需要使用转移字符 `ESC` 来进行转义:
|
透明传输是指不论何种数据都应当能够在链路上进行安全地传输。由于我们采用控制字符来封装帧,当传输数据中出现了控制字符时,就会导致无法正确区分出帧头帧尾,此时需要使用转移字符 `ESC` 来进行转义:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/透明传输.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/透明传输.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**3. 差错检测**
|
**3. 差错检测**
|
||||||
@ -181,7 +181,7 @@ MAC地址为 48 位的(6 个字节),通常表示为 12 个 16 进制数,
|
|||||||
|
|
||||||
以太网(Ethernet)是目前使用范围最广的局域网,以常用的以太网 v2 标准为例,其帧格式如下:
|
以太网(Ethernet)是目前使用范围最广的局域网,以常用的以太网 v2 标准为例,其帧格式如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/Ethernet_Type_II_Frame_format.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/Ethernet_Type_II_Frame_format.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
其中 Mac Header 分别记录了目的地的 Mac 地址和来源地的 Mac 地址。
|
其中 Mac Header 分别记录了目的地的 Mac 地址和来源地的 Mac 地址。
|
||||||
@ -192,7 +192,7 @@ MAC地址为 48 位的(6 个字节),通常表示为 12 个 16 进制数,
|
|||||||
|
|
||||||
网际协议(Internet Protocol)是网络层中最重要的协议,也是 TCP\IP 两大核心协议之一,所有需要互联的计算机网络都需要遵循该协议,以便能够将不同网络在全世界范围内连接起来。该层传输的基本数据单元是 IP 数据报,其格式如下:
|
网际协议(Internet Protocol)是网络层中最重要的协议,也是 TCP\IP 两大核心协议之一,所有需要互联的计算机网络都需要遵循该协议,以便能够将不同网络在全世界范围内连接起来。该层传输的基本数据单元是 IP 数据报,其格式如下:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="../pictures/IP数据报格式.png"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/IP数据报格式.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
各字段的含义如下:
|
各字段的含义如下:
|
||||||
@ -236,7 +236,7 @@ IP 数据报中的源地址和目标地址均是 IP 地址,而数据链路层
|
|||||||
|
|
||||||
IP 地址由 ICANN(The Internet Corporation for Assigned Names and Numbers,互联网名称与数字地址分配机构)进行分配,它是一个在全世界范围内唯一的 32 位标识符,最早的 IP 地址采用两级分类,只由 `网络号 + 主机号` 组成,分为以下五类:
|
IP 地址由 ICANN(The Internet Corporation for Assigned Names and Numbers,互联网名称与数字地址分配机构)进行分配,它是一个在全世界范围内唯一的 32 位标识符,最早的 IP 地址采用两级分类,只由 `网络号 + 主机号` 组成,分为以下五类:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="../pictures/IP地址分类.png"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/IP地址分类.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
A,B,C 三类是最常使用的类型,其类别位分别为 0,10,110 。需要注意的是另外并非所有 IP 地址都可用来分配,限制如下:
|
A,B,C 三类是最常使用的类型,其类别位分别为 0,10,110 。需要注意的是另外并非所有 IP 地址都可用来分配,限制如下:
|
||||||
@ -267,7 +267,7 @@ A,B,C 三类是最常使用的类型,其类别位分别为 0,10,110
|
|||||||
|
|
||||||
划分子网方案诞生与 1985 年,它从主机号借用若干位作为子网号,从而将 IP 地址划分为三级:网络号 + 子网号 + 主机号。假设网络地址为 192.168.10.0,利用子网掩码 255.255.255.224 对其进行划分子网,此时可以划分为四个子网:
|
划分子网方案诞生与 1985 年,它从主机号借用若干位作为子网号,从而将 IP 地址划分为三级:网络号 + 子网号 + 主机号。假设网络地址为 192.168.10.0,利用子网掩码 255.255.255.224 对其进行划分子网,此时可以划分为四个子网:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/划分子网.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/划分子网.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
由于子网对外是不可见的,所以需要使用子网掩码来辅助路由,假设目标 IP 地址为 192.168.10.198,想要正确到达该地址,必须先正确到达网络地址 192.168.10.192 。网络地址,子网掩码和主机 IP 之间的关系如下:
|
由于子网对外是不可见的,所以需要使用子网掩码来辅助路由,假设目标 IP 地址为 192.168.10.198,想要正确到达该地址,必须先正确到达网络地址 192.168.10.192 。网络地址,子网掩码和主机 IP 之间的关系如下:
|
||||||
@ -335,7 +335,7 @@ RFC 1918 中指明了一些专用地址(Private Address),这些地址只
|
|||||||
|
|
||||||
由上面的原理也可以看出,具有 n 个全球 IP 地址的路由器最多只允许 n 台主机同时接入到互联网。 为了解决这个问题,现在常用的 NAT 转换表会把传输层的端口号也利用上。
|
由上面的原理也可以看出,具有 n 个全球 IP 地址的路由器最多只允许 n 台主机同时接入到互联网。 为了解决这个问题,现在常用的 NAT 转换表会把传输层的端口号也利用上。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/NAT路由器的工作原理.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/NAT路由器的工作原理.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 4.9 IPv6
|
### 4.9 IPv6
|
||||||
@ -361,7 +361,7 @@ RFC 1918 中指明了一些专用地址(Private Address),这些地址只
|
|||||||
+ 长度:UDP 用户数报的总长度;
|
+ 长度:UDP 用户数报的总长度;
|
||||||
+ 校验和:检测 UDP 用户数据报在传输中是否有错,如果有错则丢弃。
|
+ 校验和:检测 UDP 用户数据报在传输中是否有错,如果有错则丢弃。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/UDP用户数据报.jpg"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/UDP用户数据报.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 5.2 TCP 简介
|
### 5.2 TCP 简介
|
||||||
@ -382,7 +382,7 @@ RFC 1918 中指明了一些专用地址(Private Address),这些地址只
|
|||||||
|
|
||||||
TCP 虽然是面向字节流的,但其传输的基本数据单元则是报文段。一个 TCP 报文段分为首部和数据两部分,TCP 首部的前 20 个字节是固定的,后面有 4n 字节是根据需要而增加的选项(n 为整数),具体格式如下:
|
TCP 虽然是面向字节流的,但其传输的基本数据单元则是报文段。一个 TCP 报文段分为首部和数据两部分,TCP 首部的前 20 个字节是固定的,后面有 4n 字节是根据需要而增加的选项(n 为整数),具体格式如下:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="../pictures/TCP报文首部.jpg"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/TCP报文首部.jpg"/> </div>
|
||||||
|
|
||||||
|
|
||||||
各字段的含义如下:
|
各字段的含义如下:
|
||||||
@ -417,7 +417,7 @@ TCP 虽然是面向字节流的,但其传输的基本数据单元则是报文
|
|||||||
|
|
||||||
TCP 建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个 TCP 报文段,具体如下:
|
TCP 建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个 TCP 报文段,具体如下:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="../pictures/三次握手.png"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/三次握手.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
1. 服务器进程 B 首先创建传输控制模块 TCB,然后进入 LISTEN(收听)状态,准备接受客户端的连接请求;
|
1. 服务器进程 B 首先创建传输控制模块 TCB,然后进入 LISTEN(收听)状态,准备接受客户端的连接请求;
|
||||||
@ -434,7 +434,7 @@ TCP 建立连接的过程叫做握手,握手需要在客户和服务器之间
|
|||||||
|
|
||||||
数据传输结束后,通信的双方都可以释放连接,具体过程如下:
|
数据传输结束后,通信的双方都可以释放连接,具体过程如下:
|
||||||
|
|
||||||
<div align="center"> <img width="600px" src="../pictures/四次挥手.png"/> </div>
|
<div align="center"> <img width="600px" src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/四次挥手.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
1. 假设应用进程 A 先主动关闭连接,此时需要发送连接释放报文段:首部终止控制位 FIN 为 1,序号 `seq = u`,其中 u 等于前面传送过的数据的最后一个字节的序号加 1 。之后 A 进入 FIN-WAIT-1(终止等待 1)状态;
|
1. 假设应用进程 A 先主动关闭连接,此时需要发送连接释放报文段:首部终止控制位 FIN 为 1,序号 `seq = u`,其中 u 等于前面传送过的数据的最后一个字节的序号加 1 。之后 A 进入 FIN-WAIT-1(终止等待 1)状态;
|
||||||
@ -462,7 +462,7 @@ RFC 793 建议 MSL 设置为 2 分钟,现在的网络环境已经有了质的
|
|||||||
|
|
||||||
想要实现可靠性传输,最基本的可以使用停止等待协议:每发送完一个数据单元就停止发送,并等待对方的确认。
|
想要实现可靠性传输,最基本的可以使用停止等待协议:每发送完一个数据单元就停止发送,并等待对方的确认。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/停止等待协议1.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/停止等待协议1.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
此时面临两个问题:
|
此时面临两个问题:
|
||||||
@ -472,7 +472,7 @@ RFC 793 建议 MSL 设置为 2 分钟,现在的网络环境已经有了质的
|
|||||||
|
|
||||||
针对第一个问题,解决方案是如果在给定的时间内没有收到确认,则进行超时重传:
|
针对第一个问题,解决方案是如果在给定的时间内没有收到确认,则进行超时重传:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/停止等待协议2.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/停止等待协议2.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
针对第二个问题,其解决方案依然是超时重传,具体细分为以下两种情况:
|
针对第二个问题,其解决方案依然是超时重传,具体细分为以下两种情况:
|
||||||
@ -481,12 +481,12 @@ RFC 793 建议 MSL 设置为 2 分钟,现在的网络环境已经有了质的
|
|||||||
|
|
||||||
+ 如果 B 的返回确认没有丢失,只是超过了重传时间后才到达 A,此时 A 可能会收到两次确认,一次是补传得到确认,一次是原有的延迟到达的确认,A 需要丢弃延时到达的确认,不做任何处理:
|
+ 如果 B 的返回确认没有丢失,只是超过了重传时间后才到达 A,此时 A 可能会收到两次确认,一次是补传得到确认,一次是原有的延迟到达的确认,A 需要丢弃延时到达的确认,不做任何处理:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/停止等待协议3.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/停止等待协议3.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
在基本的停止等待协议中,一次只发送一个数据单元,此时信道利用率非常低,为了解决这个问题,可以采用流水线传输,一次发送多个数据单元:
|
在基本的停止等待协议中,一次只发送一个数据单元,此时信道利用率非常低,为了解决这个问题,可以采用流水线传输,一次发送多个数据单元:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/流水线传输.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/流水线传输.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
当使用流水线传输时,为保证可靠性,需要配合使用连续 ARQ 协议和滑动窗口协议。
|
当使用流水线传输时,为保证可靠性,需要配合使用连续 ARQ 协议和滑动窗口协议。
|
||||||
@ -495,7 +495,7 @@ RFC 793 建议 MSL 设置为 2 分钟,现在的网络环境已经有了质的
|
|||||||
|
|
||||||
连续ARQ(Automatic Repeat reQuest)协议指发送方维持着一个一定大小的发送窗口,位于发送窗口内的所有分组都可连续发送出去,中途不需要等待对方的确认,发送方在每收到一个确认时就把发送窗口向前滑动一个分组的位置:
|
连续ARQ(Automatic Repeat reQuest)协议指发送方维持着一个一定大小的发送窗口,位于发送窗口内的所有分组都可连续发送出去,中途不需要等待对方的确认,发送方在每收到一个确认时就把发送窗口向前滑动一个分组的位置:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/连续ARQ工作原理.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/连续ARQ工作原理.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
通常接收方一般都是采用累积确认的方式。此时接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,它表示:这个分组及其之前的所有分组都已正确到达。
|
通常接收方一般都是采用累积确认的方式。此时接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,它表示:这个分组及其之前的所有分组都已正确到达。
|
||||||
@ -543,19 +543,19 @@ RTO = RTT_S + 4 × RTT_D
|
|||||||
|
|
||||||
拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢地增大:每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是像慢启动阶段那样加倍地增长。慢启动和拥塞避免通常是配合使用,以保证启动速度,一开始使用慢启动进行成倍增长,当达到某一个阈值 ssthresh 后采用拥塞避免进行稳步尝试:
|
拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢地增大:每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是像慢启动阶段那样加倍地增长。慢启动和拥塞避免通常是配合使用,以保证启动速度,一开始使用慢启动进行成倍增长,当达到某一个阈值 ssthresh 后采用拥塞避免进行稳步尝试:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/拥塞窗口变化情况.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/拥塞窗口变化情况.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
**3. 快重传和快恢复**
|
**3. 快重传和快恢复**
|
||||||
|
|
||||||
快重传算法要求接收方不要等待自己发送数据时才进捎带确认,而是要立即发送确认,即使收到了失序报文段也要立即发出对已收到的报文段的重复确认。示例如下:
|
快重传算法要求接收方不要等待自己发送数据时才进捎带确认,而是要立即发送确认,即使收到了失序报文段也要立即发出对已收到的报文段的重复确认。示例如下:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/快重传.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/快重传.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
如上图所示,当 M<sub>3</sub> 丢失时,之后发送 M<sub>4</sub> , M<sub>5</sub> , M<sub>6</sub> 时收到的都是对于 M<sub>2</sub> 的重复确认,此时发送方就可以知道 M<sub>3</sub> 已经丢失,需要立即进行重传。由于此时只是个别报文出现了丢失,而不是网络拥塞,所以执行**快恢复**:发送方调整 ssthresh = cwnd / 2,并设置 cwnd = ssthresh = 8 (图中点5),并开始执行拥塞避免算法。
|
如上图所示,当 M<sub>3</sub> 丢失时,之后发送 M<sub>4</sub> , M<sub>5</sub> , M<sub>6</sub> 时收到的都是对于 M<sub>2</sub> 的重复确认,此时发送方就可以知道 M<sub>3</sub> 已经丢失,需要立即进行重传。由于此时只是个别报文出现了丢失,而不是网络拥塞,所以执行**快恢复**:发送方调整 ssthresh = cwnd / 2,并设置 cwnd = ssthresh = 8 (图中点5),并开始执行拥塞避免算法。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/拥塞窗口变化情况.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/拥塞窗口变化情况.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
## 六、应用层
|
## 六、应用层
|
||||||
@ -571,7 +571,7 @@ RTO = RTT_S + 4 × RTT_D
|
|||||||
+ **基础结构域名**:又称为反向域名,用于反向域名解析,该顶级域名只有一个 arpa;
|
+ **基础结构域名**:又称为反向域名,用于反向域名解析,该顶级域名只有一个 arpa;
|
||||||
+ **新顶级域名 New gTLD**:ICANN 机构在 2011 年 6 月 20 日批准新顶级域名,允许任何满足条件的公司或机构进行申请。
|
+ **新顶级域名 New gTLD**:ICANN 机构在 2011 年 6 月 20 日批准新顶级域名,允许任何满足条件的公司或机构进行申请。
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/互联网的域名空间.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/互联网的域名空间.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
### 6.2 文件传输协议 FTP
|
### 6.2 文件传输协议 FTP
|
||||||
@ -609,7 +609,7 @@ HTTP 协议定义了浏览器如何向万维网请求文档,以及服务器如
|
|||||||
|
|
||||||
通常连接到互联网的计算机的协议软件都需要配置多个项目,如 IP 地址,子网掩码,默认路由器的 IP 地址以及域名服务器的 IP 地址等等,为了省去配置的麻烦,现在互联网普遍采用动态主机配置协议 DHCP(Dynamic Host Configuration Protocol),它提供了一种即插即用联网的机制。此时你只需要采用默认的配置即可,如下所示:
|
通常连接到互联网的计算机的协议软件都需要配置多个项目,如 IP 地址,子网掩码,默认路由器的 IP 地址以及域名服务器的 IP 地址等等,为了省去配置的麻烦,现在互联网普遍采用动态主机配置协议 DHCP(Dynamic Host Configuration Protocol),它提供了一种即插即用联网的机制。此时你只需要采用默认的配置即可,如下所示:
|
||||||
|
|
||||||
<div align="center"> <img src="../pictures/动态主机配置.png"/> </div>
|
<div align="center"> <img src="https://gitee.com/heibaiying/Full-Stack-Notes/raw/master/pictures/动态主机配置.png"/> </div>
|
||||||
|
|
||||||
|
|
||||||
此时需要进行联网的主机在启动时候会广播发现报文(DHCP DISCOVER),其目的地址为 255.255.255.255(即受限广播地址),此时本地网络上的所有主机都能接收到这个广播报文,但只有 DHCP 服务器才会通过提供报文(DHCP OFFER)对此广播进行响应。DHCP 服务器先在其数据库中查找该计算机的配置信息,若找到,则直接返回;若找不到,则从服务器的 IP 地址池取一个地址分配给该计算机。
|
此时需要进行联网的主机在启动时候会广播发现报文(DHCP DISCOVER),其目的地址为 255.255.255.255(即受限广播地址),此时本地网络上的所有主机都能接收到这个广播报文,但只有 DHCP 服务器才会通过提供报文(DHCP OFFER)对此广播进行响应。DHCP 服务器先在其数据库中查找该计算机的配置信息,若找到,则直接返回;若找不到,则从服务器的 IP 地址池取一个地址分配给该计算机。
|
||||||
|
Loading…
x
Reference in New Issue
Block a user