從0開始學(xué)微服務(wù):如何使用服務(wù)路由?
什么是服務(wù)路由呢?我的理解是服務(wù)路由就是服務(wù)消費(fèi)者在發(fā)起服務(wù)調(diào)用時(shí),必須根據(jù)特定的規(guī)則來選擇服務(wù)節(jié)點(diǎn),從而滿足某些特定的需求。
那么服務(wù)路由都有哪些應(yīng)用場景?具體都有哪些規(guī)則呢?
服務(wù)路由的應(yīng)用場景
根據(jù)我的實(shí)踐經(jīng)驗(yàn),服務(wù)路由主要有以下幾種應(yīng)用場景:
- 分組調(diào)用。一般來講,為了保證服務(wù)的高可用性,實(shí)現(xiàn)異地多活的需求,一個(gè)服務(wù)往往不止部署在一個(gè)數(shù)據(jù)中心,而且出于節(jié)省成本等考慮,有些業(yè)務(wù)可能不僅在私有機(jī)房部署,還會(huì)采用公有云部署,甚至采用多家公有云部署。服務(wù)節(jié)點(diǎn)也會(huì)按照不同的數(shù)據(jù)中心分成不同的分組,這時(shí)對(duì)于服務(wù)消費(fèi)者來說,選擇哪一個(gè)分組調(diào)用,就必須有相應(yīng)的路由規(guī)則。
- 灰度發(fā)布。在服務(wù)上線發(fā)布的過程中,一般需要先在一小部分規(guī)模的服務(wù)節(jié)點(diǎn)上先發(fā)布服務(wù),然后驗(yàn)證功能是否正常。如果正常的話就繼續(xù)擴(kuò)大發(fā)布范圍;如果不正常的話,就需要排查問題,解決問題后繼續(xù)發(fā)布。這個(gè)過程就叫作灰度發(fā)布,也叫金絲雀部署。
- 流量切換。在業(yè)務(wù)線上運(yùn)行過程中,經(jīng)常會(huì)遇到一些不可抗力因素導(dǎo)致業(yè)務(wù)故障,比如某個(gè)機(jī)房的光纜被挖斷,或者發(fā)生著火等事故導(dǎo)致整個(gè)機(jī)房的服務(wù)都不可用。這個(gè)時(shí)候就需要按照某個(gè)指令,能夠把原來調(diào)用這個(gè)機(jī)房服務(wù)的流量切換到其他正常的機(jī)房。
- 讀寫分離。對(duì)于大多數(shù)互聯(lián)網(wǎng)業(yè)務(wù)來說都是讀多寫少,所以在進(jìn)行服務(wù)部署的時(shí)候,可以把讀寫分開部署,所有寫接口可以部署在一起,而讀接口部署在另外的節(jié)點(diǎn)上。
上面四種應(yīng)用場景是實(shí)際業(yè)務(wù)中很常見的,服務(wù)路由可以通過各種規(guī)則來實(shí)現(xiàn),那么服務(wù)路由都有哪些規(guī)則呢?
服務(wù)路由的規(guī)則
根據(jù)我的實(shí)踐經(jīng)驗(yàn),服務(wù)路由主要有兩種規(guī)則:一種是條件路由,一種是腳本路由。
1. 條件路由
條件路由是基于條件表達(dá)式的路由規(guī)則,以下面的條件路由為例,我來給你詳細(xì)講解下它的用法。
- condition://0.0.0.0/dubbo.test.interfaces.TestService?category=routers&dynamic=true&priority=2&enabled=true&rule=" + URL.encode(" host = 10.20.153.10=> host = 10.20.153.11")
這里面“condition://”代表了這是一段用條件表達(dá)式編寫的路由規(guī)則,具體的規(guī)則是
- host = 10.20.153.10 => host = 10.20.153.11
分隔符“=>”前面是服務(wù)消費(fèi)者的匹配條件,后面是服務(wù)提供者的過濾條件。當(dāng)服務(wù)消費(fèi)者節(jié)點(diǎn)滿足匹配條件時(shí),就對(duì)該服務(wù)消費(fèi)者執(zhí)行后面的過濾規(guī)則。那么上面這段表達(dá)式表達(dá)的意義就是 IP 為“10.20.153.10”的服務(wù)消費(fèi)者都調(diào)用 IP 為“10.20.153.11”的服務(wù)提供者節(jié)點(diǎn)。
如果服務(wù)消費(fèi)者的匹配條件為空,就表示對(duì)所有的服務(wù)消費(fèi)者應(yīng)用,就像下面的表達(dá)式一樣。
- => host != 10.20.153.11
如果服務(wù)提供者的過濾條件為空,就表示禁止服務(wù)消費(fèi)者訪問,就像下面的表達(dá)式一樣。
- host = 10.20.153.10=>
下面我舉一些 Dubbo 框架中的條件路由,來給你講解下條件路由的具體應(yīng)用場景。
- 排除某個(gè)服務(wù)節(jié)點(diǎn)
- => host != 172.22.3.91
一旦這條路由規(guī)則被應(yīng)用到線上,所有的服務(wù)消費(fèi)者都不會(huì)訪問 IP 為 172.22.3.91 的服務(wù)節(jié)點(diǎn),這種路由規(guī)則一般應(yīng)用在線上流量排除預(yù)發(fā)布機(jī)以及摘除某個(gè)故障節(jié)點(diǎn)的場景。
- 白名單和黑名單功能
- host != 10.20.153.10,10.20.153.11 =>
這條路由規(guī)則意思是除了 IP 為 10.20.153.10 和 10.20.153.11 的服務(wù)消費(fèi)者可以發(fā)起服務(wù)調(diào)用以外,其他服務(wù)消費(fèi)者都不可以,主要用于白名單訪問邏輯,比如某個(gè)后臺(tái)服務(wù)只允許特定的幾臺(tái)機(jī)器才可以訪問,這樣的話可以機(jī)器控制訪問權(quán)限。
- host = 10.20.153.10,10.20.153.11 =>
同理,這條路由規(guī)則意思是除了 IP 為 10.20.153.10 和 10.20.153.11 的服務(wù)消費(fèi)者不能發(fā)起服務(wù)調(diào)用以外,其他服務(wù)消費(fèi)者都可以,也就是實(shí)現(xiàn)了黑名單功能,比如線上經(jīng)常會(huì)遇到某些調(diào)用方不管是出于有意還是無意的不合理調(diào)用,影響了服務(wù)的穩(wěn)定性,這時(shí)候可以通過黑名單功能暫時(shí)予以封殺。
- 機(jī)房隔離
- host = 172.22.3.* => host = 172.22.3.*
這條路由規(guī)則意思是 IP 網(wǎng)段為 172.22.3.* 的服務(wù)消費(fèi)者,才可以訪問同網(wǎng)段的服務(wù)節(jié)點(diǎn),這種規(guī)則一般應(yīng)用于服務(wù)部署在多個(gè) IDC,理論上同一個(gè) IDC 內(nèi)的調(diào)用性能要比跨 IDC 調(diào)用性能要好,應(yīng)用這個(gè)規(guī)則是為了實(shí)現(xiàn)同 IDC 就近訪問。
- 讀寫分離
- method = find*,list*,get*,is* => host =172.22.3.94,172.22.3.95
- method != find*,list*,get*,is* => host = 172.22.3.97,172.22.3.98
這條路由規(guī)則意思是 find*、get*、is* 等讀方法調(diào)用 IP 為 172.22.3.94 和 172.22.3.95 的節(jié)點(diǎn),除此以外的寫方法調(diào)用 IP 為 172.22.3.97 和 172.22.3.98 的節(jié)點(diǎn)。對(duì)于大部分互聯(lián)網(wǎng)業(yè)務(wù)來說,往往讀請求要遠(yuǎn)遠(yuǎn)大于寫請求,而寫請求的重要性往往要遠(yuǎn)遠(yuǎn)高于讀請求,所以需要把讀寫請求進(jìn)行分離,以避免讀請求異常影響到寫請求,這時(shí)候就可以應(yīng)用這種規(guī)則。
2. 腳本路由
腳本路由是基于腳本語言的路由規(guī)則,常用的腳本語言比如 JavaScript、Groovy、JRuby 等。以下面的腳本路由規(guī)則為例,我來給你詳細(xì)講解它的用法。
- "script://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule=" + URL.encode("(function route(invokers) { ... } (invokers))")
這里面“script://”就代表了這是一段腳本語言編寫的路由規(guī)則,具體規(guī)則定義在腳本語言的 route 方法實(shí)現(xiàn)里,比如下面這段用 JavaScript 編寫的 route() 方法表達(dá)的意思是,只有 IP 為 10.20.153.10 的服務(wù)消費(fèi)者可以發(fā)起服務(wù)調(diào)用。
- function route(invokers){
- var result = new java.util.ArrayList(invokers.size());
- for(i =0; i < invokers.size(); i ++){
- if("10.20.153.10".equals(invokers.get(i).getUrl().getHost())){
- result.add(invokers.get(i));
- }
- }
- return result;
- } (invokers));
既然服務(wù)路由是通過路由規(guī)則來實(shí)現(xiàn)的,那么服務(wù)消費(fèi)者該如何獲取路由規(guī)則呢?
服務(wù)路由的獲取方式
根據(jù)我的實(shí)踐經(jīng)驗(yàn),服務(wù)路由的獲取方式主要有三種:
- 本地配置
顧名思義就是路由規(guī)則存儲(chǔ)在服務(wù)消費(fèi)者本地上。服務(wù)消費(fèi)者發(fā)起調(diào)用時(shí),從本地固定位置讀取路由規(guī)則,然后按照路由規(guī)則選取一個(gè)服務(wù)節(jié)點(diǎn)發(fā)起調(diào)用。
- 配置中心管理
這種方式下,所有的服務(wù)消費(fèi)者都從配置中心獲取路由規(guī)則,由配置中心來統(tǒng)一管理。
- 動(dòng)態(tài)下發(fā)
這種方式下,一般是運(yùn)維人員或者開發(fā)人員,通過服務(wù)治理平臺(tái)修改路由規(guī)則,服務(wù)治理平臺(tái)調(diào)用配置中心接口,把修改后的路由規(guī)則持久化到配置中心。因?yàn)榉?wù)消費(fèi)者訂閱了路由規(guī)則的變更,于是就會(huì)從配置中心獲取最新的路由規(guī)則,按照最新的路由規(guī)則來執(zhí)行。
根據(jù)我的實(shí)踐經(jīng)驗(yàn),上面三種方式實(shí)際使用時(shí),還是有一定區(qū)別的。
一般來講,服務(wù)路由最好是存儲(chǔ)在配置中心中,由配置中心來統(tǒng)一管理。這樣的話,所有的服務(wù)消費(fèi)者就不需要在本地管理服務(wù)路由,因?yàn)榇蟛糠值姆?wù)消費(fèi)者并不關(guān)心服務(wù)路由的問題,或者說也不需要去了解其中的細(xì)節(jié)。通過配置中心,統(tǒng)一給各個(gè)服務(wù)消費(fèi)者下發(fā)統(tǒng)一的服務(wù)路由,節(jié)省了溝通和管理成本。
但也不排除某些服務(wù)消費(fèi)者有特定的需求,需要定制自己的路由規(guī)則,這個(gè)時(shí)候就適合通過本地配置來定制。
而動(dòng)態(tài)下發(fā)可以理解為一種高級(jí)功能,它能夠動(dòng)態(tài)地修改路由規(guī)則,在某些業(yè)務(wù)場景下十分有用。比如某個(gè)數(shù)據(jù)中心存在問題,需要把調(diào)用這個(gè)數(shù)據(jù)中心的服務(wù)消費(fèi)者都切換到其他數(shù)據(jù)中心,這時(shí)就可以通過動(dòng)態(tài)下發(fā)的方式,向配置中心下發(fā)一條路由規(guī)則,將所有調(diào)用這個(gè)數(shù)據(jù)中心的請求都遷移到別的地方。
當(dāng)然,這三種方式也可以一起使用,這個(gè)時(shí)候服務(wù)消費(fèi)者的判斷優(yōu)先級(jí)是本地配置 > 動(dòng)態(tài)下發(fā) > 配置中心管理。
總結(jié)
今天我給你講解了服務(wù)路由的作用,簡單來講就是為了實(shí)現(xiàn)某些調(diào)用的特殊需求,比如分組調(diào)用、灰度發(fā)布、流量切換、讀寫分離等。在業(yè)務(wù)規(guī)模比較小的時(shí)候,可能所有的服務(wù)節(jié)點(diǎn)都部署在一起,也就不需要服務(wù)路由。但隨著業(yè)務(wù)規(guī)模的擴(kuò)大、服務(wù)節(jié)點(diǎn)增多,尤其是涉及多數(shù)據(jù)中心部署的情況,把服務(wù)節(jié)點(diǎn)按照數(shù)據(jù)中心進(jìn)行分組,或者按照業(yè)務(wù)的核心程度進(jìn)行分組,對(duì)提高服務(wù)的可用性是十分有用的。以微博業(yè)務(wù)為例,有的服務(wù)不僅進(jìn)行了核心服務(wù)和非核心服務(wù)分組,還針對(duì)私有云和公有云所處的不同數(shù)據(jù)中心也進(jìn)行了分組,這樣的話就可以將服務(wù)之間的調(diào)用盡量都限定在同一個(gè)數(shù)據(jù)中心內(nèi)部,最大限度避免跨數(shù)據(jù)中心的網(wǎng)絡(luò)延遲、抖動(dòng)等影響。
而服務(wù)路由具體是在本地配置,還是在配置中心統(tǒng)一管理,也是視具體業(yè)務(wù)需求而定的。如果沒有定制化的需求,建議把路由規(guī)則都放到配置中心中統(tǒng)一存儲(chǔ)管理。而動(dòng)態(tài)下發(fā)路由規(guī)則對(duì)于服務(wù)治理十分有幫助,當(dāng)數(shù)據(jù)中心出現(xiàn)故障的時(shí)候,可以實(shí)現(xiàn)動(dòng)態(tài)切換流量,還可以摘除一些有故障的服務(wù)節(jié)點(diǎn)。
思考題
在實(shí)際業(yè)務(wù)場景中,經(jīng)常有一類需求就是一個(gè)新功能在全量上線前,會(huì)圈一批用戶優(yōu)先適用,如果使用服務(wù)路由功能的話,你覺得可以怎么做?