坤哥再談負載均衡

大家好,我是坤哥

上週發的一篇負載均衡的文章有一個點不少人(統計了下在其他平臺及上篇文章留言中大概有 8 人留言不解)有疑問,所以我覺得有必要單獨寫篇文章解釋一下,先看下上篇文章展示的架構圖

這裏一些朋友的疑問點是 Nginx 是否多此一舉,能否能直接從 LVS 打到站點層?即改成下面的架構

答案是不行,爲什麼?其實我在上文中有提到一些點已經暗示了,只不過不那麼明顯而已,我再單獨把這些點拎出來

  1. LVS 是四層負載均衡器

  2. Nginx 是七層負載均衡器,可以根據 url 來轉發流量

首先我們需要明白爲什麼根據 url 轉發請求這麼重要,假設現在有「營銷」,「運營中心」這兩個集羣,使用 Nginx 的話很簡單,根據 url 來決定到底將請求轉發到哪個集羣即可

由於 LVS 不能根據 url 轉發,那麼請問 LVS 收到請求後該轉給誰

那麼 LVS 爲什麼不能根據 url 來轉發呢,因爲它是四層負載均衡器,什麼是四層和七層,這裏就要簡單複習下 ISO 七層參考模型了

由此可知,七層對應着應用層,四層對應着傳輸層,如果從應用層發起一個請求會在「傳輸層」,「網絡層」,「數據鏈路層」分別加上各自層的包頭,比如現在 A 電腦要發一個「I'm Deepon」數據給 B 電腦,則在各層的轉化流程如下圖所示

但最終在互聯網上要傳輸的包(數據鏈路層傳輸的包叫禎,統稱爲包)是有大小限制的,如下圖所示

在互聯網上傳輸的包不能超過 14 + 20 + 20 + 1460 + 4 = 1518 byte,其中包含的應用層(即 payload)數據一次性不能超過 1460 個 byte, 也就是說如果一個 HTTP 請求有 2000 byte,那麼它必須分成兩個包發送才能在網絡上傳輸,再來看看 HTTP 的格式

如果一個 HTTP POST 請求很大,超過了 1460 byte(一個包 payload 的最大值),那麼它必須分成兩個包才能傳輸,也就意味着一個包可能包含 URI,另一個包不包含 URI,既然包都不包含 URI,那麼請問 LVS 如何根據 URL 來轉發給相應的集羣呢,所以理解了 TCP/IP 的工作機制相信你不難理解開頭的問題:LVS 是四層負載均衡器,無法根據 URL 來轉發請求。

其實最關鍵的原因是四層以下其實只負責包的轉發,只要拿出包頭查看一下 ip 地址就可知道該轉發哪裏,很高效,如果你還要根據 url 來匹配那麼需要拿到應用層數據根據正則等做匹配,顯然會消耗更多的性能,所以專業的人做專業的事,應該由 LVS 來負責承載所有流量,Nginx 負責根據 url 來轉發給對應的集羣,因爲它是七層負載均衡器,與上下游各建立了一個 TCP 鏈接

所以如果有多個分包,由於 Nginx 與 client 建立了 TCP 連接,可以在 Nginx 先拿到 client 發出的所有的分包再組裝成完整的報文, 然後根據 url 選擇其中一臺 server 與之建立 TCP 連接後將數據分批完整地傳給上游 server

另外需要注意的是現在在大廠中如果只將 Nginx 作爲轉發之用是不夠的,一般用的 OpenResty ,什麼是 OpenResty 呢

“OpenResty® 是一個基於 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建能夠處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。

OpenResty® 的目標是讓你的 Web 服務直接跑在 Nginx 服務內部,充分利用 Nginx 的非阻塞 I/O 模型,不僅僅對 HTTP 客戶端請求, 甚至於對遠程後端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進行一致的高性能響應。”

注意上面一句「提供了與 MySQL ,Redis 等的交互能力」這一點非常關鍵,我們之前不是說 Nginx 可以根據 url 來決定打向哪個集羣嗎,假設現在有一個這樣的場景:所有包含 operation 的請求都轉發到運營中心的集羣,則需要寫死類似如下的配置

upstream backend {
  server 192.168.1.10:8080
  server 192.168.1.11:8080
}

server {
  location /operation {
    proxy_pass http://backed
  }
}

在我們集團中類似這樣的規則非常多,難道要像上面這樣把所有的規則都一個個寫死在 Nginx 的配置文件裏嗎?顯然不可行,更合理的方式是把這些規則(哪個 url 對應哪些集羣)保存在 MySQL 中,然後 Nginx 在啓動的時候將這些規則從 MySQL 中取出並保存在 Redis 及本地緩存中,然後 Nginx 要根據 url 匹配的時候從本地緩存(如果沒有從 redis 拿,redis 過期從 MySQL 拿)裏拿這些規則再根據匹配項轉發到相應的集羣,Nginx 沒有這樣的能力,而 OpenResty 由於集成了 Lua,引入了與 MySQL, Redis 等交互的模塊,所以用它是可行的,所以最終架構如下(將 Nginx 換成 OpenResty)

關於使用 OpenResty 作爲接入層 / 網關的具體實踐,我們之前寫了一篇文章,有興趣的可以看看 ^_^

高性能網關設計實踐

大家好,我是坤哥,前獨角獸技術專家,現創業者,持續分享個人的成長收穫,關注我一定能提升你的視野,讓我們一起進階吧!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/B1pqmFqeD0IkdqG9Hf9Vfg