學習一個知識之前,我覺得比較好的方式是先理解它的來龍去脈:即這個知識產生的過程,它解決了什么問題,它是怎么樣解決的,還有它引入了哪些新的問題(沒有銀彈),這樣我們才能比較好的抓到它的脈絡和關鍵點,不會一開始就迷失在細節(jié)中。
所以,在學習分布式系統(tǒng)之前,我們需要解決的第一個問題是:分布式系統(tǒng)解決了什么問題?
第一個是單機性能瓶頸導致的成本問題,由于摩爾定律失效,廉價 PC 機性能的瓶頸無法繼續(xù)突破,小型機和大型機能提高更高的單機性能,但是成本太大高,一般的公司很難承受;
第二個是用戶量和數(shù)據(jù)量爆炸性的增大導致的成本問題,進入互聯(lián)網(wǎng)時代,用戶量爆炸性的增大,用戶產生的數(shù)據(jù)量也在爆炸性的增大,但是單個用戶或者單條數(shù)據(jù)的價值其實比軟件時代(比如銀行用戶)的價值是只低不高,所以必須尋找更經(jīng)濟的方案;
第三個是業(yè)務高可用的要求,對于互聯(lián)網(wǎng)的產品來說,都要求 7 * 24 小時提供服務,無法容忍停止服務等故障,而要提供高可用的服務,唯一的方式就是增加冗余來完成,這樣就算單機系統(tǒng)可以支撐的服務,因為高可用的要求,也會變成一個分布式系統(tǒng)。
基于上面的三個原因可以看出,在互聯(lián)網(wǎng)時代,單機系統(tǒng)是無法解決成本和高可用問題的,但是這兩個問題對幾乎對所有的公司來說都是非常關鍵的問題,所以,從單機系統(tǒng)到分布式系統(tǒng)是無法避免的技術大潮流。
那么,分布式系統(tǒng)是怎么來解決單機系統(tǒng)面臨的成本和高可用問題呢?
其實思路很簡單,就是將一些廉價的 PC 機通過網(wǎng)絡連接起來,共同完成工作,并且在系統(tǒng)中提供冗余來解決高可用的問題。
我們來看分布式系統(tǒng)的定義:分布式系統(tǒng)是由一組通過網(wǎng)絡進行通信、為了完成共同的任務而協(xié)調工作的計算機節(jié)點組成的系統(tǒng)。在定義中,我們可用看出,分布式系統(tǒng)它通過多工作節(jié)點來解決單機系統(tǒng)面臨的成本和可用性問題,但是它引入了對分布式系統(tǒng)內部工作節(jié)點的協(xié)調問題。
我們經(jīng)常說掌握一個知識需要理解它的前因后果,對于分布式系統(tǒng)來說,前因是「分布式系統(tǒng)解決了什么問題」,后果是「它是怎么做內部工作節(jié)點的協(xié)調」,所以我們要解決的第二個問題是:分布式系統(tǒng)是怎么做內部工作節(jié)點協(xié)調的?
先從簡單的情況入手,對于分布式計算(無狀態(tài))的情況,系統(tǒng)內部的協(xié)調需要做哪些工作:
在分布式系統(tǒng)內部,會有不同的服務(角色),服務 A 怎么找到服務 B 是需要解決的問題,一般來說服務注冊與發(fā)現(xiàn)機制是常用的思路,所以可以了解一下服務注冊發(fā)現(xiàn)機制實現(xiàn)原理,并且可以思考服務注冊發(fā)現(xiàn)是選擇做成 AP 還是 CP 系統(tǒng)更合理(嚴格按 CAP 理論說,我們目前使用的大部分系統(tǒng)很難滿足 C 或者 A 的,所以這里只是通常意義上的 AP 或者 CP);
找到服務后,當前的請求應該選擇發(fā)往服務的哪一個實例呢?一般來說,如果同一個服務的實例都是完全對等的(無狀態(tài)),那么按負載均衡策略來處理就足夠(輪詢、權重、hash、一致性 hash,fair 等各種策略的適用場景);如果同一個服務的實例不是對等的(有狀態(tài)),那么需要通過路由服務(元數(shù)據(jù)服務等)先確定當前要訪問的請求數(shù)據(jù)做哪一個實例上,然后再進行訪問。
系統(tǒng)雪崩是指故障的由于正反饋循序導致不斷擴大規(guī)則的故障。一次雪崩通常是由于整個系統(tǒng)中一個很小的部分出現(xiàn)故障于引發(fā),進而導致系統(tǒng)其它部分也出現(xiàn)故障。比如系統(tǒng)中某一個服務的一個實例出現(xiàn)故障,導致負載均衡將該實例摘除而引起其它實例負載升高,最終導致該服務的所有實例像多米諾骨牌一樣一個一個全部出現(xiàn)故障。
避免雪崩總體的策略比較簡單,只要是兩個思路,一個是快速失敗和降級機制(熔斷、降級、限流等),通過快速減少系統(tǒng)負載來避免雪崩的發(fā)生;另一個為彈性擴容機制,通過快速增加系統(tǒng)的服務能力來避免雪崩的發(fā)生。這個根據(jù)不同的場景可以做不同的選擇,或者兩個策略都使用。
一般來說,快速失敗會導致部分的請求失敗,如果分布式系統(tǒng)內部對一致性要求很高的話,快速失敗會帶來系統(tǒng)數(shù)據(jù)不一致的問題,彈性擴容會是一個比較好的選擇,但是彈性擴容的實現(xiàn)成本和響應時間比快速失敗要大得多。
對于一個分布式系統(tǒng),如果我們不能很清楚地了解內部的狀態(tài),那么高可用是沒有辦法完全保障的,所以對分布式系統(tǒng)的監(jiān)控(比如接口的時延和可用性等信息),分布式追蹤 Trace,模擬故障的混沌工程,以及相關的告警等機制是一定要完善的;
接下來我們再來看分布式存儲(有狀態(tài))的內部的協(xié)調是怎么做的,同時,前面介紹的分布式計算的協(xié)調方式在分布式存儲中同樣適用,就不再重復了:
ACID、BASE 和 CAP 理論,了解這三個主題,推薦這一篇文章以及文章后面相關的參考文獻:
英文版本:https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/
中文版本:https://www.infoq.cn/article/cap-twelve-years-later-how-the-rules-have-changed/
單機的存儲能力是不可能存儲所有的數(shù)據(jù)的,所以需要解決怎么將數(shù)據(jù)按一定的規(guī)則分別存儲到不同的機器上,目前使用比較多的方案為:Hash、Consistent Hash 和 Range Based 分片策略,可以了解一下它們的優(yōu)缺點和各自的應用場景;
為什么滿足系統(tǒng)的高可用要求,需要對數(shù)據(jù)做冗余處理,目前的方案主要為:中心化方案(主從復制、一致性協(xié)議比如 Raft 和 Paxos 等)和 去中心化的方案(Quorum 和 Vector Clock)了解一下它們的優(yōu)缺點和各自的應用場景,以及對系統(tǒng)外部表現(xiàn)出來的數(shù)據(jù)一致性級別(線性一致性、順序一致性、最終一致性等);
對于分布式系統(tǒng)來說,要實現(xiàn)事務,首先需要有對并發(fā)事務進行排序的能力,這樣在事務沖突的時候,確認哪個事務提供成功,哪個事務提交失敗。對于單機系統(tǒng)來說這個完全不是問題,簡單通過時間戳加序號的方式就可以實現(xiàn),但是對于分布式系統(tǒng)來說,系統(tǒng)中機器的時間不能完全同步,并且單臺機器序號也沒用全局意義,按上面的方式說行不通的。不過整個系統(tǒng)選一臺機器按單機的模式生產事務 ID 是可以的,同城多中心和短距離的異地多中心都沒有問題,不過想做成全球分布式系統(tǒng)的話,那么每一次事務都要去一個節(jié)點去獲取事務 ID 的成本太高(比如中國杭州到美國東部的 RTT 為 200 + ms ),Google 的 Spanner 是通過 GPS 和原子鐘實現(xiàn) TrueTime API 來解決這個問題從而實現(xiàn)全球分布式數(shù)據(jù)庫的。
有了事務 ID 后,通過 2PC 或者 3PC 協(xié)議來實現(xiàn)分布式事務的原子性,其他部分和單機事務差別不大,就不再細說來。
到這里,對分布式系統(tǒng)脈絡上有了基本的概念,接下來開始進入細節(jié)學習階段,這也是非常幸苦的階段,對于分布式系統(tǒng)的理解深入與否,對細節(jié)的深入度是很重要的評價指標,畢竟魔鬼在細節(jié)。這里可以往兩個方面進行系統(tǒng)的學習:
研究目前比較常用的分布式系統(tǒng)的設計,HDFS 或者 GFS(分布式文件系統(tǒng))、Kafka 和 Pulsar(分布式消息隊列),Redis Cluster 和 Codis(分布式緩存),MySQL 的分庫分表(傳統(tǒng)關系型數(shù)據(jù)庫的分布式方案),MongoDB 的 Replica Set 和 Sharing 機制集以及去中心化的 Cassandra(NoSQL 數(shù)據(jù)庫),中心化的 TiDB 和去中心化的 CockroachDB(NewSQL),以及一些微服務框架等;
從理論出發(fā),研究分布式相關的論文,這里推薦一本書「Designing Data-Intensive Applications」(中文版本:數(shù)據(jù)密集型應用系統(tǒng)設計),先整體看書,對比較感興趣的章節(jié),再讀一讀該章節(jié)中涉及到的相關參考文獻。
本文從分布式系統(tǒng)解決的問題開始,再討論它是怎么樣來解決問題的,最后討論了它引入了哪些新的問題,并且討論這些新問題的解決辦法,這個就是分布式系統(tǒng)大概的知識脈絡。掌握這個知識脈絡后,那么就可以從實踐和理論兩個角度結合起來深入細節(jié)研究分布式系統(tǒng)了。
參考
知乎 | 如何系統(tǒng)性的學習分布式系統(tǒng)
Martin Kleppmann.Designing Data-Intensive Applications
CAP Twelve Years Later: How the “Rules” Have Changed
隨著紫光展銳、ASR 等芯片廠商發(fā)布性價比更高的 Cat.1 芯片之后,Cat.1 模組廠商扎堆發(fā)布了自家的模組,
使得市場上的 Cat.1 模組價格已經(jīng)迅速降至 45-60 元,玩家眾多,競爭慘烈,基本重走 NB-IOT 的老路 —— 量未起,價已跌。
Cat.1 芯片原廠:
高通 MDM9207-1(2016 年發(fā)布)
紫光展銳春藤 8910DM(28nm工藝,集成藍牙和WiFi 室內定位)
翱捷 ASR3601
Cat.1 模組廠商(不完全統(tǒng)計):
中移物聯(lián)網(wǎng)
移遠通信
合宙電子
移柯通信
域格信息
廣和通
芯訊通
高新興物聯(lián)
美格智能
有方科技
有人信息
信位通訊
銳騏(廈門)電子
深圳信可通訊
Cat.1 優(yōu)勢
相對 NB-IOT,其通信速率優(yōu)勢明顯
相對 eMTC,其網(wǎng)絡成本低
相對 Cat.4,其具有一定的成本優(yōu)勢
Cat.1 劣勢:
現(xiàn)階段芯片廠家少
國外以高通為主,輔以 Sequans、Altair。
國內主要是展銳和翱捷。
現(xiàn)階段價格偏高
NB-IoT、Cat.1、Cat.4 模組價格:
cat1 的主要市場和應用場景:
Cat.1 仍處于商用初期,落地的應用場景和案例還較少,一些明確的場景包括了共享、金融支付、工業(yè)控制、車載支付、公網(wǎng)對講、POS 等等。
工信部辦公廳發(fā)布了《關于深入推進移動物聯(lián)網(wǎng)全面發(fā)展的通知》(以下簡稱《通知》)同時為 NB-IOT 和 Cat.1 站臺,未來 NB-IOT 依舊很香,Cat.1 則前途大好。
隨著新基建的啟動,5G 打頭,未來將是 NB-IOT、4G(包括 Cat.1)、5G 共同承載蜂窩物聯(lián)網(wǎng)的連接,以應對不同層次的物聯(lián)網(wǎng)業(yè)務需求。
QoS 等級 與 通信的流程有關,直接影響了整個通信。而且篇幅比較長,所以我覺得應該單獨拎出來講一下。
QoS 代表了 服務質量等級。 設置上,由2 位 的二進制控制,且值不允許為 3(0x11)。
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分發(fā)一次 |
1 | 0 | 1 | 至少分發(fā)一次 |
2 | 1 | 0 | 只分發(fā)一次 |
- | 1 | 1 | 保留位 |
要注意的是,QoS 是 Sender
和 Receiver
之間達成的協(xié)議,不是 Publisher
和 Subscriber
之間達成的協(xié)議。
也就是說
Publisher
發(fā)布一條 QoS1 的消息,只能保證Broker
能至少收到一次這個消息;至于對應的Subscriber
能否至少收到一次這個消息,還要取決于Subscriber
在Subscribe
的時候和Broker
協(xié)商的 QoS 等級。這里又牽扯出一個概念:"QoS 降級":在 MQTT 協(xié)議中,從 Broker 到 Subscriber 這段消息傳遞的實際 QoS 等于 "Publisher 發(fā)布消息時指定的 QoS 等級和 Subscriber 在訂閱時與 Broker 協(xié)商的 QoS 等級,這兩個 QoS 等級中的最小那一個。"
此時,整個過程中的 Sender
不關心 Receiver
是否收到消息,它"盡力"發(fā)完消息,至于是否有人收到,它不在乎。
此時,Sender
發(fā)送的一條消息,Receiver
至少能收到一次,也就是說 Sender
向 Receiver
發(fā)送消息,如果發(fā)送失敗,會繼續(xù)重試,直到 Receiver
收到消息為止,但是因為重傳的原因,Receiver
有可能會收到重復的消息;
1)Sender 向 Receiver 發(fā)送一個帶有消息數(shù)據(jù)的 PUBLISH 包, 并在本地保存這個 PUBLISH 包。
2)Receiver 收到 PUBLISH 包以后,向 Sender 發(fā)送一個 PUBACK 數(shù)據(jù)包,PUBACK 數(shù)據(jù)包沒有消息體(Payload),在可變頭中(Variable header)中有一個包標識(Packet Identifier),和它收到的 PUBLISH 包中的 Packet Identifier 一致。
3)Sender 收到 PUBACK 之后,根據(jù) PUBACK 包中的 Packet Identifier 找到本地保存的 PUBLISH 包,然后丟棄掉,一次消息的發(fā)送完成。
4)如果 Sender 在一段時間內沒有收到 PUBLISH 包對應的 PUBACK,它將該 PUBLISH 包的 DUP 標識設為 1(代表是重新發(fā)送的 PUBLISH 包),然后重新發(fā)送該 PUBLISH 包。重復這個流程,直到收到 PUBACK,然后執(zhí)行第 3 步。
QoS2 不僅要確保 Receiver 能收到 Sender 發(fā)送的消息,還要保證消息不重復。它的重傳和應答機制就要復雜一些,同時開銷也是最大的。
Sender 發(fā)送的一條消息,Receiver 確保能收到而且只收到一次,也就是說 Sender 盡力向 Receiver 發(fā)送消息,如果發(fā)送失敗,會繼續(xù)重試,直到 Receiver 收到消息為止,同時保證 Receiver 不會因為消息重傳而收到重復的消息。
QoS 使用 2 套請求/應答流程(一個 4 段的握手)來確保 Receiver 收到來自 Sender 的消息,且不重復:
1)Sender 發(fā)送 QoS 為 2 的 PUBLISH 數(shù)據(jù)包,數(shù)據(jù)包 Packet Identifier 為 P,并在本地保存該 PUBLISH 包;
2)Receiver 收到 PUBLISH 數(shù)據(jù)包以后,在本地保存 PUBLISH 包的 Packet Identifier P,并回復 Sender 一個 PUBREC 數(shù)據(jù)包,PUBREC 數(shù)據(jù)包可變頭中的 Packet Identifier 為 P,沒有消息體(Payload);
3)當 Sender 收到 PUBREC,它就可以安全地丟棄掉初始的 Packet Identifier 為 P 的 PUBLISH 數(shù)據(jù)包,同時保存該 PUBREC 數(shù)據(jù)包,同時回復 Receiver 一個 PUBREL 數(shù)據(jù)包,PUBREL 數(shù)據(jù)包可變頭中的 Packet Identifier 為 P,沒有消息體;如果 Sender 在一定時間內沒有收到 PUBREC,它會把 PUBLISH 包的 DUP 標識設為 1,重新發(fā)送該 PUBLISH 數(shù)據(jù)包(Payload);
4)當 Receiver 收到 PUBREL 數(shù)據(jù)包,它可以丟棄掉保存的 PUBLISH 包的 Packet Identifier P,并回復 Sender 一個 PUBCOMP 數(shù)據(jù)包,PUBCOMP 數(shù)據(jù)包可變頭中的 Packet Identifier 為 P,沒有消息體(Payload);
5)當 Sender 收到 PUBCOMP 包,那么它認為數(shù)據(jù)包傳輸已完成,它會丟棄掉對應的 PUBREC 包。如果 Sender 在一定時間內沒有收到 PUBCOMP 包,它會重新發(fā)送 PUBREL 數(shù)據(jù)包。
我們可以看到在 QoS2 中,完成一次消息的傳遞,Sender 和 Reciever 之間至少要發(fā)送四個數(shù)據(jù)包,QoS2 是最安全也是最慢的一種 QoS 等級了。
客戶端的會話狀態(tài)包括:
服務端的會話狀態(tài)包括:
保留消息不是服務端會話狀態(tài)的一部分,會話終止時不能刪除保留消息。
如果 Client 想接收離線消息,必須使用持久化的會話(CONNECT報文中可變頭(byte8[1])Clean Session = 0)連接到 Broker,這樣 Broker 才會存儲 Client 在離線期間沒有確認接收的 QoS 大于 1 的消息。
在以下情況下你可以選擇 QoS0:
在以下情況下你應該選擇 QoS1:
在以下情況下你應該選擇 QoS2:
實際上,QoS1 是應用最廣泛的 QoS 等級,QoS1 發(fā)送消息的速度很快,而且能夠保證消息的可靠性。雖然使用 QoS1 可能會收到重復的消息,但是在應用程序里面處理重復消息,通常并不是件難事。
yum install epel-release 從epel源中安裝 yum install -y iperf
iperf -s 開啟服務端 iperf -c ip
tcpdump進行抓包 tcpdump -i eth0 -s 3000 port 8080 -w /home/tomcat.pcap 對于抓包文件采用wireshark進行分析 丟包(TCP DUP ACK) 重傳(retransmission),超時重傳,
cdn 緩存,回源問題 304請求,瀏覽器是否使用本地緩存。比較last_modified 和if_modified_since 通過實踐戳來判斷,瀏覽器緩存和cdn緩存
路由解析
泛域名解析
http入口產生一個traceId 分發(fā)到rpc調用,cache,db,jms調用鏈路中 google的著名論文dapper和zipkin 日志聚合,綁定鏈路日志和業(yè)務日志 采樣采集,慢請求,異常服務。 日志量大。日志異步寫入,環(huán)狀數(shù)組,日志組件自研 共享信息放在ThreadLocal中。比如traceId
tsar -l -i 1 --traffic 查看網(wǎng)卡的進出流量
tsar -l -i 1 --cpu 軟件問題定位,perf 采樣所有進程數(shù)據(jù) perf record -F 99 -a -g -- sleep 30 java進程的函數(shù)map:java -cp attach-main.jar:$JAVA_HOME/lib/tools.jar net.virtualvoid.perf.AttachOnce PID 輸出函數(shù)和地址的map 輸出火焰圖 perf script | stackcollapse-perf.pl | flamegraph.pl --color=java --hash > flamegraph.svg
-堆內內存問題,
采用jmap dump內存,采用離線工具分析 jprofile、mat
-堆外內存問題
a.google-perftools
yum install -y google-perftools graphviz export LD_PRELOAD=/usr/lib64/libtcmalloc.so.4 export HEAPPROFILE=/home/testGperf.prof 執(zhí)行程序,結束程序,生成prof 分析prof 生成svg, pdf,text pprof --svg $JAVA_HOME/bin/java testGperf.prof.0001.heap > test.svg pprof --pdf $JAVA_HOME/bin/java testGperf.prof.0001.heap > test.pdf pprof --text $JAVA_HOME/bin/java testGperf.prof.0001.heap > test.txt
b.jemalloc定位(優(yōu)勢,適合長時間trace)
sudo apt-get install graphviz 編譯安裝 ./configure –enable-prof –enable-stats –enable-debug –enable-fill make make install
運行配置 export MALLOC_CONF=”prof:true,prof_gdump:true,prof_prefix:/home/jedump/jez,lg_prof_interval:30,lg_prof_sample:17”
export LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 運行 java -jar target/spring-boot-jemalloc-example-0.0.1-SNAPSHOT.jar
jeprof –show_bytes –svg jez.*.heap > app-profiling.svg
注明:如果在docker容器中,推薦用pprof,jemalloc只顯示函數(shù)地址,不顯示函數(shù)名
/etc/security/limits.conf
控制該用戶文件句柄數(shù)
tsar -l -i 1 –io