讀書筆記:《億級流量網(wǎng)站架構(gòu)核心技術(shù) -- 跟開濤學(xué)搭建高可用高并發(fā)系統(tǒng)》
from 《億級流量網(wǎng)站架構(gòu)核心技術(shù) – 跟開濤學(xué)搭建高可用高并發(fā)系統(tǒng)》
概述
一個好的設(shè)計要做到,解決現(xiàn)有的需求和問題,把控實現(xiàn)和進度風(fēng)險,預(yù)測和規(guī)劃未來,不要過度設(shè)計,從迭代中演進和完善。
在設(shè)計系統(tǒng)時,應(yīng)多思考墨菲定律:
1、任何事情都沒有表面看起來那么簡單。
2、所有的事都會比你預(yù)計的時間長。
3、可能出錯的事總會出錯。
在系統(tǒng)劃分時,也要思考康威定律:
1、系統(tǒng)架構(gòu)是公司組織架構(gòu)的反映。
2、應(yīng)該按照業(yè)務(wù)閉環(huán)進行系統(tǒng)拆分/組織架構(gòu)劃分,實現(xiàn)閉環(huán)/高內(nèi)聚/松耦合,減少溝通成本。
3、如果溝通出現(xiàn)問題,那么就應(yīng)該考慮進行系統(tǒng)和組織架構(gòu)的調(diào)整。
4、在合適的時機進行系統(tǒng)拆分,不要一開始就把系統(tǒng)/服務(wù)拆的非常細,雖然閉環(huán),但是維護成本高。
高并發(fā)原則
1、無狀態(tài)。應(yīng)用無狀態(tài),配置文件有狀態(tài)。(可以用在我的畢設(shè)指導(dǎo)里)
你可以輕易修改配置文件,但是應(yīng)用發(fā)布了,就是發(fā)布了。
2、拆分。在系統(tǒng)設(shè)計初期,是做一個大而全的系統(tǒng),還是按功能模塊拆分系統(tǒng),這個需要根據(jù)環(huán)境進行權(quán)衡。
有很多維度可以考慮,比方說:系統(tǒng)維度、功能維度、讀寫維度、and so on。
我jio的吧,有資源就拆,不然就先憋著吧。
3、服務(wù)化(不知道怎么概括那段話,經(jīng)驗不足)
4、消息隊列。基本概念就不說啦。使用消息隊列時,還要注意處理生產(chǎn)消息失敗,以及消息重復(fù)接收時的場景。對于不能容忍生產(chǎn)失敗的業(yè)務(wù)場景來說,一定要做好后續(xù)的數(shù)據(jù)處理工作。對于消息重復(fù)的問題,特別是一些分布式消息隊列,處于對性能和開銷的考慮,在一些場景下會發(fā)生消息重復(fù)接收,需要在業(yè)務(wù)層面進行防重處理。
大流量緩沖:
扣減庫存設(shè)計(正打算這樣干)
訂單交易系統(tǒng)
數(shù)據(jù)校對:
在使用了消息異步機制的場景下,可能存在消息的丟失,需要考慮進行數(shù)據(jù)校對和修正來保證數(shù)據(jù)的一致性和完整性。可以通過掃描原始表,通過對業(yè)務(wù)數(shù)據(jù)進行校對,有問題的要進行補償,掃描周期根據(jù)實際場景進行定義。
5、數(shù)據(jù)異構(gòu)與數(shù)據(jù)閉環(huán)
6、緩存銀彈
瀏覽器緩存、APP客戶端緩存、CDN緩存、接入層緩存、應(yīng)用層緩存、分布式緩存
7、并發(fā)化
高可用原則
1、降級(在我的畢設(shè)后續(xù)版本迭代中會出現(xiàn),不過之前不是很明朗具體要怎么做)。
對于高可用服務(wù),很重要的一個設(shè)計就是降級開關(guān)。
2、限流(這個也知道要做,也知道要用什么做,但是目前也是不知道要怎么做,還沒去研究)
1、對于惡意請求流量只訪問到cache
2、對于穿透到后端應(yīng)用的流量可以考慮使用 Nginx 的 limit 模塊處理
3、對于惡意 IP 可以使用 nginx deny 進行屏蔽
不過要怎么區(qū)分惡意流量呢?是在一定的時間內(nèi)請求過于頻繁嗎?或者是爬蟲?或者二者都是,加上一些其他的未知的。
那就反過來看,只放過善意流量。
3、切流量
這個目前我會用 nginx 做故障服務(wù)器下線,切換備胎上線。
4、可回滾
業(yè)務(wù)設(shè)計原則
防重設(shè)計(流水號 + 滑窗)、冪等設(shè)計、流程可定義(模板方法模式)、狀態(tài)與狀態(tài)機(待付款、待發(fā)貨、已發(fā)貨、完成)(取消、退款)等,要考慮是否要使用狀態(tài)機來驅(qū)動狀態(tài)的變更和后續(xù)流程節(jié)點操作,尤其是當(dāng)狀態(tài)很多的時候。還要考慮并發(fā)下的狀態(tài)修改問題。
文檔和注釋
在一個系統(tǒng)發(fā)展的一開始就應(yīng)該有文檔庫(設(shè)計架構(gòu)、設(shè)計思想、數(shù)據(jù)字典、業(yè)務(wù)流程、現(xiàn)有問題),業(yè)務(wù)代碼和特殊需求都要有注釋、
包括代碼和人員都應(yīng)該有備份。
代碼備份就不用多說了。人員備份可以參考一下結(jié)對編程。既能提高效率,而且即使其中一名離職了也不會出現(xiàn)新人接收之后手忙腳亂事故頻發(fā)的狀況。
隔離術(shù)
線程池隔離
進程隔離
集群隔離
機房隔離
讀寫隔離
動靜隔離
爬蟲隔離
熱點隔離
資源隔離
工具:
Hystrix
servlet3
這些目前先知道,要系統(tǒng)學(xué)習(xí)之后才能使用
限流術(shù)
在壓測時我們可以找出每個系統(tǒng)的處理峰值,然后通過設(shè)定峰值閾值,來防止當(dāng)系統(tǒng)過載時,通過拒絕處理過載的請求來保障系統(tǒng)可用。
限流需要評估好,不然會導(dǎo)致正常的流量出現(xiàn)異常,被用戶投訴。
限流算法
常見的限流算法有:令牌桶。漏桶。計數(shù)器也可以用來進行簡單粗暴限流實現(xiàn)。
令牌桶算法
存放固定容量令牌的桶,按照固定的速率往桶里添加令牌。
1、按照一定速率往桶里添加令牌。
2、桶里最多放m個令牌。
3、當(dāng)一個n個字節(jié)大小的數(shù)據(jù)包到達,將從桶中刪除n個令牌,接著數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)上。
4、如果桶中的令牌不足n個,則不會刪除令牌,該數(shù)據(jù)包要么丟棄,要么在緩沖區(qū)等待。
漏桶算法
1、一個固定容量的漏桶,按照常量速率流出水滴。
2、如果桶是空的,則不需流出水滴。
3、可以以任意速率流入水滴到漏桶。
4、如果流入的水滴超出了桶的容量,則流入的水滴溢出(被丟棄),而漏桶容量不變。
應(yīng)用級限流
1、限制 總并發(fā)/連接/請求數(shù)
對于一個應(yīng)用系統(tǒng)來說,一定會有極限并發(fā)/請求數(shù)。如果超了閾值,則系統(tǒng)就會不響應(yīng)用戶請求或響應(yīng)的非常慢,因此我們最好進行過載保護,以防止大量請求涌入擊垮系統(tǒng)。
2、限流總資源數(shù)
3、限流某個接口的總并發(fā)/請求數(shù)
4、限流某個接口的時間窗請求數(shù)
5、平滑限流某個接口的請求數(shù)(令牌桶和漏桶)
分布式限流
redis + lua
local key = KEY[1] --限流 KEY local limit = tonumber(ARGV[1]) --限流大小 local current = tonumber(redis.call('get',key) or "0") --請求數(shù)加1 if current+1 > limit then --超出限流大小 return 0 else then --請求數(shù)+1,并設(shè)置2秒過期 redis,call("INCRBY",key,"1") redis.call("expire",key,"2") return 1 end
Java中判斷是否需要限流的代碼:
public static boolean acquire() throws Exception{ string luaScript = Files.toString(new File("limit.lua"),Charset,defaultCharset()); Jedis jedis = new Jedis("127.0.0.1",6379); String key = "ip:" + System.currentTimeMillis()/1000; String limit = "3"; return (long)jedis.eval(luaScript,Lists.newArrayList(key),Lists.newArrayList(limit)) }
nginx + lua 實現(xiàn)
local locks = require "resty.lock" local function acquire() local lock = locks:new("locks") local elapsed,err = lock:lock("limit_key") --互斥鎖,實際使用的時候要考慮獲取鎖的超時問題 local limit_counter = ngx.shared.limit_counter --計數(shù)器 local key = "ip:" ..os.time() local limit = 5 -- 限流大小 local current = limit_counter:get(key) if current ~= nil and current + 1 > limit then --如果超出限流大小 lock:unlock() return 0 end if current == nil then limit_counter:set(key,1,1) --第一次需要設(shè)置過期時間,設(shè)置key值為1,過期時間為1秒 else limit_counter:incr(key,1) --第二次開始加1 end lock:unlock() return 1 end ngx.print(acquire())
使用時需要定義兩個共享字典:
http{ ··· lua_shared_dict locks 10m; lua_shared_dict limit_counter 10m; }
接入層限流
接入層通常指流量的入口,該層的主要目的有:負載均衡、非法請求過濾、請求聚合、緩存、降級、限流、A/B測試、服務(wù)質(zhì)量監(jiān)控等。
這個在我的畢設(shè)后面的版本需要系統(tǒng)學(xué)習(xí)nginx的時候會補充。目前強行看用處沒那么大。
超時與重試機制
在實際開發(fā)過程中,有太多故障是因為沒有設(shè)置超時或者設(shè)置的不對而造成的(想想我好像也根本沒有把超時當(dāng)一回事兒過,項目里的Timestamp模塊從來就是個擺設(shè))。
如果應(yīng)用不設(shè)置超時,則可能乎導(dǎo)致請求響應(yīng)慢,慢請求積累導(dǎo)致連鎖反應(yīng),甚至造成應(yīng)用雪崩。
對于重試,寫服務(wù)大多不能重試,重試次數(shù)太多會導(dǎo)致多倍請求流量,后果可能是災(zāi)難性的。
因此,務(wù)必設(shè)置合理的重試機制,并且應(yīng)該和熔斷、快速失敗機制配合。
壓測與預(yù)案
讀了《重構(gòu)》之后,我就一直在做這件事情。但是自認為沒有做的很好、
系統(tǒng)壓測
壓測之前要有壓測方案(如壓測接口、并發(fā)量、壓測策略(突發(fā)、逐步加壓、并發(fā)量)、壓測指標(biāo)(機器負載、QPS/TPS、響應(yīng)時間)),之后要產(chǎn)出壓測報告(壓測方案、機器負載、QPS/TPS、響應(yīng)時間(平均、最小、最大)、成功率、相關(guān)參數(shù) 等),最后根據(jù)壓測報告分析的結(jié)果進行系統(tǒng)優(yōu)化和容災(zāi)。
線下壓測的環(huán)境和線上壓測完全不一樣,仿真度也不高,很難進行全鏈路壓測,適合組件級的壓測,數(shù)據(jù)只能作為參考。
線上壓測的方式非常多:讀壓測、寫壓測、混合壓測、仿真壓測、隔離集群壓測、單機壓測、離散數(shù)據(jù)壓測、全鏈路壓測等。
系統(tǒng)優(yōu)化
在進行系統(tǒng)優(yōu)化時,要進行代碼走查,發(fā)現(xiàn)不合理的參數(shù)配置。
在系統(tǒng)壓測中進行慢查詢排查,包括redis、mysql等。
在應(yīng)用系統(tǒng)擴容方面,可以根據(jù)往年的流量來進行評估是否需要擴容等。
擴容之后還要預(yù)留一些機器以應(yīng)對突發(fā)狀況,在擴容上盡量支持快速擴容(上云),從而完成出現(xiàn)突發(fā)狀況可以及時擴容。
做好容災(zāi)。
分布式 網(wǎng)站 自動化測試
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。