高可用并發(fā)系統(tǒng)搭建

分布式系統(tǒng)搭建-圖.png

本篇是讀《億級流量網(wǎng)站架構(gòu)核心技術(shù)》的一些總結(jié);可以作為在實際項目搭建過程中架構(gòu)核心點實施的擴展發(fā)散或是作為一個項目架構(gòu)的參考

限流

限流算法

令牌桶

  • 固定速率生成令牌

  • 桶滿時新加的令牌丟棄

  • 批量獲取的時候,如果令牌數(shù)不夠,丟棄請求或緩沖區(qū)等待

  • -可以應(yīng)對請求量突發(fā)增加,Guava 提供RateLimiter實現(xiàn)

漏桶

  • 漏桶為容量,常量流出請求,容量可以任意速率填滿,如果溢出則丟棄

  • -可以使流量平滑過度

計數(shù)器

  • 超出設(shè)定的并發(fā)閾值,則進行限流

    • 總訪問數(shù)限流(秒殺)

      • AtomicLong

      • Semaphore

    • 時間段限流

      • 對時間進行hash,做一個緩存計數(shù),設(shè)置超時

分布式限流方案

  • Redis + Lua 或 Nginx + Lua

超時、重試機制

讀服務(wù)

天然適合重試

寫服務(wù)

需要保證服務(wù)的冪等性

網(wǎng)絡(luò)連接超時

代理層超時與重試

  • Haproxy、Nginx、Twemproxy(Redis分片代理)

  • Nginx

    • 代理超時設(shè)置

    • 客戶端超時

    • DNS解析超時

Web容器超時

  • Tomcat、Jetty

中間件客戶端超時與重試

  • Dubbo、MQ、HttpclientHttpclient

數(shù)據(jù)庫客戶端超時

  • MySql、Oracle

業(yè)務(wù)超時

  • 訂單取消、限時活動

  • Future#get

異步并發(fā)

異步是針對CPU和IO的,是指當(dāng)IO沒有就緒時要讓出CPU來處理其他任務(wù)。Java中真正實現(xiàn)異步化是非常困難的,大多數(shù)場景并不是真正的異步化

異步并發(fā)并不能使響應(yīng)變得更快,更多是為了提升吞吐量、對請求更細粒度的控制

同步阻塞調(diào)用

  • 即串行調(diào)用,響應(yīng)時間為所有依賴服務(wù)的響應(yīng)時間總和

異步Future

  • 并發(fā)發(fā)出N個請求,等待最慢的一個返回

異步Callback

  • 通過回調(diào)機制實現(xiàn),提升吞量

異步編排CompletableFuture

  • JDK8或以上,內(nèi)部使用ForkJoinPool實現(xiàn)異步處理

Hystrix

熔斷、降級

  • HystrixCircuitBreaker#allowRequest實現(xiàn)

采樣統(tǒng)計(內(nèi)存中存儲)

計數(shù)統(tǒng)計

  • BucketedCounterStream,時間滾轉(zhuǎn)采樣分組

最大并發(fā)統(tǒng)計

  • RollingConcurrencyStream

延時百分比統(tǒng)計

  • RollingDistributionStream、HdrHistogram

Turbine + Hystrix-Dashboard實現(xiàn)可視化統(tǒng)計數(shù)據(jù)

請求合并

  • 支持將多個單請求轉(zhuǎn)換為一個批量請求;調(diào)用過程中使用#queue。批量口查詢結(jié)果會回調(diào)返回到相應(yīng)的單個請求口

壓測與預(yù)案

系統(tǒng)壓測

線下壓測

JMeter、Apache ab

  • 仿真度不高,數(shù)據(jù)只能作為參考

全鏈路壓測

線上壓測

數(shù)據(jù)仿真度

  • 仿真壓測

    • 程序模擬請求
  • 引流壓測

    • 可以通過TCPCopy復(fù)制線上流量進行壓測

