OpenResty+Lua+Redis+Canal實現(xiàn)多級緩存架構(gòu)

目錄

1,OpenResty 高性能Web站點架構(gòu)

  1. OpenResty 特性介紹
  2. 搭建OpenResty
  3. Web站點動靜分離方案剖析

2,Lua語法學習

  1. Lua基本語法

3,多級緩存架構(gòu)實戰(zhàn)

  1. 多級緩存架構(gòu)分析
  2. Lua操作Redis實戰(zhàn)

4,Nginx代理緩存

  1. Nginx代理緩存學習
  2. Nginx代理緩存熱點數(shù)據(jù)應(yīng)用
  3. Cache_Purge代理緩存清理

5,緩存一致性

  1. Canal原理講解
  2. Canal安裝
  3. 多級緩存架構(gòu)緩存一致性實戰(zhàn)

1 OpenResty高性能Web站點架構(gòu)

openresty.png

http://openresty.org/en/
http://openresty.org/cn/
OpenResty? 是一款基于 NGINX 和 LuaJIT 的 Web 平臺。

1.1 OpenResty簡介

OpenResty? 是一個基于 Nginx 與 Lua 的高性能 Web 平臺,其內(nèi)部集成了大量精良的 Lua 庫、第三方模塊以及大多數(shù)的依賴項。用于方便地搭建能夠處理超高并發(fā)、擴展性極高的動態(tài) Web 應(yīng)用、Web 服務(wù)和動態(tài)網(wǎng)關(guān)。

OpenResty? 通過匯聚各種設(shè)計精良的 Nginx 模塊(主要由 OpenResty 團隊自主開發(fā)),從而將 Nginx 有效地變成一個強大的通用 Web 應(yīng)用平臺。這樣,Web 開發(fā)人員和系統(tǒng)工程師可以使用 Lua 腳本語言調(diào)動 Nginx 支持的各種 C 以及 Lua 模塊,快速構(gòu)造出足以勝任 10K 乃至 1000K 以上單機并發(fā)連接的高性能 Web 應(yīng)用系統(tǒng)。

OpenResty? 的目標是讓你的Web服務(wù)直接跑在 Nginx 服務(wù)內(nèi)部,充分利用 Nginx 的非阻塞 I/O 模型,不僅僅對 HTTP 客戶端請求,甚至于對遠程后端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進行一致的高性能響應(yīng)。

1.2 OpenResty搭建

關(guān)于OpenResty的搭建,可以參考官方提供的網(wǎng)址進行搭建。http://openresty.org/cn/installation.html,我們采用源碼安裝的方式進行安裝。官方提供了源碼安裝的方式:http://openresty.org/cn/linux-packages.html

安裝OpenResty
  1. 安裝依賴庫:

    yum install libtermcap-devel ncurses-devel libevent-devel readline-devel pcre?devel gcc openssl openssl-devel per perl wget
    
  2. 下載安裝包:

    wget https://openresty.org/download/openresty-1.11.2.5.tar.gz
    
  3. 解壓安裝包:

    tar -xf openresty-1.11.2.5.tar.gz
    
  4. 進入安裝包,并安裝:

    #進入安裝包
    cd openresty-1.11.2.5
    #安裝
    ./configure --prefix=/usr/local/openresty --with-luajit --without?http_redis2_module --with-http_stub_status_module --with-http_v2_module --with?http_gzip_static_module --with-http_sub_module --add?module=/usr/local/javacoo/ngx_cache_purge-2.3/
    #編譯并安裝
    make && make install
    
  5. 說明:

    --prefix=/usr/local/openresty:安裝路徑
    --with-luajit:安裝luajit相關(guān)庫,luajit是lua的一個高效版,LuaJIT的運行速度比標準Lua快數(shù)十
    倍。
    --without-http_redis2_module:現(xiàn)在使用的Redis都是3.x以上版本,這里不推薦使用Redis2,表示
    不安裝redis2支持的lua庫
    --with-http_stub_status_module:Http對應(yīng)狀態(tài)的庫
    --with-http_v2_module:對Http2的支持
    --with-http_gzip_static_module:gzip服務(wù)端壓縮支持
    --with-http_sub_module:過濾器,可以通過將一個指定的字符串替換為另一個字符串來修改響應(yīng)
    --add-module=/usr/local/javacoo/ngx_cache_purge-2.3/:Nginx代理緩存清理工具
    

    關(guān)于每個模塊的具體作用,大家可以參考騰訊云的開發(fā)者手冊:https://cloud.tencent.com/developer/
    doc/1158
    如下圖安裝完成后,在 /usr/local/openrestry/nginx 目錄下是安裝好的nginx,以后我們將在該目
    錄的nginx下實現(xiàn)網(wǎng)站發(fā)布。

  6. 配置環(huán)境變量:

    vi /etc/profile
    export PATH=/usr/local/openresty/nginx/sbin:$PATH
    source /etc/profile
    
  7. 開機啟動:

    linux系統(tǒng)結(jié)構(gòu) /lib/systemd/system/ 目錄,該目錄自動存放啟動文件的配置位置,里面一般包含有
    xxx.service ,例如 systemctl enable nginx.service ,就是調(diào)用
    /lib/systemd/system/nginx.service 文件,使nginx開機啟動。
    我們可以創(chuàng)建 /usr/lib/systemd/system/nginx.service ,在該文件中編寫啟動nginx腳本:

    [Service]
    Type=forking
    PIDFile=/usr/local/openresty/nginx/logs/nginx.pid
    ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t
    ExecStart=/usr/local/openresty/nginx/sbin/nginx
    ExecReload=/bin/kill -s HUP $MAINPID
    ExecStop=/bin/kill -s QUIT $MAINPID
    PrivateTmp=true
    [Install]
    WantedBy=multi-user.target
    

    執(zhí)行 systemctl daemon-reload :重新加載某個服務(wù)的配置文件
    執(zhí)行 systemctl enable nginx.service :開機啟動
    執(zhí)行 systemctl start nginx.service :啟動nginx

    訪問 http://192.168.100.130/,效果如下:

