diff --git a/notes/HTTP.md b/notes/HTTP.md
index db928dd8..d685d584 100644
--- a/notes/HTTP.md
+++ b/notes/HTTP.md
@@ -63,17 +63,14 @@
URI 包含 URL 和 URN。
-
## 请求和响应报文
### 1. 请求报文
-
### 2. 响应报文
-
# 二、HTTP 方法
客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。
@@ -160,7 +157,6 @@ CONNECT www.example.com:443 HTTP/1.1
```
-
## TRACE
> 追踪路径
@@ -303,7 +299,6 @@ CONNECT www.example.com:443 HTTP/1.1
## 连接管理
-
### 1. 短连接与长连接
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问的 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。
@@ -319,6 +314,18 @@ CONNECT www.example.com:443 HTTP/1.1
流水线是在同一条长连接上连续发出请求,而不用等待响应返回,这样可以减少延迟。
+但是流水线依然存在不少缺陷:
+
+- pipelining 只能适用于 HTTP/1.1,且只有幂等的请求(GET、HEAD、PUT 和 DELETE 等方法)才能使用 pipelining ,非幂等请求比如 POST 不能使用,因为请求之间可能会存在先后依赖关系([Clients SHOULD NOT pipeline requests using non-idempotent methods or non-idempotent sequences of methods](https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.2.2));
+
+- head of line blocking 问题,HTTP/1.1 协议规定:服务器必须按请求收到的顺序依次发送响应([A server MUST send its responses to those requests in the same order that the requests were received.](https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.2.2)。因此,如果前面的请求堵塞了,后面的请求即使处理完了,也需要等待前面的请求处理完发送后,才能发送给客户端;
+
+- 绝大部分的 HTTP 代理服务器不支持 pipelining;
+
+- 使用 pipelining 不一定会带来的性能上的提升。
+
+ 正是因为有这么多的问题,各大浏览器厂商要么是根本就不支持 pipelining,要么就是默认关掉了 pipelining 机制,而且启用的条件十分苛刻。
+
## Cookie
HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。
@@ -632,11 +639,9 @@ HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,
- 用户察觉得到正向代理的存在。
-
- 而反向代理一般位于内部网络中,用户察觉不到。
-
### 2. 网关
与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。
@@ -658,7 +663,6 @@ HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)
通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。
-
## 加密
### 1. 对称密钥加密
@@ -669,7 +673,6 @@ HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)
- 缺点:无法安全地将密钥传输给通信方。
-
### 2.非对称密钥加密
非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。
@@ -682,13 +685,11 @@ HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)
- 缺点:运算速度慢。
-
### 3. HTTPS 采用的加密方式
HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥)
-
## 认证
通过使用 **证书** 来对通信方进行认证。
@@ -700,7 +701,6 @@ HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输对
进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。
-
## 完整性保护
SSL 提供报文摘要功能来进行完整性保护。
@@ -729,7 +729,6 @@ HTTP/1.x 实现简单是以牺牲性能为代价的:
HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。
-
在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。
- 一个数据流(Stream)都有一个唯一标识符和可选的优先级信息,用于承载双向信息。
@@ -737,13 +736,11 @@ HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式
- 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。
-
## 服务端推送
HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。
-
## 首部压缩
HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。
@@ -753,7 +750,6 @@ HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见
不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。
-
# 八、HTTP/1.1 新特性
详细内容请见上文
diff --git a/notes/算法 - 排序.md b/notes/算法 - 排序.md
index 87642628..a60e25c8 100644
--- a/notes/算法 - 排序.md
+++ b/notes/算法 - 排序.md
@@ -59,7 +59,6 @@ public abstract class Sort> {
选择排序需要 \~N2/2 次比较和 \~N 次交换,它的运行时间与输入无关,这个特点使得它对一个已经排序的数组也需要这么多的比较和交换操作。
-
```java
public class Selection> extends Sort {
@@ -86,7 +85,6 @@ public class Selection> extends Sort {
在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。
-
```java
public class Bubble> extends Sort {
@@ -120,7 +118,6 @@ public class Bubble> extends Sort {
- 最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。
-
```java
public class Insertion> extends Sort {
@@ -143,7 +140,6 @@ public class Insertion> extends Sort {
希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。
-
```java
public class Shell> extends Sort {
@@ -177,7 +173,6 @@ public class Shell> extends Sort {
归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。
-
## 1. 归并方法
归并方法将数组中两个已经排序的部分归并成一个。
@@ -273,7 +268,6 @@ public class Down2UpMergeSort> extends MergeSort {
- 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。
-
```java
public class QuickSort> extends Sort {
@@ -283,7 +277,7 @@ public class QuickSort> extends Sort {
sort(nums, 0, nums.length - 1);
}
- private void sort(T[] nums, int l, int h) {
+ protected void sort(T[] nums, int l, int h) {
if (h <= l)
return;
int j = partition(nums, l, h);
@@ -292,7 +286,7 @@ public class QuickSort> extends Sort {
}
private void shuffle(T[] nums) {
- List list = Arrays.asList(nums);
+ List> list = Arrays.asList(nums);
Collections.shuffle(list);
list.toArray(nums);
}
@@ -304,7 +298,6 @@ public class QuickSort> extends Sort {
取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。
-
```java
private int partition(T[] nums, int l, int h) {
int i = l, j = h + 1;
@@ -408,7 +401,6 @@ public T select(T[] nums, int k) {
堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。
-
```java
public class Heap> {
@@ -444,7 +436,6 @@ public class Heap> {
在堆中,当一个节点比父节点大,那么需要交换这个两个节点。交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作,把这种操作称为上浮。
-
```java
private void swim(int k) {
while (k > 1 && less(k / 2, k)) {
@@ -457,7 +448,6 @@ private void swim(int k) {
类似地,当一个节点比子节点来得小,也需要不断地向下进行比较和交换操作,把这种操作称为下沉。一个节点如果有两个子节点,应当与两个子节点中最大那个节点进行交换。
-
```java
private void sink(int k) {
while (2 * k <= N) {
@@ -506,13 +496,11 @@ public T delMax() {
无序数组建立堆最直接的方法是从左到右遍历数组进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。
-
#### 5.2 交换堆顶元素与最后一个元素
交换之后需要进行下沉操作维持堆的有序状态。
-
```java
public class HeapSort> extends Sort {
/**