設計百萬 QPS 的短鏈服務

1、什麼是短鏈接和長鏈接

上圖是我們經常可以收到的一條流量通知的短信,短信有一個鏈接 A:

https://dx.100XX.cn/JG1mEA

 這個就是短鏈接;當我們點擊短鏈接 A 之後就會自動的跳轉到鏈接 B 

https://wap.zj.100XX.cn/szhy/2022/openMiniProgram/index.html?id=8ace47a58414b7d201843b8842ed15bb

這個就是真實的訪問鏈接,此鏈接我們稱爲長鏈接。

爲什麼需要短鏈接?

(1)短鏈通常較短,字符數量少,更加節省字數;

(2)各種平臺發文有字數限制;

(3)節約成本,如短信發送時短鏈接就不需要拆成多條文本發送;

(4)佔用空間少

長鏈接和短鏈接跳轉的原理

狀態碼 301 和 302 的區別:

(1)狀態碼 301

    301 是永久重定向。301 跳轉會默認被瀏覽器緩存,當用戶第一次訪問某個短鏈後,如果服務器返回 301 狀態碼,則這個用戶在後後續多次訪問同一短鏈接地址,瀏覽器會直接請求緩存中的跳轉地址,不會再請求短鏈服務重新獲取地址。這麼做的優點是降低了服務器的壓力,但是無法統計短鏈接的點擊次數。

(1)狀態碼 302

    302 是臨時重定向。302 跳轉默認不會被瀏覽器緩存,除非提示瀏覽器緩存。因此用戶每次訪問同一短鏈地址,瀏覽器都會去 duan 鏈服務器上重新取長鏈接的地址。此方式優點是能夠統計到短鏈接被點擊的次數,但是服務器的壓力變大了。

2、實現短鏈接和長鏈接的映射

(1)哈希法

    哈希法推薦 Google 出品的 MurmurHash 算法,MurmurHash 是一種非加密型哈希函數,非加密性能較高。將長鏈接通過哈希算法映射成一個短值如下:

數據存放到數據庫(如 Mysql)如下:

當前請求短鏈服務器的時候(https://www.longxia/v/short?id=longxia)

通過 id=longxia 到數據匹配到長鏈接,然後將長鏈接返回。

    哈希法的會存在哈希衝突的問題(即就是兩個不同的 URL 可能會生成相同的短鏈接),爲了解決這個問題我可以採用增加 salt 字段,方案如下:

    將短鏈字段設置爲唯一鍵,重複短鏈插入數據庫拋出異常時候進行處理: 增加鹽值到長鏈後面,然後重新使用這個拼接的長鏈進行 hah 計算。此方式最多重試三次,盡最大努力解決哈希衝突問題。訪問短鏈時,一併取出 salt 值,將長鏈處理後進行返回。

(2)id 映射方案

可以使用 Mysql 的自增主鍵值來映射長鏈接

數據庫中存儲的方式

當訪問短鏈接(https://www.longxia/v/short/1)可以通過這個短鏈後面的 1 來定位到長鏈,然後返回給服務消費方。

數據庫自增 id 映射方案的缺點:

(1)id 太規律了,容易被攻擊;

(2)id 自增到一定程度後,數字還是很長;

    由於主鍵 id 直接暴露存在一定的問題,所以使用 redis、uuid、雪花算法等替換方案。

(2)redis 方案

    在 Redis 中創建一個鍵,使用 INCR 命令遞增該鍵的值,並將遞增後的值作爲唯一 ID 返回。由於 Redis 的 INCR 命令是原子性操作,所以可以確保每次生成的 ID 都是唯一的。爲了增加 ID 的安全性,一般不建議使用 Redis 自增的數值,而是拼接一些其它信息,方案如下:

(3)uuid 方案

    UUID 是通過一系列算法生成的 128 位數字,通常基於時間戳、計算機硬件標識符、隨機數等元素;uuid 方案實現簡單,無需網絡交互就能保證了 ID 的唯一性。

UUID.randomUUID().toString()

(4)雪花算法方案

雪花算法生成一個 64 位的大小的整數,結構如下:

    雪花算法的生效 id 的效率高,但是雪花算法要避免時鐘回撥的問題(會出現 id 重複的問題)

3、設計短鏈服務

    由於是訪問量很大,所以我們在設計的時候採用了 LVS+Nginx 扛住第一層的大流量。

(1)LVS 使用 keepalived 來保證高可用,LVS 是工作在第四層並且其負載能力強,它負責將請求分發到 nginx 上;

(2)nginx 單機的併發 5 萬,針對百萬的併發至少需要 20 臺 nginx 來處理大的流量,nginx 將請求轉發到公司的網關上。

(3)網關根據服務的 URL 來解析地址找到對應的服務器,由於是百萬流量所以網關也需要做集羣來處理請求。

(4)網關將請求轉發到真實的短鏈服務上,短鏈服務自身使用了 sentinel 來限流、使用本地緩存(常見的是 Guava、caffeine)、分佈式緩存(如 redis)來緩存數據,使用布隆過濾器過濾無效的請求。

(5)有效的請求未命中緩存,此時就查詢數據庫,由於數據庫抗併發能力弱,所以對數據庫做了主從模式、讀寫分離方式來應對高併發。

(6)數據庫中查詢出來的數據要同步到緩存中,以便於下次同樣的短鏈請求可以不查詢數據庫而直接給消費者提供數據響應。

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