2.png

1.3 動靜分離站點架構(gòu)

1.3.1 什么是網(wǎng)站動靜分離架構(gòu)模式?
  1. 網(wǎng)站中的動靜分離其實就是將動態(tài)資源和靜態(tài)資源分離處理。
  2. 傳統(tǒng)網(wǎng)站架構(gòu)模式:jsp、html、img、css、js全部存放在一個服務(wù)器上,把靜態(tài)資源和動態(tài)資源共同放到一臺服務(wù)器上。
  3. 大型互聯(lián)網(wǎng)公司中前端與后端開發(fā)是分離,靜態(tài)資源和動態(tài)資源不會部署到同一臺服務(wù)器上,靜態(tài)資源部署在一個服務(wù)器上(html、css、img、js …),動態(tài)資源部署在一個服務(wù)器上(后端代碼)
1.3.2 動態(tài)資源和靜態(tài)資源的區(qū)別?
  1. 靜態(tài)資源:用戶多次訪問后,html源代碼不會發(fā)生改變,例如html、jpg 、css、js 等不需要后臺處理的資源。
  2. 動態(tài)資源:用戶多次訪問后,html源代碼可能會發(fā)生改變,例如我們訪問的JSP頁面 (本質(zhì)是一個Servlet)或*.do請求等。
  3. 區(qū)別:判斷標準是,刷新多次,html源代碼是否發(fā)生改變 。
  4. 誤區(qū):并不是頁面是動態(tài)的,就一定是動態(tài)頁面,一般動態(tài)頁面為了提高被搜索引擎搜到的機率,會使用偽靜態(tài);也就是說使用動態(tài)頁面靜態(tài)化技術(shù)將頁面靜態(tài)化,靜態(tài)化技術(shù)有Freemarker、Beetl、Velociity、Thymeleaf 等。
1.3.3 為什么要使用動靜分離?
  1. 其實就是減輕服務(wù)器的壓力、提高服務(wù)器的響應(yīng)速度和效率、保證高并發(fā)。就像數(shù)據(jù)庫的讀寫分離一樣,也是保證高并發(fā)。
  2. 靜態(tài)服務(wù)器我們同時也可以使用CDN做內(nèi)容分發(fā),訪問不同的資源轉(zhuǎn)發(fā)到不同的服務(wù)器。
  3. 靜態(tài)服務(wù)器我們一般使用Nginx,Nginx實現(xiàn)靜態(tài)服務(wù)器要比Tomcat 快得多。
1.3.4 動靜分離與前后端分離的區(qū)別?
  1. 動靜分離:指的是動態(tài)資源和靜態(tài)資源分離,分別部署在不同的服務(wù)器上。

  2. 前后端分離:網(wǎng)站架構(gòu)模式中,微服務(wù)開發(fā)基于SOA面向服務(wù)開發(fā),后臺和前端都采用調(diào)用接口方式。

    動靜分離.png
