知識分享--架構

A_aliane發表於2019-11-22

OOP

SOLID

SOLID是以下五個原則的簡寫

  1. Single Responsibility, SRP (單一職責原則)
  2. Open-Close, OCP (開閉原則)
  3. Liskov Substitution, LSP (里氏替換原則)
  4. Interface Segregation, ISP (介面隔離原則)
  5. Dependency Inversion, DIP (依賴倒置原則 )

再加一點:

組合優先於繼承 Composition/Aggregation Principle (CRP)

DRY

不做重複的事(Don't Repeat Yourself)

降低可管理單元複雜度的一個基本策略就是將他們拆解成更小的單元。

通常將其首字母縮寫為DRY,出現在Andy Hunt和Dave Thomas所作的《The Pragmatic Programmer》一書中,但究其概念本身則由來已久。它指的是軟體最小的部分。

當你在構建一個大的軟體專案時,你將經常被無處不在的複雜事物所困擾。人們並不擅長管理複雜事物;他們更擅長於在特定範圍內挖掘極具創造性的解決方案。降低可管理單元複雜度的一條最基本原則就是將系統切分成更為容易的單元。起初,你可能希望將你的系統切分成許多元件,每個元件都能體現它本身的子系統,而這個子系統包含可以實現特定功能的所有東西。

舉例來講,如果你在構建一個內容管理系統(CMS),負責人員管理的模組將會成為一個元件。這個元件可以繼續被切分為諸如角色管理單元等等更多的子元件,並且它有可能會與其他的元件進行資訊互動,比如安全元件。

就這樣不斷的將系統切分成元件,再進一步將元件切分為子元件,你終將會切分到一個層次,在這個層次上原本那些複雜單元被精簡為一個個單一職責的單元。這些職責可以在一個類裡面實現(假設我們在構建物件導向的應用)。類包含方法和屬性,方法實現演算法,演算法及其子部分(取決於我們想要得到的意願程度,不知道怎麼翻譯)計算或者囊括了構建業務邏輯的最小模組。

DRY 原則指出,這些小的業務模組在你整個系統中能且只能出現一次。在一個系統內,每個知識必須有單一的,明確的,權威的呈現。

在它內部必須有單一的呈現。

注意知識與其呈現的區別。如果我們正在CMS中實現資料庫連線,那將會有個初始化資料庫驅動的程式碼段,傳遞認證資訊並將連線的引用儲存到一個變數裡。這個程式碼段就是知識的一部分,它是講述某件事是如何完成的。而儲存有連線引用的變數則是知識的體現,並且這是可以被別的模組使用的。如果資料庫認證資訊改變了,我們將不得不修改程式碼段,而不是它的呈現。

  • 一個完美的應用,每一個小的業務邏輯將他的知識封裝在呈現裡,並且宣告為一個變數或者一個類屬性。
  • 這個變數本身是被封裝在一個類裡,這個類可以被描述為一個職責的呈現。類又被封裝在一個元件裡,元件可以被描述為功能的呈現。
  • 除非我們達到軟體專案的最頂層,即一堆日益複雜的呈現,否則我們無以繼續這樣的方案。這種看待軟體複雜度的方式被稱作模組化架構,而DRY便是它非常重要的一部分。

設計與架構模式

型別 模式 翻譯 說明
型別 模式 翻譯 說明
--- --- --- --- ---
建立模式 Abstract Factory 抽象工廠 提供用於建立相關物件系列的介面
剖析模式 Timing Functions 時序功能 包裝函式並記錄執行
同步模式 Condition Variable 條件變數 為執行緒提供一種機制,以暫時放棄訪問以等待某些條件
並行模式 N-Barrier N-二道閘 阻止程式繼續進行,直到所有N個程式都到達屏障
架構模式 CQRS 命令查詢職責分離
訊息傳遞模式 Fan-In 扇入 該模組直接呼叫上級模組的個數,像漏斗型一樣去工作
穩定模式 Bulkheads Bulkheads 實施故障遏制原則(即防止級聯故障)
結構模式 Adapter 介面卡 適配另一個不相容的介面來一起工作
行為模式 Chain of Responsibility 職責鏈 避免通過提供超過物件處理請求的機會來將傳送方與接收方耦合
Builder 生成器 使用簡單物件構建複雜物件
Factory Method 工廠方法 將物件的例項化延遲到用於建立例項的專用函式
Object Pool 物件池 例項化並維護一組相同型別的物件例項
Singleton 單例 將型別的例項化限制為一個物件
Bridge 橋接 將介面與其實現分離,以便兩者可以獨立變化
Composite 組合 封裝並提供對許多不同物件的訪問
Decorator 裝飾 靜態或動態地向物件新增行為
Facade 外觀 使用一種型別作為許多其他型別的API
Flyweight 享元 運用共享技術有效地支援大量細粒度的物件
Proxy 代理 為物件提供代理以控制其操作
Command 命令 捆綁命令和引數以便稍後呼叫
Mediator 中介者 連線物件並充當代理
Memento 備忘錄 生成可用於返回先前狀態的不透明令牌
Observer 觀察者 提供回撥以通知事件/資料更改
Registry 註冊 跟蹤給定類的所有子類
State 狀態 根據內部狀態封裝同一物件的不同行為
Strategy 策略 允許在執行時選擇演算法的行為
Template 模板 定義一個將某些方法推遲到子類的框架類
Visitor 訪問者 將演算法與其執行的物件分開
Lock/Mutex 鎖定/互斥 對資源實施互斥限制以獲得獨佔訪問許可權
Monitor 監視器 互斥和條件變數模式的組合
Read-Write Lock 讀寫鎖定 允許並行讀取訪問,但僅對資源的寫入操作進行獨佔訪問
Semaphore 訊號 允許控制對公共資源的訪問
Bounded Parallelism 有界並行 完成大量資源限制的獨立任務
Broadcast 廣播 把一個訊息同時傳輸到所有接收端
Coroutines 協同程式 允許在特定地方暫停和繼續執行的子程式
Generators 生成器 一次性生成一系列值
Reactor 反應 服務處理程式使用I/O多路複用策略來同步、有序的處理一個或多個客戶端併發請求
Parallelism 並行 完成大量獨立任務
Producer Consumer 生產者消費者 從任務執行中分離任務
Scheduler 排程器 協調任務步驟
Fan-Out 扇出 該模組直接呼叫的下級模組的個數
Futures & Promises Futures & Promises 扮演一個佔位角色,對未知的結果用於同步
Publish/Subscribe Publish/Subscribe 將資訊傳遞給訂閱者
Push & Pull Push & Pull 把一個管道上的訊息分發給多人
Circuit-Breaker 斷路器 當請求有可能失敗時,停止流動的請求
Deadline 截止日期 一旦響應變緩,允許客戶端停止一個正在等待的響應
Fail-Fast機制 快速失敗 檢查請求開始時所需資源的可用性,如果不滿足要求則失敗
Handshaking 握手 詢問元件是否可以承受更多負載,如果不能,則請求被拒絕
Steady-State 穩定狀態 為每一個服務積累一個資源,其它服務必須回收這些資源
MVC 模型-檢視-控制器
MVP 模型-檢視-主持人
MVVM 模型-檢視-檢視模型
Tiers 分層架構
Actor
SOA 面向服務
Master-Slave 主從
Event-Bus 事件匯流排
Pipeline / Filter 流水線、過濾器 Servlet和filter, inceptor就是類似的模式
Gateway

通訊

DNS

最開始確實就是直接使用 IP 地址來訪問遠端主機的。早期聯網的每臺計算機都是採用主機檔案(即我們俗稱的 hosts 檔案)來進行地址配置和解析的,後來聯網機器越來越多,主機檔案的更新和同步就成了很大的問題。於是,1983 年保羅·莫卡派喬斯發明了域名解析服務和域名系統,在 1985 年 1 月 1 日,世界上第一個域名 nordu.net 才被註冊成功。

域名比 IP 地址更容易記憶,本質上只是為數字化的網際網路資源提供了易於記憶的別名,就像在北京提起「故宮博物院」就都知道指的是「東城區景山前街 4 號」的那個大院子一樣。如果把 IP 地址看成電話號碼,那域名系統就是通訊錄。我們在通訊錄裡儲存了朋友和家人的資訊,每次通過名字找到某人打電話的時候,通訊錄就會查出與之關聯的電話號碼,然後撥號過去。我們可能記不下多少完整的電話號碼,但是聯絡人的名字卻是一定記得的。

域名解析是怎麼完成的

DNS 解析的過程是什麼樣子的呢?在開始這個問題之前,我們先看一看域名的層次結構。

域名的層級結構

在討論域名的時候,我們經常聽到有人說「頂級域名」、「一級域名」、「二級域名」等概念,域名級別究竟是怎麼劃分的呢?

  • 根域名。還是以百度為例,通過一些域名解析工具,我們可以看到百度官網域名顯示為 [www.baidu.com](http://www.baidu.com/).,細心的人會注意到,這裡最後有一個 .,這不是 bug,而是所有域名的尾部都有一個根域名。[www.baidu.com](http://www.baidu.com/) 真正的域名是 [www.baidu.com](http://www.baidu.com/).root,簡寫為[www.baidu.com](http://www.baidu.com/).,又因為根域名 .root 對於所有域名都是一樣的,所以平時是省略的,最終就變成了我們常見的樣子。

  • 根域名的下一級叫做頂級域名(top-level domain,縮寫為 TLD),也叫做一級域名,常見的如 .com / .net / .org / .cn 等等,他們就是頂級域名。

  • 再下一級叫做二級域名(second-level domain,縮寫為 SLD),比如 baidu.com。這是我們能夠購買和註冊的最高階域名。

  • 次級域名之下,就是主機名(host),也可以稱為三級域名,比如 www.baidu.com,由此往下,基本上 N 級域名就是在 N-1 級域名前追加一級。

總結一下,常見的域名層級結構如下:

主機名.次級域名.頂級域名.根域名
# ie
www.baidu.com.root

一般來說我們購買一個域名就是購買一個二級域名(SLD)的管理權(如 leancloud.cn),有了這個管理權我們就可以隨意設定三級、四級域名了。

域名解析的過程

與域名的分級結構對應,DNS 系統也是一個樹狀結構,不同級別的域名由不同的域名伺服器來解析,整個過程是一個「層級式」的。

層級式域名解析體系的第一層就是根域名伺服器,全世界 IPv4 根域名伺服器只有 13 臺(名字分別為 A 至 M),1 個為主根伺服器在美國,其餘 12 個均為輔根伺服器,它們負責管理世界各國的域名資訊。在根伺服器下面是頂級域名伺服器,即相關國家域名管理機構的資料庫,如中國網際網路絡資訊中心(CNNIC)。然後是再下一級的權威域名伺服器和 ISP 的快取伺服器。

一個域名必須首先經過根資料庫的解析後,才能轉到頂級域名伺服器進行解析,這一點與生活中問路的情形有幾分相似。

分級查詢的例項

現在我們來看一個實際的例子。如果我們在瀏覽器中輸入 [https://news.qq.com](https://news.qq.com/),那瀏覽器會從接收到的 URL 中抽取出域名欄位(news.qq.com),然後將它傳給 DNS 客戶端(作業系統提供)來解析。

首先我們說明一下本機 DNS 配置(就是 /etc/resolv.conf 檔案,裡面指定了本地 DNS 伺服器的地址,Windows 系統可能會有所不同):

$ cat /etc/resolv.conf
nameserver 202.106.0.20
nameserver 202.106.196.115

然後我們用 dig 這個工具檢視一下 news.qq.com 的解析結果(其中中文部分是解釋說明):

$ dig news.qq.com

; <<>> DiG 9.10.6 <<>> news.qq.com
這是 dig 程式的版本號與要查詢的域名

;; global options: +cmd
;; Got answer:
以下是要獲取的內容。

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47559
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
這個是返回應答的頭部資訊:
1. opcode:操作碼,QUERY 代表查詢操作;
2. status: 狀態,NOERROR 代表沒有錯誤;
3. id:編號,在 DNS 協議中通過編號匹配返回和查詢;
4. flags: 標誌,含義如下:
- qr:query,查詢標誌,代表是查詢操作
- rd:recursion desired,代表希望進行遞迴查詢操作;
- ra:recursive available,代表查詢的伺服器支援遞迴查詢操作;
5. QUERY 查詢數,與下面 QUESTION SECTION 的記錄數一一對應;
6. ANSWER 結果數,與下面的 ANSWER SECTION 的記錄數一一對應;
7. AUTHORITY 權威回覆數,如果查詢結果由管理域名的域名伺服器而不是快取伺服器提供的,則稱為權威回覆。
0 表示所有結果都不是權威回覆;
8. ADDITIONAL 額外記錄數;

;; QUESTION SECTION:
;news.qq.com.           IN  A
查詢部分,從左到右部分意義如下:
1、要查詢的域名;
2、要查詢資訊的類別,IN 代表類別為 IP 協議,即 Internet。
3、查詢的記錄型別,A 記錄(Address)代表要查詢 IPv4 地址。

;; ANSWER SECTION:
news.qq.com.        136 IN  CNAME   https.qq.com.
https.qq.com.       476 IN  A   125.39.52.26
迴應部分,從左到右各部分意義:
1、對應的域名
2、TTL,time to live,快取時間,單位秒,代表快取域名伺服器可以在快取中儲存的期限。
3、查詢資訊的類別
4、查詢的記錄型別,CNAME 表示別名記錄,A 記錄(Address)代表 IPv4 地址。
5、域名對應的 ip 地址。

;; Query time: 56 msec
;; SERVER: 202.106.0.20#53(202.106.0.20)
查詢使用的伺服器地址和埠,其實就是本地 DNS 域名伺服器
;; WHEN: Thu Jul 11 15:59:37 CST 2019
;; MSG SIZE  rcvd: 65
查詢的時間與迴應的大小,收到 65 位元組的應答資料。

從這個應答可以看到,我們得到的結果不是權威回覆,只是本地 DNS 伺服器從快取中給了應答。

接下來我們在 dig 命令中增加一個引數 +trace,看看完整的分級查詢過程:

$ dig +trace news.qq.com

; <<>> DiG 9.10.6 <<>> +trace news.qq.com
;; global options: +cmd
.           432944  IN  NS  g.root-servers.net.
.           432944  IN  NS  k.root-servers.net.
.           432944  IN  NS  b.root-servers.net.
.           432944  IN  NS  h.root-servers.net.
.           432944  IN  NS  i.root-servers.net.
.           432944  IN  NS  f.root-servers.net.
.           432944  IN  NS  d.root-servers.net.
.           432944  IN  NS  e.root-servers.net.
.           432944  IN  NS  j.root-servers.net.
.           432944  IN  NS  l.root-servers.net.
.           432944  IN  NS  c.root-servers.net.
.           432944  IN  NS  m.root-servers.net.
.           432944  IN  NS  a.root-servers.net.
;; Received 228 bytes from 202.106.0.20#53(202.106.0.20) in 45 ms
這些就是神祕的根域名伺服器,由本地 DNS 伺服器返回了所有根域名伺服器地址。

com.            172800  IN  NS  g.gtld-servers.net.
com.            172800  IN  NS  a.gtld-servers.net.
com.            172800  IN  NS  b.gtld-servers.net.
com.            172800  IN  NS  m.gtld-servers.net.
com.            172800  IN  NS  d.gtld-servers.net.
com.            172800  IN  NS  c.gtld-servers.net.
com.            172800  IN  NS  j.gtld-servers.net.
com.            172800  IN  NS  h.gtld-servers.net.
com.            172800  IN  NS  f.gtld-servers.net.
com.            172800  IN  NS  l.gtld-servers.net.
com.            172800  IN  NS  e.gtld-servers.net.
com.            172800  IN  NS  k.gtld-servers.net.
com.            172800  IN  NS  i.gtld-servers.net.
;; Received 1171 bytes from 192.36.148.17#53(i.root-servers.net) in 57 ms
這裡顯示的是 .com 域名的 13 條 NS 記錄,本地 DNS 伺服器向這些頂級域名伺服器發出查詢請求,
詢問 qq.com 的 NS 記錄。

qq.com.         172800  IN  NS  ns1.qq.com.
qq.com.         172800  IN  NS  ns2.qq.com.
qq.com.         172800  IN  NS  ns3.qq.com.
qq.com.         172800  IN  NS  ns4.qq.com.
;; Received 805 bytes from 192.48.79.30#53(j.gtld-servers.net) in 331 ms
這裡顯示的是 qq.com 的 4 條 NS 記錄,由 j.gtld-servers.net 這臺伺服器最先返回。
然後本地 DNS 伺服器向這四臺伺服器查詢下一級域名 news.qq.com 的 NS 記錄。

news.qq.com.        86400   IN  NS  ns-cnc1.qq.com.
news.qq.com.        86400   IN  NS  ns-cnc2.qq.com.
;; Received 180 bytes from 58.144.154.100#53(ns4.qq.com) in 37 ms
這裡顯示的是 news.qq.com 的 NS 記錄,它們是由上面的 ns4.qq.com 域名伺服器返回的。
然後本地 DNS 伺服器向這兩臺機器查詢 news.qq.com 的主機名。

news.qq.com.        600 IN  CNAME   https.qq.com.
https.qq.com.       600 IN  A   125.39.52.26
;; Received 76 bytes from 223.167.83.104#53(ns-cnc2.qq.com) in 29 ms
這是上面的 ns-cnc2.qq.com 返回的最終查詢結果:
news.qq.com 是 https.qq.com 的別名,而 https.qq.com 的 A 記錄地址是 125.39.52.26

實際的流程裡面,本地 DNS 伺服器相當於門衛大爺,根域名伺服器相當於局長同志,其餘以此類推。客戶端與本地 DNS 伺服器之間的查詢叫遞迴查詢,本地 DNS 伺服器與其他域名伺服器之間的查詢就叫迭代查詢。

域名記錄的型別

域名伺服器之所以能知道域名與 IP 地址的對映資訊,是因為我們在域名服務商那裡提交了域名記錄。購買了一個域名之後,我們需要在域名服務商那裡設定域名解析的記錄,域名服務商把這些記錄推送到權威域名伺服器,這樣我們的域名才能正式生效。

在設定域名記錄的時候,會遇到「A 記錄」、「CNAME」 等不同型別,這正是前面做域名解析的時候我們碰到的結果。這些型別是什麼意思,它們之間有什麼區別呢?接下來我們看看常見的記錄型別。

  • A 記錄。A (Address) 記錄用來直接指定主機名(或域名)對應的 IP 地址。主機名就是域名字首,常見有如下幾種:

www:解析後的域名為 [www.yourdomain.com](http://www.yourdomain.com/),一般用於網站地址。

@:直接解析主域名。

:泛解析,指將 .yourdomain.com 解析到同一 IP。

  • CNAME 記錄。CNAME 的全稱是 Canonical Name,通常稱別名記錄。如果需要將域名指向另一個域名,再由另一個域名提供 IP 地址,就需要新增 CNAME 記錄。

  • MX 記錄。郵件交換記錄,用於將以該域名為結尾的電子郵件指向對應的郵件伺服器以進行處理。

  • NS 記錄。域名伺服器記錄,如果需要把子域名交給其他 DNS 伺服器解析,就需要新增 NS 記錄。

  • AAAA 記錄。用來指定主機名(或域名)對應的 IPv6 地址,不常用。

  • TXT 記錄。可以填寫任何東西,長度限制 255。絕大多數的 TXT 記錄是用來做 SPF 記錄(反垃圾郵件),MX 記錄的作用是給寄信者指明某個域名的郵件伺服器有哪些。SPF 的作用跟 MX 相反,它向收信者表明,哪些郵件伺服器是經過某個域名認可會傳送郵件的。

  • 顯性 URL。從一個地址 301 重定向(也叫「永久性轉移」)到另一個地址的時候,就需要新增顯性 URL 記錄。

  • 隱性 URL。從一個地址 302 跳轉(也叫「臨時跳轉」)到另一個地址,需要新增隱性 URL 記錄。它類似於顯性 URL,區別在於隱性 URL 不會改變位址列中的域名。

在填寫各種記錄的時候,我們還會碰到一個特殊的設定項——TTL,生存時間(Time To Live)。

TTL 表示解析記錄在 DNS 伺服器中的快取時間,時間長度單位是秒,一般為3600秒。比如:在訪問 [news.qq.com](http://news.qq.com/)時,如果在 DNS 伺服器的快取中沒有該記錄,就會向某個 NS 伺服器發出請求,獲得該記錄後,該記錄會在 DNS 伺服器上儲存 TTL 的時間長度,在 TTL 有效期內訪問 [news.qq.com](http://news.qq.com/),DNS 伺服器會直接快取中返回剛才的記錄。

DNS 智慧解析

DNS 主要的工作就是完成域名到 IP 的對映,但是也不是簡單到查查字典就可以搞定的程度。在設定 DNS 解析的時候,我們還有一些額外的需求,例如:

  • 將一個域名解析到多個 IP

例如我們一個網站有多臺前端機,希望使用者訪問的時候,可以隨機分散到這些機器上,以增加網站承載能力。有一種解決的辦法就是對同一個域名設定多條 A 記錄,分別指定到不同的 IP 上。

  • 根據特徵差異將不同請求解析到不同 IP(智慧解析)

國內網際網路的架構其實遠比我們想象的複雜,基本上還是根據運營商的不同切割成多個平行網路,只有在固定的幾個節點這些平行網路才會有交叉。例如電信和聯通之間的互聯是通過「國家級網際網路骨幹直聯點」接入的,目前我們一共建設了三批國家級網際網路骨幹直聯點:

1.第一批 2001 年投入使用:北京,上海,廣州

2.第二批 2014 年投入使用:成都,鄭州,武漢,西安,瀋陽,南京,重慶

3.第三批 2017 年投入使用:杭州,貴陽/貴安,福州

教育網目前還只能通過北上廣三個點進行連線。這樣的網路拓撲結構,給 DNS 解析帶來了新的挑戰。

傳統 DNS 解析,不判斷訪問者來源,會隨機選擇其中一個 IP 地址返回給訪問者。如果讓電信使用者使用了聯通 IP 來訪問網站,那結果自然不如使用電信 IP 訪問來的快捷。而智慧 DNS 解析,會判斷訪問者的來源特徵,為不同的訪問者返回不同的 IP 地址,能夠減少解析時延,並提升網路訪問速度。例如,國內某著名 DNS 服務商不光可以區分網路運營商,還可以根據訪問者的地理位置來設定不同的解析線路,而且甚至還可以為搜尋引擎設定特定的解析地址。

  • CNAME 和 A 記錄區別

按照前面的解釋,A 記錄就是把一個域名解析到一個 IP 地址,而 CNAME 記錄就是把一個域名解析到另外一個域名,其功能差不多。但是 CNAME 相當於將域名和 IP 地址之間加了一箇中間層,可以帶來很大的靈活性,特別是當你要使用但是並不擁有那些域名的時候。

例如我們使用 CDN 服務,服務商提供給我們的是一個 CNAME 地址,我們可以把自己的域名繫結到這一個地址上,這樣萬一以後服務商的 IP 地址更換了,我們自己的域名解析是不需要做任何變更的,只要服務商調整一下 CNAME 地址的解析結果,所有使用者都可以無感知的切換。

從 6 月底開始,LeanCloud 新推出了 繫結自定義域名 的功能,全面支援開發者設定自己的 API、檔案、雲引擎域名,也正是依賴於 CNAME 記錄的這一特點來實現的。

DNS 汙染與安全挑戰

DNS 是最早商用的大型分散式系統,雖然現在看起來已經很完備了,但是實際使用的時候,特別是國內複雜的網路環境,我們還是會遇到很多問題。

作為網際網路早期產物,DNS 使用無連線的 UDP 協議雖然降低了開銷也保證了高效的通訊,但是沒有太考慮安全問題。由於它使用目的埠為 53 的 UDP 明文進行通訊,DNS 解析器識別是自己發出的資料包的唯一標準就是隨機的源埠號,如果埠號匹配則認為是正確回覆,而不會驗證來源。所以也帶來了諸如 DNS 欺騙、DNS Cache 汙染、DNS 放大攻擊等問題,同時給一些區域運營商帶來了「商機」。

為此業界提出了 DNSSec(Domain Name System Security Extensions,也叫「DNS安全擴充套件」)機制,使用密碼學方法,讓客戶端對域名來源身份進行驗證,並且檢查來自 DNS 域名伺服器應答記錄的完整性,以及驗證是否在傳輸過程中被篡改過,等等一系列措施來保證資料通訊的安全性。

HTTP

1. HTTP/0.9

1990年提出的,是最早期的版本,只有一個命令GET。

2. HTTP/1.0

1996年5月提出的。

  • 缺點:每個TCP連線只能傳送一個請求。
  • 解決方法:Connection:keep-alive

3. HTTP/1.1

1997年1月提出,現在使用最廣泛的。

3.1 特性
  1. 長連線:TCP連線預設不關閉,可以被多個請求複用。對於同一個域名,大多數瀏覽器允許同時建立6個持久連線。預設開啟Connection:keep-alive。
  2. 管道機制:在同一個TCP連線裡,可以同時傳送多個請求。但是伺服器還是要按照請求的順序進行響應,會造成“隊頭阻塞”。
3.2 HTTP首部

HTTP首部分為請求報文和響應報文。它們的格式如下所示:

  • 請求報文:

知識分享

  • 響應報文:

知識分享

其中首部欄位又分為很多種,我們先看通用首部欄位,這是請求報文和響應報文種都會使用的首部。

3.2.1 通用首部欄位

1、Cache-Control:操作快取的工作機制

引數:

  • public:明確表明其他使用者也可以利用快取
  • private:快取只給特定的使用者
  • no-cache:客戶端傳送這個指令,表示客戶端不接收快取過的響應,必須到伺服器取;伺服器返回這個指令,指快取伺服器不能對資源進行快取。其實是不快取過期資源,要向伺服器進行有效期確認後再處理資源。
  • no-store:指不進行快取
  • max-age:快取的有效時間(相對時間)

2、Connection

  • Connection:keep-Alive (持久連線)
  • Connection:不再轉發的首部欄位名

3、Date:表明建立http報文的日期和時間

4、Pragma:相容http1.0,與Cache-Control:no-cache含義一樣。但只用在客戶端傳送的請求中,告訴所有的中間伺服器不返回快取。形式唯一:Pragma:no-cache

5、Trailer:會事先說明在報文主體後記錄了哪些首部欄位,該首部欄位可以應用在http1.1版本分塊傳輸編碼中。

6、Transfer-Encoding:chunked (分塊傳輸編碼),
規定傳輸報文主體時採用的編碼方式,http1.1的傳輸編碼方式只對分塊傳輸編碼有效

7、Upgrade:升級一個成其他的協議,需要額外指定Connection:Upgrade。伺服器可用101狀態碼作為相應返回。

8、Via:追蹤客戶端和伺服器之間的請求和響應報文的傳輸路徑。可以避免請求迴環發生,所以在經過代理時必須要附加這個欄位。

3.2.2 請求首部欄位

1、Accept:通知伺服器,使用者代理能夠處理的媒體型別及媒體型別的相對優先順序
q表示優先順序的權重值,預設為q = 1.0,範圍是0~1(可精確到小數點後3位,1為最大值)
當伺服器提供多種內容時,會先返回權重值最高的媒體型別

2、Accept-Charset:支援的字符集及字符集的相對優先順序,跟Accept一樣,用q來表示相對優先順序。這個欄位應用於內容協商機制的伺服器驅動協商。

3、Accept-Encoding:支援的內容編碼及內容編碼的優先順序順序,q表示相對優先順序。
內容編碼:gzip、compress、deflate、identity(不執行壓縮或者不會變化的預設編碼格式)。
可以使用*作為萬用字元,指定任意的編碼格式。

4、Accept-Language:能夠處理的自然語言集,以及相對優先順序。

3.3 狀態碼

101 協議升級,主要用於升級到websocket,也可以用於http2

200 OK

204 No content,伺服器成功處理請求,但是返回的響應報文中不含實體的主體部分

206 Partial Content,表示客戶端像伺服器進行了範圍請求(Content-Range欄位),伺服器成功返回指定範圍的實體內容

301 永久性重定向,表示請求的資源已經被分配了新的url,舊地址以後都不能再訪問了,伺服器會返回location欄位,包含的是新的地址。

302 臨時性重定向,表示請求的資源臨時移動到一個新地址

注意:儘量使用301跳轉,因為302會造成網址劫持,可能被搜尋引擎判為可疑轉向,甚至認為是作弊。

原因:從網站A(網站比較爛)上做了一個302跳轉到網站B(搜尋排名很靠前),這時候有時搜尋引擎會使用網站B的內容,但卻收錄了網站A的地址,這樣在不知不覺間,網站B在為網站A作貢獻,網站A的排名就靠前了。

303 See Other,與302功能相同,但是它明確規定客戶端應採用GET方法獲取資源

304 未修改,協商快取中返回的狀態碼

307 臨時重定向,與302功能相同,但規定不能從POST變成GET

當301、302、303響應狀態碼返回時,幾乎所有瀏覽器都會把post改成get,並刪除請求報文內的主體,之後請求會自動再次傳送。然而301、302標準是禁止將post方法改變成get方法的,但實際使用時大家都會這麼做。所以需要307。

400 Bad Request,表示請求報文中存在語法錯誤。當錯誤發生時,需要修改請求的內容再次傳送請求

401 unauthorized,表示傳送的請求需要有通過HTTP認證(BASIC認證、DIGEST認證)的認證資訊。如果之前已經進行過一次請求,表示使用者認證失敗。

403 禁止,表示拒絕對請求資源的訪問

404 Not Found,表明伺服器上無法找到請求的資源

500 Internet Server Error,該狀態碼錶示伺服器在執行請求時發生了錯誤

500 Service Unavailable,表示伺服器暫時處於超負荷或者處於停機維護狀態,現在無法處理請求

4. SPDY協議 、HTTP / 2的原型

2009年穀歌提出。

  • SPDY結構

知識分享

  • 新增特性

(1)多路複用:通過一個TCP連線,可以無限制處理多個HTTP請求。

(2)賦予請求優先順序:給請求逐個分配優先順序順序。可以解決在傳送多個請求時,因頻寬低而導致響應變慢的問題。

(3)壓縮HTTP首部:壓縮方式:DELEFT

(4)推送功能

(5)伺服器提示功能:伺服器可以主動提示客戶端請求所需的資源。

  • 缺點

SPDY強制使用https。而且SPDY基本上只是將單個域名下的通訊多路複用,所以當一個web網站上使用多個域名下的資源時,改善效果就會受到限制。

5. WebSocket

html5新提出來的,是web瀏覽器與web伺服器之間的全雙工通訊標準。主要是為了解決ajax和comet裡的xmlhttprequest附帶的缺陷所引起的問題。

5.1 特性

(1)推送功能:伺服器可直接傳送資料,不需要等待客戶端的請求;

(2)基於TCP傳輸協議,並複用HTTP的握手通道;

(3)支援雙向通訊,用於實時傳輸訊息;

(4)更好的二進位制支援;

(5)更靈活,更高效。

5.2 建立連線過程

1、客戶端:發起協議升級請求

GET / HTTP/1.1      `採用HTTP報文格式,只支援get請求`
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade   `表示要升級協議`
Upgrade: websocket    `表示升級到websocket協議`
Sec-WebSocket-Version: 13    `表示websocket 的版本`
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==   `是一個 Base64 encode 的值,是瀏覽器隨機生成的`
Sec-WebSocket-Protocol:chat, superchat  `用來指定一個特定的子協議,一旦這個欄位有設定,那麼伺服器需要在建立連線的響應頭中包含同樣的欄位,內容就是選擇的子協議之一。`

2、服務端:響應協議升級

HTTP/1.1 101 Switching Protocols    `101表示協議切換==`
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU= `經過伺服器確認,並且加密過後的 Sec-WebSocket-Key`
Sec-WebSocket-Protocol:chat  `表示最終使用的協議`

Sec-WebSocket-Key 的加密過程為:

  1. 將Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
  2. 通過SHA1計算出摘要,並轉成base64字串。

3、雙方握手成功後,就是全雙工的通訊了,接下來就是用websocket協議來進行通訊了。

5.3 Ajax 輪詢、長輪詢、WebSocket原理解析

1、ajax輪詢

讓瀏覽器每隔一定的時間就傳送一次請求,詢問伺服器是否有新資訊。

2、長輪詢(Long Poll)

採用的阻塞模式。客戶端發起連線後,如果沒訊息,伺服器不會馬上告訴你沒訊息,而是將這個請求掛起(pending),直到有訊息才返回。返回完成或者客戶端主動斷開後,客戶端再次建立連線,周而復始。Comet就是採用的長輪詢。

3、websocket

WebSocket 是類似 Socket 的TCP長連線通訊模式。一旦 WebSocket 連線建立後,後續資料都以幀序列的形式傳輸。而且瀏覽器和伺服器就可以隨時主動傳送訊息給對方,是全雙工通訊。

優點:在海量併發及客戶端與伺服器互動負載流量大的情況下,極大的節省了網路頻寬資源的消耗,有明顯的效能優勢,且客戶端傳送和接受訊息是在同一個持久連線上發起,實時性優勢明顯。

6. HTTP2

2015年釋出,它是基於SPDY的,以下是它的一些新特性:

1、 二進位制分幀

http/1.x 是一個超文字協議,而 http2 是一個二進位制協議,被稱之為二進位制分幀。
二進位制格式在協議的解析和優化擴充套件上帶來更多的優勢和可能。

協議格式為幀,幀由 Frame Header(頭資訊幀)和 Frame Payload(資料幀)組成,如下所示:

知識分享

  • Length 欄位用來表示 Frame Payload 資料大小。
  • Type 欄位用來表示該幀中的 Frame Payload 儲存的是 header 資料還是 body 資料。除了用於標識 header/body,還有一些額外的 Frame Type。
  • Stream Identifier 用來標識該 frame 屬於哪個 stream。
  • Frame Payload 用來儲存 header 或者 body 的資料。

2、頭部壓縮 HPACK

請求和響應首部壓縮,客戶端和服務端共同維護一張頭資訊表,所有欄位存入這個表,生成一個索引號,通過傳送索引號提高速度。HPACK壓縮會經過兩步:

  • 傳輸的value,會經過一遍Huffman coding來節省資源;
  • 為了server和client同步, 兩邊都需要保留一份Header list, 並且,每次傳送請求時,都會檢查更新。

3、服務端推送

服務端主動向客戶端推送資料。如果客戶端請求一個html檔案,服務端把html檔案返回給客戶端之後,還會相應的把html檔案中的js、css、圖片推送給客戶端。

4、多路複用

只需要建立一個TCP連線,瀏覽器和伺服器可以同時傳送多個請求或者回應,而且不需要按照順序一一對應,避免了“隊頭阻塞”。

5、資料流

當客戶端同時向服務端發起多個請求,那麼這些請求會被分解成一一個的幀,每個幀都會在一個 TCP 鏈路中無序的傳輸,同一個請求的幀的 Stream Identifier 都是一樣的。當幀到達服務端之後,就可以根據 Stream Identifier 來重新組合得到完整的請求。

並且規定:客戶端發出的資料流ID為奇數,伺服器發出的ID為偶數。Stream Identifier (資料流ID)就是用來標識該幀屬於哪個請求的。

7. HTTPS

HTTPS = HTTP+加密+認證+完整性保護

它的加密過程是:

  1. server生成一個公鑰和私鑰,把公鑰傳送給第三方認證機構(CA);
  2. CA把公鑰進行MD5加密,生成數字簽名;再把數字簽名用CA的私鑰進行加密,生成數字證照。CA會把這個數字證照返回給server;
  3. server拿到數字證照之後,就把它傳送給瀏覽器;
  4. 瀏覽器會對數字證照進行驗證,首先,瀏覽器本身會內建CA的公鑰,會用這個公鑰對數字證照解密,驗證是否是受信任的CA生成的數字證照;
  5. 驗證成功後,瀏覽器會隨機生成對稱祕鑰,用server的公鑰加密這個對稱祕鑰,再把加密的對稱祕鑰傳送給server;
  6. server收到對稱祕鑰,會用自己的私鑰進行解密,之後,它們之間的通訊就用這個對稱祕鑰進行加密,來維持通訊。

下圖是加密過程的圖解,可以對照著圖片理一遍。

知識分享

8. HTTP快取機制

8.1 快取分類

HTTP的快取分為強快取和協商快取(對比快取)。

  1. 強制快取

在快取資料未失效的情況下,可以直接使用快取資料;在沒有快取資料的時候,瀏覽器向伺服器請求資料時,伺服器會將資料和快取規則一併返回,快取規則資訊包含在響應header中。

  • Expires:快取過期時間(HTTP1.0)

缺點:生成的是絕對時間,但是客戶端時間可以隨意修改,會導致誤差。

  • Cache-Control :HTTP1.1,優先順序高於Expires

可設定引數:

private: 客戶端可以快取

public: 客戶端和代理伺服器都可快取

max-age=xxx: 快取的內容將在 xxx 秒後失效

no-cache: 需要使用協商快取來驗證快取資料(後面介紹)

no-store: 所有內容都不會快取,強制快取,對比快取都不會觸發

Expires和Cache-Control決定了瀏覽器是否要傳送請求到伺服器,ETag和Last-Modified決定了伺服器是要返回304+空內容還是新的資原始檔。

  1. 協商快取

瀏覽器第一次請求資料時,伺服器會將快取標識與資料一起返回給客戶端,客戶端將二者備份至快取資料庫中。再次請求資料時,客戶端將備份的快取標識傳送給伺服器,伺服器根據快取標識進行判斷,判斷成功後,返回304狀態碼,通知客戶端比較成功,可以使用快取資料。

  • Last-Modified / If-Modified-Since

Last-Modified:伺服器在響應請求時,告訴瀏覽器資源的最後修改時間。
If-Modified-Since:再次請求伺服器時,通過此欄位通知伺服器上次請求時,伺服器返回的資源最後修改時間。

缺點:Last-Modified 標註的最後修改時間只能精確到秒,如果有些資源在一秒之內被多次修改的話,他就不能準確標註檔案的新鮮度了。如果某些資源會被定期生成,當內容沒有變化,但 Last-Modified 卻改變了,導致檔案沒使用快取有可能存在伺服器沒有準確獲取資源修改時間,或者與代理伺服器時間不一致的情形。

  • Etag / If-None-Match(優先順序高於Last-Modified / If-Modified-Since)

Etag:給資源計算得出的一個唯一標誌符。
If-None-Match:再次請求伺服器時,通過此欄位通知伺服器客戶端快取資料的唯一標識。

8.2 快取判斷順序
  1. 先判斷Cache-Control,在Cache-Control的max-age之內,直接返回200 from cache;
  2. 沒有Cache-Control再判斷Expires,再Expires之內,直接返回200 from cache;
  3. Cache-Control=no-cache或者不符合Expires,瀏覽器向伺服器傳送請求;
  4. 伺服器同時判斷ETag和Last-Modified,都一致,返回304,有任何一個不一致,返回200。

具體過程如下圖:

知識分享

8.3 cookie、session
8.3.1 cookie

解決http的無狀態問題,是客戶端儲存使用者資訊的一種機制,用來記錄使用者的一些資訊,來實現session的跟蹤。

  1. cookie屬性

name、value :以key/value的形式存在

comment:說明該cookie的用處

domain:可以訪問該cookie的域名

Expires/maxAge:cookie失效時間。負數:臨時cookie,關閉瀏覽器就失效;0:表示刪除cookie,預設為-1

path:可以訪問此cookie的頁面路徑

size:cookie的大小

secure:是否以https協議傳輸

version:該cookie使用的版本號,0遵循Netscape規範,大多數用這種,1遵循W3C規範

HttpOnly:此屬性為true,則只有在http請求頭中會帶有此cookie的資訊,而不能通過document.cookie來訪問此cookie,能防止XSS攻擊。

  1. cookie機制原理:

客戶端請求伺服器時,如果伺服器需要記錄該使用者狀態,就使用response向客戶端瀏覽器頒發一個Cookie。而客戶端瀏覽器會把Cookie儲存起來。當瀏覽器再請求伺服器時,瀏覽器把請求的網址連同該Cookie一同提交給伺服器。伺服器通過檢查該Cookie來獲取使用者狀態。

  1. cookie同源和跨域:

cookie的同源是域名相同,忽略協議和埠,不可跨域。

8.3.2 session

session是在服務端儲存的一個資料結構,用來跟蹤使用者的狀態,這個資料可以儲存在叢集、資料庫、檔案中。

  1. session的執行依賴session id,而session id是存在cookie中的
  2. session機制原理:

當客戶端請求建立一個session的時候,伺服器會先檢查這個客戶端的請求裡是否已包含了一個session標識——sessionId。如果已包含這個sessionId,則說明以前已經為此客戶端建立過session,伺服器就按照sessionId把這個session檢索出來使用(如果檢索不到,可能會新建一個。如果客戶端請求不包含sessionId,則為此客戶端建立一個session並且生成一個與此session相關聯的sessionId。

  1. 如果禁用cookie怎麼辦?

使用URL重寫技術來進行會話跟蹤。在 url 中傳遞 session id,即每次HTTP互動,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的引數,服務端據此來識別使用者。

8.3.3 區別
  1. cookie和session的區別
  • cookie 資料存放在客戶的瀏覽器上,session資料放在伺服器上;
  • cookie 不是很安全,別人可以分析存放在本地的 cookie 並進行 cookie 欺騙考慮到安全應當使用 session;
  • session 會在一定時間內儲存在伺服器上。當訪問增多,會比較佔用你伺服器的效能考慮到減輕伺服器效能方面,應當使用 cookie;
  • 單個cookie儲存的資料不能超過4K,很多瀏覽器都限制一個站點最多儲存20個 cookie。

鑑於上述區別我們建議:

(1)將登陸資訊等重要資訊存放為 session;

(2)其他資訊如果需要保留,可以放在 cookie 中。

  1. localStorage,sessionStorage和cookie的區別

共同點:都是儲存在瀏覽器端、且同源的。

  • 資料儲存方面

  • cookie資料始終在同源的http請求中攜帶(即使不需要),即cookie在瀏覽器和伺服器間來回傳遞。cookie資料還有路徑(path)的概念,可以限制cookie只屬於某個路徑下。

  • sessionStorage和localStorage不會自動把資料傳送給伺服器,僅在本地儲存。

  • 儲存資料大小

  • 儲存大小限制也不同,cookie資料不能超過4K,同時因為每次http請求都會攜帶cookie,所以cookie只適合儲存很小的資料,如會話標識。

  • sessionStorage和localStorage雖然也有儲存大小的限制,但比cookie大得多,可以達到5M或更大。

  • 資料儲存有效期

  • sessionStorage:僅在當前瀏覽器視窗關閉之前有效;

  • localStorage:始終有效,視窗或瀏覽器關閉也一直儲存,本地儲存,因此用作持久資料;

  • cookie:只在設定的cookie過期時間之前有效,即使視窗關閉或瀏覽器關閉。

  • 作用域不同

  • sessionStorage不在不同的瀏覽器視窗中共享,即使是同一個頁面;

  • localstorage在所有同源視窗中都是共享的;也就是說只要瀏覽器不關閉,資料仍然存在。

  • cookie: 也是在所有同源視窗中都是共享的.也就是說只要瀏覽器不關閉,資料仍然存在。

9. 跨域

跨域產生的原因,是因為受到同源策略的限制。同源策略指的是協議、域名、埠不相同。這裡我將介紹三種跨域的方式:JSONP、CORS(跨域資源共享)、document.domain + iframe。

9.1 JSONP

1. 原理

動態插入script標籤(因為script標籤不受同源策略的限制),通過插入script標籤引入一個js檔案,這個js檔案載入成功之後會執行我們在url中指定的回撥函式,並且會把我們需要的json資料作為引數傳入。

2. 實現

(1)原生實現:

var script = document.createElement('script');
script.type = 'text/javascript';

// 傳參並指定回撥執行函式為onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);

// 回撥函式
function onBack(res) {
alert(JSON.stringify(res));
}

//服務端返回如下(返回時即執行全域性函式):
onBack({"status": true, "user": "admin"})

(2)jquery ajax:

$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp',  // 請求方式為jsonp
jsonpCallback: "onBack",    // 自定義回撥函式名
data: {}
});
9.2 CORS

1. 原理

伺服器在響應頭中設定相應的選項,瀏覽器如果支援這種方法的話就會將這種跨站資源請求視為合法,進而獲取資源。

2. 實現

CORS分為簡單請求和複雜請求,簡單請求指的是:

(1)請求方法是以下三種方法之一:HEAD、GET、POST;

(2)HTTP的頭資訊不超出以下幾種欄位:
Accept、Accept-Language、Content-Language、Last-Event-ID、
Content-Type(只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain)。

其他情況就是非簡單請求了。

  • 簡單請求

(1)請求頭

Origin: http://www.domain.com

(2)響應頭

Access-Control-Allow-Origin: http://www.domain.com
Access-Control-Allow-Credentials: true   `是否允許傳送cookie`
Access-Control-Expose-Headers: FooBar `CORS請求時,只能拿到6個基本欄位:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他欄位,就必須指定。`

(3)另外,ajax請求中,如果要傳送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名,還要設定以下內容:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
  • 非簡單請求

(1)預檢請求:

OPTIONS /cors HTTP/1.1  `OPTIONS請求是用來詢問的`
Origin: http://www.domian.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header

(2)響應頭

Access-Control-Allow-Origin: http://www.domain.com
Access-Control-Allow-Methods: GET, POST, PUT  `伺服器支援的所有跨域請求的方法`
Access-Control-Allow-Headers: X-Custom-Header  `伺服器支援的所有頭資訊欄位,不限於瀏覽器在"預檢"中請求的欄位。`
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000  `指定本次預檢請求的有效期,單位為秒`

(3)之後的步驟就同簡單請求了

這是CORS的整個流程圖:

與JSOP的比較

JSONP只支援GET請求,CORS支援所有型別的HTTP請求。JSONP的優勢在於支援老式瀏覽器,以及可以向不支援CORS的網站請求資料。
9.3 document.domain + iframe

此方案僅限主域相同,子域不同的跨域應用場景。

1.原理

兩個頁面都通過js強制設定document.domain為基礎主域,就實現了同域。

2.實現

(1)父視窗:(www.domain.com/a.html)

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>

<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>

(2)子視窗:(child.domain.com/b.html)

<script>
    document.domain = 'domain.com';
    // 獲取父視窗中變數
    alert('get js data from parent ---> ' + window.parent.user);
</script>

10. 安全

10.1 HTTP Security Headers
Content-Security-Policy

CSP 通過指定允許載入哪些資源來防止跨站點指令碼。在此列表的所有專案中,這可能是建立和維護最耗時的,也是最容易出現風險的。在開發 CSP 期間,請務必仔細測試它 —— 以有效的方式阻止您的站點使用的內容源將會破壞站點的功能。

一個建立初稿的好工具是 Mozilla laboratory CSP 瀏覽器擴充套件。在瀏覽器中安裝這個,徹底瀏覽要為其建立 CSP 的站點,然後在您的站點上使用生成的 CSP。理想情況下,還可以重構 JavaScript,因此不會保留內聯指令碼,因此您可以刪除“unsafe inline”指令。

CSP 是複雜而令人困惑的,所以如果你想要更深入的研究,請參閱官方網站

一個好的 CSP 的開始可能是這樣的(這可能需要在一個真實的站點上進行大量的修改)。在站點包含的每個部分中新增域。

# 預設只允許來自當前站點的內容
# 允許來自當前網站和 imgur.com 的圖片
# 不允許使用 Flash 和 Java 等物件
# 只允許來自當前站點的指令碼
# 僅允許當前站點的樣式
# 只允許當前站點的 frame
# 將 <base> 標記中的 URL 限制為當前站點
# 允許表單僅提交到當前站點
Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';
Strict-Transport-Security

這個 Header 告訴瀏覽器,該網站應僅允許 HTTPS 訪問 —— 始終在您的網站啟用 HTTPS 時啟用。如果您使用子域名,我也建議在任何被使用的子域名上強制開啟它。

Strict-Transport-Security: max-age=3600; includeSubDomains
X-Content-Type-Options

此 header 確保瀏覽器遵守應用程式設定的 MIME 型別。這有助於防止某些型別的跨站點指令碼繞過。

它還減少了由於瀏覽器可能不正確猜測某些內容導致的意外應用程式行為,例如當開發人員標記一個頁面 HTML,但瀏覽器認為它看起來像 JavaScript,並試圖將其作為 JavaScript 來渲染。這這個 Header 將確保瀏覽器始終遵守伺服器設定的 MIME 型別。

X-Content-Type-Options: nosniff
Cache-Control

這一個比其他的稍微複雜一些,因為您可能需要針對不同的內容型別使用不同的快取策略。

任何具有敏感資料的頁面,例如使用者頁面或客戶結帳頁面,都應該設定為無快取。原因之一是防止其他使用共享計算機的人按下後退按鈕或瀏覽歷史並檢視個人資訊。

但是,很少更改的頁面,如靜態資源(影象,CSS 檔案和 JavaScript 檔案)很適合快取。這可以在逐頁的基礎上完成,也可以在伺服器配置上使用正規表示式完成。

# 預設情況下不快取
Header set Cache-Control no-cache

# 快取靜態資源 1 天
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
Header set Cache-Control "max-age=86400, public"
</filesMatch>
Expires

這將設定當前請求快取到期的時間。如果設定了 Cache-Control max-age 的 Header,它將被忽略。所以我們只在一個簡單的掃描器測試它而不考慮 cache-control 的情況下設定它。

出於安全考慮,我們假設瀏覽器不應該快取任何東西,因此我們將把這個設定為一個日期,該日期的計算值總是為過去。

Expires: 0
X-Frame-Options

這個 Header 指是否應該允許站點在 iFrame 中顯示。

如果惡意網站將您的網站置於 iFrame 中,則惡意網站可以通過執行一些 JavaScript 來執行點選攻擊,該 JavaScript 會捕獲 iFrame 上的滑鼠點選,然後代表使用者與該網站進行互動(不一定點選他們認為他們點選的地方!)。

這應該總是設定為 deny,除非您特別使用 Frames, 在這種情況下,它應該設定為同源(same-origin)。如果您在設計中將 Frames 與其他網站一起使用,您也可以在此處白名單列出其他域名。

還應注意,此 Header 已被 CSP frame-ancestrs 指令取代。我仍然建議現在就設定它以作為緩衝工具,但將來它可能會逐步被淘汰。

X-Frame-Options: deny
Access-Control-Allow-Origin

告訴瀏覽器哪些其他站點的前端 JavaScript 程式碼可能會對該頁面發出請求。除非需要設定此值,否則預設值通常是正確的設定。

例如,如果 SiteA 提供了一些想要向 SiteB 發出請求的 JavaScript,那麼 SiteB 必須提供帶有 Header 的響應,這個 Header 指定 SiteA 被允許發出這個請求。如果需要設定多個源,請參閱 MDN 上的詳細資訊頁面.

這可能有點令人困惑,所以我繪製了一個圖表來說明這個 Header 如何運作:

知識分享

具有 Access-Control-Allow-Origin 的資料流

Access-Control-Allow-Origin: http://www.one.site.com
Set-Cookie

確保您的 Cookie 僅通過 HTTPS(加密)傳送,並且不能通過 JavaScript 訪問它們。如果您的站點也支援 HTTPS,則只能傳送 HTTPS Cookie,這是應該的。您應該始終設定以下標誌:

  • Secure
  • HttpOnly

Cookie 定義示例:

Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

有關更多 Cookie 的資訊,請參閱有關 Cookie 的優秀 Mozilla 文件

X-XSS-Protection

這個 Header 指示瀏覽器停止檢測到的跨站點指令碼攻擊的執行。它通常是低風險設定,但仍應在投入生產前進行測試。

X-XSS-Protection: 1; mode=block

10. HTTP / 3 (QUIC)

HTTP/3 協議即將標準化。作為一個老協議使用者,我想我該寫一些看法了。

Google(pbuh) 公司擁有最流行的 web 瀏覽器(Chrome)和兩個最流行的網站(#1 Google.com #2 Youtube.com)。因此谷歌可以控制 web 協議的發展。他們的第一次升級稱之為 SPDY (發音"speedy"),這次更新最終成為 HTTP 協議第二版標準,即 HTTP/2 。他們的第二次升級稱之為 QUIC(發音"quick"),將成為 HTTP/3 協議標準。

QUIC實際上更像是TCP (TCP/2???)的新版本,而不是HTTP (HTTP/3)的新版本。它並沒有真正改變HTTP/2的功能,而是改變了傳輸的工作方式。因此,我下面的評論集中在傳輸問題上,而不是HTTP問題。

主要的標題特性是更快的連線設定和延遲。TCP要求在建立連線之前來回傳送大量資料包。SSL同樣需要在建立加密之前來回傳送大量資料包。如果網路延時很大,比如人們使用半秒ping時間的衛星網際網路,建立連線需要相當長的時間。通過減少往返,連線可以更快地建立,這樣當您單擊連結時,連結的資源就會立即彈出

下一個主要特性是頻寬。網路連線的源和目的之間總是存在頻寬限制,這幾乎總是由於擁塞。雙方都需要使用這個速度,以便他們能夠以適當的速度傳送資料包。如果傳送資料包太快,那麼它們就會被丟棄,這會在不提高傳輸速率的情況下給其他資料包造成更大的擁塞。傳送資料包太慢意味著不能最優地使用網路。

HTTP 傳統上這一點做得很糟糕。 使用單個 TCP 連線不適用於 HTTP,因為與網站的互動需要同時傳輸多個內容,因此瀏覽器開啟了與 Web 伺服器的多個連線(通常為 6 個)。但是,這會打破對頻寬的估計,因為每個 TCP 連線都嘗試獨立完成,就像其他連線不存在一樣。SPDY 通過其多路複用功能解決了這個問題,該功能將瀏覽器/伺服器之間的多個互動與單個頻寬計算相結合。

QUIC 擴充套件了這種多路複用,使得處理瀏覽器/伺服器之間的多個互動變得更加容易,而沒有任何一個互動阻止另一個互動,且具有共同頻寬。 從使用者的角度來看,這將使互動更加順暢,同時減少路由器遇到的擁塞。

我們現在來談談使用者模式棧。 特別是在伺服器上,TCP連線由作業系統核心處理,而服務本身在使用者模式中執行。跨核心/使用者模式邊界移動會導致效能降低。追蹤大量TCP連線會導致擴充套件性問題。有些人嘗試將服務放入核心來避免轉換,這並不可取,因為它破壞了作業系統的穩定性。我的解決方案是用BlackICE IPS和masscan,使用自定義TCP棧,利用硬體的使用者模式驅動程式,將資料包從網路晶片直接傳送到使用者模式程式,繞過核心(參見PoC || GTFO#15)。近年來,DPDK套件已經變得流行。

但是,從TCP遷移到UDP可以在沒有使用者模式驅動程式的情況下獲得相同的效能。您可以呼叫recvmmsg()一次接收一堆UDP資料包,而不是呼叫眾所周知的recv()函式來一次接收一個資料包。它仍然是核心/使用者模式轉換,但是一次性收到的一百個資料包分攤,而不是每個資料包的轉換。

在我自己的測試中,使用典型的recv()函式限制為大約500,000 UDP資料包 /秒,但使用recvmmsg()和其他一些優化(使用RSS的多核),可以在低端四核伺服器上獲得5,000,000 UDP資料包/秒。由於每個核心的擴充套件性很好,因此遷移到具有64個核心的強大伺服器可以進一步提高。

BTW,“RSS”是網路硬體的一個特點,它將傳入的資料包分成多個接收佇列。多核擴充套件性的最大問題是兩個CPU核心需要同時讀取/修改同一個東西,因此共享相同的UDP佇列成為最大的瓶頸。因此,首先英特爾和其他乙太網供應商新增了RSS,為每個核心提供了自己的非共享資料包佇列。 Linux和其他作業系統升級UDP以支援單個套接字(SO_REUSEPORT)的多個檔案描述符來處理多個佇列。現在,QUIC使用這些改進使得每個核心管理自己的UDP資料包流,不會有與其他CPU核心共享內容的導致可擴充套件性問題。之所以提到這一點,是因為在2000年,我親自與英特爾硬體工程師討論過有多個資料包佇列問題。這個問題很普遍,也有對應的解決方案,在HTTP / 3出現之前,看看在過去二十年中的發展也是很有意思。如果沒有網路硬體中的RSS,QUIC就不太可能成為標準。

QUIC 的另一個優美的解決方案是對移動的支援。當你帶著你的膝上型電腦四處移動到不同的 WIFI 網路時,或者帶著你的手機四處移動時,你裝置的 IP 地址會發生變化的。作業系統以及協議不會優雅的關閉掉老的連線而開啟新的連線。然而,QUIC,網路連線的識別符號並不是傳統概念上的一個“socket”(源/目標 埠/地址 協議的繫結),而是一個64位的賦值到連線上的識別符號。

這意味著當你移動時,即使 IP 地址改變了,你依然能夠和 YouTube 繼續保持一個持續不間斷的視訊流,或者繼續撥打一個視訊電話而不被異常中斷。網路工程師們已經和“移動IP”的技術問題攻關了幾十年,試圖想出一個有效的解決方案。他們專注於端到端原則,也就是在你移動時以某種方式保持一個恆定的 IP 地址,這不是一個實際的解決方案。很高興看到 QUIC / HTTP/3 最終解決了這個問題。

如何使用這種新的交通工具?幾十年來,網路程式設計的標準一直是被稱為“sockets”的傳輸層API。呼叫recv()之類的函式來接收程式碼中的包。使用QUIC/HTTP/3,我們不再擁有作業系統傳輸層API。相反,它是一個更高層次的特性,可以在go程式語言中使用,或者在OpenResty nginx web伺服器中使用Lua。

我之所以提到這一點,是因為在您對OSI模型的學習中,有一件事遺漏了,那就是它最初設想每個人都編寫應用層(7)api,而不是傳輸層(4)api。應該有一些應用程式服務元素,它們可以以標準的方式為不同的應用程式處理檔案傳輸和訊息傳遞之類的事情。我認為人們正越來越多地轉向這種模式,尤其是由帶有go、QUIC、protobufs等的谷歌驅動。

我之所以提到這一點,是因為谷歌和微軟之間的差異。微軟擁有一個流行的作業系統,所以它的創新是由它在該作業系統中所能做的事情驅動的。谷歌的創新是由它可以放在作業系統上的東西驅動的。然後是Facebook和亞馬遜自己,它們必須在谷歌提供的堆疊之上(或之外)進行創新。世界上排名前五的公司依次是蘋果、谷歌、微軟、亞馬遜和facebook,因此,每一家公司推動創新的地方都很重要。

**結論**

在 20 世紀 70 年代,TCP 被創造出來的時候,是非常了不起的。它處理的事情,如擁塞,比其他競爭性協議要好很多。人們沒有辦法預料到會有 40 億個 IPV4 地址的出現,那時候預計現代網際網路的競爭性設計會比七八十年代要好。

IPv4 到 IPv6 的升級,很大程度上保持了 IP 的優勢。 從 TCP 到 QUIC 的升級同樣是基於 TCP 的優點的,並將其擴充套件到現代需求上。實際上令人驚訝的是,TCP 已經持續瞭如此長的時間,它在沒有升級的情況下,做得很好。

TCP

UDP

Websorcket

併發

分散式

相關文章