consul服務註冊與服務發現的巨坑

來源:互聯網
上載者:User

最近使用consul作為項目的服務註冊與服務發現的基礎功能。在塔建叢集使用中遇到一些坑,下面一個個的記錄下來。

consul叢集多node

consul叢集的node也就是我們所說的consul執行個體。叢集由多個node組成,為了叢集的可用性,需要超過半數的node啟用server。如5個node中建議3個啟用server模式,3個node組成的叢集就2個node啟用server模式。
看到這裡的時候你一定覺得沒有什麼問題呀,但是consul坑就是多。加入你的叢集組成如下:

Node          Address              Status  Type    Build  Protocol  DC                    SegmentBJ-MQTEST-01  10.163.145.117:8301  alive   server  1.0.6  2         iget-topology-aliyun  <all>BJ-MQTEST-02  10.163.147.47:8301   alive   server  1.0.6  2         iget-topology-aliyun  <all>BJ-TGO-01     10.163.145.110:8301     alive   client  1.0.6  2         iget-topology-aliyun  <default>

那麼client可以使用上述的3個ip串連到consul叢集,假設client A使用使用10.163.145.117註冊了service,重啟後使用地址10.163.145.110註冊之前的service資訊,此時你就會驚喜的發現,UI可以同時看到在同一個servicename下存在兩個相同的serviceid。

這就是consul叢集多node的坑,因為service底層雖然使用了KV儲存,但是service的KEY與serviceid無關,所以在叢集中可以重複。

解決方案一

叢集中只有一個node使用server模式,其他的都是client模式。缺點很明顯,如果server的node掛了,那麼叢集的可用性就沒有了。

解決方案二

相同的用戶端使用相同的node地址,這樣就可以確保同一個servicename下不存在兩個相同的serviceid。缺點是如果用戶端綁定的node掛了,那麼client就不能使用。
代碼給出

package registryimport (    "fmt"    "math"    "net"    "sort"    "strings"    log "github.com/golang/glog")type ConsulBind struct {    Addr  string    IpInt float64}type ConsulBindList []ConsulBindfunc (s ConsulBindList) Len() int {    return len(s)}func (s ConsulBindList) Swap(i, j int) {    s[i], s[j] = s[j], s[i]}func (s ConsulBindList) Less(i, j int) bool {    return s[i].IpInt < s[j].IpInt}func (s ConsulBindList) ToStrings() []string {    ret := make([]string, 0, len(s))    for _, cbl := range s {        ret = append(ret, cbl.Addr)    }    return ret}func BingConsulSort(consulAddrs []string) []string {    localIpStr, err := GetAgentLocalIP()    if err != nil {        return consulAddrs    }    localIp := net.ParseIP(localIpStr)    localIpInt := int64(0)    if localIp != nil {        localIpInt = util.InetAton(localIp)    }    addrslist := make([]ConsulBind, 0, len(consulAddrs))    for _, addr := range consulAddrs {        ads := strings.Split(addr, ":")        if len(ads) == 2 {            ip := net.ParseIP(ads[0])            if ip != nil {                ipInt := util.InetAton(ip)                fmt.Println("ip:", ip, ipInt, localIpInt, (ipInt - localIpInt))                addrslist = append(addrslist, ConsulBind{                    Addr:  addr,                    IpInt: math.Abs(float64(ipInt - localIpInt)),                })            }        }    }    consulBindList := ConsulBindList(addrslist)    sort.Sort(consulBindList)    log.Infof("sort addrs %v", consulBindList)    return consulBindList.ToStrings()}

解決方案三

用戶端隨機使用叢集中的任意一個地址,但是註冊之前先判斷該servicename是否已經存在要註冊的serviceid了,如果存在就刪除重新註冊。缺點就是watch會有較多事件,可以升級為如果存在並且是健康的就不允許重複註冊,我使用的就是該方案。

刪除service

一開始很多人都會覺得服務出現問題了下架了掛了,那麼就會被移出了。但是在consul中刪除service沒有那麼簡單!
請查看官網文檔:
catalog文檔

agent/service文檔

看著似乎任選一個就可以做到正確刪除service了!可以繼續說一聲,沒有那麼簡單,consul的坑就是多。

選擇了/agent/service/deregister/:service_id介面,會發現你無法刪除別的node的service。比如10.163.145.117中有個serviceid為agent_xxxx_v1,但是用戶端串連consul使用的IP為10.163.145.110,那麼就無法刪除掉agent_xxxx_v1

沒事不是還有一個介面沒有使用嗎?再來看看/catalog/deregister,執行完成後看了UI,嗯嗯的確是刪除了agent_xxxx_v1。等等。。。 。。。 30s後發現agent_xxxx_v1又出現了,這是怎麼回事????

請查看consul的bugUnable to deregister a service #1188。

解決方案

第一步:查詢出serviceid所屬的servicename所有的列表;
第二步:遍曆列表擷取到node的地址後刪除所有的serviceid;

if len(c.Options.Addrs) > 0 {        addrMap := make(map[string]string, len(c.Options.Addrs))        for _, host := range c.Options.Addrs {            addr, _, err := net.SplitHostPort(host)            if err != nil {                log.Warningf("%v is err=%v", host, err)                continue            }            addrMap[addr] = host        }        rsp, _, _ := c.Client.Health().Service(s.Name, "", false, nil)        for _, srsp := range rsp {            if srsp.Service.ID == serviceId {                if host, ok := addrMap[srsp.Node.Address]; ok {                    config := consul.DefaultNonPooledConfig()                    config.Address = host                    // 建立consul串連                    client, err := consul.NewClient(config)                    if err != nil {                        log.Warningf("NewClient is err=%v", host, err)                    }                    err = client.Agent().ServiceDeregister(serviceId)                    log.Infof("ServiceDeregister host=%v , serviceId=%v", host, serviceId)                }            }        }    } else {        err = c.Client.Agent().ServiceDeregister(serviceId)        log.Infof("ServiceDeregister  serviceId=%v", serviceId)    }

可以肯定的是consul還有其他的坑的,但是這兩個坑讓我記憶深刻,記錄下來給準備使用consul或者已經遇到這些坑的同學一個提醒。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.