1.3.5 Nginx配置示例
  1. Nginx的nginx.conf文件配置中配置如下:

    #靜態(tài)資源服務(wù)配置(一定要配置在動態(tài)資源之前,因為動態(tài)資源location采用/匹配的,否則請求就被統(tǒng)一當做動態(tài)資源處理)
     server {
         listen       80;   # 表示當前的代理服務(wù)器監(jiān)聽的端口,默認的是監(jiān)聽80端口。注意,如果我們配置了多個server,這個listen要配置不一樣,不然就不能確定轉(zhuǎn)到哪里去了。
         server_name  javacoo;  
         
         #靜態(tài)資源
         location .*\.(js|css|ico|png|jpg|eot|svg|ttf|woff) {
         root html;
         index index.html index.htm;
         }
         
     }
     
     
     #配置上游服務(wù)器 集群,默認輪詢機制
     upstream backServer{
         server 127.0.0.1:81;
         server 127.0.0.1:82;
     }
     #動態(tài)資源服務(wù)配置
        server {
            listen       80;   # 表示當前的代理服務(wù)器監(jiān)聽的端口,默認的是監(jiān)聽80端口。注意,如果我們配置了多個server,這個listen要配置不一樣,不然就不能確定轉(zhuǎn)到哪里去了。
            server_name  localhost;   # 表示監(jiān)聽到之后需要轉(zhuǎn)到哪里去,這時我們直接轉(zhuǎn)到本地,這時是直接到nginx文件夾內(nèi)。
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {    # 表示匹配的路徑,這時配置了/表示所有請求都被匹配到這里
                #root   html;   # 里面配置了root這時表示當匹配這個請求的路徑時,將會在這個文件夾內(nèi)尋找相應(yīng)的文件,這里對我們之后的靜態(tài)文件伺服很有用。
             #指定上游負載均衡服務(wù)器
             proxy_pass http://backServer/;
                #index  index.html index.htm;   # 當沒有指定主頁時,默認會選擇這個指定的文件,它可以有多個,并按順序來加載,如果第一個不存在,則找第二個,依此類推。
            }
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    
1.3.6 實際應(yīng)用
  • 其實我們在實際做動靜分離的時候我們一般不這樣做。因為大多數(shù)網(wǎng)站靜態(tài)資源比動態(tài)資源大得多,往往影響網(wǎng)站加載速度的往往是靜態(tài)資源,這其實對我們的帶寬要求非常高。
  • 例如我服務(wù)器帶寬是1Mbps(1Mbps≈128kb/s),發(fā)一個請求動態(tài)資源10kb、靜態(tài)資源590kb,這時候我們也要(590+10)/128≈5秒才能完成加載,更別說并發(fā)訪問的情況了,我們雖然也可以提高帶寬,而且?guī)捠欠浅YF的(比服務(wù)器配置還貴),治標不治本,一般我們采用三方服務(wù)商CDN來做靜態(tài)資源緩存,它會將我們的靜態(tài)資源緩存到各個節(jié)點,并支持就近原則訪問。
  • 為什么將靜態(tài)資源存放到第三方服務(wù)器效率非常高呢?
    1.云服務(wù)器簽訂帶寬都是將T算;
    2.CDN內(nèi)容分發(fā),能夠?qū)㈧o態(tài)資源緩存到全國各地節(jié)點能夠減少客戶端與CDN帶寬距離從而提高響應(yīng)速度;就近原則訪問,舉個例子,比如說我是四川的用戶就訪問成都的節(jié)點或離四川比較近的節(jié)點。
    3.春節(jié)人口遷徙,根據(jù)就近原則會導致某些節(jié)點壓力很大,這時也會訪問其他節(jié)點的。

2 Lua語法學習

lua.png

官網(wǎng):http://www.lua.org/

學習站點:https://www.runoob.com/lua/lua-tutorial.html

2.1 lua簡介

  1. Lua 是一個小巧的腳本語言。它是巴西里約熱內(nèi)盧天主教大學(Pontifical Catholic University of Rio de
    Janeiro)里的一個由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人
    所組成的研究小組于1993年開發(fā)的。 其設(shè)計目的是為了通過靈活嵌入應(yīng)用程序中從而為應(yīng)用程序提供靈
    活的擴展和定制功能。Lua由標準C編寫而成,幾乎在所有操作系統(tǒng)和平臺上都可以編譯,運行。Lua并
    沒有提供強大的庫,這是由它的定位決定的。所以Lua不適合作為開發(fā)獨立應(yīng)用程序的語言。Lua 有一
    個同時進行的JIT項目,提供在特定平臺上的即時編譯功能。
  2. Lua腳本可以很容易的被C/C++ 代碼調(diào)用,也可以反過來調(diào)用C/C++的函數(shù),這使得Lua在應(yīng)用程序中可
    以被廣泛應(yīng)用。不僅僅作為擴展腳本,也可以作為普通的配置文件,代替XML,ini等文件格式,并且更容
    易理解和維護。 Lua由標準C編寫而成,代碼簡潔優(yōu)美,幾乎在所有操作系統(tǒng)和平臺上都可以編譯,運
    行。 一個完整的Lua解釋器不過200k,在所有腳本引擎中,Lua的速度是最快的。這一切都決定了Lua是
    作為嵌入式腳本的最佳選擇。

