基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

本篇文章給大家分享的是有關(guān)基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

在成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作中從網(wǎng)站色彩、結(jié)構(gòu)布局、欄目設(shè)置、關(guān)鍵詞群組等細(xì)微處著手,突出企業(yè)的產(chǎn)品/服務(wù)/品牌,幫助企業(yè)鎖定精準(zhǔn)用戶,提高在線咨詢和轉(zhuǎn)化,使成都網(wǎng)站營(yíng)銷(xiāo)成為有效果、有回報(bào)的無(wú)錫營(yíng)銷(xiāo)推廣。創(chuàng)新互聯(lián)專業(yè)成都網(wǎng)站建設(shè)10多年了,客戶滿意度97.8%,歡迎成都創(chuàng)新互聯(lián)客戶聯(lián)系。

gRPC是一個(gè)現(xiàn)代的、高性能、開(kāi)源的和語(yǔ)言無(wú)關(guān)的通用RPC框架,基于HTTP2協(xié)議設(shè)計(jì),序列化使用PB(Protocol Buffer),PB是一種語(yǔ)言無(wú)關(guān)的高性能序列化框架,基于HTTP2+PB保證了的高性能。go-zero是一個(gè)開(kāi)源的微服務(wù)框架,支持http和rpc協(xié)議,其中rpc底層依賴gRPC,本文會(huì)結(jié)合gRPC和go-zero源碼從實(shí)戰(zhàn)的角度和大家一起分析下服務(wù)注冊(cè)與發(fā)現(xiàn)和負(fù)載均衡的實(shí)現(xiàn)原理

基本原理

原理流程圖如下:

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

從圖中可以看出go-zero實(shí)現(xiàn)了gRPC的resolver和balancer接口,然后通過(guò)gprc.Register方法注冊(cè)到gRPC中,resolver模塊提供了服務(wù)注冊(cè)的功能,balancer模塊提供了負(fù)載均衡的功能。當(dāng)client發(fā)起服務(wù)調(diào)用的時(shí)候會(huì)根據(jù)resolver注冊(cè)進(jìn)來(lái)的服務(wù)列表,使用注冊(cè)進(jìn)來(lái)的balancer選擇一個(gè)服務(wù)發(fā)起請(qǐng)求,如果沒(méi)有進(jìn)行注冊(cè)gRPC會(huì)使用默認(rèn)的resolver和balancer。服務(wù)地址的變更會(huì)同步到etcd中,go-zero監(jiān)聽(tīng)etcd的變化通過(guò)resolver更新服務(wù)列表

Resolver模塊

通過(guò)resolver.Register方法可以注冊(cè)自定義的Resolver,Register方法定義如下,其中Builder為interface類(lèi)型,因此自定義resolver需要實(shí)現(xiàn)該接口,Builder定義如下

// Register 注冊(cè)自定義resolver
func Register(b Builder) {
	m[b.Scheme()] = b
}

// Builder 定義resolver builder
type Builder interface {
	Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error)
	Scheme() string
}

Build方法的第一個(gè)參數(shù)target的類(lèi)型為T(mén)arget定義如下,創(chuàng)建ClientConn調(diào)用grpc.DialContext的第二個(gè)參數(shù)target經(jīng)過(guò)解析后需要符合這個(gè)結(jié)構(gòu)定義,target定義格式為: scheme://authority/endpoint_name

type Target struct {
	Scheme    string // 表示要使用的名稱系統(tǒng)
	Authority string // 表示一些特定于方案的引導(dǎo)信息
	Endpoint  string // 指出一個(gè)具體的名字
}

Build方法返回的Resolver也是一個(gè)接口類(lèi)型。定義如下

type Resolver interface {
	ResolveNow(ResolveNowOptions)
	Close()
}

流程圖下圖

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

因此可以看出自定義Resolver需要實(shí)現(xiàn)如下步驟:

  • 定義target

  • 實(shí)現(xiàn)resolver.Builder

  • 實(shí)現(xiàn)resolver.Resolver

  • 調(diào)用resolver.Register注冊(cè)自定義的Resolver,其中name為target中的scheme

  • 實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)邏輯(etcd、consul、zookeeper)

  • 通過(guò)resolver.ClientConn實(shí)現(xiàn)服務(wù)地址的更新

go-zero中target的定義如下,默認(rèn)的名字為discov

// BuildDiscovTarget 構(gòu)建target
func BuildDiscovTarget(endpoints []string, key string) string {
	return fmt.Sprintf("%s://%s/%s", resolver.DiscovScheme,
		strings.Join(endpoints, resolver.EndpointSep), key)
}

// RegisterResolver 注冊(cè)自定義的Resolver
func RegisterResolver() {
	resolver.Register(&dirBuilder)
	resolver.Register(&disBuilder)
}

Build方法的實(shí)現(xiàn)如下

