Go Micro(6)——構(gòu)建彈性、高容錯(cuò)的應(yīng)用
構(gòu)建分布式系統(tǒng)是很有挑戰(zhàn)性的,這毫無(wú)疑問(wèn)。雖然我們已經(jīng)解決了很多工程上的問(wèn)題,我們?nèi)匀恢貜?fù)的在構(gòu)建許多模塊。目前,由于我們開(kāi)始了更高級(jí)別的抽象,虛擬機(jī)到容器技術(shù),適應(yīng)新的語(yǔ)言,作用于云計(jì)算,都對(duì)微服務(wù)提出了要求。總有一些事情需要我們不斷的去學(xué)習(xí),怎樣構(gòu)建高性能的、高容錯(cuò)的系統(tǒng)仍然是下一波的技術(shù)浪潮。
重復(fù)與創(chuàng)新之間的戰(zhàn)爭(zhēng)從未停止,但我們需要做一些事情,通過(guò)云計(jì)算、容器技術(shù)、微服務(wù)來(lái)緩解我們的痛苦。
動(dòng)機(jī)
我們?yōu)槭裁催@樣做?為什么我們持續(xù)的重新構(gòu)建同樣的模塊,為什么我們持續(xù)的嘗試解決大規(guī)模、容錯(cuò)性和分布式系統(tǒng)的問(wèn)題?
我腦海中出現(xiàn)的是『bigger, stronger, faster』,或者是『speed, scale, agility』,你可能經(jīng)常從 C
級(jí)別的管理人員口中聽(tīng)到這個(gè)說(shuō)法。但關(guān)鍵的是,確實(shí)存在這樣的需求,需要我們構(gòu)建更加高性能和要彈性的系統(tǒng)。
在互聯(lián)網(wǎng)的早期,只有數(shù)千或者數(shù)萬(wàn)用戶在線,隨著時(shí)間的推移,我們看到開(kāi)始加速,現(xiàn)在我們面對(duì)的是數(shù)十億用戶和數(shù)十億的設(shè)備。我們需要學(xué)習(xí)怎樣為目前的情況構(gòu)建系統(tǒng)。
上一代的人也許記得 C10K problem,我不確定我們現(xiàn)在處在什么階段,但我想我們現(xiàn)在談?wù)摰氖前偃f(wàn)級(jí)的并發(fā)。世界上最大的技術(shù)公司,在10年前就已經(jīng)解決了這個(gè)問(wèn)題,也有了模式來(lái)構(gòu)建這樣大規(guī)模的系統(tǒng)。但剩下的其他人仍然在學(xué)習(xí)。
像 Amazon,Google,Microsoft 現(xiàn)在提供給我們的云計(jì)算平臺(tái),對(duì)大規(guī)模部署是有益的。但我們?nèi)匀慌υ诟闱宄鯓泳帉?xiě)應(yīng)用程序,可以高效的利用這些大規(guī)模的資源。你也許這些天聽(tīng)說(shuō)了容器的編排,微服務(wù)、云計(jì)算很多了。工作在很多層面上推進(jìn)著,當(dāng)我們完全確定了我們的模式,確定了需要解決的問(wèn)題,我們的 Micro
就能作為工業(yè)級(jí)的產(chǎn)品發(fā)布了,這還需要一段時(shí)間。
許多公司現(xiàn)在求助的問(wèn)題是『我該怎樣構(gòu)建可擴(kuò)展的、高容錯(cuò)的系統(tǒng)?』但目前對(duì)這些重要的問(wèn)題,有幫助的回答是很少的。
我該怎樣編寫(xiě)可擴(kuò)展的、高容錯(cuò)的系統(tǒng)?
Micro
看起來(lái)通過(guò)專(zhuān)注于微服務(wù)的必要的軟件開(kāi)發(fā)工具,定位了問(wèn)題。我們會(huì)詳細(xì)的談?wù)勗鯓訋椭銟?gòu)建有彈性的、高容錯(cuò)的系統(tǒng),我們從 client
端開(kāi)始。
客戶端
客戶端在 go-micro
中是用于發(fā)起請(qǐng)求的模塊,如果你已經(jīng)構(gòu)建過(guò)微服務(wù)或者 SOA
架構(gòu),你會(huì)知道重要的一部分時(shí)間和執(zhí)行過(guò)程是花在調(diào)用其他服務(wù)獲取相關(guān)信息上面。
然而在巨大的應(yīng)用中,關(guān)注點(diǎn)主要在接受請(qǐng)求返回內(nèi)容,在微服務(wù)世界中,更像是取回或者發(fā)布內(nèi)容。
這里是精簡(jiǎn)過(guò)的 go-micro
中 client
的接口,有最重要的三個(gè)方法 Call, Publish
和 Stream
。
type Client interface {
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
Publish(ctx context.Context, p Publication, opts ...PublishOption) error
Stream(ctx context.Context, req Request, opts ...CallOption) (Streamer, error)
}
type Request interface {
Service() string
Method() string
ContentType() string
Request() interface{}
Stream() bool
}
Call
和 Stream
是用來(lái)做同步通信請(qǐng)求,Call
返回一個(gè)單一的結(jié)果,而 Stream
是一個(gè)雙向的流式連接,與另一個(gè)服務(wù)維持著,其中任何消息都可以發(fā)進(jìn)來(lái)也可以發(fā)出去。Publish
用于發(fā)布異步的消息,通過(guò) broker
,但我們今天不討論它。
客戶端是怎樣工作的,前面的文章已經(jīng)討論過(guò)了。翻看以前的文章即可。
我們只是特別的討論一些重要的內(nèi)部細(xì)節(jié)。
客戶端使用 RPC
層,結(jié)合 broker,codec,register,selector
和 transport
來(lái)提供豐富的組合。分層的架構(gòu)非常重要,所以我們可以把單個(gè)的組件進(jìn)行分離,減少了整體的復(fù)雜性,也提供了插件化的能力。
為什么客戶端重要?
客戶端本質(zhì)上抽象了與服務(wù)端之間的有彈性的、高容錯(cuò)的通信過(guò)程。像另一個(gè)服務(wù)發(fā)起請(qǐng)求看起來(lái)是非常直接的,但有幾種情況是可能潛在的發(fā)生失敗的。
下面我們開(kāi)始了解這些功能和他們是怎樣運(yùn)作的。
服務(wù)發(fā)現(xiàn)
在分布式系統(tǒng)中,服務(wù)因?yàn)楦鞣N原因會(huì)頻繁的加入和脫離集群。網(wǎng)絡(luò)隔離、機(jī)器故障、調(diào)度等等。我們并不真正想關(guān)心它們。
當(dāng)我們像另一個(gè)服務(wù)發(fā)起請(qǐng)求,我們通過(guò)名字識(shí)別服務(wù),并允許客戶端通過(guò)服務(wù)發(fā)現(xiàn)獲取到服務(wù)的一系列實(shí)例,得到各個(gè)實(shí)例的地址和端口。服務(wù)在啟動(dòng)時(shí)在服務(wù)發(fā)現(xiàn)中心進(jìn)行注冊(cè),在退出時(shí)進(jìn)行注銷(xiāo)。
正如我們提到的,任何類(lèi)型的問(wèn)題都會(huì)出現(xiàn)在分布式系統(tǒng),服務(wù)發(fā)現(xiàn)也不例外。所以我們依賴于經(jīng)過(guò)嚴(yán)格測(cè)試的分布式服務(wù)發(fā)現(xiàn)系統(tǒng),例如 consul、etcd
和 zookeeper
,使用它們存儲(chǔ)服務(wù)的信息。
它們都使用基于 Paxos
的 Raft
算法來(lái)進(jìn)行網(wǎng)絡(luò)選舉,這解決了我們的一致性問(wèn)題。通過(guò)運(yùn)行一個(gè)3到5個(gè)節(jié)點(diǎn)的集群,我們可以容忍大部分的系統(tǒng)故障,為客戶端提供穩(wěn)定可靠的服務(wù)。
節(jié)點(diǎn)選擇
現(xiàn)在我們可靠的把服務(wù)名字解析到了一堆地址列表。我們?cè)鯓舆x擇其中的一個(gè)進(jìn)行調(diào)用呢?這就是 go-micro
中的 selector
發(fā)揮作用的地方。它基于 register
模塊構(gòu)建,提供負(fù)載均衡策略,比如輪詢或者隨機(jī),也提供過(guò)濾、緩存和黑名單的功能。
這里是定義的接口:
type Selector interface {
Select(service string, opts ...SelectOption) (Next, error)
Mark(service string, node *registry.Node, err error)
Reset(service string)
}
type Next func() (*registry.Node, error)
type Filter func([]*registry.Service) []*registry.Service
type Strategy func([]*registry.Service) Next
負(fù)載策略
當(dāng)前的策略是非常簡(jiǎn)單直接的,當(dāng) Selector
被調(diào)用時(shí),它從 register
獲取到服務(wù),然后創(chuàng)建一個(gè) Next
函數(shù),從節(jié)點(diǎn)池中選擇出符合要求的節(jié)點(diǎn)。
客戶端會(huì)調(diào)用這個(gè) Next
函數(shù),根據(jù)負(fù)載均衡策略,獲取到下一個(gè)符合要求的節(jié)點(diǎn),并發(fā)出請(qǐng)求。如果這個(gè)請(qǐng)求失敗了,而且重試次數(shù)大于1,它會(huì)使用相同的程序,獲取下一個(gè)節(jié)點(diǎn),再次調(diào)用。
這里是有很多不同的策略的,比如輪詢、隨機(jī)、最少連接、權(quán)重等等。負(fù)載均衡策略對(duì)于分布式系統(tǒng)是必不可少的。
緩存
雖然有一個(gè)可靠的服務(wù)發(fā)現(xiàn)系統(tǒng)是很好的,但每次請(qǐng)求都去查詢一次并不高效。如果你想象一個(gè)大規(guī)模的系統(tǒng),每個(gè)服務(wù)都這樣做,很容易就會(huì)使服務(wù)發(fā)現(xiàn)系統(tǒng)過(guò)載。這會(huì)讓整個(gè)系統(tǒng)不可用。
為了避免這種情況,我們可以使用緩存。大部分的服務(wù)發(fā)現(xiàn)系統(tǒng)提供了一個(gè)監(jiān)聽(tīng)更新的機(jī)制,一般來(lái)說(shuō)叫做 Watcher
。不是去輪詢服務(wù)發(fā)現(xiàn)系統(tǒng),而是等待事件發(fā)送給我們。go-micro
的 Registry
提供了 Watch
的概念。
我們已經(jīng)編寫(xiě)了一個(gè)帶緩存的 selector
,它把服務(wù)緩存在內(nèi)存中。如果緩存中不存在時(shí),它會(huì)去服務(wù)發(fā)現(xiàn)系統(tǒng)查找,緩存,并用于之后的請(qǐng)求。如果 watch
事件收到了,緩存模塊會(huì)與 register
進(jìn)行更新。
首先,通過(guò)移除服務(wù)查找,大大的提高了性能。這也提供了一定的容錯(cuò),萬(wàn)一服務(wù)發(fā)現(xiàn)系統(tǒng)宕機(jī)了呢?我們?nèi)匀挥幸稽c(diǎn)偏執(zhí),害怕緩存由于節(jié)點(diǎn)發(fā)生故障而被污染了,所以節(jié)點(diǎn)都維持著合適的 TTL
。
黑名單
下面介紹一下黑名單,注意一下 Selector
的接口有 Mark
和 Reset
方法。我們不同真正的保證,注冊(cè)進(jìn)來(lái)的節(jié)點(diǎn)都是健康的,所以我們需要做黑名單。
任何一個(gè)請(qǐng)求發(fā)送之后,我們都會(huì)跟蹤它的結(jié)果。如果這個(gè)服務(wù)的實(shí)例出現(xiàn)了多次失敗,我們就可以大體上把這個(gè)節(jié)點(diǎn)加入黑名單,并過(guò)濾掉它。
節(jié)點(diǎn)在回到節(jié)點(diǎn)池之前會(huì)在黑名單中會(huì)存在一段時(shí)間,這是很?chē)?yán)格的,如果這個(gè)節(jié)點(diǎn)失敗了我們就需要移除掉它。這樣我們可以持續(xù)的返回成功的請(qǐng)求,不會(huì)有任何延遲。
超時(shí)與重試
Adrian Cockroft 最近開(kāi)始討論在微服務(wù)架構(gòu)中消失的組件,其中一個(gè)有意思的是,傳統(tǒng)的超時(shí)和重試策略導(dǎo)致了雪崩效應(yīng)。我建議你看看這個(gè)演示。這個(gè)演示把問(wèn)題總結(jié)的特別好。
Adrian 在上面描述的是一種常見(jiàn)的情況,一個(gè)緩慢的響應(yīng)會(huì)導(dǎo)致超時(shí),然后客戶端會(huì)觸發(fā)重試。這事實(shí)上是一個(gè)請(qǐng)求鏈路,這創(chuàng)造了一系列的新請(qǐng)求,而舊有的請(qǐng)求仍然在處理中。這樣的配置失誤會(huì)導(dǎo)致大量服務(wù)的過(guò)載,造成的調(diào)用失敗是很難回滾的。
在微服務(wù)世界,我們需要重新想想,處理重試和超時(shí)的策略。Adrian 繼續(xù)討論了潛在的解決方案。其中一種方式是超時(shí)之后,在新的節(jié)點(diǎn)上發(fā)起請(qǐng)求。
在重試的這方面,我們已經(jīng)在 Micro
中使用了。重試的次數(shù)可以進(jìn)行配置,如果你調(diào)用了一個(gè)失敗的節(jié)點(diǎn),客戶端會(huì)在新的節(jié)點(diǎn)發(fā)起重試。
超時(shí)經(jīng)常被深思熟慮,但事實(shí)上經(jīng)常從傳統(tǒng)的靜態(tài)超時(shí)設(shè)置開(kāi)始。直到Adrian演示了他的想法,超時(shí)策略變得很清晰了。
預(yù)算型超時(shí)策略現(xiàn)在也內(nèi)置在Micro
中,讓我們看看它是怎樣工作的。
第一個(gè)調(diào)用設(shè)置了超時(shí),每個(gè)調(diào)用鏈上的請(qǐng)求都會(huì)消耗整體的超時(shí)時(shí)間。如果時(shí)間為0了,我們就會(huì)停止請(qǐng)求或重試,并返回調(diào)用鏈。
按照 Adrian 提到的,提供動(dòng)態(tài)的預(yù)算型超時(shí)是非常好的,避免了不必要的雪崩。
更遠(yuǎn)一點(diǎn)來(lái)說(shuō),下一步應(yīng)該是移除任何類(lèi)型靜態(tài)的超時(shí)。服務(wù)的響應(yīng)時(shí)間根據(jù)環(huán)境的不同,請(qǐng)求的不同是不同的。這應(yīng)該是動(dòng)態(tài)的 SLA,根據(jù)當(dāng)前的狀態(tài)進(jìn)行調(diào)整,但這些事會(huì)留在未來(lái)解決。
連接池
連接池是構(gòu)建可擴(kuò)展系統(tǒng)的很重要部分,我們很快就看到了沒(méi)有連接池的局限性,經(jīng)常導(dǎo)致文件描述符數(shù)量達(dá)到限制,導(dǎo)致端口用盡。
目前有個(gè)進(jìn)行中的 PR 為 go-micro
增加了連接池,由于 Micro
插件化的特性,把連接池放在 transport
的上層很重要,這樣 HTTP,NATS,RabbitMQ
等等,都會(huì)受益。
你也許會(huì)想,這是特定實(shí)現(xiàn)的,一些 transport
也許已經(jīng)支持了。這是對(duì)的,不能總是保證在不同的 transport
下工作效果是一樣的。通過(guò)把這個(gè)放置于上層,我們減少了 transport
模塊的復(fù)雜性。
其他?
確實(shí)有很多好用的東西是go-micro內(nèi)置的,那么還有什么呢?我很高興你這么問(wèn)…
服務(wù)版本
我們有這個(gè)功能,這個(gè)功能在前面的文章也討論過(guò)了。服務(wù)包括名字和版本,注冊(cè)在服務(wù)發(fā)現(xiàn)系統(tǒng)。當(dāng)一個(gè)服務(wù)從注冊(cè)器中查詢出來(lái)時(shí),它的節(jié)點(diǎn)是按照版本分組的。這樣一樣,selector
就可以根據(jù)版本,進(jìn)行流量負(fù)載。
為什么版本很重要
當(dāng)我們發(fā)布新版本時(shí),這非常重要,它可以確保所有事情運(yùn)作正常,這樣才能把所有服務(wù)進(jìn)行升級(jí)。新版本可以被部署到一個(gè)小型的節(jié)點(diǎn)上,客戶端會(huì)自動(dòng)的分發(fā)一定比例的請(qǐng)求到這個(gè)新的節(jié)點(diǎn)。通過(guò)結(jié)合一些編排系統(tǒng)比如 Kubernetes,你可以非常有信心的部署,一旦有任何問(wèn)題也可以回滾。
過(guò)濾
我們也有,selector
是非常強(qiáng)大的,它有能力把過(guò)濾條件傳遞進(jìn)去,對(duì)節(jié)點(diǎn)進(jìn)行過(guò)濾。這在 client
端調(diào)用時(shí)可以傳遞參數(shù)。一些已經(jīng)存在的過(guò)濾可以在這里看到,比如 metadata,endpoint
和版本過(guò)濾。
為什么過(guò)濾重要
你也許有一些功能只在某些特定版本的服務(wù)上存在。需要將這些請(qǐng)求分發(fā)到這些特定版本的服務(wù)上。這是非常好的功能,特別是多個(gè)不同版本的服務(wù)在同時(shí)運(yùn)行時(shí)。
另外一個(gè)有用的地方是,你想要根據(jù)地區(qū)對(duì)服務(wù)進(jìn)行路由。通過(guò)設(shè)置數(shù)據(jù)中心的標(biāo)簽在服務(wù)上,你可以過(guò)濾出本地的節(jié)點(diǎn)。根據(jù) metadata 進(jìn)行過(guò)濾是非常強(qiáng)大的,希望有更多的應(yīng)用能夠把這個(gè)功能使用起來(lái)。
插件化的架構(gòu)
Micro
原生的插件化架構(gòu)是你一次又一次聽(tīng)到的。這從設(shè)計(jì)的第一天就已經(jīng)確定了。這是非常重要的,Micro
提供模塊來(lái)構(gòu)建整個(gè)系統(tǒng)。有時(shí)候的運(yùn)行會(huì)超出控制,但這些都可以改善。
為什么插件化很重要?
每個(gè)人對(duì)怎樣構(gòu)建分布式系統(tǒng)都有自己的想法,我們實(shí)際上是提供了一個(gè)方式,讓人們能設(shè)計(jì)他們想要的解決方案。不僅如此,現(xiàn)在也有很多經(jīng)過(guò)嚴(yán)格測(cè)試的工具,我們可以直接使用,而不是自己重寫(xiě)任何東西。
技術(shù)始終在進(jìn)化,全新的、更好的工具每天都在出現(xiàn)。我們?cè)鯓颖苊庵共讲磺?,插件化的架?gòu)意味著我們可以使用目前的組件,未來(lái)也可以使用更好的組件進(jìn)行替代。
插件
每個(gè) go-micro
的特性都被設(shè)計(jì)成 golang
中的接口,通過(guò)這樣做,我們可以實(shí)際上替換底層的實(shí)現(xiàn),這幾乎不需要進(jìn)行代碼改動(dòng)。在大部分情況下,只需要簡(jiǎn)單的引用這個(gè)包,然后在啟動(dòng)時(shí)加入?yún)?shù)就可以了。
在 go-plugins
有很多現(xiàn)成的插件可以使用。
go-micro
目前提供了默認(rèn)的 consul
作為服務(wù)發(fā)現(xiàn)系統(tǒng),http
作為 transport
,你也許會(huì)想要使用一些別的東西,或者實(shí)現(xiàn)自己的插件。我們已經(jīng)有社區(qū)的貢獻(xiàn)者分享了 Kubernetes 的注冊(cè)插件和 Zookeeper
的注冊(cè)插件。
怎樣使用插件
大部分時(shí)候,插件的使用類(lèi)似這樣:
# Import the plugin
import _ "github.com/micro/go-plugins/registry/etcd"
go run main.go --registry=etcd --registry_address=10.0.0.1:2379
Wrappers 中間件
客戶端和服務(wù)端都支持中間件的概念,稱為 wrapper
。通過(guò)支持中間件,我們可以增加在請(qǐng)求和返回的業(yè)務(wù)邏輯前面或者后面,添加自定義的邏輯。
中間件是很容易理解的概念,數(shù)以千計(jì)的庫(kù)在使用它。在處理崩潰、限制并發(fā)、認(rèn)證、日志、記錄等場(chǎng)景下,很容易發(fā)現(xiàn)它的妙處。
# Client Wrappers
type Wrapper func(Client) Client
type StreamWrapper func(Streamer) Streamer
# Server Wrappers
type HandlerWrapper func(HandlerFunc) HandlerFunc
type SubscriberWrapper func(SubscriberFunc) SubscriberFunc
type StreamerWrapper func(Streamer) Streamer
怎樣使用 Wrapper
這里是一個(gè)很直接的插件
import (
"github.com/micro/go-micro"
"github.com/micro/go-plugins/wrapper/breaker/hystrix"
)
func main() {
service := micro.NewService(
micro.Name("myservice"),
micro.WrapClient(hystrix.NewClientWrapper()),
)
}
很容易對(duì)不對(duì),我們發(fā)現(xiàn)很多公司在 Micro
上層,創(chuàng)建了自己的層級(jí),用于初始化大部分默認(rèn)的 wrapper
,所以所有的 wrapper
可以在同一個(gè)地方進(jìn)行添加。
現(xiàn)在我們看看 wrapper
怎樣讓?xiě)?yīng)用更有彈性,更能容錯(cuò)。
circuit breaker 斷路器
在SOA或者微服務(wù)世界,一個(gè)單獨(dú)的請(qǐng)求可能會(huì)調(diào)用多個(gè)服務(wù)。大部分情況下,聚合許多信息返回給調(diào)用者。在成功的情況下,它運(yùn)行的很好,但一旦發(fā)生錯(cuò)誤,很容易觸發(fā)雪崩式的錯(cuò)誤,除了重啟整個(gè)系統(tǒng),很難恢復(fù)。
我們部分的解決了這個(gè)問(wèn)題,通過(guò)在客戶端使用重試機(jī)制和黑名單。但在一些情況下,我們需要組織客戶端發(fā)起這個(gè)請(qǐng)求。
這里是 circuit breaker
怎樣起作用的
circuit breakers
的理念非常直接,方法的執(zhí)行是根據(jù)對(duì)失敗的情況進(jìn)行監(jiān)控而進(jìn)行封裝的。當(dāng)失敗的情況達(dá)到一個(gè)閾值時(shí),breaker
開(kāi)始起作用,任何未來(lái)的調(diào)用嘗試都會(huì)返回錯(cuò)誤,而不會(huì)調(diào)用實(shí)際的業(yè)務(wù)函數(shù)。在超時(shí)時(shí)間過(guò)了以后,進(jìn)入一個(gè)半開(kāi)狀態(tài)。如果某個(gè)請(qǐng)求失敗了,breaker
會(huì)再次生效,如果成功了就會(huì)恢復(fù)到正常。
雖然內(nèi)部的 Micro
客戶端有一些容錯(cuò)特性,但我們不應(yīng)該依賴它來(lái)解決所有問(wèn)題。在 wrapper
中使用 circuit breakers
讓我們受益很多。
Rate Limiting 速率限制 限流
如果我們非常輕松的能響應(yīng)世界上所有的請(qǐng)求,那就太好了,不過(guò)是在夢(mèng)里。真實(shí)的世界不是這樣工作的,執(zhí)行一個(gè)查詢需要消耗時(shí)間,資源的限制讓我們只能響應(yīng)一定數(shù)量的請(qǐng)求。
我們需要考慮限制發(fā)起請(qǐng)求的數(shù)量,或者限制并發(fā)響應(yīng)的數(shù)量。這就是 rate limiting
發(fā)揮作用的地方。如果沒(méi)有 rate limiting
,很容易會(huì)把資源耗盡,或者完全的讓系統(tǒng)崩潰,讓系統(tǒng)不能響應(yīng)未來(lái)的任何請(qǐng)求。這經(jīng)常是 DDOS
攻擊的常見(jiàn)做法。
每個(gè)人都聽(tīng)說(shuō)過(guò),使用過(guò)或者實(shí)現(xiàn)過(guò)一些類(lèi)型的 rate limiting
。這里有很多不同的算法,其中一種是 Leaky Bucket
算法,我們不會(huì)在這里展開(kāi),但值得一讀。
我們可以使用 Micro Wrapper
和已經(jīng)存在的庫(kù)來(lái)使用這個(gè)函數(shù),一個(gè)已經(jīng)存在的庫(kù)在這里。
我們實(shí)際上對(duì) YouTube 實(shí)現(xiàn)的 Doorman
算法很感興趣,一個(gè)全局的客戶端 rate limiter
,我們也在尋求社區(qū)的其他實(shí)現(xiàn)。
服務(wù)端
前面介紹了很多客戶端的很多特性和使用方式,那么服務(wù)端呢,第一件事需要注意的是 Micro
在 go-micro
的 API、CLI、Sidecar
等等都使用了客戶端,客戶端的特性讓整個(gè)架構(gòu)都收益,但我們?nèi)匀恍枰诜?wù)端解決一些問(wèn)題。
在客戶端,register
用于發(fā)現(xiàn)服務(wù),服務(wù)端進(jìn)行注冊(cè)。當(dāng)一個(gè)服務(wù)的實(shí)例運(yùn)行起來(lái)時(shí),它在服務(wù)發(fā)現(xiàn)系統(tǒng)進(jìn)行注冊(cè),在退出時(shí)進(jìn)行注銷(xiāo),關(guān)鍵詞是『gracefully』。
處理錯(cuò)誤
在分布式環(huán)境中,我們都需要處理錯(cuò)誤,我們需要容忍錯(cuò)誤。register
支持通過(guò)ttl來(lái)進(jìn)行過(guò)期檢查,一旦過(guò)期節(jié)點(diǎn)就是不健康的,底層的服務(wù)發(fā)現(xiàn)機(jī)制類(lèi)型 consul
都支持這些功能。同時(shí)服務(wù)端也支持重新注冊(cè)。這兩者的結(jié)合意味著,節(jié)點(diǎn)可以在間隔時(shí)間內(nèi)會(huì)重新注冊(cè),如果節(jié)點(diǎn)因?yàn)檫\(yùn)行失敗等等沒(méi)有重新注冊(cè),register
就會(huì)因?yàn)槌瑫r(shí)而認(rèn)為節(jié)點(diǎn)不健康,將節(jié)點(diǎn)從 register
刪除。
這種容錯(cuò)設(shè)計(jì)最先沒(méi)有出現(xiàn)在 go-micro
中,但我們很快發(fā)現(xiàn),在真實(shí)的世界中,因?yàn)榉?wù)的崩潰或其他原因程序退出時(shí),并沒(méi)有注銷(xiāo)自己,所以需要這種 ttl
健康監(jiān)測(cè)。
帶來(lái)的影響就是,客戶端需要處理一系列污染的請(qǐng)求??蛻舳艘残枰蒎e(cuò)性,我們認(rèn)為這樣的功能設(shè)計(jì)排除了許多明顯的問(wèn)題。
增加更多功能設(shè)計(jì)
另一件需要注意的事情是,服務(wù)端也提供了能力來(lái)使用 Wrapper
和中間件,這意味著我們也可以做 circuit breaking
, rate limiting
等其他一些特性。
服務(wù)端的這個(gè)功能故意的設(shè)計(jì)的簡(jiǎn)單,但插件化的特性可以讓你自由擴(kuò)展。
客戶端與Sidecar
大部分我們討論的都是存在于 go-micro
庫(kù)中,這對(duì)所有的 golang
使用者是很好的,但其他人在想,我怎樣從這里收益呢。
在最開(kāi)始,Micro
就包含了 Sidecar
的設(shè)計(jì)理念,這是一個(gè) HTTP
的代理,所有的 go-micro
的功能都內(nèi)置其中,所以不管你用哪種語(yǔ)言構(gòu)建你的應(yīng)用,你都可以收益于我們?cè)谏厦娴挠懻摗?/p>
sidecar
的設(shè)計(jì)模式并不是新東西,NetflixOSS
有一個(gè)叫做 Prana
的項(xiàng)目。Buoyant
有一個(gè)叫 Linkerd
的項(xiàng)目。
Micro Sidecar
使用了默認(rèn)的 go-micro
客戶端,如果你想使用其他功能,你可以添加參數(shù),很容易的重新編譯。我們會(huì)想辦法在未來(lái)簡(jiǎn)化這個(gè)程序。
還有更多
這里討論了許多 go-micro
的包和相關(guān)的工具,這些工具是很好的開(kāi)始,但他們還不夠。當(dāng)你想要運(yùn)行一個(gè)可擴(kuò)展的、數(shù)以百計(jì)的微服務(wù),處理數(shù)百萬(wàn)請(qǐng)求,仍然有許多問(wèn)題需要解決。
Platform
這是 go-platform 和 platform 發(fā)揮作用的地方了,micro
解決了基礎(chǔ)的組件,Platform
則更進(jìn)一步,解決運(yùn)行可擴(kuò)展的服務(wù)的更多問(wèn)題。比如認(rèn)證、分布式 trace、同步鎖、健康檢查等等。
分布式系統(tǒng)需要一系列的工具用于提高容錯(cuò)性,Platform
看起來(lái)會(huì)有幫助。通過(guò)提供一個(gè)分層的架構(gòu),我們可以在原始的核心工具上,構(gòu)建任何自己需要的功能。
Platform
仍然在早期,但 Platform
會(huì)解決大部分公司構(gòu)建分布式平臺(tái)時(shí)會(huì)遇到的問(wèn)題。
總結(jié)
科技在快速的進(jìn)化,云計(jì)算給了我們不受限制的擴(kuò)展能力。設(shè)法與變化保持同步很難,構(gòu)建一個(gè)可擴(kuò)展的,高容錯(cuò)的系統(tǒng)在今天仍然具有很大的挑戰(zhàn)。
但不應(yīng)該用以前的方式解決問(wèn)題,作為一個(gè)社區(qū),我們可以互相幫助,適應(yīng)這個(gè)新的環(huán)境,構(gòu)建隨著不斷增長(zhǎng)的需求而不斷擴(kuò)張的系統(tǒng)。
Micro
在這個(gè)過(guò)程中看起來(lái)提供了一些幫助,通過(guò)提供工具,簡(jiǎn)化了構(gòu)建和管理分布式系統(tǒng)。希望這個(gè)文章能示范我們處理這些問(wèn)題的方式。