2.2 Lua特性:

  1. 一個小巧的腳本語言
  2. 設(shè)計目的是為了通過靈活嵌入應(yīng)用程序中從而為應(yīng)用程序提供靈活的擴展和定制功能
  3. 所有操作系統(tǒng)和平臺上都可以編譯、運行Lua腳本
  4. 所有腳本引擎中,Lua的速度是最快的

2.3 應(yīng)用場景:

  1. 游戲開發(fā)
  2. 獨立應(yīng)用腳本
  3. 高性能Web應(yīng)用(天貓、京東都有應(yīng)用)
  4. 擴展和數(shù)據(jù)庫插件如:MySQL Proxy 和 MySQL WorkBench
  5. 安全系統(tǒng),如入侵檢測系統(tǒng)

2.4 Lua常用語法

2.4.1Lua安裝

首先我們準備一個linux虛擬機來安裝Lua,在linux系統(tǒng)中按照如下步驟進行安裝:

curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
tar xf lua-5.3.5.tar.gz
cd lua-5.3.5
make linux test
2.4.2 Lua常用操作

Lua 提供了交互式編程模式。我們可以在命令行中輸入程序并立即查看效果,這種編程模式類似我們控
制臺操作,Lua 交互式編程模式可以通過命令 lua -i 或 lua 來啟用:

[root@server1 lua-5.3.5]# lua -i
Lua 5.3.5 Copyright (C) 1994-2018 Lua.org, PUC-Rio
>

打印:

print("springcloud alibaba")

數(shù)據(jù)類型

Lua 是動態(tài)類型語言,變量不要類型定義,只需要為變量賦值。 值可以存儲在變量中,作為參數(shù)傳遞或結(jié)果返回。

Lua 中有 8 個基本類型分別為:nil、boolean、number、string、userdata、function、thread 和 table。

lua-type.png

類型測試:

print(type("Hello world")) --> string
print(type(10.4*3)) --> number
print(type(print)) --> function
print(type(type)) --> function
print(type(true)) --> boolean
print(type(nil)) --> nil
print(type(type(X))) --> string

變量

變量在使用前,需要在代碼中進行聲明,即創(chuàng)建該變量。Lua 變量有三種類型:全局變量、局部變量、
表中的域。

全局變量定義:

> age=19
> pr

局部變量定義:

> local username=wangwu
> print(username)
nil

此時username不是全局變量,一般在某個方法中使用,不能全局使用,所以輸出nil。

對象(table)

> --定義對象resp
> resp = {}
> --往對象resp中添加屬性name,賦值為zhangsan
> resp["name"]="zhangsan"
> --往對象resp中添加屬性address,賦值為hunanchangsha
> resp["address"]="hunanchangsha"
> --輸出對象resp中的name屬性值
> print(resp["name"])
zhangsan

函數(shù)

創(chuàng)建一個函數(shù),其實就是創(chuàng)建一個方法,函數(shù)以function開始,end結(jié)束,可以在end之前有返回值,也
可以有入?yún)?,定義一個方法如下:

> --定義userinfo方法,入?yún)閍ge
> function userinfo(age)
>> --age在原有基礎(chǔ)上+1
>> age=age+1
>> --返回變化后的age
>> return age
>> --結(jié)束
>> end
> print(userinfo(19))
20

拼接

在上面方法調(diào)用上拼接一段字符串,可以使用亮點來做..,如下:

> print(userinfo(19).."歲了")
20歲了

邏輯判斷:

我們經(jīng)常會做一些條件判斷,在lua中也可以實現(xiàn),lua中有 if xx then else end 的流程判斷語法。

> function userinfo(age)
>> if age>=18 then
>> return "成年人"
>> else
>> return "未成年"
>> end
>> end
> print(userinfo(17))
未成年

腳本編程

我們可以像寫java一樣,將lua腳本寫到一個文件中,并且可以在一個腳本文件引入另外一個腳本文件,
類似java中的導包。
創(chuàng)建 course.lua ,代碼如下:

--定義一個對象
local course = {}
--定義一個方法
function course.courseName(id)
if id==1 then
return "java"
else
return "UI"
end
end
return course

創(chuàng)建 student.lua ,代碼如下:

--導入course.lua
local cr = require("course")
--調(diào)用courseName方法
local result = cr.courseName(1)
print(result)

執(zhí)行 student.lua :

[root@server1 lua]# lua student.lua
java

3 多級緩存架構(gòu)實戰(zhàn)

