OpenResty-Lua-Redis-MQ 實現高 QPS 下熱點數據的存取方案

    京東、淘寶首頁每天的訪問量是非常的大,如果不對首頁上的一些熱點數據做技術上的處理就會出現首頁的訪問速度慢,甚至在高併發下無法打開首頁,這些都會嚴重的影響用戶的體驗。首頁常見的熱點數據是輪播圖,下圖是京東和淘寶首頁的輪播圖:

    每個用戶打開京東、淘寶網站的首頁都會出現這個輪播圖,由於平臺是國內知名的購物平臺,所以其 QPS 是非常高的;另外這個輪播圖也是可以做更改和替換來達到展示不同的圖片的效果。如果不做好應對措施那麼在高併發下就會出現讀取速度慢,甚至擊垮後端的服務器。

    針對這個場景,我們使用 OpenResty+Lua+Redis+MQ 實現一套支持高 QPS 的熱點數據緩存和讀取的方案。

1、技術點—— OpenResty

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

    這裏就不介紹 OpenResty 的具體搭建過程,給大家展示一張搭建 OpenResty 成功之後訪問首頁的效果圖:

    OpenResty 利用 Nginx 的負載均衡功能,將請求分發到不同的 Lua 進程中,從而提高系統的併發處理能力。Lua 和 Redis 這裏就不再做介紹了。

2、熱點數據的存取方案設計

2.1 熱點數據的發佈

    運營人員發佈熱點數據(如網站首頁的輪播圖的配置),首先將熱點數據同步到 Mysql 中,然後熱點數據發送一條添加或者修改消息到 MQ 中(如 Kafka、RocketMQ 等),通過 MQ 來同步 Redis 中數據一致性。通過 MQ 同步熱點數據到 Redis 中會存在一定的延遲,但是針對首頁輪播圖業務場景來講是可以忽略的(因爲只是做展示而已,不會影響實際的業務),但是如果想要低延遲我們可以採用 Canal 來監聽 Mysql 的 binlog 方案來實現,這裏就不具體的展開了。

2.2 熱點數據的讀取過程

    用戶通過客戶端訪問數據的時候,請求先到達 OpenResty 上(單機支持 10K-1000K 併發連接),由於 OpenResty 是基於 Nginx 實現的,所以 OpenResty 可以通過內部的 location 配置來執行預先配置的 Lua 腳本,執行 Lua 腳本的時候我們的邏輯如下:

(1)先讀取 Redis 集羣,判斷是否有需要的數據,如果 Redis 中存在需要的數據就直接返回數據給客戶端。

(2)如果在 Redis 中沒有獲取到數據,Lua 會連接 Mysql 獲取需要的數據,數據讀取到之後同步一份數據到 Redis 集羣中,最後返回數據到客戶端。

2.3  整體熱點數據發佈和存取的流程

Lua 腳本的代碼

ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];
local cache_ngx = ngx.shared.dis_cache;
local hotCache = cache_ngx:get('hot_cache_'..id);
ngx.say(hotCache)
if hotCache == "" or hotCache == nil then
    local redis = require("resty.redis");
    local red = redis:new()
    red:set_timeout(2000)
    red:connect("192.168.223.134", 6379)   
    local rescontent=red:get("content_"..id);
    if ngx.null == rescontent then
        local cjson = require("cjson");
        local mysql = require("resty.mysql");
        local db = mysql:new();
        db:set_timeout(2000)
        local props = {
            host = "192.168.223.132",
            port = 3306,
            database = "test",
            user = "root",
            password = "123456"
        }
        local res = db:connect(props);
        local select_sql = "select * from tb_hot where;
        res = db:query(select_sql);
        local responsejson = cjson.encode(res);
        red:set("content_"..id,responsejson);
        ngx.say(responsejson);
        db:close()
    else
        cache_ngx:set('hot_cache_'..id, rescontent, 10*60);
        ngx.say(rescontent)
    end
    red:close()
else
    ngx.say(contentCache)
end

總結:

    高併發下熱點的數據的存取本文采用 OpenResty+Lua+Redis 的方案,方案的實現難度小,可用性高,支持幾十萬甚至百萬的 QPS。

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