func (d *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (
	resolver.Resolver, error) {
	hosts := strings.FieldsFunc(target.Authority, func(r rune) bool {
		return r == EndpointSepChar
	})
  // 獲取服務(wù)列表
	sub, err := discov.NewSubscriber(hosts, target.Endpoint)
	if err != nil {
		return nil, err
	}

	update := func() {
		var addrs []resolver.Address
		for _, val := range subset(sub.Values(), subsetSize) {
			addrs = append(addrs, resolver.Address{
				Addr: val,
			})
		}
    // 調(diào)用UpdateState方法更新
		cc.UpdateState(resolver.State{
			Addresses: addrs,
		})
	}
  
  // 添加監(jiān)聽(tīng),當(dāng)服務(wù)地址發(fā)生變化會(huì)觸發(fā)更新
	sub.AddListener(update)
  // 更新服務(wù)列表
	update()

	return &nopResolver{cc: cc}, nil
}

那么注冊(cè)進(jìn)來(lái)的resolver在哪里用到的呢?當(dāng)創(chuàng)建客戶端的時(shí)候調(diào)用DialContext方法創(chuàng)建ClientConn的時(shí)候回進(jìn)行如下操作

  • 攔截器處理

  • 各種配置項(xiàng)處理

  • 解析target

  • 獲取resolver

  • 創(chuàng)建ccResolverWrapper

創(chuàng)建clientConn的時(shí)候回根據(jù)target解析出scheme,然后根據(jù)scheme去找已注冊(cè)對(duì)應(yīng)的resolver,如果沒(méi)有找到則使用默認(rèn)的resolver

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

ccResolverWrapper的流程如下圖,在這里resolver會(huì)和balancer會(huì)進(jìn)行關(guān)聯(lián),balancer的處理方式和resolver類(lèi)似也是通過(guò)wrapper進(jìn)行了一次封裝

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

緊著著會(huì)根據(jù)獲取到的地址創(chuàng)建htt2的鏈接

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

到此ClientConn創(chuàng)建過(guò)程基本結(jié)束,我們?cè)僖黄鹗崂硪幌抡麄€(gè)過(guò)程,首先獲取resolver,其中ccResolverWrapper實(shí)現(xiàn)了resovler.ClientConn接口,通過(guò)Resolver的UpdateState方法觸發(fā)獲取Balancer,獲取Balancer,其中ccBalancerWrapper實(shí)現(xiàn)了balancer.ClientConn接口,通過(guò)Balnacer的UpdateClientConnState方法觸發(fā)創(chuàng)建連接(SubConn),最后創(chuàng)建HTTP2 Client

Balancer模塊

balancer模塊用來(lái)在客戶端發(fā)起請(qǐng)求時(shí)進(jìn)行負(fù)載均衡,如果沒(méi)有注冊(cè)自定義的balancer的話gRPC會(huì)采用默認(rèn)的負(fù)載均衡算法,流程圖如下

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

在go-zero中自定義的balancer主要實(shí)現(xiàn)了如下步驟:

  • 實(shí)現(xiàn)PickerBuilder,Build方法返回balancer.Picker

  • 實(shí)現(xiàn)balancer.Picker,Pick方法實(shí)現(xiàn)負(fù)載均衡算法邏輯

  • 調(diào)用balancer.Registet注冊(cè)自定義Balancer

  • 使用baseBuilder注冊(cè),框架已提供了baseBuilder和baseBalancer實(shí)現(xiàn)了Builer和Balancer

Build方法的實(shí)現(xiàn)如下

func (b *p2cPickerBuilder) Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker {
	if len(readySCs) == 0 {
		return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
	}

	var conns []*subConn
	for addr, conn := range readySCs {
		conns = append(conns, &subConn{
			addr:    addr,
			conn:    conn,
			success: initSuccess,
		})
	}

	return &p2cPicker{
		conns: conns,
		r:     rand.New(rand.NewSource(time.Now().UnixNano())),
		stamp: syncx.NewAtomicDuration(),
	}
}

go-zero中默認(rèn)實(shí)現(xiàn)了p2c負(fù)載均衡算法,該算法的優(yōu)勢(shì)是能彈性的處理各個(gè)節(jié)點(diǎn)的請(qǐng)求,Pick的實(shí)現(xiàn)如下

func (p *p2cPicker) Pick(ctx context.Context, info balancer.PickInfo) (
	conn balancer.SubConn, done func(balancer.DoneInfo), err error) {
	p.lock.Lock()
	defer p.lock.Unlock()

	var chosen *subConn
	switch len(p.conns) {
	case 0:
		return nil, nil, balancer.ErrNoSubConnAvailable // 沒(méi)有可用鏈接
	case 1:
		chosen = p.choose(p.conns[0], nil) // 只有一個(gè)鏈接
	case 2:
		chosen = p.choose(p.conns[0], p.conns[1])
	default: // 選擇一個(gè)健康的節(jié)點(diǎn)
		var node1, node2 *subConn
		for i := 0; i < pickTimes; i++ {
			a := p.r.Intn(len(p.conns))
			b := p.r.Intn(len(p.conns) - 1)
			if b >= a {
				b++
			}
			node1 = p.conns[a]
			node2 = p.conns[b]
			if node1.healthy() && node2.healthy() {
				break
			}
		}

		chosen = p.choose(node1, node2)
	}

	atomic.AddInt64(&chosen.inflight, 1)
	atomic.AddInt64(&chosen.requests, 1)
	return chosen.conn, p.buildDoneFunc(chosen), nil
}

客戶端發(fā)起調(diào)用的流程如下,會(huì)調(diào)用pick方法獲取一個(gè)transport進(jìn)行處理

基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的

以上就是基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)頁(yè)標(biāo)題:基于gRPC的注冊(cè)發(fā)現(xiàn)與負(fù)載均衡的原理和實(shí)戰(zhàn)是怎么樣的
分享網(wǎng)址:http://muchs.cn/article24/ghjsje.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管網(wǎng)站建設(shè)、面包屑導(dǎo)航、網(wǎng)站維護(hù)電子商務(wù)、搜索引擎優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

綿陽(yáng)服務(wù)器托管