做者 | 董鵬 阿里巴巴技術專家java
好處:實現跨團隊的解耦,實現更高的併發(目前單機只能實現 c10k)不用再拷貝代碼,基礎服務能夠公用,更好的支持服務治理,可以更好的兼容雲計算平臺。mysql
客戶端還須要維護負載均衡、超時處理、鏈接池管理等,鏈接池維護了和多個 server 的鏈接,靠此作負載均衡,當某個服務器宕機後去除該鏈接。請求上下文維護了請求 ID 和回調函數,超時的請求當回覆報文到達後因爲找不到請求上下文就會丟棄。linux
前者節省空間,後者反序列化速度快,目前的序列化框架也是在反序列化時間和佔用空間之間權衡。有點相似哈夫曼編碼,或者數據庫怎麼存儲一行一行的數據。git
通常有 3 種模式:ajax
zookeeper 不適合作註冊中心的緣由:zookeeper 爲了一致性犧牲了可用性,可是註冊中心實際上對一致性要求並不高,不一致產生的後果也就是某個服務下線了而客戶端並不知道,可是客戶端經過重試其餘節點就能夠了。另外當發生網絡分區的時候,若是超過半數節點掛了,zookeeper 就不可用,可是實際上它應該仍然能夠對它所在機房的節點提供註冊服務,例如三個機房分別放了 2 臺、2 臺、1 臺,若是各個機房之間網絡斷了,可是機房內部上是通的,這樣註冊中心不可用即便內部節點也不能服務了。redis
zookeeper 並非嚴格的一致性,它支持讀寫分離,其它節點收到寫請求會轉發給 master 節點,而其它節點能夠支持讀請求,當數據尚未從主節點複製過來的時候讀到的多是過時的數據。算法
配置中心的需求:保證高可用、實時通知、灰度發佈、權限控制、一鍵回滾、環境隔離(開發/測試/生產)等,目前的開源實現:nacos disconf apollo。sql
apollo 有如下 4 個模塊:docker
- portal 做爲一個管理後臺,提供管理員操做的入口。 有獨立的數據庫;
- adminservice 提供配置的修改和發佈服務的底層服務,和 configservice 公用一個數據庫 configdb,每次修改配置就會往數據庫裏插入一條記錄 releasemessage;
- configservice 用一個定時任務去掃描數據庫是否有新的 releasemessage,有的話就通知客戶端,而客戶端採用定時輪詢的方式去查詢 configservice 是否有新消息,這裏採用 deferredresult 異步執行;
- eruka 爲 adminservice 和 configservice 提供了註冊發現的服務。客戶端獲取到配置文件後也會寫入磁盤。
SET resource_name my_random_value NX PX 30000
先清空緩存仍是先更新數據庫?shell
以上是考慮到分佈式事務中一個成功一個失敗的狀況,可是這種機率畢竟是小的,能夠用在併發量不是很高可是對數據一致性要求很高的狀況,若是併發很高建議先更新數據庫後清空緩存。
若是先清空緩存,後更新數據庫,在尚未更新到數據庫的狀況下另一個事務去查詢,發現緩存沒命中就去數據庫取,而後又寫入緩存,以後上一個事務的數據庫更新,這樣就致使了緩存和數據庫不一致,若是先更新數據庫再清空緩存,更新完數據庫後緩存還沒更新,這個時候來讀取緩存是舊的值,也出現不一致,可是最終清空緩存後會一致。
不過這種方式也會產生永久不一致,可是機率很小,例如一個讀請求,沒有命中緩存,這個時候可能另外一個線程恰好清空緩存,而後它就去數據裏面取,可是又有一個線程在它讀完數據庫後將數據庫改成另一個值,這樣那個讀請求寫入到緩存的數據就是髒數據了。
redis 採用單線程模型,對只有 io 操做來講性能很好,可是 redis 也提供了計算功能,如排序聚合,cpu 在計算的時候全部的 io 操做都是阻塞的。
memecached 先申請一塊內存,將其分割成大小不等的若干內存塊以存儲不一樣大小的鍵值對。這種方式效率高可是可能產生空間浪費。而 redis 只是單純的包裝了下 malloc 和 free。
redis 提供了兩種方式持久化數據,一種方式是把某一時刻全部的數據都寫入磁盤,另一種方式經過增量日誌的形式
memecache 提供了 cas 來保證數據一致性;redis 提供了事務,將一連串指令一塊兒執行或者回滾。
memechache 只能經過一致性哈希來進行集羣,而 redis 提供了集羣功能,客戶端作路由選擇那個 master 節點,master 節點能夠有多個 slave 節點作爲備用和讀。
redis 中的字符串沒有采用 c 語言裏的結構,額外加上了空閒內存和已佔用內存,這樣讀取的時候因爲已經知道 char 數組大小,因此能夠直接取出,避免遍歷操做,當字符串變大或縮小的時候能夠避免從新分配內存,能夠用到空閒空間,也就是 redis 會預分配一個空間。另外 redis 裏的哈希,用了兩個 table 存儲,主要爲了擴容,也就是 rehash,這樣當擴容的時候雙方就能夠互換,redis 採用漸近式擴容,也就是每一次操做都執行兩個哈希表,當新增的時候只在新表。set 數據結構能夠用來存儲總的點贊次數,而 zset 是一個有序鏈表,爲了加快查詢用跳錶進行存儲。
嚴格的一致,只能一個生產者,發送到一個 broker 上,而後只有一個隊列一個消費者,可是這種模式有不少弊端,一個地方異常將阻塞整個流程,RocketMQ 將這個問題交給應用層處理,也就是發送端本身選擇發送到哪一個隊列,例如同一個訂單的消息發送到同一個隊列。可是算法在其中一個隊列異常的時候也會有問題。
只要網絡上傳輸確定會有這種問題,因此應用層最好可以支持冪等,或者用一張去重表存儲每個處理過的消息 ID。
每一個 commitlog 大小爲 1G,第二個文件的起始偏移量就是 1G 的 byte 大小,當根據一個偏移量獲取對應某個文件的時候,根據偏移量對 1G 取餘就能夠,這些 commitlog 文件經過一個文件隊列維護,每次寫文件返回隊列的最後一個文件,而後須要加鎖。
建立完文件後會進行預熱,預熱的時候會在每個內存頁 4kb 裏面寫一個 byte0,讓系統對緩存頁緩存,防止真正寫入的時候發生缺頁,mmap 的機制是隻會記錄一個虛擬地址,當缺頁時纔會去獲取物理內存的地址。
建立文件有兩種方式:
一個隊列只能被一個客戶端消費。
當存在多個隊列,但只有一個客戶端的時候,這個客戶端須要去 4 個隊列上消費,當只有一個隊列的時候只會有一個客戶端能夠收到消息,因此通常狀況下須要客戶端數量和隊列數量一致,客戶端通常會保存每一個隊列消費的位置,由於這個隊列只會有一個客戶端消費,因此這個客戶端每次消費都會記錄下隊列的 offset,broker 端,也會記錄同一個 grouo 消費的 offset。
MappedByteBuffer 的原理是老的 read 是先將數據從文件系統讀取到操做系統內核緩存,而後再將數據拷貝到用戶態的內存供應用使用,而使用 mmap 能夠將文件的數據或者某一段數據映射到虛擬內存,這個時候並無進行數據讀取,當用戶訪問虛擬內存的地址的時候會觸發缺頁異常,這個時候會從底層文件系統直接將數據讀取到用戶態內存。而 MappedByteBuffer 經過 FileChannel 的 map 方法進行映射的時候會返回一個虛擬地址,MappedByteBuffer就是經過這個虛擬地址配合 UnSafe 獲取字節數據。
操做系統在觸發缺頁異常的時候會去文件系統讀取數據加載到內存,這個時候通常會進行預讀取,通常爲 4KB,當系統下次訪問數據的時候就不會發生缺頁異常,由於數據已經在內存裏了,爲了讓 MappedByteBuffer 讀取文件的速度更高,咱們能夠對 MappedByteBuffer 所映射的文件進行預熱,例如將每一個 pagecache 寫一個數據,這樣在真正寫數據的時候就不會發生缺頁了。
通常三種方式:在 dao 層和 orm 層利用 mybatis 攔截器,基於 jdbc 層進行攔截重寫 JDBC 接口作加強,基於數據庫代理。
jdbc 代理,實現 datasource,connection,preparestatement,druid 解析 sql,生成執行計劃,利用 resultset 對結果集進行合併(group by order max sum)。
分表策略,通常是哈希,要保證分庫和分表的算法徹底沒有關聯,否則會數據分佈不均勻。
數據擴容的時候能夠經過配置中心動態的修改寫入策略,如何一開始能夠先讀老表,數據同時寫入新表和老表,等數據遷移完成後,在讀新表並雙寫,以後在讀新表寫新表。
數據庫自增 id,一次取多個,單機限制,另外數據庫自增 id 內部也用了個鎖,只是在 sql 執行結束即便事務沒提交也會釋放鎖。
雪花算法變種 : 15 位時間戳,4 位自增序列,2 位區分訂單類型,7 位機器ID,2 位分庫後綴,2 位分表後綴,共 32 位。
利用 zookeeper 的順序節點獲取自增 ID。
兩階段提交:事務管理器,資源管理器,一階段準備,二階段提交 (XA 方案對業務無侵入,由數據庫廠商提供支持,可是性能不好)。
事物補償
TCC :也是兩階段,第一階段嘗試鎖定資源,第二階段確認或者回滾。
設計規範:
框架事務(seata)
開啓事務的時候會向 tc 申請一個全局的事務 id,這個事務 id 會經過 rpc 框架的攔截器傳入到被調用端,而後放入 threadlocal,被調用方在執行 sql 的時候會去檢查一下是否在一個全局事務裏。
默認的隔離級別爲讀未提交,由於事務一階段已經本地事務提交而全局事務並無完成,後續可能會回滾,其餘事務能夠看到這個狀態,提供的讀已提交的方式是經過 for update,當解析到該語句的時候會檢查是否存在行鎖衝突,若是存在衝突就等待直到釋放。
一致性消息隊列:先發送半消息,若是成功了在執行本地事務,本地事務成功就提交半消息,本地事務失敗就回滾半消息,若是消息隊列長期沒有收到確認或者回滾能夠反查本地事務的狀態,消費端收到消息後,執行消費端業務,若是執行失敗能夠從新獲取,執行成功發送消費成功的確認。
MYCAT
能夠簡單地這樣理解:MySQL 單機是C;主從同步複製 CP;主從異步複製 AP。
Zookeeper 選擇了 P,可是既沒有實現 C,也沒有實現 A,而是選擇最終一致性。能夠在多個節點上讀取,可是隻容許一個節點接受寫請求,其餘節點接收的寫請求會轉發給主節點,只要過半節點返回成功就會提交。
若是一個客戶端鏈接的正好是沒有被提交的 follower 節點,那麼這個節點上讀取到的數據就是舊的,這樣就出現了數據的不一致,因此沒有徹底實現 C。因爲須要過半節點返回成功才提交,若是超過半數返回失敗或者不返回,那麼 zookeeper 將出現不可用,因此也沒有徹底實現 A。
固然衡量一個系統是 CP 仍是 AP,能夠根據它犧牲 A 更多仍是犧牲 C 更多,而 ZK 其實就是犧牲了 A 來知足 C,當超過集羣半數的節點宕機後,系統將不可用,這也是不建議使用 zk 作註冊中心的緣由。
CAP 理論只是描述了在分佈式環境中一致性、可用性、分區容忍不能同時知足,並無讓咱們必定要三選二,因爲網絡分區在分佈式環境下是不可避免的,因此爲了追求高可用,每每咱們會犧牲強一執行,採用弱一致性和最終一致性的方案,也就是著名的 BASE 理論,而 base 理論實際上是針對傳統關係型數據的 ACID 而言的。
但 ACID 的提出是基於單節點下的,在分佈式環境下,如何協調數據一致性,也就是在數據的隔離級別上作出取捨,即便是單機的關係型數據庫爲了提升性能,也就是可用性,定義了隔離級別,去打破 ACID 裏面的強一致性 C,固然數據庫也是爲業務服務的,某些業務或者說大部分業務都沒有強一致性的需求。
正常電商採用第三種,秒殺採用第一種,不超賣的控制不用放在應用層,直接在 sql 層加 where 語句進行判斷,可是 mysql 針對同一行記錄也就是同一個商品的減庫存,確定會高併發下爭取行鎖,這將致使數據庫的 tps 降低(死鎖檢測會遍歷全部須要等待鎖的鏈接,這個操做很是耗 cpu),從而影響其餘商品的銷售,因此咱們能夠將請求在應用層進行排隊,若是份額較少能夠直接捨棄,另外一種方案是在數據庫層排隊,這種方案須要採用 mysql 的補丁。
docker 在建立容器進程的時候能夠指定一組 namespace 參數,這樣容器就只能看到當前 namespace 所限定的資源、文件、設備、網絡、用戶、配置信息,而對於宿主機和其餘不相關的程序就看不到了,PID namespace 讓進程只看到當前 namespace 內的進程,Mount namespace 讓進程只看到當前 namespace 內的掛載點信息,Network namespace 讓進程只看到當前 namespace 內的網卡和配置信息,
全名 linux control group,用來限制一個進程組可以使用的資源上限,如 CPU、內存、網絡等,另外 Cgroup 還可以對進程設置優先級和將進程掛起和恢復,cgroup 對用戶暴露的接口是一個文件系統,/sys/fs/cgroup 下這個目錄下面有 cpuset,memery 等文件,每個能夠被管理的資源都會有一個文件,如何對一個進程設置資源訪問上限呢?
在 /sys/fs/cgroup 目錄下新建一個文件夾,系統會默認建立上面一系列文件,而後 docker 容器啓動後,將進程 ID 寫入 taskid 文件中,在根據 docker 啓動時候傳人的參數修改對應的資源文件。
經過 chroot 來更改 change root file system 更改進程的根目錄到掛載的位置,通常會經過 chroot 掛載一個完整的 linux 的文件系統,可是不包括 linux 內核,這樣當咱們交付一個 docker 鏡像的時候,不只包含須要運行的程序還包括這個程序依賴運行的這個環境,由於咱們打包了整個依賴的 linux 文件系統,對一個應用來講,操做系統纔是它所依賴的最完整的依賴庫。
docker 在鏡像的設計中引入層的概念,也就是用戶在製做 docker 鏡像中的每一次修改,都是在原來的 rootfs 上新增一層 roofs,以後經過一種聯合文件系統 union fs 的技術進行合併,合併的過程當中若是兩個 rootfs 中有相同的文件,則會用最外層的文件覆蓋原來的文件來進行去重操做。
舉個例子,咱們從鏡像中心 pull 一個 mysql 的鏡像到本地,當咱們經過這個鏡像建立一個容器的時候,就在這個鏡像原有的層上新加了一個增 roofs,這個文件系統只保留增量修改,包括文件的新增刪除、修改,這個增量層會藉助 union fs 和原有層一塊兒掛載到同一個目錄,這個增長的層能夠讀寫,原有的其餘層只能讀,因而就保證了全部對 docker 鏡像的操做都是增量。
以後用戶能夠 commit 這個鏡像將對該鏡像的修改生成一個新的鏡像,新的鏡像就包含了原有的層和新增的層,只有最原始的層纔是一個完整的 linux fs, 那麼既然只讀層不容許修改,我怎麼刪除只讀層的文件呢?這時只須要在讀寫層(也就是最外層),生成一個 whiteout 文件來遮擋原來的文件就能夠了。
目前的大部分公司採用下面的部署方式。
-
阿里雲 - 雲原生應用平臺 - 基礎軟件中臺團隊(原容器平臺基礎軟件團隊)誠邀 Kubernetes/容器/ Serverless/應用交付技術領域專家( P6-P8 )加盟。
工做年限:建議 P6-7 三年起,P8 五年起,具體看實際能力。
工做地點:
簡歷馬上回復,2~3 周出結果。節後入職。
基礎產品事業部是阿里雲智能事業羣的核心研發部門,負責計算、存儲、網絡、安全、中間件、系統軟件等研發。而云原生應用平臺基礎軟件終態團隊致力於打造穩定、標準、先進的雲原生應用系統平臺,推進行業面向雲原生技術升級與革命。
在這裏,既有 CNCF TOC 和 SIG 聯席主席,也有 etcd 創始人、K8s Operator 創始人與 Kubernetes 核心維護成員組成的、國內最頂尖的 Kubernetes 技術團隊。
在這裏,你將同來自全球的雲原生技術領域專家們(如 Helm 項目的創始人、Istio 項目的創始人)密切合做,在獨一無二的場景與規模中從事 Kubernetes、Service Mesh、Serverless、Open Application Model ( OAM )等雲計算生態核心技術的研發與落地工做,在業界標杆級的平臺上,既賦能阿里巴巴全球經濟體,更服務全世界的開發者用戶。
技術要求:Go/Rust/Java/C++,Linux,分佈式系統
lei.zhang AT alibaba-inc.com