項目運行過程中往往為了提升項目對數(shù)據(jù)加載效率,一般都會增加緩存,但緩存如何加載效率最高?如
何加載對后端服務(wù)造成的壓力最小?我們需要設(shè)計一套完善的緩存架構(gòu)體系。

3.1 多級緩存架構(gòu)分析

緩存設(shè)計.png

用戶請求到達后端服務(wù),先經(jīng)過代理層nginx,nginx將請求路由到后端tomcat服務(wù),tomcat去數(shù)據(jù)庫中
取數(shù)據(jù),這是一個非常普通的流程,但在大并發(fā)場景下,需要做優(yōu)化,而緩存是最有效的手段之一。緩
存優(yōu)化有,執(zhí)行過程如下:

  1. 請求到達Nginx,Nginx抗壓能力極強。
  2. Tomcat抗壓能力很弱,如果直接將所有請求路由給Tomcat,Tomcat壓力會非常大,很有可能宕機。我們
    可以在Nginx這里設(shè)置2道緩存,第1道是Redis緩存,第2道是Nginx緩存。
  3. 先加載Redis緩存,如果Redis沒有緩存,則加載Nginx緩存,Nginx如果沒有緩存,則將請求路由到
    Tomcat。
  4. Tomcat發(fā)布的程序會加載數(shù)據(jù),加載完成后需要做緩存的,及時將數(shù)據(jù)存入Redis緩存,再響應(yīng)數(shù)據(jù)給用
    戶。
  5. 用戶下次查詢的時候,查詢Redis緩存或Nginx緩存。
  6. 后面用戶請求的時候,就可以直接從Nginx緩存拿數(shù)據(jù)了,這樣就可以實現(xiàn)后端Tomcat發(fā)布的服務(wù)被調(diào)用
    的次數(shù)大幅減少,負載大幅下降

上面這套緩存架構(gòu)被多個大廠應(yīng)用,除了可以有效提高加載速度、降低后端服務(wù)負載之外,還可以防止
緩存雪崩,為服務(wù)穩(wěn)定健康打下了堅實的基礎(chǔ),這也就是鼎鼎有名的多級緩存架構(gòu)體系。

3.2 多級緩存-Lua+Redis

按照上面分析的架構(gòu),可以每次在Nginx的時候使用Lua腳本查詢Redis,如果Redis有數(shù)據(jù),則將數(shù)據(jù)存
入到Nginx緩存,再將數(shù)據(jù)響應(yīng)給用戶,此時我們需要實現(xiàn)使用Lua將數(shù)據(jù)從Redis中加載出來。

我們在 /usr/local/openresty/nginx/lua 中創(chuàng)建文件 aditem.lua ,腳本如下:

--數(shù)據(jù)響應(yīng)類型JSON
ngx.header.content_type="application/json;charset=utf8"
--Redis庫依賴
local redis = require("resty.redis");
local cjson = require("cjson");
--獲取id參數(shù)(type)
local id = ngx.req.get_uri_args()["id"];
--key組裝
local key = "ad-items-skus::"..id
--創(chuàng)建鏈接對象
local red = redis:new()
--設(shè)置超時時間
red:set_timeout(2000)
--設(shè)置服務(wù)器鏈接信息
red:connect("192.168.100.130", 6379)
--查詢指定key的數(shù)據(jù)
local result=red:get(key);
--關(guān)閉Redis鏈接
red:close()修改 nginx.conf 添加如下配置:(最后記得將content_by_lua_file改成rewrite_by_lua_file)
訪問 http://www.javacoo.com/sku/aditems/type?id=1 效果如下:
4 Nginx代理緩存
if result==nil or result==null or result==ngx.null then
return true
else
--輸出數(shù)據(jù)
ngx.say(result)
end

修改 nginx.conf 添加如下配置:(最后記得將content_by_lua_file改成rewrite_by_lua_file)

#推廣產(chǎn)品查詢
location /sku/aditems/type {
content_by_lua_file /usr/local/openresty/nginx/lua/aditem.lua;
}

4 Nginx代理緩存

proxy_cache 是用于 proxy 模式的緩存功能,proxy_cache 在 Nginx 配置的 http 段、server 段中分別
寫入不同的配置。http 段中的配置用于定義 proxy_cache 空間,server 段中的配置用于調(diào)用 http 段中
的定義,啟用對server 的緩存功能。
使用:

1、定義緩存空間
2、在指定地方使用定義的緩存