讀、寫

  • 讀壓測

    • 商品的詳情,價格
  • 寫壓測

    • 下單;盡量回滾或刪除寫入數(shù)據(jù)
  • 混合壓測

業(yè)務(wù)服務(wù)

  • 隔離集群壓測

    • 可以先將需要壓測的集群從線上摘除,再將線上流量引流到該集群
  • 線上集群壓測

    • 直接壓測線上集群,風(fēng)險高

連接池、線程池

數(shù)據(jù)庫連接池

C3P0

DBCP

Druid

HikariCP

切記要設(shè)置destroy-method=“close”,否則多次重啟tomcat后舊的數(shù)據(jù)庫連接池的連接不會釋放

建議需要有熔斷和快速失敗機制

HttpClient連接池

  • 3.x、4.x、5.x API完全不兼容

  • 只有在開啟長連接時才是真正的連接池,如果是短連接,只作為一個信號量來限制總請求數(shù)

  • HttpClient是線程安全的,不要每次使用創(chuàng)建一個

  • 使用連接池時,要盡快消費響應(yīng)體并釋入連接到連接池

連接復(fù)用條件還很苛刻,使用的時候要格外注意

JVM設(shè)置線程棧大小

  • -Xss128k

線程池大小配置

  • 利特爾法則

  • 實際業(yè)務(wù)壓測

java實現(xiàn)

  • ThreadPoolExecutor

    • 標(biāo)準(zhǔn)線程池
  • ScheduledThreadPoolExecutor

    • 支持延遲任務(wù)的線程池

ForkJoinPool

  • 使用work-stealing算法,使得空閑線程可以竊取其它隊列的任務(wù)去處理

隊列術(shù)

異步處理

系統(tǒng)解耦

  • 不需要實時處理、不需要強一致

數(shù)據(jù)同步

流量削峰

  • 緩存+隊列將數(shù)據(jù)流量流削峰;秒殺系統(tǒng)下單服務(wù)的應(yīng)用場景

緩沖隊列

  • 使用緩沖隊列應(yīng)對突發(fā)流量時,并不能使處理速度變快,而是使處理速度變平滑

任務(wù)隊列

  • 不需要與主線程同步執(zhí)行的任務(wù)扔到任務(wù)隊列進行異步處理

消息隊列

訂閱模式

  • 點對點

  • 發(fā)布訂閱

雙寫模式

  • DB寫入和MQ發(fā)送在同一個線程處理;保證數(shù)據(jù)最終一致

單寫DB

  • 可以訂閱數(shù)據(jù)庫日志機制進行業(yè)務(wù)處理;不存在數(shù)據(jù)不一致情況

通過消息隊列可以實現(xiàn)異步處理、系統(tǒng)解耦和數(shù)據(jù)異構(gòu)

請求隊列

  • 可以實現(xiàn)流量控制、請求分級、請求隔離;提高系統(tǒng)的可用性,一般用于前端接入層

數(shù)據(jù)總線隊列

  • 使用場景只是數(shù)據(jù)維度的同步,阿里的otter,全量離線數(shù)據(jù)同步 kettle

混合隊列

  • 應(yīng)用層按照不同維度發(fā)送MQ,下游應(yīng)用接收到該消息后會將其放入Redis中,使用RedisList來存儲這些任務(wù),消息被消費后再次發(fā)送MQ出去;使用Redis隊列的主要原因是想提升消息堆積能力和并發(fā)處理能力

分布式服務(wù)隔離機制

線程

  • 項目折分服務(wù)化

進程

  • 負載均衡

集群

  • 對服務(wù)化分組

讀、寫隔離

動、靜隔離

  • 路由服務(wù)接口,靜態(tài)js/css路由CDN

爬蟲隔離

  • user-agent路由方案

熱點隔離

  • 秒殺、搶購系統(tǒng)單獨部署

資源隔離

  • 硬件資源

