Jaeger源碼分析——服務註冊與服務發現

原文:Jaeger源碼分析——服務註冊與服務發現html

聲明

 Jaeger官方並無明確說明其 服務註冊和服務發現的具體使用和介紹,這部分功能是在分析源碼的時候,發現其原理與 服務註冊和服務發現相似,因此結合本身對服務註冊和服務發現的認識,作一次總結,有錯還請指點。

TChannel服務註冊和服務發現

image

 Jaeger不借助第三方工具也能實現服務註冊和服務發現,這部分功能由其依賴的RPC框架提供。

第三方註冊——手動註冊

go run cmd/agent/main.go --collector.host-port=192.168.0.10:14267,192.168.0.11:14267
 在啓動agent的時候,可配置多個collector靜態地址,這部分地址會造成一張註冊表。

註冊表

  • 註冊表結構
github.com/uber/tchannel-go/peer.go #59
type PeerList struct {
    sync.RWMutex
    parent          *RootPeerList
    //以hostPort爲下標組成註冊表
    peersByHostPort map[string]*peerScore
    //負載均衡實現
    peerHeap        *peerHeap
    scoreCalculator ScoreCalculator
    lastSelected    uint64
}
  • 健康檢查
github.com/jaegertracing/jaeger/pkg/discovery/peerlistmgr/peer_list_mgr.go  #150
func (m *PeerListManager) ensureConnections() {
    peers := m.peers.Copy()
    minPeers := m.getMinPeers(peers)
    numConnected, notConnected := m.findConnected(peers)
    //有必定量的連接,就不進行健康檢查
    if numConnected >= minPeers {
      return
    }
    ......
    for i := range notConnected {
      // swap current peer with random from the remaining positions
      r := i + m.rnd.Intn(len(notConnected)-i)
      notConnected[i], notConnected[r] = notConnected[r], notConnected[i]
      // try to connect to current peer (swapped)
      peer := notConnected[i]
      m.logger.Info("Trying to connect to peer", zap.String("host:port", peer.HostPort()))
      //用於控制超時
      ctx, cancel := context.WithTimeout(context.Background(), m.connCheckTimeout)
      conn, err := peer.GetConnection(ctx)
      cancel()
      if err != nil {
        m.logger.Error("Unable to connect", zap.String("host:port", peer.HostPort()), zap.Duration("connCheckTimeout", m.connCheckTimeout), zap.Error(err))
        continue
      }
      ......
    }
}
 在註冊表上的地址,TChannel都會進行健康檢查,每秒進行一次,若是0.25秒沒有鏈接上,視爲服務不可用。若是鏈接成功則保留當前服務實例,供agent提交數據使用。
github.com/uber/tchannel-go/connection.go #228
func (ch *Channel) newOutboundConnection(timeout time.Duration, hostPort string, events connectionEvents) (*Connection, error) {
    conn, err := net.DialTimeout("tcp", hostPort, timeout)
    if err != nil {
      if ne, ok := err.(net.Error); ok && ne.Timeout() {
        ch.log.WithFields(LogField{"hostPort", hostPort}, LogField{"timeout", timeout}).Infof("Outbound net.Dial timed out")
        err = ErrTimeout
      }
      return nil, err
    }

    return ch.newConnection(conn, hostPort, connectionWaitingToSendInitReq, events), nil
}

客戶端服務發現

  • 軟負載均衡
github.com/uber/tchannel-go/peer.go #149
func (l *PeerList) choosePeer(prevSelected map[string]struct{}, avoidHost bool) *Peer {
    var psPopList []*peerScore
    var ps *peerScore
    ......
    size := l.peerHeap.Len()
    for i := 0; i < size; i++ {
      //把peer從Heap頭部彈出來
      popped := l.peerHeap.popPeer()
      if canChoosePeer(popped.HostPort()) {
          ps = popped
          break
      }
      psPopList = append(psPopList, popped)
    }
    //不符合的放入Heap尾部
    for _, p := range psPopList {
        heap.Push(l.peerHeap, p)
    }

    if ps == nil {
        return nil
    }
    //符合條件的打分,再放入Heap尾部
    l.peerHeap.pushPeer(ps)
    ps.chosenCount.Inc()
    return ps.Peer
}
 當Agent須要提交數據的時候,會從TChannel的負載均衡獲取peer(服務信息),當有多個的時候,TChannel經過輪詢方式,查詢peer。實現方式:註冊表把全部peer放入peerHeap,先把peer從頭部彈出,再把peer放回尾部,從而實現輪詢策略的負載均衡。
  • 重試
