記一次服務註冊&服務發現的問題和解決(etcd和k8s同時結合使用情況下)

目錄

 

背景

分析

原因

總結


背景

  1. 早先的都採用基於grpc+etcd做服務註冊和服務發現,都是正常的
  2. 後來有部分服務採用k8s部署,爲了新舊兼容,服務會按照老方式把宿主機的ip註冊到etcd上,k8s體系類使用體系類的服務發現,k8s體系外的依然使用原來的方式,互不影響
  3. 但是有服務基於k8s部署之後,發現client調用接口超時,而直接通過ip調用卻是正常
  4. 且多數是發生在k8s的出現pod彈性伸縮之後

分析

  1. 服務端註冊地址,從etcd來看,是正常的
  2. 服務端直接通過ip調用也正常的,所以排除服務端本身的問題
  3. etcd裏面顯示的地址的確會出現沒有的情況,但是通過日誌發現服務端lease維持的心跳卻是正常的,一度覺得懷疑人生~~
  4. 後來發現是下面的原因A導致的,
  5. 解決後,發現依然有問題,O(∩_∩)O,很奇怪~
  6. 通過打開grpc日誌,發現grpc的client端會收到地址列表爲空的問題:balancerWrapper: got update addr from Notify:[]
  7. 但是etcd裏面顯示的註冊地址卻都是存在的,一度覺得懷疑人生~~
  8. 後來通過閱讀grcp源碼分析,增加日誌排查 :google.golang.org/grpc/balancer.go  文件中 函數 (rr *roundRobin) watchAddrUpdates()  裏面,增加 etcd watch的數據變動行爲,發現了原因
  9. 通過下面的原因B的處理方式解決

原因

  1. 問題存在兩個
  2. 原因A
    1. 早先服務註冊:類似於

      key:service/go_server_xx/10.1.1.1:9999
      val:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}

    2. 並且通過etcd的lease機制維持心跳

    3. 假設一個宿主機起了兩個pod,也就兩個服務,ser1和ser2
    4. ser1啓動的時候會註冊 service/go_server_xx/10.1.1.1:9999 並且綁定 lease1,並且維持lease1心跳
    5. 之後,ser2啓動的時候也會註冊 service/go_server_xx/10.1.1.1:9999 並且綁定 lease2,並且維持lease2心跳,住以此時這個key和lease1解綁了
    6. 當發生彈性伸縮的時候,比如ser2關閉了,此時 lease2過期,同時與其綁定的key也被刪除,所以client會發生找不到地址列表的問題
    7. 但是從服務端ser1來看,依然提供服務,並且與etcd的lease1心跳也正常~~,哈哈
  3. 解決方法A
    1. 通過在key後邊增加後綴解決,類似於:service/go_server_xx/lease1/10.1.1.1:9999, 這樣即使一個宿主機上有多個服務,各自也會註冊各自的
  4. 原因B
    1. 上述問題解決之後,還有個問題
    2. 比如同一個宿主機註冊的兩個服務地址

      key1:service/go_server_xx/lease1/10.1.1.1:9999
      val1:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}
      和
      key2:service/go_server_xx/lease2/10.1.1.1:9999
      val2:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":null}
      

       

    3. 在client端watch etcd變化的時候會發現兩個add行爲,但是 由於val中的地址是一樣的,可用addrs列表裏面只會保存一個 10.1.1.1:9999,另一個會跳過:
      grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr)
    4. 當發生彈性伸縮的時候,比如ser2關閉了
      1. watch etcd變化的時候會收到一個del行爲,這個時候會刪除10.1.1.1:9999
      2. 這樣client可用地址列表裏面就是空的了
      3. grpc的client對象收到 balancerWrapper: got update addr from Notify:[]
  5. 解決方法B:
    1. 註冊的時候增加metadata做區分就可以了,類似

      key1:service/go_server_xx/lease1/10.1.1.1:9999
      val1:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":lease1}
      和
      key2:service/go_server_xx/lease2/10.1.1.1:9999
      val2:{"Op":0,"Addr":"10.1.1.1:9999","Metadata":lease2}
    2. 這樣增加的時候,可用地址列表裏面就會增加兩個元素

總結

etcd+grpc的一整套機制(服務註冊&服務發現等)是好的

k8s的那一套機制也是好的

但是兩者結合使用,就得注意很多地方,要不然會出現很多蛋疼的問題提

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章