構(gòu)建需求響應(yīng)式

單品頁技術(shù)架構(gòu)發(fā)展

1.0

  • DB+memcached

2.0

  • 靜態(tài)化HTML

3.0

  • 多維度數(shù)據(jù)異構(gòu)

詳情頁架構(gòu)設(shè)計原則

數(shù)據(jù)閉環(huán)

  • 數(shù)據(jù)都在自己的系統(tǒng)里維護、自我管理,不依賴于任何其他系統(tǒng)。包括:數(shù)據(jù)異構(gòu)、數(shù)據(jù)原子化、數(shù)據(jù)聚合、數(shù)據(jù)存儲

數(shù)據(jù)維度化

  • 按照商品自身維度和作用進行維度化,進行更有效地存儲和使用

worker無狀態(tài)化+任務(wù)化

  • 數(shù)據(jù)異構(gòu)和數(shù)據(jù)同步worker無狀態(tài)設(shè)計;任務(wù)的多隊列化;任務(wù)副本隊列用來執(zhí)行修正邏輯回放

異步化+并發(fā)化

多級緩存化

動態(tài)化

  • 數(shù)據(jù)獲取動態(tài)化

  • 模板渲染實時化

  • 重啟應(yīng)用秒級化

彈性化

  • 自動擴容

降級開關(guān)

擴容

單體應(yīng)用垂直擴容

  • 通過硬件來擴容

單體應(yīng)用水平擴容

  • 部署更多的鏡像

應(yīng)用拆分

  • 單體應(yīng)用垂直和水平擴容都不能解決問題的時候,需要采取應(yīng)用拆分

數(shù)據(jù)拆分

單庫查詢要改為跨庫查詢

  • 全局表、ES搜索

讀、寫分離

水平拆分;分庫分表

  • 策略

    • 取模

      • 可以按照主鍵哈希取模進行分庫分表

      • 擴容:成倍增量,每一個舊節(jié)點與之對應(yīng)一個或一組哈希后的新節(jié)點,只需要把對應(yīng)分組的數(shù)據(jù)復(fù)制遷移就可以了

      • 優(yōu)點:數(shù)據(jù)熱點分散

      • 缺點:按照非主鍵維度進行查詢時需要跨庫/跨表查詢

    • 分區(qū)

      • 時間段分區(qū)

        • 例:一個月一張表,一年一個庫
      • 數(shù)據(jù)量分區(qū)

        • 每2000萬記錄一個表
      • 優(yōu)點:易于水平擴展

      • 缺點:存在熱點問題

    • 路由表

  • 注意點

    • 應(yīng)用層支持,還是通過中間件層

    • 分庫分表的算法是什么

    • join是否支持,排序分頁是否支持,事務(wù)是否支持

    • 中間件層的實現(xiàn)有奇虎360的Atlas、阿里的Cobar、Mycat;對oracle支持低

    • 應(yīng)用層的實現(xiàn)有當(dāng)當(dāng)?shù)膕harding-jdbc,阿里的cobar-client;對oracle支持低

數(shù)據(jù)異構(gòu)

  • 查詢維度異構(gòu)

  • 聚合據(jù)異構(gòu)

垂直拆分;寬表拆子表

緩存的應(yīng)用

  • 數(shù)據(jù)最終一致

  • 緩存的同步寫

    • 性能差,數(shù)據(jù)一致性好
  • 緩存的異步寫

    • 性能好,數(shù)據(jù)一致性有延時,可以把用戶指到同一集群,避免多次刷新看到的數(shù)據(jù)不一致

任務(wù)系統(tǒng)擴容

簡單任務(wù)

  • 可以使用Thread死循環(huán);周期性的任務(wù)可以使用Timer

分布式任務(wù)

  • 可以選擇Quartz集群版、tbschedule、elastic-job

回滾機制

事務(wù)回滾