github.com/uber/tchannel-go/retry.go #212
func (ch *Channel) RunWithRetry(runCtx context.Context, f RetriableFunc) error {
    var err error

    opts := getRetryOptions(runCtx)
    rs := ch.getRequestState(opts)
    defer requestStatePool.Put(rs)
    //默認重試5次
    for i := 0; i < opts.MaxAttempts; i++ {
        rs.Attempt++

        if opts.TimeoutPerAttempt == 0 {
            err = f(runCtx, rs)
        } else {
            attemptCtx, cancel := context.WithTimeout(runCtx, opts.TimeoutPerAttempt)
            err = f(attemptCtx, rs)
            cancel()
        }

        if err == nil {
            return nil
        }
        if !opts.RetryOn.CanRetry(err) {
            if ch.log.Enabled(LogLevelInfo) {
                ch.log.WithFields(ErrField(err)).Info("Failed after non-retriable error.")
            }
          return err
        }
        ......
    }

    // Too many retries, return the last error
    return err
}
 網絡之間的通信避免不了網絡異常,因此爲了提升可用性,重試是其中一種方式。當從負載均衡獲取peer提交數據到Collector,若是提交失敗,會再從負載均衡獲取peer,最多5次,若是5次都不成功就會放棄此次提交。

Consul+docker 服務註冊和服務發現

image

 使用consul實現服務註冊和服務發現是一件很簡單的事情。不少功能都是開箱即用。

準備工做

  • 啓動Consul——ip:172.18.0.2
docker run -itd --network=backend \
-p 8400:8400 -p 8500:8500 -p 8600:53/udp \
-h node1 progrium/consul -server -bootstrap -ui-dir /ui
  • 啓動Agent
docker run \
-itd --network=backend \
--name=jaeger-agent \
-p5775:5775/udp \
-p6831:6831/udp \
-p6832:6832/udp \
-p5778:5778/tcp \
--dns-search="service.consul" --dns=172.18.0.2 \
jaegertracing/jaeger-agent \
/go/bin/agent-linux --collector.host-port=jaeger-collector:14267
  • 啓動Collector
#node1
docker run -itd --network=backend \
--name=jaeger-collector-node1 \
-p :14267 \
--dns-search="service.consul" --dns=172.18.0.2 \
jaegertracing/jaeger-collector \
/go/bin/collector-linux \
--span-storage.type=cassandra \
--cassandra.keyspace=jaeger_v1_dc \
--cassandra.servers=cassandra:9042

#node2
docker run -itd --network=backend \
--name=jaeger-collector-node2 \
-p :14267 \
--dns-search="service.consul" --dns=172.18.0.2 \
jaegertracing/jaeger-collector \
/go/bin/collector-linux \
--span-storage.type=cassandra \
--cassandra.keyspace=jaeger_v1_dc \
--cassandra.servers=cassandra:9042

服務註冊——自動註冊

docker run -itd --net=backend --name=registrator \
--volume=/var/run/docker.sock:/tmp/docker.sock \
gliderlabs/registrator:latest \
consul://172.18.0.2:8500
 使用consul+docker的形式,只要部署好服務,就會被自動註冊到consul,十分簡單。

註冊表

  • 查看註冊表信息
查看註冊表信息 http://localhost:8500/ui/#/dc1/nodes/node1

image

 能夠看到啓動的2個Collector服務ip分別爲:172.18.0.5和172.18.0.8
 consul提供了不少種健康檢查方式:HTTP、TCP、Docker、Shell和TTL。詳情能夠查看官網。

服務端服務發現

 Consul相對於Agent和Collector是遠程服務,因此提供了2種服務發現方式:HTTP和DNS,在這裏主要使用是DNS,由於簡單,輕量。
  • DNS和軟負載均衡
 當Agent經過DNS解析出多個IP的時候,Consul會 隨機選擇一個IP給Agent實現負載均衡。

 因爲DNS存在緩存,因此有可能出現,服務不健康,同樣會被正常解析,因此在默認狀況下Consul是沒有設置緩存時間,TTL爲0,可是也考慮到了不緩存對Consul的壓力,因此開放配置,讓咱們去決定緩存時間點DNS Cachingnode

總結

TChannel與Consul+docker實現的服務發現和服務註冊中都有他們的優缺點:

服務註冊

  • TChannel
 TChannel的服務註冊適用於一些基礎服務,例如Jaeger就屬於一種基礎服務,這種服務一旦部署不多會變更。
  • Consul + docker
 在如今docker流行的大環境下使用Consul實現的服務註冊會簡單不少,docker有一個特色就是ip地址是動態,因此它很適合業務場景,由於業務常常變更,服務也隨着變化。

健康檢查

 TChannel和Consul都提供了健康檢查,可是都只是檢測服務是否正在運行,沒法瞭解是否可以正常處理請求。

服務發現

  • TChannel
 TChannel使用的是客戶端服務發現,這種方式相對於Consul的服務端服務發現的優勢就是沒有了遠程網絡開銷,單點問題。同時缺點就是各個語言都須要本身實現註冊表,負載均衡等功能。
  • Consul
 Consul使用服務端服務發現,它能夠很好的和其餘服務結合使用,不須要關心註冊表,負載均衡等。並且關於網絡開銷和單點問題都提供了方案。
相關文章
相關標籤/搜索