九、應用級緩存
A.緩存簡介
1.先從緩存中讀取數據,如果沒有,再從慢速設備上讀取實際數據並同步到緩存
2.經常讀取的數據、頻繁訪問的數據、熱點數據、I/O瓶頸數據、計算昂貴的數據、符合5分鐘法則和局部性原理的數據都可以緩存
B.緩存命中率
1.緩存命中率=從緩存中讀取次數/【總讀取次數(從緩存中讀取次數+從慢速設備上讀取次數)】
C.緩存回收策略
1.基於空間,指緩存設置了存儲空間
2.基於容量,指緩存設置了最大大小
3.基於時間
* TTL(Time To Live)存活期,即緩存數據從創建開始直到到期的一個時間段
* TTI(Time To Idle)空閒期,即緩存數據多久沒被訪問後移除緩存的時間
4.基於Java對象引用
5.回收算法
* FIFO(First In First Out)先進先出算法
* LRU(Least Recently Used)最近最少使用算法
* LFU(Least Frequently Used)最不常用算法
D.Java緩存類型
1.堆緩存、堆外緩存、磁盤緩存、分佈式緩存、
E.應用級緩存示例
1.三個名詞
* SoR(system-of-record):記錄系統,即實際存儲原始數據的系統
* Cache:緩存,是SoR的快照數據
* 回源:即回到數據源頭獲取數據
2.Cache-Aside,即業務代碼圍繞著Cache寫,是由業務代碼直接維護緩存
* 讀場景,先從緩存獲取數據,如果沒有命中,則回源到SoR並將源數據放入緩存
* 寫場景,先將數據寫入SoR,寫入成功後立即將數據同步寫入緩存
3.Cache-As-SoR,把Cache看作SoR,所以操作都是對Cache進行,然後Cache再委託給SoR進行真實的讀寫
* read-through,業務代碼首先調用Cache,如果Cache不命中由Cache回源到SoR,而不是業務代碼(即由Cache讀SoR)
* write-through,稱為穿透寫模式/直寫模式,業務代碼首先調用Cache寫數據,然後由Cache負責寫緩存和寫SoR,而不是由業務代碼
* write-behind,也叫write-back,稱為回寫模式,是異步寫,可以實現批量寫、合併寫、延時和限流
* copy-pattern,copy-on-read(讀時複製)和copy-on-write(寫時複製)
F.性能測試
十、HTTP緩存
A.簡介
1.瀏覽器緩存是指當我們使用瀏覽器訪問一些網站頁面或者HTTP服務時,根據服務器端返回的緩存設置響應頭將響應內容緩存到瀏覽器,下次可以直接使用緩存內容或者僅需要去服務器端驗證內容是否過期即可
B.HTTP緩存
1.Last-Modified:表示文檔的最後修改時間
2.Expires:表示文檔在瀏覽器中的過期時間
3.Cache-Control,表示瀏覽器緩存控制
4.from cache,從A頁面跳轉到A頁面或者從A頁面中轉到B頁面時
5.Age,一般用於緩存代理層(如CDN),表示此內容在緩存代理層從創建到現在生存了多長時間
6.Vary,一般用於緩存代理層(如CDN),用於通知緩存服務器對於相同URL有著不同版本的響應,比如壓縮版本和非壓縮版本
7.via,一般用於代理層(如CDN),表示訪問到最終內容前經過了哪些代理層,用的什麼協議,代理層是否緩存命中等
8.ETag,用於發送到服務器端進行內容變更驗證的,而Catch-Control是用於控制緩存時間的(瀏覽器、代理層等)
C.HttpClient客戶端緩存
1.通過職責鏈模式來支持可插拔的組件結構,客戶端緩存就是通過該模式實現的
D.Nginx HTTP緩存設置
1.Nginx提供了expires、etag、if-modified-since指令來實現
E.Nginx代理層緩存
F.一些經驗
1.只緩存200狀態碼的響應,像302等要根據實際場景決定
2.有些頁面不需要強一致,可以進行幾秒的緩存
3.JS/CSS/image等一些內容緩存時間可以設置為很久
4.假設商品詳情頁異步加載的一些數據,使用last-modified進行過期控制,而服務器端做了邏輯修改,但內容還是沒有修改的,即內容的最後修改時間沒變
5.商品詳情頁異步加載的一些數據,可以考慮更長時間的緩存
6.服務器端考慮使用tmpfs內存文件系統緩存、SSD緩存,使用服務器端負載均衡算法致性哈希來提升緩存命中率
7.緩存key要合理設計
8.AB測試/個性化需求時,一般會在響應頭中添加源服務器信息,但要考慮服務器端緩存
9.為了便於查找問題,一般會在響應頭中添加源服務器信息
十一、多級緩存
A.多級緩存介紹
1.是指在整個系統架構的不同系統層級進行數據緩存,以提升訪問效率
B.如何緩存數據
1.過期與不過期
* 不過期緩存場景,對於長尾訪問的數據、大多數數據訪問頻率都很高的場景,或者是緩存空間足夠,都可以考慮不過期緩存,比如用戶、分類、商品、價格、訂單等
* 過期緩存,如採用懶加載,一般用於緩存其他系統的數據(無法訂閱變更消息,或者成本很高)、緩存空間有限、低頻熱點緩存等場景
2.維度化緩存與增量緩存:將數據進行維度化並增量更新(只更新變的部分)
3.大Value緩存:可以使用多線程實現的緩存,如Memcached來緩存大Value,Reids不合適
4.熱點緩存:一是使用更多的從緩存,二是在客戶端所有的應用/代理層本地存儲一份避免訪問遠程緩存
C.分佈式緩存與應用負載均衡
1.緩存分佈式:一般採用分片實現,即將數據分散到多個實例或多臺服務器,算法一般採用取模和一致性哈希
2.應用負載均衡:一般採用輪詢和一致性哈希
3.根據實際情況動態選擇使用哪種算法
* 負載較低時,使用一致性哈希
* 熱點請求降級一致性哈希為輪詢,或者如果請求數據有規律,則可考慮帶權重的一致性哈希
* 將熱點數據推送到接入層Nginx,直接響應給用戶
D.熱點數據與更新緩存
1.單機全量緩存+主從:所有緩存都存儲在應用本機,回源之後會把數據更新到主Redis集群,然後通過主從模式複製到其他從Redis集群,緩存的更新可以採用懶加載或者訂閱消息進行同步
2.分佈式緩存+應用本地熱點:需要在Nginx+Lua應用中進行應用緩存來減少Redis集群的訪問衝擊,即首先查詢應用本地緩存,如果命中,則直接緩存,如果沒有命中,則接著查詢Redis集群、回源到Tomcat,然後將數據緩存到應用本地
3.實時熱點發現系統:配合kafka或flume、storm等大數據工具
E.更新緩存與原子性
1.更新數據時使用更新時間戳或者版本對比
2.使用如canal訂閱數據庫binlog
3.將更新請求按照相應的規則分期到多個隊列,然後每個隊列進行單線程更新,更新時摘取最新的數據保存
4.用分佈式鎖,在更新之前獲取相關的鎖
F.緩存崩潰與快速修復
1.取模:採用主從機制來避免實例壞了的問題
2.一致性哈希:主從機制、降級機制
十二、連接池線程池詳解
1.連接池如數據庫連接池、Redis連接池、HTTP連接池,通過複用TCP連接來減少創建和釋放連接的時間來提升性能
2.線程池也是類似的,通過複用線程提升性能
3.池化可以使用Apache commons-pool 2來實現
A.數據庫連接池
1.數據庫連接池有很多實現,如C3P0、DBCP、Druid等
2.如果併發量大建議:幾個池大小設置為一樣,禁用關閉孤兒連接,禁用定時器
3.如果併發量不大建議:可以按需設置池大小,禁用關閉孤兒連接,啟用定時器(注意MySQL空閒連接8小時自動斷開)
4.要注意網絡阻塞/不穩定時的級聯效應,即有熔斷和快速失敗機制
5.等待超時應該儘可能小點(除非很有必要)
B.HttpClient連接池
1.須要注意:
* 在開啟長連接時才是真正的連接池
* JVM在停止或重啟時,記得關閉連接池釋放連接
* HttpClient是線程安全的,不要每次使用創建一個
* 如果連接池配置得比較大,則可以考慮創建多個HttpClient實例,而不是使用一個HttpClient實例
* 使用連接池時,要儘快消費響應體並釋放連接到連接池,不要保持太久
C.線程池
1.每個線程都需要一個內存棧,用於存儲如局部變量、操作棧等信息,可以通過-Xss參數來調整每個線程棧大小
2.線程池一般配合隊列一起工作,使用線程池限制併發處理任務的數量
3.當任務超過隊列大小時,通過一定的拒絕策略來處理,這樣可以保護系統免受大流量而導致崩潰
4.線程池一般有核心線程池大小和線程池最大大小配置,當線程池中的線程空閒一段時間將會被回收,而核心線程池中的線程不會被回收
5.Java提供了ExecutorService的三種實現
* ThreadPoolExecutor:標準線程池
* ScheduledThreadPoolExecutor:支持延遲任務的線程池
* ForkJoinPool:類似於ThreadPoolExecutor,但是使用work-stealing(任務竊取)模式
十三、異步併發實戰
1.聚合數據數據需要調用多個其他服務獲取數據、拼裝數據/模板,然後返回給前端,聚合數據來源主要有依賴系統/服務、緩存、數據庫等
2.系統之間的調用可以通過如HTTP接口調用(如HttpClient)、SOA服務調用(如dubbo、thrift)等實現
3.通過異步併發並不能使響應變得更快,更多是為了提升吞吐量、對請求更細粒度控制,或是通過多依賴服務併發調用降低服務響應時間
4.異步是針對CPU和IO的,當IO沒有就緒時要讓出CPU來處理其他任務,這才是異步
A.同步阻塞調用
1.即串行調用,響應時間為所有依賴服務的響應時間總和
B.異步Future
1.線程池配合Future實現,但是阻塞主請求線程,高併發時依然會造成線程數過多、CPU上下文切換
C.異步Callback
1.通過回調機制實現,即首先發出網絡請求,當網絡返回時回調相關方法
2.這種機制並不能提升性能,而是為了支撐大量併發連接或者提升吞吐量
D.異步編排CompletableFuture
1.可以對多個異步處理進行編排,實現更復雜的異步處理,其內部使用ForkJoinPool實現異步處理
E.異步Web服務實現
1.藉助Servlet3、CompletableFuture實現異步Web服務
F.請求緩存
1.一個服務需要多交查詢時(如一個商品需要重複調用多次商品接口),可以使用異步多線程查詢
E.請求合併
1.CompletableFuture必須提前構造好批量查詢,而Hystrix支持將多個單個請求黯然失色為單個批量請求,即可以按照單個命令來請求,但是,實際是以批量請求模式執行
十四、如何擴容
1.分而治之的思想來解決問題:
* 嘗試通過簡單擴容來解決
* 如果簡單擴容搞不定,就需要水平拆分和垂直拆分數據/應用來提升系統的伸縮性,即通過擴容提升系統負載能力
* 如果通過水平拆分/垂直拆分還是搞不定,那就需要根據現有系統特性,從架構層面進行重構甚至是重新設計,即推倒重來
2.對於系統設計,理想的情況下應支持線性擴容和彈性擴容
A.單體應用垂直擴容
1.如果能通過硬件快速解決,而且成本不高,應該首先通過硬件擴容來解決問題
2.硬件擴容包括升級現有服務器
B.單體應用水平擴容
1.單體應用水平擴容是通過部署更多的鏡像來實現的,應用提供統一入口,此時就需要負載均衡機制來實現
2.如果用戶會話數據分散在應用系統,就需要在負載均衡器開啟會話黏滯特性
3.如果數據庫的瓶頸是讀造成的,可以通過主從數據庫架構將讀的流量分散到更多的從服務器上
C.應用拆分
1.按照業務將一個大系統拆分為多個子系統,要進行業務代碼解耦,將功能分離到不同系統上,拆分後系統之間是物理隔離的,應用層面原來是直接進程內方法調用,現在需要改成遠程方法調用,如WebService、RMI等,SOA方向等
2.服務化後,服務提供者可以根據當前網站狀況隨時擴容,通過服務註冊中心,服務消費者不需要進行任何配置的更改,如Dubbo
3.可以使用MyCat/Corbar這種數據庫中間件提升連接數
4.所有應用只調用讀/寫服務中間件,由讀/寫服務中間件訪問數據庫,減少整體的連接數,然後通過MQ異構數據,從而不訪問有瓶頸的數據庫
5.可以將緩存/限流/防刷從各應用系統中拆出來,放到單獨系統實現,即接入層
D.數據庫拆分
1.按照業務維度進行垂直拆分,目的是解決多個表之間的IO競爭、單機容量問題等,拆分後會出現join查詢不行了,要解決跨庫join,分佈式事務等問題
2.跨庫join可以考慮通過如全局表、ES搜索等異構數據機制來實現
3.分庫分表是一種水平數據拆分,會按照如ID、用戶、時間等維度進行數據拆分,拆分算法可以是取模、哈希、區間、或者使用數據路由表等,也會導致跨庫/表join、排序分頁、自增ID、分佈式事務等問題
4.對於跨庫/表join和排序分頁,可以對所有表進行掃描然後做聚合,或者生成全局表、進行查詢維度的數據異構,再或者將數據同步到ES搜索
5.自增ID問題可以通過不同表、不同增長步長或分佈式ID生成器解決
6.分佈式事務可以考慮事務表、補償機制(執行/回滾)、TCC模式(預佔/確認/取消)、Sagas模式(拆分事務+補償機制),業務應儘量設計為最終一致性,而不是強一致性
7.對於一些特殊數據,可以考慮NoSQL,讀流量多可以考慮Redis進行數據緩存
8.部署多個Redis實例,通過Twemproxy並使用一致性哈希算法進行分片,先通過HaProxy進行Twemproxy的負載均衡,然後通過內網域名進行訪問
E.數據庫分庫分表示例
1.主要關心幾個問題:
* 是否需要在應用層做改造來支持分庫分表,即是在應用層進行支持,還是通過中間件層呢?
* 如果需要應用層做支持,那麼分庫分表的算法是什麼?
* 分庫分表後,join是否支持,排序分頁是否支持,事務是否支持
2.中間件層
* 好處是對應用透明,就像查單庫一樣去查詢中間件層,可以支持多種編程語言,可以減少應用的總數據庫連接數
* 缺點是除了維護中間件外,還要考慮中間件的HA/負載均衡等,增加了部署和維護的困難
* 開源中間件有Atlas、Cobar、Mycat等
3.分庫分表策略
* 取模:按照數值型主鍵取模來進行分庫分表,也可以按照字符串主鍵哈希取模,優點是數據熱點分散,缺點是按照非主鍵維度進行查詢時需要跨庫跨表查詢,擴容需要建立新集群並進行數據遷移
* 分區:可按照時間分區、範圍分區進行分庫分表,缺點是存在熱點,但是易於水平擴展,能避免數據遷移,也可以取模+分區組合使用
F.數據異構
1.主要按照不同查詢維度建立表結構,這樣就可以按照這種不同維度進行查詢,有查詢維度異構、聚合數據異構等
2.在數據量和訪問量雙高時使用數據異構是非常有效的,但增加了架構的複雜度,可以通過訂閱MQ或者binlog並解析實現
3.查詢維度異構:異構數據主要存儲數據之間的關係,然後通過查詢源庫查詢實際數據,有時可以通過數據冗餘存儲來減少源庫查詢或者提升查詢性能
4.聚合數據異構:將數據聚合後異構存儲到KV存儲集群(如存儲JSON),這樣只需要一次查詢就能得到所有的展示數據
G.任務系統擴容
1.Elastic-Job
十五、隊列術
1.隊列,在數據結構中是一種線性表,從一端插入數據,然後從另一端刪除數據
2.保證最終一致性,不需要強一致性,可以考慮隊列處理,需要考慮消息處理的有序性如何保證、是否能重複消費及如何保證重複消費的冪等性
3.經常使用隊列進行異步處理、系統解耦、數據同步、流量削峰、擴展性、緩衝等
A.應用場景
1.異步處理:發送郵件、積分等,緩存過期時異步更新緩存、寫日誌等,通過異步處理,可以提升主流程響應速度,而主流程/非重要處理可以集中處理,還可以將任務聚合批量處理,可以使用消息隊列/任務隊列來進行異步處理
2.系統解耦:如下單後通知生產配貨系統、發票系統等,業務不需要實時處理、不需要強一致,只需要保證最終一致性即可,可能通過消息隊列/任務隊列進行系統解耦
3.數據同步:如MySQL同步到Redis、或機房同步、主從同步等,可以考慮使用databus、canal、otter等,使用數據總線隊列進行數據同步的好處是可以保證數據修改的有序性
4.流量削峰:系統瓶頸一般在數據庫上,可以考慮使用隊列將變更請求暫時放入隊列,通過緩存+隊列暫存的方式將數據庫流量削峰,對於秒殺系統,可以使用隊列進行排隊和限流
B.緩衝隊列
1.典型的如Log4j的日誌緩衝區
2.通過緩衝區隊列可以實現批量處理、異步處理和平滑流量
C.任務隊列
1.可以將一些不需要與主線程同步執行的任務扔到任務隊列進行異步處理
2.可以實現異步處理、任務分解/聚合處理
D.消息隊列
1.ActiveMQ、Kafka、Redis等
2.使用消息隊列存儲各業務數據,其他系統根據需要訂閱即可,常見的訂閱模式是:點對點(一個消息只有一個消費者)、發佈訂閱(一個消息可以有多個消費者)
3.雙寫模式,同時寫DB和MQ,然後異構系統可以訂閱MQ進行業務處理,沒有事務保證
4.不要在事務中摻雜MQ、RPC等
5.訂閱數據庫日誌機制來實現數據庫變更捕獲,生產系統只需要單寫DB,然後通過Canal訂閱數據庫binlog實現數據庫數據變更捕獲,然後業務端訂閱Canal進行業務處理,這種方式可以保證一致性
6.可以實現異步處理、系統解耦和數據異構
E.請求隊列
1.在Web環境下對用戶請求排隊,可進行:流量控制、請求分級、請求隔離
2.一般用於前端入口
3.漏斗模型
F.數據總線隊列
1.如數據庫變更後需要同步數據到緩存,或者需要將一個機房的數據同步到另一個機房,只是數據維度的同步
2.可以保證數據的有序性
3.Canal、databusotter、kettle
G.混合隊列
H.其他隊列
1.優先級隊列:優先處理緊急任務,考慮對隊列進行分級
2.副本隊列:系統重構或上新功能時,考慮副本隊列,當業務出現問題時,可以對這些消息進行回放
3.鏡像隊列:在訂閱量達到極限時,使用鏡像隊列解決
4.隊列併發數:不是增大隊列併發連接數消費能力也隨著增加,也不會因為增加了消費服務器消費,併發能力也隨之增加,需要根據實際情況來設置合理的併發連接數
5.推送拉取:消息體內容不是越全越好,需要根據業務設計消息體,根據實際情況決定是使用推送方式(將系統需要的所有信息推送過去)還是使用拉取方式(只推送ID)
I.Disruptor+Redis隊列
J.下單系統水平可擴展架構
1.如果把訂單放入緩衝隊列,然後能迅速同步到訂單中心,就可以把下單邏輯和操作訂單邏輯分開,用戶下單隻操作緩衝表,而操作訂單隻操作訂單表
2.整體流程
* 用戶提交訂單後,調用訂單號生成服務,然後結算服務會進行一些業務處理,最後調用 下單服務提交訂單
* 下單服務將訂單寫入訂單緩衝表,下單服務和訂單緩存表可以水平擴展。寫入緩衝表成功後,將訂單寫入緩存,從而前端用戶可以查看到當前訂單。如果下單服務有問題,則可以考慮直接降級將訂單寫入訂單中心
* 接著緩衝同步Worker輪詢這些緩衝表
* 同步Wroker將訂單同步到訂單中心,如果訂單中心數據有變更,則更新訂單緩存
K.基於Canal實現數據異構
1.訂閱數據庫binlog日誌,模擬數據庫的主從同步機制,然後解析變更日誌將數據異構,也能保證數據一致性
2.可以進行訂單列表異構、商家維度異構、ES搜索異構、訂單緩存異構等
3.Canal架構
十六、構建需求響應式億級商品詳情頁
1.數據閉環,即數據的自我管理,或者說是數據都在自己的系統裡維護,不依賴於任何其他系統,包括:
* 數據異構
* 數據原子化
* 數據聚合,將多個原子數據聚合為一個大JSON數據
* 數據存儲
2.數據維度化,數據按照維度和作用進行維度化,可以分離存儲,進行更有效地存儲和使用,如:
* 商品基本信息,標題、擴展屬性、特殊屬性、圖片等
* 商品介紹信息,商品維度商家模板、商品介紹等
* 非商品維度的其他信息,包括分類信息、商家信息、店鋪信息等
* 商品維度其他信息(異步加載),價格、促銷、配送至等
3.拆分系統
4.Worker無狀態化+任務化
* 對數據異構和數據同步Worker進行無狀態化設計,這樣可以水平擴展
* 應用雖然是無狀態化的,但是配置文件還是有狀態的,每個機房一套配置,這樣每個機房只讀取當前機房數據
* 任務多隊列化,包括任務等待隊列、任務排重隊列、本地任務隊列、失敗任務隊列
* 隊列優先級化,分為普通隊列、刷數據隊列、高優先級隊列
* 任務副本隊列,當上線後業務出現問題時,修正邏輯可以回放,從而修復數據
* 在設計消息時,按照維度更新
5.異步化+併發化
* 使用消息異步化進行系統解耦合,通過消息通知變更,然後再調用相應接口獲取相關數據
* 緩存數據更新異步化,同步調用服務,但異步更新緩存
* 讓可並行任務併發化,可以併發調用聚合
* 前端服務異步化/聚合,可以對異步請求做合併
6.多級緩存化
* 瀏覽器緩存:頁面之間來回跳轉時走local cache,打開頁面時使用Last-Modified去CDN驗證是否過期
* CDN緩存:用戶去離自己最近的CDN節點拿數據
* 服務器端應用本地緩存:Nginx+Lua架構,使用HttpLuaModule模塊的shared dict做本地緩存或內存級Proxy Cache
7.動態化
* 數據獲取動態化:按維度獲取數據
* 模板渲染實時化:支持隨時變更模板需求
* 重啟應用秒級化:使用Nginx+Lua架構
* 需求上線快速化
8.彈性化:使用容器技術
9.降級開關
* 推送服務器推送降級開關,使開關集中化維護,然後通過推送機制推送到各個服務器
* 可降級的多級讀服務為前端數據集群->數據異構集群->動態服務(調用依賴系統)
10.多機房多活:應用無狀態,通過在配置文件中配置各自機房的數據集群來完成數據讀取
11.兩種壓測方案
* 線下壓測:Apache ab、Apache Jmeter,可以簡單壓測單機峰值吞吐量,但會存在熱點問題
* 線上壓測:可以使用Tcpcopy直接把線上流量導入到壓測服務器,可以壓測出機器的性能,或直接在頁面埋點,讓用戶壓測
十七、京東商品詳情頁服務閉環實踐
A.為什麼需要統一服務
1.在統一管理和監控下,出問題可以統一降級
2.可以把一些相關接口合併輸出,減少頁面的異步加載請求
3.一些前端邏輯後移到服務器端,前端只做展示,不進行邏輯處理
B.整體架構
C.一些架構思路和總結
1.兩種讀服務架構
* 讀取分佈式Redis數據架構
* 讀取本地Redis數據架構
2.本地緩存
* 使用Nginx共享字典作為本地緩存
* 採用維度化存儲緩存數據,增量獲取失效緩存數據
* 使用一致性哈希和本地緩存可以提升命中率
3.多級緩存
4.統一入口/服務閉環
D.引入Nginx接入層
1.在設計系統時需要把一些邏輯儘可能前置,以此來減輕後端核心邏輯的壓力,而且可以讓服務升級/服務降級非常方便地進行切換
2.數據校驗/過濾邏輯前置,請求進入接入層後,對參數進行校驗,如果校驗不合法,直接拒絕這次請求
3.緩存前置,緩存前置到接入層來進行熱點數據的削峰,配合一致性哈希也許可以提升緩存的命中率
4.業務邏輯前置,接入層實現一些業務邏輯,如果在高峰時出問題,可以在這一層做一些邏輯升級
5.降級開關前置
6.A/B測試,可以在Lua中根據請求的信息調用不同的服務,或者通過upstream分組
7.灰度發佈/流量切換
8.監控服務質量,記錄status、request_time、response_time來監控服務質量
9.限流,對大多數請求按照IP請求數限流,對於登錄用戶按照用戶限流,對於讀取緩存的請求不進行限流,只對打到後端系統的請求進行限流
E.前端業務邏輯後置
1.前端JS應該儘可能少寫業務邏輯和一些切換邏輯(CDN原因)
F.前端接口服務器端聚合
1.在接入層使用Lua協程機制併發調用多個相關服務,最後把這些服務進行合併
G.服務隔離
1.目的是防止因為某些服務抖動而造成整個應用內的所有服務不可用
2.應用內線程池隔離
3.部署/分組隔離,為不同的消費方提供不同的分組,相互間不影響
4.拆應用隔離:如果一個服務調用量巨大,可以把這個服務單獨拆出去做成一個應用
十八、使用OpenResty開發高性能Web應用
A.OpenResty簡介
1.Nginx設計為一個主進程多個工作進程的工作模式,每個進程是單線程來處理多個連接,而且每個工作進程採用了非阻塞I/O來處理多個連接,從而減少了線程上下文切換,實現了公認的高性能、高併發
2.Lua是一種輕量級、可嵌入式的腳本語言,可以非常容易地嵌入到其他語言中使用,提供了協程併發,即以同步調用的方式進行異步執行,從而實現併發,還提供了閉包機制,函數可以作為First Class Value進行參數傳遞,實現了標記清除垃圾收集
3.ngx_lua將Lua嵌入到Nginx中,就是接收請求、參數解析、功能處理、返回響應這幾步的API
4.場景:
* Web應用:進行一些業務邏輯處理,甚至進行耗CPU的模板渲染,一般流程包括mysql/Reids/HTTP獲取數據、業務處理、產生JSON/XML/模板渲染內容
* 接入網關:實現如數據校驗前置、緩存前置、數據過濾、API請求聚合、A/B測試、灰度發佈、降級、監控等功能
* Web防火牆:可以進行IP/URL/UserAgent/Referer黑名單、限流等功能
* 緩存服務器:可以對響應內容進行緩步,減少到達後端的請求,從而提升性能
* 其他:如靜態資源服務器、消息推送服務、縮略圖裁剪等
B.基於OpenResty的常用架構模式
1.負載均衡
2.單機閉環
* 單機閉環即所有想要的數據都能從本服務器中直接獲取,在大多數時候無須通過網絡去其他服務器獲取
* 左一,應用誰也不依賴,例如Cookie白名單功能
* 中間,讀取本機文件系統,如靜態資源合併,nginx-http-concat,nginx-lua-static-merger,頁面嵌套,Nginx自帶的SSI(Server Side Include)
* 右一,讀取的本機的Redis,或者Redis集群,或者如SSDB這種持久化存儲,或者其他存儲系統
* 都需要Wroker進行數據推送,為防止本機數據丟失,可採用
* 首先讀本機,如果沒數據,則會回源到相應的Web應用,從數據源拉取原始數據進行處理
3.分佈式閉環
* 單機閉環兩個問題:數據不一致問題;存儲瓶頸問題
* 解決數據不一致的比較好的辦法是採用主從或者分佈式集中存儲,遇到存儲瓶頸就需要進行按照業務鍵進行分片,將數據分散到多臺服務器中
4.接入網關
* 接入網關也叫接入層,即接收到流量的入口
* 核心接入Nginx功能:動態負載均衡(balancer_by_lua、upsync)、防DDoS攻擊限流、非法請求過濾、請求聚合、請求頭過濾、緩存服務(Nginx Proxy Cache)
* 業務Nginx功能:緩存(Shared Dict/Nginx Proxy Cache->Redis->回源)、業務邏輯、細粒度限流(lua-resty-limit-traffic)、降級、A/B測試和灰度發佈、服務質量監控
5.Web應用
* 指頁面模板渲染類型應用或者API服務類型應用
C.如果使用OpenResty開發Web應用
D.基於OpenResty的常用功能總結
1.適合開發業務邏輯單一、核心代碼行數較少的應用,不適合業務邏輯複雜、功能繁多的業務型或者企業級應用
2.包括:動態負載均衡、防火牆(DDoS、IP/URL/UserAgent/Referer黑名單、防盜鏈等)、限流、降級、A/B測試和灰度發佈、多級緩存模式、服務器端請求聚合、服務質量監控
E.一些問題
1.在開發Nginx應用時,使用UTF-8編碼
2.GBK轉碼解碼時,應使用GB18030
3.cjson庫對於像\\uab1這種錯誤的unicode轉碼會失敗,可以使用純Lua編寫的dkjson
4.社區版Nginx不支持upstream的域名動態解析,可以考慮proxy_pass,然後配合resolver來實現,或者在Lua中進行HTTP調用,如果DNS遇到性能瓶頸,可以考慮在本機部署如dnsmasq來緩存,或者考慮使用balancer_by_lua功能實現動態upstream
5.為響應添加處理服務器IP的響應頭,方便定位問題
6.根據業務設置合理的超時時間
7.運行CDN的業務,發生錯誤時,不要給返回的500/503/302/301等非正常響應設置緩存
十九、應用數據靜態化架構高性能單頁Web應用
A.整體架構
1.靜態化頁面的方案:直接將生成的靜態頁推送到相關服務器上即可,需要考慮文件操作的原子化問題
2.動態化方案:CMS系統、控制系統、前端展示系統
3.CMS系統
* 模板動態在CMS系統中維護
* 原始數據存儲到“元數據存儲MySQL”中即可
* 提供發佈到“發佈數據存儲Redis”的控制,將CMS系統中的原始數據和模板數據組裝成聚合數據(JSON存儲)同步到“發佈數據存儲Redis”
4.前端展示系統
* 獲取URL,使用URL作為Key從本機“發佈數據存儲Redis”獲取數據
* 如果沒有數據或者異常,則從主“發佈數據存儲Redis”獲取
* 如果也發生異常,直接調用CMS系統暴露的API,直接從元數據存儲MySQL中獲取數據
5.控制系統
* 版本降級,使用URL和當前版本的字段即可
* 灰度發佈,控制哪些URL需要灰度發佈
B.數據和模板動態化
1.將數據和模板都進行動態化存儲,這樣可以在CMS進行數據和模板的變更,實現前端和後端開發人員的分離
2.模板和數據可以是一對多的關係
C.多版本機制
1.預發佈版本,更容易讓測試人員在實際環境中進行驗證
2.灰度版本,只需要簡單的開關控制,就可以進行A/B測試
3.正式版本,存儲多個歷史正式版本
D.異常問題
1.本機從“發佈數據存儲Redis”和主"發佈數據存儲Redis"都不能用了,可以直接調用CMS系統暴露的HTTP服務,直接從元數據存儲MySQL獲取數據
2.數據和模板獲取到了,但是渲染模板出錯了,使用上一個版本的數據進行渲染
3.數據和模板都沒問題,但是因為一些疏忽,渲染出來的頁面錯亂了,或者有些區域出現了空白,可以根據自己的場景定義異常掃描庫,發警告給相關人員,並自動降級到上一個版本
二十、使用OpenResty開發Web服務
A.單DB架構
1.DB+Cache/數據庫讀寫分離架構
2.OpenResty+Local Redis+MySQL集群架構
3.OpenResty+Redis集群+MySQL集群架構
4.使用Twemproxy集群分片
二十一、使用OpenResty開發商品詳情頁
閱讀更多 ZyBlog 的文章