【GoLang 那點事】gRPC 註冊中心,常用的註冊中心你懂了嗎?AP 還是 CP(七)

a_wei發表於2019-10-08

看完有幫助的話,大家點贊,關注,收藏,作者需要你們的鼓勵哦

gRPC是一個跨語言的微服務框架,但gRPC本身不支援微服務框架生態圈的一些功能,比如註冊中心,限流,熔斷等,今天我們就看看如何利用gRPC提供的介面實現簡單的註冊中心,文章將介紹什麼是註冊中心、註冊中心方案,常用的註冊中心實現方式,優劣,以及為gRPC-go實現一個註冊中心

什麼是註冊中心

註冊中心是微服務框架的一個標配,其核心作用是解耦服務提供者和服務消費者,服務提供者可以往註冊中心註冊自己提供了那些服務,服務消費者可以根據服務名稱查詢註冊中心有哪些服務提供者,從而解耦了消費者和提供者之間互相通訊協調,當一個服務有多個提供者時,消費者該選擇哪個呢?這是註冊中心還有一個作用負載均衡,註冊中心會根據某種演算法(隨機,輪詢,權重,流量)選擇一個合適服務提供者供消費者使用,那萬一這個服務提供者死亡了呢?這就又引出心跳檢測概念了,意思是服務提供者和註冊中心之間會定期通訊(幾十毫秒一次,幾秒一次)檢測服務提供者是否存活,註冊中心會定期移除沒有往註冊中心檢測心跳的服務提供者,以此保證給消費方提供的服務提供者是有效的。

註冊中心的兩種方案

集中式LB(Load Balance),也是代理模型(Proxy Model)

優點:這種方式比如nginx,F5等,本身就具備負載均衡演算法,心跳檢測,失敗重試,失敗轉移等功能,不需要開發人員再次開發,服務提供者往註冊中心註冊自己的ip地址,消費者訪問註冊中心,註冊中心通過演算法處理後轉發到服務提供者之後返回處理結果,註冊中心再把處理結果返回給消費者,
缺點:比如,不支援動態的新增服務提供者,併發量大以後,代理會成為瓶頸,代理也有可能會成為單點等問題。

gRPC註冊中心,常用的註冊中心你懂了嗎?AP還是CP(七)

程式內LB(Load Balance), 軟負載

優點:這種方式解決了上面方式的缺點,註冊中心本身支援高可用,可擴充套件,服務提供者啟動後往註冊中心註冊自己,消費者啟動後拉去註冊中心服務提供者列表,快取一份,請求到來時,可走本地快取,也可查詢註冊中心重新獲取,負載均衡演算法,失效轉移等在客戶端實現,自控性強,少了一層代理,效能也會更好。

缺點: 需要一定的研發實力,開發起來複雜,如果公司使用多語言開發專案,需要為每種語言都實現一個客戶端,如果服務端升級,勢必也要求客戶端升級重新發布等。

gRPC註冊中心,常用的註冊中心你懂了嗎?AP還是CP(七)

常用的註冊中心以及各自的優劣

  • 事實上,只要具備儲存功能或者配置功能的一些軟體或者硬體都可以作為註冊中心來使用,就看這個註冊中心的完備性,高可用性,可擴充套件性是否足夠好。
  • 如nginx,redis,mysql,zookeeper,etcd,eureka,consul等都可以作為註冊中心
nginx

在公司規模不大,要求不高的情況下的,採用http協議通訊,可以選用nginx來作為註冊中心,使用簡單,不需要額外的開發工作,往nginx配置檔案配置服務端ip地址,配置負載均衡策略,服務消費者只需要配置nginx的ip,就可以通過http請求形式訪問到服務提供者後端,nginx做了一層代理,提供了負載均衡,失效轉移,心跳檢測,服務轉發等功能,能讓開發人員開箱即用,這其實就是上面的第一種方案,但缺點也很明顯,如同上面所說,所以在公司發展初期(流量不大),是能快速提高研發效率,節省成本,專心發展業務的一種手段。

zookeeper