4.1 Nginx代理緩存學習

  1. 開啟Proxy_Cache緩存:

    我們需要在nginx.conf中配置才能開啟緩存:

    proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2
    keys_zone=proxy_cache:10m max_size=1g inactive=60m use_temp_path=off;
    

    參數(shù)說明:

    • 【proxy_cache_path】指定緩存存儲的路徑,緩存存儲在/usr/local/openresty/nginx/cache目錄
      【levels=1:2】設(shè)置一個兩級目錄層次結(jié)構(gòu)存儲緩存,在單個目錄中包含大量文件會降低文件訪問速度,因此我們建議對大多數(shù)部署使用兩級目錄層次結(jié)構(gòu)。如果 levels 未包含該參數(shù),Nginx 會將所有文件放在同一目錄中。
    • 【keys_zone=proxy_cache:10m】設(shè)置共享內(nèi)存區(qū)域,用于存儲緩存鍵和元數(shù)據(jù),例如使用計時器。擁有內(nèi)存中的密鑰副本,Nginx 可以快速確定請求是否是一個 HIT 或 MISS 不必轉(zhuǎn)到磁盤,從而大大加快了檢查速度。1 MB 區(qū)域可以存儲大約 8,000 個密鑰的數(shù)據(jù),因此示例中配置的 10 MB 區(qū)域可以存儲大約
      80,000 個密鑰的數(shù)據(jù)。
    • 【max_size=1g】設(shè)置緩存大小的上限。它是可選的; 不指定值允許緩存增長以使用所有可用磁盤空間。當緩存大小達到限制時,一個稱為緩存管理器的進程將刪除最近最少使用的緩存,將大小恢復到限制之下的文件。
    • 【inactive=60m】指定項目在未被訪問的情況下可以保留在緩存中的時間長度。在此示例中,緩存管理器進程會自動從緩存中刪除 60 分鐘未請求的文件,無論其是否已過期。默認值為 10 分鐘(10m)。非活動內(nèi)容與過期內(nèi)容不同。Nginx 不會自動刪除緩存 header 定義為已過期內(nèi)容(例如 CacheControl:max?age=120)。過期(陳舊)內(nèi)容僅在指定時間內(nèi)未被訪問時被刪除。訪問過期內(nèi)容時,Nginx 會從原始服務(wù)器刷新它并重置 inactive 計時器。
    • 【use_temp_path=off】表示NGINX會將臨時文件保存在緩存數(shù)據(jù)的同一目錄中。這是為了避免在更新緩存時,磁盤之間互相復制響應(yīng)數(shù)據(jù),我們一般關(guān)閉該功能。
  2. Proxy_Cache屬性:

    • proxy_cache:設(shè)置是否開啟對后端響應(yīng)的緩存,如果開啟的話,參數(shù)值就是zone的名稱,比
      如:proxy_cache。

    • proxy_cache_valid:針對不同的response code設(shè)定不同的緩存時間,如果不設(shè)置code,默認為
      200,301,302,也可以用any指定所有code。

    • proxy_cache_min_uses:指定在多少次請求之后才緩存響應(yīng)內(nèi)容,這里表示將緩存內(nèi)容寫入到磁盤。
      proxy_cache_lock:默認不開啟,開啟的話則每次只能有一個請求更新相同的緩存,其他請求要么等待緩存有數(shù)據(jù)要么限時等待鎖釋放;nginx 1.1.12才開始有。配套著proxy_cache_lock_timeout一起使用。

    • proxy_cache_key:緩存文件的唯一key,可以根據(jù)它實現(xiàn)對緩存文件的清理操作。