分布式事務(wù)

  • 最終一致性

    • 事務(wù)表

    • 消息隊列

    • 補償機制

    • TCC模式

    • Sagas模式

  • -記錄事務(wù)日志

  • -批處理任務(wù)核驗

代碼庫回滾

SVN

  • 集中版本控制系統(tǒng)

GIT

  • 分布式版本控制系統(tǒng)

部署版本回滾

部署版本化

  • 留存歷史發(fā)布包,以備回全量主機回滾

小版本增量發(fā)布

  • 部分主機部署驗證后,全量主機發(fā)布

大版本灰度發(fā)布

  • 新、老版本共存

架構(gòu)升級并發(fā)發(fā)布

  • 新、老版本共存

例:新、老共存遷移部署比例1%->10%->50%->100%

靜態(tài)資源版本回滾

數(shù)據(jù)版本回滾

全量回滾

  • 保存的數(shù)據(jù)多,回滾方便

增量回滾

  • 保存數(shù)據(jù)少,需要逐級回溯

應(yīng)用級緩存

五分鐘法則

局布性原理

緩存命中率

  • 可以做為評定緩存優(yōu)劣的標(biāo)準(zhǔn)

回收策略

基于空間

  • 達到設(shè)定空間上限時,指定策略刪除

基于容量

  • 達到設(shè)定記錄條數(shù)上限時,指定策略刪除

基于時間

  • TTL(Time To Live)

    • 存活期,超過設(shè)定時間段內(nèi)的數(shù)據(jù)全部視為過期
  • TTI(Time To Idle)

    • 空閑時期,數(shù)據(jù)在設(shè)定時間內(nèi)沒有被訪問過視為過期

基于Java對象引用

  • JVM回收機制

回收算法

FIFO(First In First Out)

  • 先進先出算法

LRU(Least Recently Used)

  • 最近最少使用算法,使用時間距離當(dāng)前時間最長的數(shù)據(jù)將被移除;Guava Cache、Ehcache

LFU(Least Frequently Used)

  • 最不常用算法,約定時間段內(nèi)使用頻率最少的數(shù)據(jù)將被移除

java緩存類型

堆緩存

  • 優(yōu)點:不需要序列化

  • 缺點:GC暫停時間變長

  • 重啟需要重新加截

  • 實現(xiàn):Guava Cache、Ehcache 3.x、MapDB

堆外緩存

  • 優(yōu)點:可以有更大的緩存空間,GC時間短

  • 缺點:需要序列化,讀、寫相對堆緩存慢很多

  • 重啟需要重新加載

  • 實現(xiàn):Ehcache 3.x、MapDB

磁盤緩存

  • 重啟不需要重新加載

分布式緩存

  • 解決了單機容量問題。但需要注意數(shù)據(jù)一致性問題

單機方案:最熱的數(shù)據(jù)到堆緩存,相對熱的數(shù)據(jù)到堆外緩存,不熱的數(shù)據(jù)到磁盤緩
集群方案:存儲最熱的數(shù)據(jù)到堆緩存,相對熱的數(shù)據(jù)到堆外緩存,全量數(shù)據(jù)到分布式緩存

Cache-Aside方式

  • 理解:業(yè)務(wù)代碼圍繞著Cache寫,由業(yè)務(wù)代碼直接維護緩存

  • 并發(fā)更新問題

    • 用戶維度的緩存發(fā)生并發(fā)概率很小,可以不考慮

    • 如商品數(shù)據(jù)更新,可以使用canal訂閱binlog來進行增量更新分布式緩存

Cache-As-SoR方式

  • 概念:所有操作都只對Cache進行操作,Cache看作為SoR,然后Cache再委托給SoR進行真實的讀、寫。

  • read-through

    • cache不命中需要回源SoR

    • Guava Cache、Ehcache 3.x支持,需要配置一個CacheLoader

  • write-through

    • 穿透寫模式,由cache直接寫入SoR

    • Ehcache支持,需要配置CacheLoaderWriter

  • write-behind

    • 回寫模式,不同于write-through的同步寫,轉(zhuǎn)為異步寫

    • Ehcache支付,需要配置CacheLoaderWriter

