codis配置_codis 源码理解
這里介紹一下 codis 幾個(gè)主要的點(diǎn),對(duì)理解源碼有幫助。
1. 先看 ServerGroup 和 Slot。
一個(gè) Proxy 可以對(duì)應(yīng)多個(gè) ServerGroup;
ServerGroup 是一組 Codis Server,一個(gè) ServerGroup 只有一個(gè) Master(Codis Server),而且雖然有多個(gè) Codis Server,Proxy 只訪問(wèn) ServerGroup 中的 Master,Slave 可用作故障切換;
Slot 是一個(gè)邏輯概念,一共 1024 個(gè),使用 crc32(key) % 1024 計(jì)算 Slot id,而且一個(gè)(或多個(gè)) Slot 屬于一個(gè) ServerGroup,1024 個(gè) Slot 一起分用多個(gè) ServerGroup 資源;
當(dāng) Slot 所在 ServerGroup 內(nèi)存不夠的時(shí)候可以把此 Slot 遷移到另一個(gè)內(nèi)存使用少的 ServerGroup,實(shí)現(xiàn)擴(kuò)容的目的,而當(dāng)所有 ServerGroup 內(nèi)存不夠的時(shí)候增加新的 ServerGroup 即可。
ServerGroup 數(shù)據(jù)結(jié)構(gòu):
Slot 數(shù)據(jù)結(jié)構(gòu):
2. 再看看 Router,Router 用來(lái)轉(zhuǎn)發(fā) linstener 接收的 Codis 請(qǐng)求。
最核心的是 SharedBackendConn 的數(shù)據(jù)結(jié)構(gòu):
再看看這里 Slot 的數(shù)據(jù)結(jié)構(gòu):
每一個(gè) ServerGroup 有一個(gè) Master (Codis Server),bc 只能是 Master (的連接)。
Proxy 啟動(dòng)的時(shí)候,會(huì)去 fill Proxy 的 router,這部分代碼讀起來(lái)感覺(jué)怪怪的,其實(shí)就是對(duì)于 bc,首先建立到 bc.addr 的連接,然后先創(chuàng)建從 bc 讀取結(jié)果的 chan *Request(讀取之后會(huì)設(shè)置結(jié)果,以便發(fā)送給請(qǐng)求方),放入 goroutine 不斷讀取,最后循環(huán)從 input 中獲取請(qǐng)求,經(jīng)過(guò)路由轉(zhuǎn)發(fā)之后寫(xiě)入 bc。
說(shuō)說(shuō)路由轉(zhuǎn)發(fā)的邏輯,分為兩部分:
1. Proxy 接收請(qǐng)求和返回?cái)?shù)據(jù),這個(gè)實(shí)現(xiàn)類(lèi)似上面說(shuō)的 Proxy 和 Master 的交互;
2. Proxy 收到請(qǐng)求方的數(shù)據(jù)之后需要 decode,如果是類(lèi)似 MGET 指令的話會(huì)分拆成多個(gè) GET 請(qǐng)求,然后向 Master 發(fā)送請(qǐng)求;根據(jù) crc32(key) % 1024 計(jì)算出 Slot id,然后走 Proxy 和 Master 的交互流程(ps,如果此 Slot 處在遷移狀態(tài),那么會(huì)先調(diào)用 SLOTSMGRTTAGONE 把 key 遷移至新 ServerGroup)。
3. 看看 Proxy。
conf 是 Proxy 的配置數(shù)據(jù);
topo 是 ZK 或 etcd 操作接口,里面保存了 ProductName,一個(gè) Proxy 實(shí)例只有一個(gè) ProductName(就是一個(gè)服務(wù));
groups 是 ServerGroup 信息,key 是 Slot id,value 是 ServerGroup id;
lastActionSeq 用于保存 action seq,其中 evtbus 保存 watch proxy 和 action 的 事件信息;
router 下面再講;
listener 是 Proxy 的 Listener;
kill、wait、stop 用來(lái)正確的處理退出邏輯。
proxy 的處理過(guò)程(不說(shuō) load 配置和如何處理退出的部分):
1). 先初始化 Router,主要是初始化 Router 中的 1024 個(gè) Slot,只是把 Slot 的 id 標(biāo)識(shí)出來(lái) ;
2). 在 zk 中注冊(cè) proxy 的臨時(shí)節(jié)點(diǎn),節(jié)點(diǎn)路徑:/zk/codis/db_{productName}/proxy/{proxyId},內(nèi)容是以 ProxyInfo 數(shù)據(jù)結(jié)構(gòu)報(bào)錯(cuò)的 proxy 信息;
3). 在 zk 中注冊(cè) proxy fence 的永久節(jié)點(diǎn),節(jié)點(diǎn)路徑:/zk/codis/db_{productName}/fence/{proxyAddr},內(nèi)容為空。
為什么有了 proxy 節(jié)點(diǎn)還需要 fence 節(jié)點(diǎn)呢,是為了來(lái)判斷 proxy 是否是正常退出的,比如使用 kill -9 殺 proxy 以后,proxy 節(jié)點(diǎn)會(huì)消失,fence 節(jié)點(diǎn)不會(huì)消失,對(duì)比下就知道是非正常退出。
proxy 在收到 kill 信號(hào)(os.Interrupt, syscall.SIGTERM, os.Kill)后會(huì)把 proxy fence 和 proxy 節(jié)點(diǎn)刪除,proxy 也就下線了,但是如果 kill -9 就不會(huì)刪除,需要手動(dòng)刪除。
4). 此時(shí)還要等待 proxy 是在線狀態(tài),這里的邏輯是 proxy 剛啟動(dòng)時(shí)候的狀態(tài)是 PROXY_STATE_OFFLINE(main.go 會(huì)調(diào)用 dashboard api 設(shè)置自己為 PROXY_STATE_ONLINE,為了保證 proxy 信息已經(jīng)注冊(cè)到 zk,main.go 會(huì)等待一秒鐘再設(shè)置),一旦 proxy 是在線狀態(tài)之后,會(huì)開(kāi)一個(gè) goroutine rewatch proxy 狀態(tài)(zk 節(jié)點(diǎn)是 /zk/codis/db_{productName}/proxy),如果 proxy 有變化會(huì)通知到 evtbus 這個(gè) channel 里;
5). 開(kāi)啟一個(gè) goroutine 來(lái) watch action 節(jié)點(diǎn) /zk/codis/db_{productName}/actions 的 children,如果有變化也通知給 evtbus;
6). 下一步,fill Router 的 Slot,有兩部分,一部分是 fill Server 數(shù)據(jù)結(jié)構(gòu)中的 groups,key 是 Slot id,value 是 Group id,另一部分是 fill Router 中的 Slot 中的 bc,Router 中的 pool 是連接池,Slot 從 pool 中取 bc;
7). 此時(shí)開(kāi)始處理請(qǐng)求,使用 goroutine,拿到請(qǐng)求扔給 Router 部分來(lái)處理;
8). 開(kāi)啟一個(gè) loopEvents,如果檢查到有 kill 信號(hào),刪除 zk 中的 proxy 節(jié)點(diǎn),下線;而且從 evtbus 里讀取事件,做處理;額外的,定時(shí)器,每隔一段事件 PING 一次 proxy 后端的 Codis Server,以保持探活。
4. proxy 之間如何協(xié)調(diào)。
第 3 步說(shuō)了,proxy 會(huì)監(jiān)聽(tīng) /zk/codis/db_{productName}/proxy 和 /zk/codis/db_{productName}/actions 的變化,codis 就是通過(guò)這兩個(gè)監(jiān)聽(tīng)機(jī)制保證 proxy 的信息一致。
/zk/codis/db_{productName}/proxy 主要是獲取 proxy 的狀態(tài)信息,如果狀態(tài)變成 PROXY_STATE_MARK_OFFLINE,則刪除 fence 節(jié)點(diǎn)和 proxy 節(jié)點(diǎn),并在內(nèi)存中標(biāo)記狀態(tài)為 PROXY_STATE_MARK_OFFLINE,此處 loopEvents 會(huì)停止,然后觸發(fā) serve() 的 s.close(),然后 handleConns 停止,serve() 停止,proxy 退出。
/zk/codis/db_{productName}/actions 則是監(jiān)聽(tīng) slot、group 等的變化,比如:
ACTION_TYPE_SLOT_MIGRATE
ACTION_TYPE_SLOT_CHANGED
ACTION_TYPE_SLOT_PREMIGRATE
ACTION_TYPE_SERVER_GROUP_CHANGED
ACTION_TYPE_MULTI_SLOT_CHANGED
收到這些變化之后,從 zk 中拿新的信息,來(lái) fill 內(nèi)存中 Slot 中的信息,然后會(huì)創(chuàng)建 /zk/codis/db_{productName}/ActionResponse/{seq}/proxyId 來(lái)確認(rèn) proxy 已經(jīng)響應(yīng)此 action。
還有一點(diǎn),新建 action 的時(shí)候有個(gè)開(kāi)關(guān):needConfirm,如果為真,則會(huì)確認(rèn) proxy node 和 fence node 一致,而且會(huì)等待所有 proxy 回復(fù)了 action,如果有 proxy 沒(méi)回復(fù),則設(shè)置此 proxy 為 PROXY_STATE_MARK_OFFLINE,并報(bào)錯(cuò)。
5. 關(guān)于 Slot 遷移。
通過(guò) dashboard api (/api/migrate) 來(lái)遷移,傳入的數(shù)據(jù)結(jié)構(gòu)如下:
然后把 From 到 To 的每個(gè) slot 生成 MigrateTaskInfo。
然后把 MigrateTaskInfo 推到 globalMigrateManager,dashboard 啟動(dòng)的時(shí)候會(huì)初始化 globalMigrateManager,globalMigrateManager 數(shù)據(jù)結(jié)構(gòu)如下:
MigrateTask 結(jié)構(gòu)如下:
SlotMigrateProgress 結(jié)構(gòu)如下:
初始化 globalMigrateManager 會(huì)創(chuàng)建 /zk/codis/db_{productName}/migrate_tasks,然后進(jìn)入執(zhí)行遷移的 loop,不斷從 /zk/codis/db_{productName}/migrate_tasks 讀取任務(wù)并遷移。
globalMigrateManager 收到 MigrateTaskInfo 后會(huì)創(chuàng)建任務(wù) /zk/codis/db_{productName}/migrate_tasks/{seq},內(nèi)容就是 MigrateTaskInfo 信息,然后遷移 api 返回。
主要的處理在 loop 里面:
1). 從 /zk/codis/db_{productName}/migrate_tasks/ 取出最早的 task,封裝成 MigrateTask;
2). 做遷移 check,檢查所有 slot,如果狀態(tài)是 SLOT_STATUS_MIGRATE 或者 SLOT_STATUS_PRE_MIGRATE 的數(shù)量大于1,報(bào)錯(cuò),如果等于1,判斷是否是此 MigrateTask 中的 slot,如果不是則報(bào)錯(cuò);
3). 修改 task 的狀態(tài)為 MIGRATE_TASK_MIGRATING (migrating);
4). 遷移 slot,在遷移之前要把 slot 的狀態(tài)改掉,如果原狀態(tài)不是 SLOT_STATUS_MIGRATE,改成 SLOT_STATUS_PRE_MIGRATE,之后強(qiáng)制把狀態(tài)改成 SLOT_STATUS_MIGRATE,而且修改 from group 和 to group。
5). 然后不斷地把源 group master 的數(shù)據(jù)向 目標(biāo) group master 拷貝,完成之后修改 slot 狀態(tài)為 SLOT_STATUS_ONLINE,from group 和 to group 為 INVALID_ID。
6). 遷移完之后會(huì)刪除 task,也就是刪除 zk /zk/codis/db_{productName}/migrate_tasks/{seq}。如果遷移失敗而且 slot 狀態(tài)為 SLOT_STATUS_PRE_MIGRATE(如果不是 SLOT_STATUS_PRE_MIGRATE,說(shuō)明已經(jīng)在遷移,需手動(dòng)處理),會(huì)把 slot 狀態(tài)改為 SLOT_STATUS_ONLINE。
7). 額外重要的一點(diǎn),每次更新 slot 狀態(tài)時(shí),都會(huì)發(fā)起 slot 的 action,等待所有 proxy 回復(fù)才繼續(xù)。而且 proxy 收到 slot 變化后,會(huì)更新 slot 狀態(tài),如果 slot 在遷移狀態(tài)(根據(jù) slot 的 migrate.bc 判斷)有訪問(wèn)到達(dá) proxy,會(huì)先把數(shù)據(jù)從 from group 拷到 to group,然后再?gòu)?to group 請(qǐng)求,這點(diǎn)銜接的挺好。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的codis配置_codis 源码理解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 200万辆库存未卖完 车企呼吁:暂缓国六
- 下一篇: oracledatabase11gr2怎