4.2 Nginx代理緩存熱點數(shù)據(jù)應(yīng)用

  1. 開啟代理緩存

    修改 nginx.conf ,添加如下配置:

    proxy_cache_path /usr/local/openresty/nginx/cache levels=1:2
    keys_zone=proxy_cache:10m max_size=1g inactive=60m use_temp_path=off;
    

    修改 nginx.conf ,添加如下配置:

    #門戶發(fā)布
    server {
        listen 80;
        server_name www.javacoo.com;
        #推廣產(chǎn)品查詢
        location /sku/aditems/type {重啟nginx或者重新加載配置文件 nginx -s reload ,再次測試,可以發(fā)現(xiàn)下面?zhèn)€規(guī)律:
        我們還可以發(fā)現(xiàn)cache目錄下多了目錄和一個文件,這就是Nginx緩存:
        4.3 Cache_Purge代理緩存清理
        很多時候我們?nèi)绻幌氲却彺娴倪^期,想要主動清除緩存,可以采用第三方的緩存清除模塊清除緩存
        nginx_ngx_cache_purge 。安裝nginx的時候,需要添加 purge 模塊, purge 模塊我們已經(jīng)下載了,
        在 /usr/local/javacoo 目錄下,添加該模塊 --add-module=/usr/local/javacoo/ngx_cache_purge-2.3/ ,
        這一個步驟我們在安裝 OpenRestry 的時候已經(jīng)實現(xiàn)了。
        安裝好了后,我們配置一個清理緩存的地址:http://192.168.100.130/purge/sku/aditems/type?id=1
        #先找Nginx緩存
        rewrite_by_lua_file /usr/local/openresty/nginx/lua/aditem.lua;
        #啟用緩存openresty_cache
        proxy_cache proxy_cache;
        #針對指定請求緩存
        #proxy_cache_methods GET;
        #設(shè)置指定請求會緩存
        proxy_cache_valid 200 304 60s;
        #最少請求1次才會緩存
        proxy_cache_min_uses 1;
        #如果并發(fā)請求,只有第1個請求會去服務(wù)器獲取數(shù)據(jù)
        #proxy_cache_lock on;
        #唯一的key
        proxy_cache_key $host$uri$is_args$args;
        #動態(tài)代理
        proxy_pass http://192.168.100.1:8081;
        }
        #其他所有請求
        location / {
        root /usr/local/javacoo/web/static/frant;
        }
    }
    

    重啟nginx或者重新加載配置文件 nginx -s reload ,再次測試,可以發(fā)現(xiàn)下面?zhèn)€規(guī)律:

    1:先查找Redis緩存
    2:Redis緩存沒數(shù)據(jù),直接找Nginx緩存
    3:Nginx緩存沒數(shù)據(jù),則找真實服務(wù)

    我們還可以發(fā)現(xiàn)cache目錄下多了目錄和一個文件,這就是Nginx緩存:

4.3 Cache_Purge代理緩存清理

很多時候我們?nèi)绻幌氲却彺娴倪^期,想要主動清除緩存,可以采用第三方的緩存清除模塊清除緩存
nginx_ngx_cache_purge 。安裝nginx的時候,需要添加 purge 模塊, purge 模塊我們已經(jīng)下載了,
在 /usr/local/javacoo 目錄下,添加該模塊 --add-module=/usr/local/javacoo/ngx_cache_purge-2.3/ ,
這一個步驟我們在安裝 OpenRestry 的時候已經(jīng)實現(xiàn)了。

安裝好了后,我們配置一個清理緩存的地址:http://192.168.100.130/purge/sku/aditems/type?id=1

#清理緩存
location ~ /purge(/.*) {
#清理緩存
proxy_cache_purge proxy_cache $host$1$is_args$args;
}

此時訪問http://www.javacoo.com/purge/sku/aditems/type?id=1,表示清除緩存,如果出現(xiàn)如下
效果表示清理成功:

3.png

5 緩存一致性