Guava Cache、Ehcache中的堆緩存都是基于引用,在存儲到SoR時應(yīng)該復(fù)制對象

性能測試

  • 可以使用JMH

HTTP緩存

基于瀏覽器的last-modified

  • spring MVC 請求響應(yīng)設(shè)置last-modified,expires,cache-control,http-status = 304

  • nginx 也提拱了expires、etag、if-modified-since指令來實現(xiàn)緩存控制

HttpClient客戶端緩存

多級緩存

搭建層級

接入Nginx將請求負載均衡到應(yīng)用Nginx

  • 輪詢算法

    • 使用服務(wù)器的請求更加均衡
  • 一致性哈希

    • 提升應(yīng)用的緩存命中率

應(yīng)用Nginx讀取本地緩存

  • 提升吞吐量,降低后端壓力

  • 解決熱點問題

讀取分布式緩存

  • 如果應(yīng)用Nginx沒有命中本地緩存

  • 如果是讀寫分離的集群,可以再嘗試讀取一次主集群

  • 減少回源訪問率

回源Tomcat集群

  • 如果分存式緩存也沒有命中

  • 可以使用輪詢、一致性哈希算法回源Tomcat

Tomcat讀取本地緩存

  • 添加一層本地緩存,目的在于預(yù)防緩存崩潰,和快速修復(fù)緩存

回源DB讀取數(shù)據(jù)

  • 如果所有緩存都未命中,則回源數(shù)據(jù)源讀取數(shù)據(jù)

數(shù)據(jù)緩存

時限

  • 設(shè)定過期

    • 常見用法,讀取緩存未命中,回源數(shù)據(jù)源,異步存入緩存,設(shè)定過期時間;需要考慮數(shù)據(jù)一至性問題
  • 設(shè)定不過期

    • 長尾數(shù)據(jù),比如用戶、分類、商品、價格、訂單等;可以考慮使用LRU機制驅(qū)逐緩存數(shù)據(jù)

維度化緩存與增量緩存

  • 比如一個商品會由多維度拼裝而成,如果全量更新成本會很高,這時可以建立多維度緩存,增量更新某一維度

大Value緩存

  • 首先要盡量避免大Value來緩存數(shù)據(jù);如果當(dāng)前場景必須使用,則可以考慮memcached的多線程來實現(xiàn),或者對Value進行壓縮,或者還可以拆分多個小Value,由業(yè)務(wù)端來聚合

熱點緩存

  • 可以在客戶端做一份本地緩存,避免拉取遠程數(shù)量訪問量太大導(dǎo)致遠程緩存請求過多、負載過高或帶寬過高等問題

  • 實現(xiàn)架構(gòu)

    • 單機全量緩存+主從

      • 所有緩存都存儲在應(yīng)用本機,如果緩存未命中,回源數(shù)據(jù)更新到主緩存服務(wù)器,再同步在從緩存服務(wù)器中;回源更新可以采用懶加載或訂閱消息
    • 分布式緩存+應(yīng)用本地?zé)狳c

  • 更新緩存與原子性

    • 使用時間戳或者版本對比,如果使用的是Redis,則可以利用其單線程機制進行原子化更新

    • 使用如canal訂閱數(shù)據(jù)庫binlog

緩存的分步式

  • 將數(shù)據(jù)分散在多個實例或多臺服務(wù)器中;如果使用redis可以考慮redis-cluster

緩存崩潰與快速修復(fù)

  • 主從機制,做好冗余

  • 部分用戶降級,慢慢減少降級量;通過worker預(yù)熱緩存數(shù)據(jù)

寫緩存操作不要放在事務(wù)中,防止寫操作異常引起整個事務(wù)回滾

?著作權(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)容