提到註冊中心,zk就不得不說,dubbo開源時註冊中心預設的方案就是zk,直到現在也有很多公司使用dubbo,也採用zk作為註冊中心,zk能作為註冊中心主要是zk提供了一種臨時節點和節點監控的功能,服務提供啟動時往zk某個目錄下(一般是服務名稱)建立臨時節點,節點一建立就會通知監控該服務的消費者服務消費者就會獲取到,然後服務提供者和zk之間維持心跳檢測,因為該節點是臨時節點,所以一旦心跳失敗,zk就會刪除臨時節點,同時服務消費者也會收到通知,這樣就摘除了不健康的節點。
問題:看上去很完美,沒有問題,但真的是這樣嗎,zk是採用zab協議的來保證分散式中的一致性,zab全稱(Zookeeper Atomic Broadcast)),zk在選舉的過程中的為了保證最終一致性是不能寫入和讀取的,也就是說zk在選舉過程中是不可用的,所以從cap理論來說zk屬於cp,而註冊中心往往的要求的是ap,同時,zk寫入只能在leader節點操作,當有大量服務提供者同時啟動時,有可能造成leader節點負載過大,從而當機,然後重新選舉,當機,形成惡性迴圈,所以zk並不太適合作為註冊中心,但這也僅針對一些大公司,一些中小公司並沒那麼大的業務量,為了節約成本,選用zk也是可以的。zk是使用Java語言開發的,所以天然的和Java無縫整合,有很多Java的zk客戶端

eureka

eureka是開源微服務方案解決框架springcloud的一個元件,作為springcloud的註冊中心,為什麼springcloud要選擇eureka作為註冊中心呢?


Eureka是一個cs架構,提供基於rest服務的服務註冊和服務發現元件,服務提供者啟動後往EurekaServer端註冊提供者資訊,服務註冊後,服務提供者會維護一個心跳用來持續告訴EurekaServer:"我還活著,"以防止Eureka Server的”剔除任務“將該服務例項從服務列表中排除出去,eureka多個server端互相同步服務提供者資訊,也就是說只要有一個EurekaServer端存活,那麼就滿足高可用,一個EurekaServer叢集各個節點是平等的,服務消費者引入client的jar包,啟動後從server端獲取服務提供者息,快取本地(預設30秒更新一次),即便所有eureka的server死亡,線上的服務提供者,消費者也能正常使用。

etcd

etcd是一個採用go語言的開發的類似zk的分散式協調服務框架,是一個用於共享配置,服務發現的分散式,一致性kv儲存系統,etcd在實現上,一致性協議選擇上,運維上,安全上比zk要優秀好多,etcd提供HTTP+JSON, gRPC介面,跨平臺跨語言,這一點要比zk好很多,對多語言開發者來說是福音,etcd也支援http所訪問,安全性好。


etcd同zk一樣也是cp系統,在leader節點選舉時,讀寫不可用,下面我們gRPC-go也是採用etcd實現一個註冊中心

gRPC-go實現註冊中心

gRPC本身沒有提供註冊中心,但為開發者提供了實現註冊中心的介面,開發者是要實現其介面可以了,而且gRPC也提供了一個DNS的demo,下面我們一起看一看這個介面。


//介面所在路徑如下,grpc版本是1.23.1
google.golang.org/grpc@v1.23.1/resolver/resolver.go

//儲存呼叫物件的資訊
type Target struct {
    Scheme    string
    Authority string
    Endpoint  string
}
type BuildOption struct {
    //禁止服務的配置 
    DisableServiceConfig bool
}
type Builder interface {
    //構造一個Resolver
    Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error)
    //返回名稱空間字串
    Scheme() string
}
type ResolveNowOption struct{}
type Resolver interface {
    //GRPC將呼叫resolveNow來嘗試解析目標名稱
    ResolveNow(ResolveNowOption)
    //關閉這個解析器
    Close()
}

接下來我們實現這些介面完成一個簡單的註冊中心,程式碼地址如下:

https://github.com/sunpengwei1992/go_common/tree/master/grpc/helloworld_demo

核心流程如下:

  • 定義服務資訊的結構體
  • 定義註冊服務和下線服務的介面,並實現
  • 實現gRPC提供的介面
//服務描述資訊
type ServiceDescInfo struct {
    //服務名稱
    ServiceName string
    //ip地址
    Host string
    //埠
    Port int
    //心跳間隔 秒
    IntervalTime time.Duration
}

//服務註冊和下線的介面
type RegisterI interface {
    //服務註冊
    Register(serviceInfo ServiceDescInfo) error
    //服務下線
    UnRegister(serviceInfo ServiceDescInfo) error
}

實現gRPC提供的介面,定義etcdBuild結構體,定義etcdResolver結構體

type etcdBuilder struct {
    //etcd地址
    address     string
    //etcd客戶端
    client      *clientv3.Client
    //服務的名稱
    serviceName string
}

//etcd解析結構體
type etcdResolver struct {
    clientConn              *resolver.ClientConn
    etcdBuilder             *etcdBuilder
    t                           *time.Ticker
    wg                        sync.WaitGroup
    rn                         chan struct{}
    ctx                        context.Context
    cancel                    context.CancelFunc
    disableServiceConfig bool
}
//具體的實現可以檢視github程式碼

歡迎大家關注微信公眾號:“golang那點事”,更多精彩期待你的到來

gRPC註冊中心,常用的註冊中心你懂了嗎?AP還是CP(七)

那小子阿偉

相關文章