上面我們雖然實現(xiàn)了多級緩存架構(gòu),但是問題也出現(xiàn)了,如果數(shù)據(jù)庫中數(shù)據(jù)發(fā)生變更,如何更新Redis緩
存呢?如何更新Nginx緩存呢?
我們可以使用阿里巴巴的技術(shù)解決方案Canal來實現(xiàn),通過Canal監(jiān)聽數(shù)據(jù)庫變更,并實時消費變更數(shù)
據(jù),并更新緩存。
canal [k?'n?l],譯意為水道/管道/溝渠,主要用途是基于 MySQL 數(shù)據(jù)庫增量日志解析,提供增量數(shù)據(jù)
訂閱和消費
學習地址:https://github.com/alibaba/canal
早期阿里巴巴因為杭州和美國雙機房部署,存在跨機房同步的業(yè)務(wù)需求,實現(xiàn)方式主要是基于業(yè)務(wù)
trigger 獲取增量變更。從 2010 年開始,業(yè)務(wù)逐步嘗試數(shù)據(jù)庫日志解析獲取增量變更進行同步,由此衍
生出了大量的數(shù)據(jù)庫增量訂閱和消費業(yè)務(wù)。
基于日志增量訂閱和消費的業(yè)務(wù)包括:

  • 數(shù)據(jù)庫鏡像

  • 數(shù)據(jù)庫實時備份

  • 索引構(gòu)建和實時維護(拆分異構(gòu)索引、倒排索引等)

  • 業(yè)務(wù) cache 刷新

  • 帶業(yè)務(wù)邏輯的增量數(shù)據(jù)處理

    當前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x。

5.1 Canal原理

MySQL主備復制原理:

  • MySQL master 將數(shù)據(jù)變更寫入二進制日志( binary log, 其中記錄叫做二進制日志事件binary log
    events,可以通過 show binlog events 進行查看)
  • MySQL slave 將 master 的 binary log events 拷貝到它的中繼日志(relay log)
  • MySQL slave 重放 relay log 中事件,將數(shù)據(jù)變更反映它自己的數(shù)據(jù)

Canal 工作原理:

  • canal 模擬 MySQL slave 的交互協(xié)議,偽裝自己為 MySQL slave ,向 MySQL master 發(fā)送dump
    協(xié)議

  • MySQL master 收到 dump 請求,開始推送 binary log 給 slave (即 canal )

  • canal 解析 binary log 對象(原始為 byte 流)

    canal.png

5.2 Canal安裝

5.2.1 MySQL開啟binlog

對于MySQL , 需要先開啟 Binlog 寫入功能,配置 binlog-format 為 ROW 模式,my.cnf 中配置如下

docker exec -it mysql /bin/bash
cd /etc/mysql/mysql.conf.d
vi mysqld.cnf

在最文件尾部添加如下配置:

log-bin=mysql-bin # 開啟 binlog
binlog-format=ROW # 選擇 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定義,不要和 canal 的 slaveId 重復

注意:針對阿里云 RDS for MySQL , 默認打開了 binlog , 并且賬號默認具有 binlog dump 權(quán)限 , 不需要
任何權(quán)限或者 binlog 設(shè)置,可以直接跳過這一步。

授權(quán) canal 鏈接 MySQL 賬號具有作為 MySQL slave 的權(quán)限, 如果已有賬戶可直接 grant:

CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES

重啟mysql容器

docker restart canal

查看是否開啟binlog:

show variables like 'log_bin';
5.2.2 Canal安裝

我們采用docker安裝方式:

docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server

進入容器,修改核心配置canal.properties 和instance.properties,canal.properties 是canal自身的配
置,instance.properties是需要同步數(shù)據(jù)的數(shù)據(jù)庫連接配置。
修改配置如下:

# position info
canal.instance.master.address=192.168.100.130:3306

另一處配置:

# table regex
#canal.instance.filter.regex=.*\\..*
#監(jiān)聽配置
canal.instance.filter.regex=shop_goods.ad_items

配置完成后,重啟 canal 容器

docker restart canal

5.3 多級緩存架構(gòu)緩存一致性實戰(zhàn)

多級緩存架構(gòu)一致性.png
5.3.1 Canal微服務(wù)搭建

工程坐標:

<groupId>com.javacoo</groupId>
<version>1.0.0-SNAPSHOT</version>
<artifactId>demo-canal-service</artifactId>

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo-service</artifactId>
        <groupId>com.javacool</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>demo-canal-service</artifactId>
    <description>
        Canal微服務(wù)
    </description>
    <dependencies>
        <!--springboot-canal快速構(gòu)建依賴包-->
        <dependency>
            <groupId>top.javatool</groupId>
            <artifactId>canal-spring-boot-starter</artifactId>
            <version>1.2.1-RELEASE</version>
        </dependency>
        <!--依賴demo-goods-api-->
        <dependency>
            <groupId>com.javaoo</groupId>
            <artifactId>goods-api</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

bootstrap.yml:

server:
    port: 8083
spring:
    application:
        name: demo-canal
    cloud:
        nacos:
            config:
                file-extension: yaml
                server-addr: 192.168.100.130:8848
            discovery:
                #Nacos的注冊地址
                server-addr: 192.168.100.130:8848
#Canal配置
canal:
    server: 192.168.100.130:11111
    destination: example
#日志配置
logging:
    pattern:
        console: "%msg%n"
    level:
        root: error

創(chuàng)建監(jiān)聽類: com.javacoo.service.canal.listener.AdItemsHandler

@CanalTable(value = "ad_items")
@Component
public class AdItemsHandler implements EntryHandler<AdItems> {
    @Autowired
    private SkuFeign skuFeign;
    @Override
    public void insert(AdItems adItems) {
        //加載緩存
        skuFeign.updateTypeItems(adItems.getType());
    }
    /***
    * 修改
    * @param before
    * @param after
    */
    @Override
    public void update(AdItems before, AdItems after) {
        //分類不同,則重新加載之前的緩存
        if(before.getType().intValue()!=after.getType().intValue()){
            //修改緩存
            skuFeign.updateTypeItems(before.getType());
        }
        //加載緩存
        skuFeign.updateTypeItems(after.getType());
    }
    @Override
    public void delete(AdItems adItems) {
        //刪除緩存
        skuFeign.deleteTypeItems(adItems.getType());
    }
}

創(chuàng)建啟動類:com.javacoo.service.canal.MallCanalApplication

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients(basePackages = {"com.javacoo.service.goods.feign"})
public class MallCanalApplication {
    public static void main(String[] args) {
        SpringApplication.run(MallCanalApplication.class,args);
    }
}

一些信息

路漫漫其修遠兮,吾將上下而求索
碼云:https://gitee.com/javacoo
QQ群:164863067
作者/微信:javacoo
郵箱:xihuady@126.com
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容