[{"data":1,"prerenderedAt":3174},["ShallowReactive",2],{"blog:\u002Fblog\u002F2021\u002F01\u002F11\u002Fservice-registry-discovery\u002F":3},{"id":4,"title":5,"body":6,"categories":3130,"comments":2259,"date":3132,"description":3133,"draft":3134,"extension":3135,"legacySlug":3136,"meta":3137,"navigation":2259,"path":3167,"seo":3168,"stem":3169,"tags":3170,"updated":3172,"__hash__":3173},"blog\u002Fblog\u002F2021\u002F01\u002F11\u002FService-Registry-Discovery.md","bilibili注册中心Discovery架构设计与源码分析",{"type":7,"value":8,"toc":3124},"minimark",[9,13,17,24,27,30,36,39,42,45,56,60,65,73,79,82,88,91,97,100,103,106,109,112,115,121,124,127,133,136,141,144,150,153,331,334,338,344,498,501,505,511,781,783,787,790,1047,1049,1052,1056,1062,1065,1068,1071,1154,1157,1160,1282,1285,1288,1336,1339,1342,1399,1402,1405,1516,1519,1611,1613,1616,1658,1661,1881,1884,1887,1891,1894,1900,1903,1906,2064,2066,2069,2093,2095,2098,2356,2359,2509,2511,2515,2521,2524,2527,2530,2730,2732,2735,2739,2745,2748,2751,2754,3046,3049,3053,3059,3062,3065,3072,3079,3086,3089,3097,3109,3120],[10,11,12],"h3",{"id":12},"什么是注册中心",[14,15,16],"p",{},"注册中心可以说是 一个“通讯录”，它记录了服务和服务地址的映射关系。在服务启动时，服务会注册到这里，当服务需要调用其它服务时，就到这里找到服务的地址，进行调用。",[14,18,19],{},[20,21],"img",{"alt":22,"src":23},"image-20210111172545300","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210111172545300.png",[10,25,26],{"id":26},"常见的注册中心及对比",[14,28,29],{},"业界有许多成熟的注册中心实现，下图中对一些常见的注册中心做一对比：",[14,31,32],{},[20,33],{"alt":34,"src":35},"image-20210111173112358","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210111173112358.png",[14,37,38],{},"ZooKeeper是一款经典的服务注册中心产品（虽然它最初的定位并不在于此），在很长一段时间里，它是国人在提起RPC服务注册中心时心里想到的唯一选择，这很大程度上与Dubbo在中国的普及程度有关。",[14,40,41],{},"Consul和Eureka都出现于2014年，Consul在设计上把很多分布式服务治理上要用到的功能都包含在内，可以支持服务注册、健康检查、配置管理、Service Mesh等。而Eureka则借着微服务概念的流行，与SpringCloud生态的深度结合，也获取了大量的用户。",[14,43,44],{},"去年开源的Nacos，则携带着阿里巴巴大规模服务生产经验，试图在服务注册和配置管理这个市场上，提供给用户一个新的选择。",[14,46,47,48,55],{},"而本文要介绍的",[49,50,54],"a",{"href":51,"rel":52},"https:\u002F\u002Fgithub.com\u002Fbilibili\u002Fdiscovery",[53],"nofollow","discovery","是Eureka的go语言实现版本，由bilibili开发并开源。",[10,57,59],{"id":58},"discovery的设计与实现","Discovery的设计与实现",[61,62,64],"h4",{"id":63},"cp-vs-ap","CP VS AP",[14,66,67,68,72],{},"在大多数分布式环境中，尤其是涉及到数据存储的场景，数据一致性应该是首先被保证的，这也是zookeeper设计成CP的原因。但是对于服务发现场景来说，情况就不太一样了：针对同一个服务，即使注册中心的不同节点保存的服务提供者信息不尽相同，也并不会造成灾难性的后果。",[69,70,71],"strong",{},"因为对于服务消费者来说，能消费才是最重要的———拿到可能不正确的服务实例信息后尝试消费一下，也好过因为无法获取实例信息而不去消费。"," （尝试一下可以快速失败，之后可以快速重试）所以，对于服务发现而言，可用性比数据一致性更加重要——AP胜过CP。",[14,74,75],{},[20,76],{"alt":77,"src":78},"image-20210111174501640","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210111174501640.png",[61,80,81],{"id":81},"设计目标与改进",[14,83,84],{},[20,85],{"alt":86,"src":87},"image-20210111174839107","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210111174839107.png",[61,89,90],{"id":90},"架构图",[14,92,93],{},[20,94],{"alt":95,"src":96},"image-20210111175328622","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210111175328622.png",[14,98,99],{},"1.通过AppID(服务名)和hostname定位实例",[14,101,102],{},"2.Node: discovery server节点",[14,104,105],{},"3.Provider: 服务提供者，目前托管给k8s平台，容器启动后发起register请求给Discover server，后定期（30s）心跳一次",[14,107,108],{},"4.Consumer: 启动时拉取node节点信息，后随机选择一个node发起long polling(30s一次)拉取服务instances列表",[14,110,111],{},"5.Instance: 保存在node内存中的AppID对应的容器节点信息，包含hostname\u002Fip\u002Fservice等",[61,113,114],{"id":114},"存储结构",[14,116,117],{},[20,118],{"alt":119,"src":120},"image-20210111180238598","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210111180238598.png",[14,122,123],{},"上图中列出了discovery的主要存储结构，并用数字标出了服务启动后的一些主要过程，下边将会注意做详细讲解。",[61,125,126],{"id":126},"初始化过程",[14,128,129],{},[20,130],{"alt":131,"src":132},"image-20210112093109489","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112093109489.png",[14,134,135],{},"步骤1，2初始化了对应的内存结构，这里不再细讲，我们从第三步说起：",[137,138,140],"h5",{"id":139},"_1-node节点","1. Node节点",[14,142,143],{},"每个node节点代表一个discovery服务实例，每个中都存储着服务的所有node节点信息。示例图如下：",[14,145,146],{},[20,147],{"alt":148,"src":149},"image-20210112093905718","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112093905718.png",[14,151,152],{},"节点初始化代码如下：",[154,155,160],"pre",{"className":156,"code":157,"language":158,"meta":159,"style":159},"language-go shiki shiki-themes github-light github-dark","func NewNodes(c *conf.Config) *Nodes {\n    nodes := make([]*Node, 0, len(c.Nodes))\n    for _, addr := range c.Nodes {\n        n := newNode(c, addr)\n        n.zone = c.Env.Zone\n        n.pRegisterURL = fmt.Sprintf(\"http:\u002F\u002F%s%s\", c.HTTPServer.Addr, _registerURL)\n        nodes = append(nodes, n)\n    }\n    zones := make(map[string][]*Node)\n    for name, addrs := range c.Zones {\n        var znodes []*Node\n        for _, addr := range addrs {\n            n := newNode(c, addr)\n            n.otherZone = true\n            n.zone = name\n            n.pRegisterURL = fmt.Sprintf(\"http:\u002F\u002F%s%s\", c.HTTPServer.Addr, _registerURL)\n            znodes = append(znodes, n)\n        }\n        zones[name] = znodes\n    }\n    return &Nodes{\n        \u002F\u002F同一discovery集群的所有node节点\n        nodes: nodes,\n        \u002F\u002F其他可用区zone所有node节点\n        zones:    zones,\n        selfAddr: c.HTTPServer.Addr,\n    }\n}\n","go","",[161,162,163,171,177,183,189,195,201,207,213,219,225,231,237,243,249,255,261,267,273,279,284,290,296,302,308,314,320,325],"code",{"__ignoreMap":159},[164,165,168],"span",{"class":166,"line":167},"line",1,[164,169,170],{},"func NewNodes(c *conf.Config) *Nodes {\n",[164,172,174],{"class":166,"line":173},2,[164,175,176],{},"    nodes := make([]*Node, 0, len(c.Nodes))\n",[164,178,180],{"class":166,"line":179},3,[164,181,182],{},"    for _, addr := range c.Nodes {\n",[164,184,186],{"class":166,"line":185},4,[164,187,188],{},"        n := newNode(c, addr)\n",[164,190,192],{"class":166,"line":191},5,[164,193,194],{},"        n.zone = c.Env.Zone\n",[164,196,198],{"class":166,"line":197},6,[164,199,200],{},"        n.pRegisterURL = fmt.Sprintf(\"http:\u002F\u002F%s%s\", c.HTTPServer.Addr, _registerURL)\n",[164,202,204],{"class":166,"line":203},7,[164,205,206],{},"        nodes = append(nodes, n)\n",[164,208,210],{"class":166,"line":209},8,[164,211,212],{},"    }\n",[164,214,216],{"class":166,"line":215},9,[164,217,218],{},"    zones := make(map[string][]*Node)\n",[164,220,222],{"class":166,"line":221},10,[164,223,224],{},"    for name, addrs := range c.Zones {\n",[164,226,228],{"class":166,"line":227},11,[164,229,230],{},"        var znodes []*Node\n",[164,232,234],{"class":166,"line":233},12,[164,235,236],{},"        for _, addr := range addrs {\n",[164,238,240],{"class":166,"line":239},13,[164,241,242],{},"            n := newNode(c, addr)\n",[164,244,246],{"class":166,"line":245},14,[164,247,248],{},"            n.otherZone = true\n",[164,250,252],{"class":166,"line":251},15,[164,253,254],{},"            n.zone = name\n",[164,256,258],{"class":166,"line":257},16,[164,259,260],{},"            n.pRegisterURL = fmt.Sprintf(\"http:\u002F\u002F%s%s\", c.HTTPServer.Addr, _registerURL)\n",[164,262,264],{"class":166,"line":263},17,[164,265,266],{},"            znodes = append(znodes, n)\n",[164,268,270],{"class":166,"line":269},18,[164,271,272],{},"        }\n",[164,274,276],{"class":166,"line":275},19,[164,277,278],{},"        zones[name] = znodes\n",[164,280,282],{"class":166,"line":281},20,[164,283,212],{},[164,285,287],{"class":166,"line":286},21,[164,288,289],{},"    return &Nodes{\n",[164,291,293],{"class":166,"line":292},22,[164,294,295],{},"        \u002F\u002F同一discovery集群的所有node节点\n",[164,297,299],{"class":166,"line":298},23,[164,300,301],{},"        nodes: nodes,\n",[164,303,305],{"class":166,"line":304},24,[164,306,307],{},"        \u002F\u002F其他可用区zone所有node节点\n",[164,309,311],{"class":166,"line":310},25,[164,312,313],{},"        zones:    zones,\n",[164,315,317],{"class":166,"line":316},26,[164,318,319],{},"        selfAddr: c.HTTPServer.Addr,\n",[164,321,323],{"class":166,"line":322},27,[164,324,212],{},[164,326,328],{"class":166,"line":327},28,[164,329,330],{},"}\n",[14,332,333],{},"文件：registry\u002Fnodes.go",[137,335,337],{"id":336},"_2-syncup","2. syncup",[14,339,340],{},[20,341],{"alt":342,"src":343},"image-20210112094758812","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112094758812.png",[154,345,347],{"className":156,"code":346,"language":158,"meta":159,"style":159},"func (d *Discovery) syncUp() {\n    nodes := d.nodes.Load().(*registry.Nodes)\n    \u002F\u002F本zone所有node，其他zone分别一个node\n    for _, node := range nodes.AllNodes() {\n        if nodes.Myself(node.Addr) {\n            continue\n        }\n        uri := fmt.Sprintf(_fetchAllURL, node.Addr)\n        var res struct {\n            Code int                          `json:\"code\"`\n            Data map[string][]*model.Instance `json:\"data\"`\n        }\n        if err := d.client.Get(context.TODO(), uri, \"\", nil, &res); err != nil {\n            log.Error(\"d.client.Get(%v) error(%v)\", uri, err)\n            continue\n        }\n        if res.Code != 0 {\n            log.Error(\"service syncup from(%s) failed \", uri)\n            continue\n        }\n        \u002F\u002F sync success from other node,exit protected mode\n        d.protected = false\n        for _, is := range res.Data {\n            for _, i := range is {\n                _ = d.registry.Register(i, i.LatestTimestamp)\n            }\n        }\n        \u002F\u002F NOTE: no return, make sure that all instances from other nodes register into self.\n    }\n    nodes.UP()\n}\n",[161,348,349,354,359,364,369,374,379,383,388,393,398,403,407,412,417,421,425,430,435,439,443,448,453,458,463,468,473,477,482,487,493],{"__ignoreMap":159},[164,350,351],{"class":166,"line":167},[164,352,353],{},"func (d *Discovery) syncUp() {\n",[164,355,356],{"class":166,"line":173},[164,357,358],{},"    nodes := d.nodes.Load().(*registry.Nodes)\n",[164,360,361],{"class":166,"line":179},[164,362,363],{},"    \u002F\u002F本zone所有node，其他zone分别一个node\n",[164,365,366],{"class":166,"line":185},[164,367,368],{},"    for _, node := range nodes.AllNodes() {\n",[164,370,371],{"class":166,"line":191},[164,372,373],{},"        if nodes.Myself(node.Addr) {\n",[164,375,376],{"class":166,"line":197},[164,377,378],{},"            continue\n",[164,380,381],{"class":166,"line":203},[164,382,272],{},[164,384,385],{"class":166,"line":209},[164,386,387],{},"        uri := fmt.Sprintf(_fetchAllURL, node.Addr)\n",[164,389,390],{"class":166,"line":215},[164,391,392],{},"        var res struct {\n",[164,394,395],{"class":166,"line":221},[164,396,397],{},"            Code int                          `json:\"code\"`\n",[164,399,400],{"class":166,"line":227},[164,401,402],{},"            Data map[string][]*model.Instance `json:\"data\"`\n",[164,404,405],{"class":166,"line":233},[164,406,272],{},[164,408,409],{"class":166,"line":239},[164,410,411],{},"        if err := d.client.Get(context.TODO(), uri, \"\", nil, &res); err != nil {\n",[164,413,414],{"class":166,"line":245},[164,415,416],{},"            log.Error(\"d.client.Get(%v) error(%v)\", uri, err)\n",[164,418,419],{"class":166,"line":251},[164,420,378],{},[164,422,423],{"class":166,"line":257},[164,424,272],{},[164,426,427],{"class":166,"line":263},[164,428,429],{},"        if res.Code != 0 {\n",[164,431,432],{"class":166,"line":269},[164,433,434],{},"            log.Error(\"service syncup from(%s) failed \", uri)\n",[164,436,437],{"class":166,"line":275},[164,438,378],{},[164,440,441],{"class":166,"line":281},[164,442,272],{},[164,444,445],{"class":166,"line":286},[164,446,447],{},"        \u002F\u002F sync success from other node,exit protected mode\n",[164,449,450],{"class":166,"line":292},[164,451,452],{},"        d.protected = false\n",[164,454,455],{"class":166,"line":298},[164,456,457],{},"        for _, is := range res.Data {\n",[164,459,460],{"class":166,"line":304},[164,461,462],{},"            for _, i := range is {\n",[164,464,465],{"class":166,"line":310},[164,466,467],{},"                _ = d.registry.Register(i, i.LatestTimestamp)\n",[164,469,470],{"class":166,"line":316},[164,471,472],{},"            }\n",[164,474,475],{"class":166,"line":322},[164,476,272],{},[164,478,479],{"class":166,"line":327},[164,480,481],{},"        \u002F\u002F NOTE: no return, make sure that all instances from other nodes register into self.\n",[164,483,485],{"class":166,"line":484},29,[164,486,212],{},[164,488,490],{"class":166,"line":489},30,[164,491,492],{},"    nodes.UP()\n",[164,494,496],{"class":166,"line":495},31,[164,497,330],{},[14,499,500],{},"文件：discovery\u002Fsyncup.go",[137,502,504],{"id":503},"_3自注册","3.自注册",[14,506,507],{},[20,508],{"alt":509,"src":510},"image-20210112094936577","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112094936577.png",[154,512,514],{"className":156,"code":513,"language":158,"meta":159,"style":159},"func (d *Discovery) regSelf() context.CancelFunc {\n    ctx, cancel := context.WithCancel(context.Background())\n    now := time.Now().UnixNano()\n    ins := &model.Instance{\n        Region:   d.c.Env.Region,\n        Zone:     d.c.Env.Zone,\n        Env:      d.c.Env.DeployEnv,\n        Hostname: d.c.Env.Host,\n        AppID:    model.AppID,\n        Addrs: []string{\n            \"http:\u002F\u002F\" + d.c.HTTPServer.Addr,\n        },\n        Status:          model.InstanceStatusUP,\n        RegTimestamp:    now,\n        UpTimestamp:     now,\n        LatestTimestamp: now,\n        RenewTimestamp:  now,\n        DirtyTimestamp:  now,\n    }\n    d.Register(ctx, ins, now, false, false)\n    go func() {\n        ticker := time.NewTicker(30 * time.Second)\n        defer ticker.Stop()\n        for {\n            select {\n            case \u003C-ticker.C:\n                arg := &model.ArgRenew{\n                    AppID:    ins.AppID,\n                    Zone:     d.c.Env.Zone,\n                    Env:      d.c.Env.DeployEnv,\n                    Hostname: d.c.Env.Host,\n                }\n                if _, err := d.Renew(ctx, arg); err != nil && err == ecode.NothingFound {\n                    d.Register(ctx, ins, now, false, false)\n                }\n            case \u003C-ctx.Done():\n                arg := &model.ArgCancel{\n                    AppID:    model.AppID,\n                    Zone:     d.c.Env.Zone,\n                    Env:      d.c.Env.DeployEnv,\n                    Hostname: d.c.Env.Host,\n                }\n                if err := d.Cancel(context.Background(), arg); err != nil {\n                    log.Error(\"d.Cancel(%+v) error(%v)\", arg, err)\n                }\n                return\n            }\n        }\n    }()\n    return cancel\n}\n",[161,515,516,521,526,531,536,541,546,551,556,561,566,571,576,581,586,591,596,601,606,610,615,620,625,630,635,640,645,650,655,660,665,670,676,682,688,693,699,705,711,716,721,726,731,737,743,748,754,759,764,770,776],{"__ignoreMap":159},[164,517,518],{"class":166,"line":167},[164,519,520],{},"func (d *Discovery) regSelf() context.CancelFunc {\n",[164,522,523],{"class":166,"line":173},[164,524,525],{},"    ctx, cancel := context.WithCancel(context.Background())\n",[164,527,528],{"class":166,"line":179},[164,529,530],{},"    now := time.Now().UnixNano()\n",[164,532,533],{"class":166,"line":185},[164,534,535],{},"    ins := &model.Instance{\n",[164,537,538],{"class":166,"line":191},[164,539,540],{},"        Region:   d.c.Env.Region,\n",[164,542,543],{"class":166,"line":197},[164,544,545],{},"        Zone:     d.c.Env.Zone,\n",[164,547,548],{"class":166,"line":203},[164,549,550],{},"        Env:      d.c.Env.DeployEnv,\n",[164,552,553],{"class":166,"line":209},[164,554,555],{},"        Hostname: d.c.Env.Host,\n",[164,557,558],{"class":166,"line":215},[164,559,560],{},"        AppID:    model.AppID,\n",[164,562,563],{"class":166,"line":221},[164,564,565],{},"        Addrs: []string{\n",[164,567,568],{"class":166,"line":227},[164,569,570],{},"            \"http:\u002F\u002F\" + d.c.HTTPServer.Addr,\n",[164,572,573],{"class":166,"line":233},[164,574,575],{},"        },\n",[164,577,578],{"class":166,"line":239},[164,579,580],{},"        Status:          model.InstanceStatusUP,\n",[164,582,583],{"class":166,"line":245},[164,584,585],{},"        RegTimestamp:    now,\n",[164,587,588],{"class":166,"line":251},[164,589,590],{},"        UpTimestamp:     now,\n",[164,592,593],{"class":166,"line":257},[164,594,595],{},"        LatestTimestamp: now,\n",[164,597,598],{"class":166,"line":263},[164,599,600],{},"        RenewTimestamp:  now,\n",[164,602,603],{"class":166,"line":269},[164,604,605],{},"        DirtyTimestamp:  now,\n",[164,607,608],{"class":166,"line":275},[164,609,212],{},[164,611,612],{"class":166,"line":281},[164,613,614],{},"    d.Register(ctx, ins, now, false, false)\n",[164,616,617],{"class":166,"line":286},[164,618,619],{},"    go func() {\n",[164,621,622],{"class":166,"line":292},[164,623,624],{},"        ticker := time.NewTicker(30 * time.Second)\n",[164,626,627],{"class":166,"line":298},[164,628,629],{},"        defer ticker.Stop()\n",[164,631,632],{"class":166,"line":304},[164,633,634],{},"        for {\n",[164,636,637],{"class":166,"line":310},[164,638,639],{},"            select {\n",[164,641,642],{"class":166,"line":316},[164,643,644],{},"            case \u003C-ticker.C:\n",[164,646,647],{"class":166,"line":322},[164,648,649],{},"                arg := &model.ArgRenew{\n",[164,651,652],{"class":166,"line":327},[164,653,654],{},"                    AppID:    ins.AppID,\n",[164,656,657],{"class":166,"line":484},[164,658,659],{},"                    Zone:     d.c.Env.Zone,\n",[164,661,662],{"class":166,"line":489},[164,663,664],{},"                    Env:      d.c.Env.DeployEnv,\n",[164,666,667],{"class":166,"line":495},[164,668,669],{},"                    Hostname: d.c.Env.Host,\n",[164,671,673],{"class":166,"line":672},32,[164,674,675],{},"                }\n",[164,677,679],{"class":166,"line":678},33,[164,680,681],{},"                if _, err := d.Renew(ctx, arg); err != nil && err == ecode.NothingFound {\n",[164,683,685],{"class":166,"line":684},34,[164,686,687],{},"                    d.Register(ctx, ins, now, false, false)\n",[164,689,691],{"class":166,"line":690},35,[164,692,675],{},[164,694,696],{"class":166,"line":695},36,[164,697,698],{},"            case \u003C-ctx.Done():\n",[164,700,702],{"class":166,"line":701},37,[164,703,704],{},"                arg := &model.ArgCancel{\n",[164,706,708],{"class":166,"line":707},38,[164,709,710],{},"                    AppID:    model.AppID,\n",[164,712,714],{"class":166,"line":713},39,[164,715,659],{},[164,717,719],{"class":166,"line":718},40,[164,720,664],{},[164,722,724],{"class":166,"line":723},41,[164,725,669],{},[164,727,729],{"class":166,"line":728},42,[164,730,675],{},[164,732,734],{"class":166,"line":733},43,[164,735,736],{},"                if err := d.Cancel(context.Background(), arg); err != nil {\n",[164,738,740],{"class":166,"line":739},44,[164,741,742],{},"                    log.Error(\"d.Cancel(%+v) error(%v)\", arg, err)\n",[164,744,746],{"class":166,"line":745},45,[164,747,675],{},[164,749,751],{"class":166,"line":750},46,[164,752,753],{},"                return\n",[164,755,757],{"class":166,"line":756},47,[164,758,472],{},[164,760,762],{"class":166,"line":761},48,[164,763,272],{},[164,765,767],{"class":166,"line":766},49,[164,768,769],{},"    }()\n",[164,771,773],{"class":166,"line":772},50,[164,774,775],{},"    return cancel\n",[164,777,779],{"class":166,"line":778},51,[164,780,330],{},[14,782,500],{},[137,784,786],{"id":785},"_4维护nodes节点","4.维护nodes节点",[14,788,789],{},"循环拉取appid=infra.discovery的instances信息,并由此生成nodes信息",[154,791,793],{"className":156,"code":792,"language":158,"meta":159,"style":159},"func (d *Discovery) nodesproc() {\n    var (\n        lastTs int64\n    )\n    for {\n        \u002F\u002F循环拉取appid=infra.discovery的instances信息,并由此生成nodes信息\n        arg := &model.ArgPolls{\n            AppID:           []string{model.AppID},\n            Env:             d.c.Env.DeployEnv,\n            Hostname:        d.c.Env.Host,\n            LatestTimestamp: []int64{lastTs},\n        }\n        ch, _, _, err := d.registry.Polls(arg)\n        if err != nil && err != ecode.NotModified {\n            log.Error(\"d.registry(%v) error(%v)\", arg, err)\n            time.Sleep(time.Second)\n            continue\n        }\n        apps := \u003C-ch\n        ins, ok := apps[model.AppID]\n        if !ok || ins == nil {\n            return\n        }\n        var (\n            nodes []string\n            zones = make(map[string][]string)\n        )\n        for _, ins := range ins.Instances {\n            for _, in := range ins {\n                for _, addr := range in.Addrs {\n                    u, err := url.Parse(addr)\n                    if err == nil && u.Scheme == \"http\" {\n                        if in.Zone == d.c.Env.Zone {\n                            nodes = append(nodes, u.Host)\n                                               } else {\n                            zones[in.Zone] = append(zones[in.Zone], u.Host)\n                        }\n                    }\n                }\n            }\n        }\n        lastTs = ins.LatestTimestamp\n        c := new(conf.Config)\n        *c = *d.c\n        c.Nodes = nodes\n        c.Zones = zones\n        ns := registry.NewNodes(c)\n        ns.UP()\n        d.nodes.Store(ns)\n        log.Info(\"discovery changed nodes:%v zones:%v\", nodes, zones)\n    }\n}\n",[161,794,795,800,805,810,815,820,825,830,835,840,845,850,854,859,864,869,874,878,882,887,892,897,902,906,911,916,921,926,931,936,941,946,951,956,961,966,971,976,981,985,989,993,998,1003,1008,1013,1018,1023,1028,1033,1038,1042],{"__ignoreMap":159},[164,796,797],{"class":166,"line":167},[164,798,799],{},"func (d *Discovery) nodesproc() {\n",[164,801,802],{"class":166,"line":173},[164,803,804],{},"    var (\n",[164,806,807],{"class":166,"line":179},[164,808,809],{},"        lastTs int64\n",[164,811,812],{"class":166,"line":185},[164,813,814],{},"    )\n",[164,816,817],{"class":166,"line":191},[164,818,819],{},"    for {\n",[164,821,822],{"class":166,"line":197},[164,823,824],{},"        \u002F\u002F循环拉取appid=infra.discovery的instances信息,并由此生成nodes信息\n",[164,826,827],{"class":166,"line":203},[164,828,829],{},"        arg := &model.ArgPolls{\n",[164,831,832],{"class":166,"line":209},[164,833,834],{},"            AppID:           []string{model.AppID},\n",[164,836,837],{"class":166,"line":215},[164,838,839],{},"            Env:             d.c.Env.DeployEnv,\n",[164,841,842],{"class":166,"line":221},[164,843,844],{},"            Hostname:        d.c.Env.Host,\n",[164,846,847],{"class":166,"line":227},[164,848,849],{},"            LatestTimestamp: []int64{lastTs},\n",[164,851,852],{"class":166,"line":233},[164,853,272],{},[164,855,856],{"class":166,"line":239},[164,857,858],{},"        ch, _, _, err := d.registry.Polls(arg)\n",[164,860,861],{"class":166,"line":245},[164,862,863],{},"        if err != nil && err != ecode.NotModified {\n",[164,865,866],{"class":166,"line":251},[164,867,868],{},"            log.Error(\"d.registry(%v) error(%v)\", arg, err)\n",[164,870,871],{"class":166,"line":257},[164,872,873],{},"            time.Sleep(time.Second)\n",[164,875,876],{"class":166,"line":263},[164,877,378],{},[164,879,880],{"class":166,"line":269},[164,881,272],{},[164,883,884],{"class":166,"line":275},[164,885,886],{},"        apps := \u003C-ch\n",[164,888,889],{"class":166,"line":281},[164,890,891],{},"        ins, ok := apps[model.AppID]\n",[164,893,894],{"class":166,"line":286},[164,895,896],{},"        if !ok || ins == nil {\n",[164,898,899],{"class":166,"line":292},[164,900,901],{},"            return\n",[164,903,904],{"class":166,"line":298},[164,905,272],{},[164,907,908],{"class":166,"line":304},[164,909,910],{},"        var (\n",[164,912,913],{"class":166,"line":310},[164,914,915],{},"            nodes []string\n",[164,917,918],{"class":166,"line":316},[164,919,920],{},"            zones = make(map[string][]string)\n",[164,922,923],{"class":166,"line":322},[164,924,925],{},"        )\n",[164,927,928],{"class":166,"line":327},[164,929,930],{},"        for _, ins := range ins.Instances {\n",[164,932,933],{"class":166,"line":484},[164,934,935],{},"            for _, in := range ins {\n",[164,937,938],{"class":166,"line":489},[164,939,940],{},"                for _, addr := range in.Addrs {\n",[164,942,943],{"class":166,"line":495},[164,944,945],{},"                    u, err := url.Parse(addr)\n",[164,947,948],{"class":166,"line":672},[164,949,950],{},"                    if err == nil && u.Scheme == \"http\" {\n",[164,952,953],{"class":166,"line":678},[164,954,955],{},"                        if in.Zone == d.c.Env.Zone {\n",[164,957,958],{"class":166,"line":684},[164,959,960],{},"                            nodes = append(nodes, u.Host)\n",[164,962,963],{"class":166,"line":690},[164,964,965],{},"                                               } else {\n",[164,967,968],{"class":166,"line":695},[164,969,970],{},"                            zones[in.Zone] = append(zones[in.Zone], u.Host)\n",[164,972,973],{"class":166,"line":701},[164,974,975],{},"                        }\n",[164,977,978],{"class":166,"line":707},[164,979,980],{},"                    }\n",[164,982,983],{"class":166,"line":713},[164,984,675],{},[164,986,987],{"class":166,"line":718},[164,988,472],{},[164,990,991],{"class":166,"line":723},[164,992,272],{},[164,994,995],{"class":166,"line":728},[164,996,997],{},"        lastTs = ins.LatestTimestamp\n",[164,999,1000],{"class":166,"line":733},[164,1001,1002],{},"        c := new(conf.Config)\n",[164,1004,1005],{"class":166,"line":739},[164,1006,1007],{},"        *c = *d.c\n",[164,1009,1010],{"class":166,"line":745},[164,1011,1012],{},"        c.Nodes = nodes\n",[164,1014,1015],{"class":166,"line":750},[164,1016,1017],{},"        c.Zones = zones\n",[164,1019,1020],{"class":166,"line":756},[164,1021,1022],{},"        ns := registry.NewNodes(c)\n",[164,1024,1025],{"class":166,"line":761},[164,1026,1027],{},"        ns.UP()\n",[164,1029,1030],{"class":166,"line":766},[164,1031,1032],{},"        d.nodes.Store(ns)\n",[164,1034,1035],{"class":166,"line":772},[164,1036,1037],{},"        log.Info(\"discovery changed nodes:%v zones:%v\", nodes, zones)\n",[164,1039,1040],{"class":166,"line":778},[164,1041,212],{},[164,1043,1045],{"class":166,"line":1044},52,[164,1046,330],{},[14,1048,500],{},[61,1050,1051],{"id":1051},"主要操作",[137,1053,1055],{"id":1054},"_1注册心跳下线","1.注册、心跳、下线",[14,1057,1058],{},[20,1059],{"alt":1060,"src":1061},"image-20210112095652369","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112095652369.png",[14,1063,1064],{},"注册、更新、下线的过程类似，都是现在本节点处理（上图过程1），再扩散至本zone所有节点以及其他每个zone任一节点（过程2），最后扩散到其他zone的其他节点（过程3）",[14,1066,1067],{},"主要代码如下(以注册过程为例)：",[14,1069,1070],{},"相关路由定义(其中也包括了获取实例的路由)：",[154,1072,1074],{"className":156,"code":1073,"language":158,"meta":159,"style":159},"func innerRouter(e *bm.Engine) {\n    group := e.Group(\"\u002Fdiscovery\")\n    {\n        group.POST(\"\u002Fregister\", register)\n        group.POST(\"\u002Frenew\", renew)\n        group.POST(\"\u002Fcancel\", cancel)\n        group.GET(\"\u002Ffetch\u002Fall\", initProtect, fetchAll)\n        group.GET(\"\u002Ffetch\", initProtect, fetch)\n        group.GET(\"\u002Ffetchs\", initProtect, fetchs)\n        group.GET(\"\u002Fpoll\", initProtect, poll)\n        group.GET(\"\u002Fpolls\", initProtect, polls)\n        \u002F\u002Fmanager\n        group.POST(\"\u002Fset\", set)\n        group.GET(\"\u002Fnodes\", initProtect, nodes)\n    }\n}\n",[161,1075,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131,1136,1141,1146,1150],{"__ignoreMap":159},[164,1077,1078],{"class":166,"line":167},[164,1079,1080],{},"func innerRouter(e *bm.Engine) {\n",[164,1082,1083],{"class":166,"line":173},[164,1084,1085],{},"    group := e.Group(\"\u002Fdiscovery\")\n",[164,1087,1088],{"class":166,"line":179},[164,1089,1090],{},"    {\n",[164,1092,1093],{"class":166,"line":185},[164,1094,1095],{},"        group.POST(\"\u002Fregister\", register)\n",[164,1097,1098],{"class":166,"line":191},[164,1099,1100],{},"        group.POST(\"\u002Frenew\", renew)\n",[164,1102,1103],{"class":166,"line":197},[164,1104,1105],{},"        group.POST(\"\u002Fcancel\", cancel)\n",[164,1107,1108],{"class":166,"line":203},[164,1109,1110],{},"        group.GET(\"\u002Ffetch\u002Fall\", initProtect, fetchAll)\n",[164,1112,1113],{"class":166,"line":209},[164,1114,1115],{},"        group.GET(\"\u002Ffetch\", initProtect, fetch)\n",[164,1117,1118],{"class":166,"line":215},[164,1119,1120],{},"        group.GET(\"\u002Ffetchs\", initProtect, fetchs)\n",[164,1122,1123],{"class":166,"line":221},[164,1124,1125],{},"        group.GET(\"\u002Fpoll\", initProtect, poll)\n",[164,1127,1128],{"class":166,"line":227},[164,1129,1130],{},"        group.GET(\"\u002Fpolls\", initProtect, polls)\n",[164,1132,1133],{"class":166,"line":233},[164,1134,1135],{},"        \u002F\u002Fmanager\n",[164,1137,1138],{"class":166,"line":239},[164,1139,1140],{},"        group.POST(\"\u002Fset\", set)\n",[164,1142,1143],{"class":166,"line":245},[164,1144,1145],{},"        group.GET(\"\u002Fnodes\", initProtect, nodes)\n",[164,1147,1148],{"class":166,"line":251},[164,1149,212],{},[164,1151,1152],{"class":166,"line":257},[164,1153,330],{},[14,1155,1156],{},"文件：http\u002Fhttp.go",[14,1158,1159],{},"http注册接口：",[154,1161,1163],{"className":156,"code":1162,"language":158,"meta":159,"style":159},"func register(c *bm.Context) {\n    arg := new(model.ArgRegister)\n    if err := c.Bind(arg); err != nil {\n        return\n    }\n    i := model.NewInstance(arg)\n    if i.Status == 0 || i.Status > 2 {\n        log.Error(\"register params status invalid\")\n        return\n    }\n    if arg.Metadata != \"\" {\n        \u002F\u002F check the metadata type is json\n        if !json.Valid([]byte(arg.Metadata)) {\n            c.JSON(nil, ecode.RequestErr)\n            log.Error(\"register params() metadata(%v) invalid json\", arg.Metadata)\n            return\n        }\n    }\n    \u002F\u002F register replication\n    if arg.DirtyTimestamp > 0 {\n        i.DirtyTimestamp = arg.DirtyTimestamp\n    }\n    dis.Register(c, i, arg.LatestTimestamp, arg.Replication, arg.FromZone)\n    c.JSON(nil, nil)\n}\n",[161,1164,1165,1170,1175,1180,1185,1189,1194,1199,1204,1208,1212,1217,1222,1227,1232,1237,1241,1245,1249,1254,1259,1264,1268,1273,1278],{"__ignoreMap":159},[164,1166,1167],{"class":166,"line":167},[164,1168,1169],{},"func register(c *bm.Context) {\n",[164,1171,1172],{"class":166,"line":173},[164,1173,1174],{},"    arg := new(model.ArgRegister)\n",[164,1176,1177],{"class":166,"line":179},[164,1178,1179],{},"    if err := c.Bind(arg); err != nil {\n",[164,1181,1182],{"class":166,"line":185},[164,1183,1184],{},"        return\n",[164,1186,1187],{"class":166,"line":191},[164,1188,212],{},[164,1190,1191],{"class":166,"line":197},[164,1192,1193],{},"    i := model.NewInstance(arg)\n",[164,1195,1196],{"class":166,"line":203},[164,1197,1198],{},"    if i.Status == 0 || i.Status > 2 {\n",[164,1200,1201],{"class":166,"line":209},[164,1202,1203],{},"        log.Error(\"register params status invalid\")\n",[164,1205,1206],{"class":166,"line":215},[164,1207,1184],{},[164,1209,1210],{"class":166,"line":221},[164,1211,212],{},[164,1213,1214],{"class":166,"line":227},[164,1215,1216],{},"    if arg.Metadata != \"\" {\n",[164,1218,1219],{"class":166,"line":233},[164,1220,1221],{},"        \u002F\u002F check the metadata type is json\n",[164,1223,1224],{"class":166,"line":239},[164,1225,1226],{},"        if !json.Valid([]byte(arg.Metadata)) {\n",[164,1228,1229],{"class":166,"line":245},[164,1230,1231],{},"            c.JSON(nil, ecode.RequestErr)\n",[164,1233,1234],{"class":166,"line":251},[164,1235,1236],{},"            log.Error(\"register params() metadata(%v) invalid json\", arg.Metadata)\n",[164,1238,1239],{"class":166,"line":257},[164,1240,901],{},[164,1242,1243],{"class":166,"line":263},[164,1244,272],{},[164,1246,1247],{"class":166,"line":269},[164,1248,212],{},[164,1250,1251],{"class":166,"line":275},[164,1252,1253],{},"    \u002F\u002F register replication\n",[164,1255,1256],{"class":166,"line":281},[164,1257,1258],{},"    if arg.DirtyTimestamp > 0 {\n",[164,1260,1261],{"class":166,"line":286},[164,1262,1263],{},"        i.DirtyTimestamp = arg.DirtyTimestamp\n",[164,1265,1266],{"class":166,"line":292},[164,1267,212],{},[164,1269,1270],{"class":166,"line":298},[164,1271,1272],{},"    dis.Register(c, i, arg.LatestTimestamp, arg.Replication, arg.FromZone)\n",[164,1274,1275],{"class":166,"line":304},[164,1276,1277],{},"    c.JSON(nil, nil)\n",[164,1279,1280],{"class":166,"line":310},[164,1281,330],{},[14,1283,1284],{},"文件：http\u002Fdiscovery.go",[14,1286,1287],{},"执行注册：",[154,1289,1291],{"className":156,"code":1290,"language":158,"meta":159,"style":159},"\u002F\u002F Register a new instance.\nfunc (d *Discovery) Register(c context.Context, ins *model.Instance, latestTimestamp int64, replication bool, fromzone bool) {\n    \u002F\u002F本节点注册\n    _ = d.registry.Register(ins, latestTimestamp)\n    \u002F\u002F节点扩散\n    if !replication {\n        _ = d.nodes.Load().(*registry.Nodes).Replicate(c, model.Register, ins, fromzone)\n    }\n}\n",[161,1292,1293,1298,1303,1308,1313,1318,1323,1328,1332],{"__ignoreMap":159},[164,1294,1295],{"class":166,"line":167},[164,1296,1297],{},"\u002F\u002F Register a new instance.\n",[164,1299,1300],{"class":166,"line":173},[164,1301,1302],{},"func (d *Discovery) Register(c context.Context, ins *model.Instance, latestTimestamp int64, replication bool, fromzone bool) {\n",[164,1304,1305],{"class":166,"line":179},[164,1306,1307],{},"    \u002F\u002F本节点注册\n",[164,1309,1310],{"class":166,"line":185},[164,1311,1312],{},"    _ = d.registry.Register(ins, latestTimestamp)\n",[164,1314,1315],{"class":166,"line":191},[164,1316,1317],{},"    \u002F\u002F节点扩散\n",[164,1319,1320],{"class":166,"line":197},[164,1321,1322],{},"    if !replication {\n",[164,1324,1325],{"class":166,"line":203},[164,1326,1327],{},"        _ = d.nodes.Load().(*registry.Nodes).Replicate(c, model.Register, ins, fromzone)\n",[164,1329,1330],{"class":166,"line":209},[164,1331,212],{},[164,1333,1334],{"class":166,"line":215},[164,1335,330],{},[14,1337,1338],{},"文件：discovery\u002Fregister.go",[14,1340,1341],{},"本节点注册(过程1)：",[154,1343,1345],{"className":156,"code":1344,"language":158,"meta":159,"style":159},"\u002F\u002F Register a new instance.\nfunc (r *Registry) Register(ins *model.Instance, latestTime int64) (err error) {\n    a := r.newApp(ins)\n    i, ok := a.NewInstance(ins, latestTime)\n    if ok {\n        r.gd.incrExp()\n    }\n    \u002F\u002F NOTE: make sure free poll before update appid latest timestamp.\n    r.broadcast(i.Env, i.AppID)\n    return\n}\n",[161,1346,1347,1351,1356,1361,1366,1371,1376,1380,1385,1390,1395],{"__ignoreMap":159},[164,1348,1349],{"class":166,"line":167},[164,1350,1297],{},[164,1352,1353],{"class":166,"line":173},[164,1354,1355],{},"func (r *Registry) Register(ins *model.Instance, latestTime int64) (err error) {\n",[164,1357,1358],{"class":166,"line":179},[164,1359,1360],{},"    a := r.newApp(ins)\n",[164,1362,1363],{"class":166,"line":185},[164,1364,1365],{},"    i, ok := a.NewInstance(ins, latestTime)\n",[164,1367,1368],{"class":166,"line":191},[164,1369,1370],{},"    if ok {\n",[164,1372,1373],{"class":166,"line":197},[164,1374,1375],{},"        r.gd.incrExp()\n",[164,1377,1378],{"class":166,"line":203},[164,1379,212],{},[164,1381,1382],{"class":166,"line":209},[164,1383,1384],{},"    \u002F\u002F NOTE: make sure free poll before update appid latest timestamp.\n",[164,1386,1387],{"class":166,"line":215},[164,1388,1389],{},"    r.broadcast(i.Env, i.AppID)\n",[164,1391,1392],{"class":166,"line":221},[164,1393,1394],{},"    return\n",[164,1396,1397],{"class":166,"line":227},[164,1398,330],{},[14,1400,1401],{},"文件：registry\u002Fregistry.go",[14,1403,1404],{},"节点扩散：",[154,1406,1408],{"className":156,"code":1407,"language":158,"meta":159,"style":159},"\u002F\u002F Replicate replicate information to all nodes except for this node.\nfunc (ns *Nodes) Replicate(c context.Context, action model.Action, i *model.Instance, otherZone bool) (err error) {\n    if len(ns.nodes) == 0 {\n        return\n    }\n    eg, c := errgroup.WithContext(c)\n    \u002F\u002F复制到本zone的其他节点(过程2、过程3)\n    for _, n := range ns.nodes {\n        if !ns.Myself(n.addr) {\n            ns.action(c, eg, action, n, i)\n        }\n    }\n    \u002F\u002F复制到其他每个zone的任一节点（过程2）\n    if !otherZone {\n        for _, zns := range ns.zones {\n            if n := len(zns); n > 0 {\n                ns.action(c, eg, action, zns[rand.Intn(n)], i)\n            }\n        }\n    }\n    err = eg.Wait()\n    return\n}\n",[161,1409,1410,1415,1420,1425,1429,1433,1438,1443,1448,1453,1458,1462,1466,1471,1476,1481,1486,1491,1495,1499,1503,1508,1512],{"__ignoreMap":159},[164,1411,1412],{"class":166,"line":167},[164,1413,1414],{},"\u002F\u002F Replicate replicate information to all nodes except for this node.\n",[164,1416,1417],{"class":166,"line":173},[164,1418,1419],{},"func (ns *Nodes) Replicate(c context.Context, action model.Action, i *model.Instance, otherZone bool) (err error) {\n",[164,1421,1422],{"class":166,"line":179},[164,1423,1424],{},"    if len(ns.nodes) == 0 {\n",[164,1426,1427],{"class":166,"line":185},[164,1428,1184],{},[164,1430,1431],{"class":166,"line":191},[164,1432,212],{},[164,1434,1435],{"class":166,"line":197},[164,1436,1437],{},"    eg, c := errgroup.WithContext(c)\n",[164,1439,1440],{"class":166,"line":203},[164,1441,1442],{},"    \u002F\u002F复制到本zone的其他节点(过程2、过程3)\n",[164,1444,1445],{"class":166,"line":209},[164,1446,1447],{},"    for _, n := range ns.nodes {\n",[164,1449,1450],{"class":166,"line":215},[164,1451,1452],{},"        if !ns.Myself(n.addr) {\n",[164,1454,1455],{"class":166,"line":221},[164,1456,1457],{},"            ns.action(c, eg, action, n, i)\n",[164,1459,1460],{"class":166,"line":227},[164,1461,272],{},[164,1463,1464],{"class":166,"line":233},[164,1465,212],{},[164,1467,1468],{"class":166,"line":239},[164,1469,1470],{},"    \u002F\u002F复制到其他每个zone的任一节点（过程2）\n",[164,1472,1473],{"class":166,"line":245},[164,1474,1475],{},"    if !otherZone {\n",[164,1477,1478],{"class":166,"line":251},[164,1479,1480],{},"        for _, zns := range ns.zones {\n",[164,1482,1483],{"class":166,"line":257},[164,1484,1485],{},"            if n := len(zns); n > 0 {\n",[164,1487,1488],{"class":166,"line":263},[164,1489,1490],{},"                ns.action(c, eg, action, zns[rand.Intn(n)], i)\n",[164,1492,1493],{"class":166,"line":269},[164,1494,472],{},[164,1496,1497],{"class":166,"line":275},[164,1498,272],{},[164,1500,1501],{"class":166,"line":281},[164,1502,212],{},[164,1504,1505],{"class":166,"line":286},[164,1506,1507],{},"    err = eg.Wait()\n",[164,1509,1510],{"class":166,"line":292},[164,1511,1394],{},[164,1513,1514],{"class":166,"line":298},[164,1515,330],{},[14,1517,1518],{},"action方法：",[154,1520,1522],{"className":156,"code":1521,"language":158,"meta":159,"style":159},"func (ns *Nodes) action(c context.Context, eg *errgroup.Group, action model.Action, n *Node, i *model.Instance) {\n    switch action {\n    case model.Register:\n        eg.Go(func() error {\n            _ = n.Register(c, i)\n            return nil\n        })\n    case model.Renew:\n        eg.Go(func() error {\n            _ = n.Renew(c, i)\n            return nil\n        })\n    case model.Cancel:\n        eg.Go(func() error {\n            _ = n.Cancel(c, i)\n            return nil\n        })\n    }\n}\n",[161,1523,1524,1529,1534,1539,1544,1549,1554,1559,1564,1568,1573,1577,1581,1586,1590,1595,1599,1603,1607],{"__ignoreMap":159},[164,1525,1526],{"class":166,"line":167},[164,1527,1528],{},"func (ns *Nodes) action(c context.Context, eg *errgroup.Group, action model.Action, n *Node, i *model.Instance) {\n",[164,1530,1531],{"class":166,"line":173},[164,1532,1533],{},"    switch action {\n",[164,1535,1536],{"class":166,"line":179},[164,1537,1538],{},"    case model.Register:\n",[164,1540,1541],{"class":166,"line":185},[164,1542,1543],{},"        eg.Go(func() error {\n",[164,1545,1546],{"class":166,"line":191},[164,1547,1548],{},"            _ = n.Register(c, i)\n",[164,1550,1551],{"class":166,"line":197},[164,1552,1553],{},"            return nil\n",[164,1555,1556],{"class":166,"line":203},[164,1557,1558],{},"        })\n",[164,1560,1561],{"class":166,"line":209},[164,1562,1563],{},"    case model.Renew:\n",[164,1565,1566],{"class":166,"line":215},[164,1567,1543],{},[164,1569,1570],{"class":166,"line":221},[164,1571,1572],{},"            _ = n.Renew(c, i)\n",[164,1574,1575],{"class":166,"line":227},[164,1576,1553],{},[164,1578,1579],{"class":166,"line":233},[164,1580,1558],{},[164,1582,1583],{"class":166,"line":239},[164,1584,1585],{},"    case model.Cancel:\n",[164,1587,1588],{"class":166,"line":245},[164,1589,1543],{},[164,1591,1592],{"class":166,"line":251},[164,1593,1594],{},"            _ = n.Cancel(c, i)\n",[164,1596,1597],{"class":166,"line":257},[164,1598,1553],{},[164,1600,1601],{"class":166,"line":263},[164,1602,1558],{},[164,1604,1605],{"class":166,"line":269},[164,1606,212],{},[164,1608,1609],{"class":166,"line":275},[164,1610,330],{},[14,1612,333],{},[14,1614,1615],{},"其他node注册：",[154,1617,1619],{"className":156,"code":1618,"language":158,"meta":159,"style":159},"\u002F\u002F Register send the registration information of Instance receiving by this node to the peer node represented.\nfunc (n *Node) Register(c context.Context, i *model.Instance) (err error) {\n    err = n.call(c, model.Register, i, n.registerURL, nil)\n    if err != nil {\n        log.Warn(\"node be called(%s) register instance(%v) error(%v)\", n.registerURL, i, err)\n    }\n    return\n}\n",[161,1620,1621,1626,1631,1636,1641,1646,1650,1654],{"__ignoreMap":159},[164,1622,1623],{"class":166,"line":167},[164,1624,1625],{},"\u002F\u002F Register send the registration information of Instance receiving by this node to the peer node represented.\n",[164,1627,1628],{"class":166,"line":173},[164,1629,1630],{},"func (n *Node) Register(c context.Context, i *model.Instance) (err error) {\n",[164,1632,1633],{"class":166,"line":179},[164,1634,1635],{},"    err = n.call(c, model.Register, i, n.registerURL, nil)\n",[164,1637,1638],{"class":166,"line":185},[164,1639,1640],{},"    if err != nil {\n",[164,1642,1643],{"class":166,"line":191},[164,1644,1645],{},"        log.Warn(\"node be called(%s) register instance(%v) error(%v)\", n.registerURL, i, err)\n",[164,1647,1648],{"class":166,"line":197},[164,1649,212],{},[164,1651,1652],{"class":166,"line":203},[164,1653,1394],{},[164,1655,1656],{"class":166,"line":209},[164,1657,330],{},[14,1659,1660],{},"call方法：",[154,1662,1664],{"className":156,"code":1663,"language":158,"meta":159,"style":159},"func (n *Node) call(c context.Context, action model.Action, i *model.Instance, uri string, data interface{}) (err error) {\n    params := url.Values{}\n    params.Set(\"region\", i.Region)\n    params.Set(\"zone\", i.Zone)\n    params.Set(\"env\", i.Env)\n    params.Set(\"appid\", i.AppID)\n    params.Set(\"hostname\", i.Hostname)\n    \u002F\u002F关键参数1\n    params.Set(\"from_zone\", \"true\")\n    \u002F\u002F关键参数2\n    if n.otherZone {\n        params.Set(\"replication\", \"false\")\n    } else {\n        params.Set(\"replication\", \"true\")\n    }\n    switch action {\n    case model.Register:\n        params.Set(\"addrs\", strings.Join(i.Addrs, \",\"))\n        params.Set(\"status\", strconv.FormatUint(uint64(i.Status), 10))\n        params.Set(\"version\", i.Version)\n        meta, _ := json.Marshal(i.Metadata)\n        params.Set(\"metadata\", string(meta))\n        params.Set(\"reg_timestamp\", strconv.FormatInt(i.RegTimestamp, 10))\n        params.Set(\"dirty_timestamp\", strconv.FormatInt(i.DirtyTimestamp, 10))\n        params.Set(\"latest_timestamp\", strconv.FormatInt(i.LatestTimestamp, 10))\n    case model.Renew:\n        params.Set(\"dirty_timestamp\", strconv.FormatInt(i.DirtyTimestamp, 10))\n    case model.Cancel:\n        params.Set(\"latest_timestamp\", strconv.FormatInt(i.LatestTimestamp, 10))\n    }\n    var res struct {\n        Code int             `json:\"code\"`\n        Data json.RawMessage `json:\"data\"`\n    }\n    if err = n.client.Post(c, uri, \"\", params, &res); err != nil {\n        log.Error(\"node be called(%s) instance(%v) error(%v)\", uri, i, err)\n        return\n    }\n    if res.Code != 0 {\n          log.Error(\"node be called(%s) instance(%v) response code(%v)\", uri, i, res.Code)\n        if err = ecode.Int(res.Code); err == ecode.Conflict {\n            _ = json.Unmarshal([]byte(res.Data), data)\n        }\n    }\n    return\n}\n",[161,1665,1666,1671,1676,1681,1686,1691,1696,1701,1706,1711,1716,1721,1726,1731,1736,1740,1744,1748,1753,1758,1763,1768,1773,1778,1783,1788,1792,1796,1800,1804,1808,1813,1818,1823,1827,1832,1837,1841,1845,1850,1855,1860,1865,1869,1873,1877],{"__ignoreMap":159},[164,1667,1668],{"class":166,"line":167},[164,1669,1670],{},"func (n *Node) call(c context.Context, action model.Action, i *model.Instance, uri string, data interface{}) (err error) {\n",[164,1672,1673],{"class":166,"line":173},[164,1674,1675],{},"    params := url.Values{}\n",[164,1677,1678],{"class":166,"line":179},[164,1679,1680],{},"    params.Set(\"region\", i.Region)\n",[164,1682,1683],{"class":166,"line":185},[164,1684,1685],{},"    params.Set(\"zone\", i.Zone)\n",[164,1687,1688],{"class":166,"line":191},[164,1689,1690],{},"    params.Set(\"env\", i.Env)\n",[164,1692,1693],{"class":166,"line":197},[164,1694,1695],{},"    params.Set(\"appid\", i.AppID)\n",[164,1697,1698],{"class":166,"line":203},[164,1699,1700],{},"    params.Set(\"hostname\", i.Hostname)\n",[164,1702,1703],{"class":166,"line":209},[164,1704,1705],{},"    \u002F\u002F关键参数1\n",[164,1707,1708],{"class":166,"line":215},[164,1709,1710],{},"    params.Set(\"from_zone\", \"true\")\n",[164,1712,1713],{"class":166,"line":221},[164,1714,1715],{},"    \u002F\u002F关键参数2\n",[164,1717,1718],{"class":166,"line":227},[164,1719,1720],{},"    if n.otherZone {\n",[164,1722,1723],{"class":166,"line":233},[164,1724,1725],{},"        params.Set(\"replication\", \"false\")\n",[164,1727,1728],{"class":166,"line":239},[164,1729,1730],{},"    } else {\n",[164,1732,1733],{"class":166,"line":245},[164,1734,1735],{},"        params.Set(\"replication\", \"true\")\n",[164,1737,1738],{"class":166,"line":251},[164,1739,212],{},[164,1741,1742],{"class":166,"line":257},[164,1743,1533],{},[164,1745,1746],{"class":166,"line":263},[164,1747,1538],{},[164,1749,1750],{"class":166,"line":269},[164,1751,1752],{},"        params.Set(\"addrs\", strings.Join(i.Addrs, \",\"))\n",[164,1754,1755],{"class":166,"line":275},[164,1756,1757],{},"        params.Set(\"status\", strconv.FormatUint(uint64(i.Status), 10))\n",[164,1759,1760],{"class":166,"line":281},[164,1761,1762],{},"        params.Set(\"version\", i.Version)\n",[164,1764,1765],{"class":166,"line":286},[164,1766,1767],{},"        meta, _ := json.Marshal(i.Metadata)\n",[164,1769,1770],{"class":166,"line":292},[164,1771,1772],{},"        params.Set(\"metadata\", string(meta))\n",[164,1774,1775],{"class":166,"line":298},[164,1776,1777],{},"        params.Set(\"reg_timestamp\", strconv.FormatInt(i.RegTimestamp, 10))\n",[164,1779,1780],{"class":166,"line":304},[164,1781,1782],{},"        params.Set(\"dirty_timestamp\", strconv.FormatInt(i.DirtyTimestamp, 10))\n",[164,1784,1785],{"class":166,"line":310},[164,1786,1787],{},"        params.Set(\"latest_timestamp\", strconv.FormatInt(i.LatestTimestamp, 10))\n",[164,1789,1790],{"class":166,"line":316},[164,1791,1563],{},[164,1793,1794],{"class":166,"line":322},[164,1795,1782],{},[164,1797,1798],{"class":166,"line":327},[164,1799,1585],{},[164,1801,1802],{"class":166,"line":484},[164,1803,1787],{},[164,1805,1806],{"class":166,"line":489},[164,1807,212],{},[164,1809,1810],{"class":166,"line":495},[164,1811,1812],{},"    var res struct {\n",[164,1814,1815],{"class":166,"line":672},[164,1816,1817],{},"        Code int             `json:\"code\"`\n",[164,1819,1820],{"class":166,"line":678},[164,1821,1822],{},"        Data json.RawMessage `json:\"data\"`\n",[164,1824,1825],{"class":166,"line":684},[164,1826,212],{},[164,1828,1829],{"class":166,"line":690},[164,1830,1831],{},"    if err = n.client.Post(c, uri, \"\", params, &res); err != nil {\n",[164,1833,1834],{"class":166,"line":695},[164,1835,1836],{},"        log.Error(\"node be called(%s) instance(%v) error(%v)\", uri, i, err)\n",[164,1838,1839],{"class":166,"line":701},[164,1840,1184],{},[164,1842,1843],{"class":166,"line":707},[164,1844,212],{},[164,1846,1847],{"class":166,"line":713},[164,1848,1849],{},"    if res.Code != 0 {\n",[164,1851,1852],{"class":166,"line":718},[164,1853,1854],{},"          log.Error(\"node be called(%s) instance(%v) response code(%v)\", uri, i, res.Code)\n",[164,1856,1857],{"class":166,"line":723},[164,1858,1859],{},"        if err = ecode.Int(res.Code); err == ecode.Conflict {\n",[164,1861,1862],{"class":166,"line":728},[164,1863,1864],{},"            _ = json.Unmarshal([]byte(res.Data), data)\n",[164,1866,1867],{"class":166,"line":733},[164,1868,272],{},[164,1870,1871],{"class":166,"line":739},[164,1872,212],{},[164,1874,1875],{"class":166,"line":745},[164,1876,1394],{},[164,1878,1879],{"class":166,"line":750},[164,1880,330],{},[14,1882,1883],{},"文件：registry\u002Fnode.go",[14,1885,1886],{},"上述代码的两个关键参数要尤其留意，正是这两个参数保证了操作正确地扩散到其他node节点",[137,1888,1890],{"id":1889},"_2获取实例","2.获取实例",[14,1892,1893],{},"1.内存结构Registry.conns->hosts->conn维护了阻塞等待instances的连接信息\n2.注册、下线、修改等操作会执行broadcast，broadcast会遍历所有的conn并向对应chan发送instances信息",[14,1895,1896],{},[20,1897],{"alt":1898,"src":1899},"image-20210112100010629","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112100010629.png",[14,1901,1902],{},"下面以polls为例说明：",[14,1904,1905],{},"polls接口：",[154,1907,1909],{"className":156,"code":1908,"language":158,"meta":159,"style":159},"func polls(c *bm.Context) {\n    arg := new(model.ArgPolls)\n    if err := c.Bind(arg); err != nil {\n        return\n    }\n    if len(arg.AppID) != len(arg.LatestTimestamp) {\n        c.JSON(nil, ecode.RequestErr)\n        return\n    }\n    ch, new, miss, err := dis.Polls(c, arg)\n    if err != nil {\n        c.JSON(nil, err)\n        return\n    }\n    \u002F\u002F wait for instance change\n    select {\n    case e := \u003C-ch:\n        c.JSONMap(map[string]interface{}{\n            \"data\": e,\n            \"error\": map[ecode.Code]interface{}{\n                ecode.NothingFound: miss,\n            },\n        }, nil)\n        if !new {\n            dis.DelConns(arg) \u002F\u002F broadcast will delete all connections of appid\n        }\n        return\n    case \u003C-time.After(_pollWaitSecond):\n    case \u003C-c.Done():\n    }\n    c.JSON(nil, ecode.NotModified)\n    dis.DelConns(arg)\n}\n",[161,1910,1911,1916,1921,1925,1929,1933,1938,1943,1947,1951,1956,1960,1965,1969,1973,1978,1983,1988,1993,1998,2003,2008,2013,2018,2023,2028,2032,2036,2041,2046,2050,2055,2060],{"__ignoreMap":159},[164,1912,1913],{"class":166,"line":167},[164,1914,1915],{},"func polls(c *bm.Context) {\n",[164,1917,1918],{"class":166,"line":173},[164,1919,1920],{},"    arg := new(model.ArgPolls)\n",[164,1922,1923],{"class":166,"line":179},[164,1924,1179],{},[164,1926,1927],{"class":166,"line":185},[164,1928,1184],{},[164,1930,1931],{"class":166,"line":191},[164,1932,212],{},[164,1934,1935],{"class":166,"line":197},[164,1936,1937],{},"    if len(arg.AppID) != len(arg.LatestTimestamp) {\n",[164,1939,1940],{"class":166,"line":203},[164,1941,1942],{},"        c.JSON(nil, ecode.RequestErr)\n",[164,1944,1945],{"class":166,"line":209},[164,1946,1184],{},[164,1948,1949],{"class":166,"line":215},[164,1950,212],{},[164,1952,1953],{"class":166,"line":221},[164,1954,1955],{},"    ch, new, miss, err := dis.Polls(c, arg)\n",[164,1957,1958],{"class":166,"line":227},[164,1959,1640],{},[164,1961,1962],{"class":166,"line":233},[164,1963,1964],{},"        c.JSON(nil, err)\n",[164,1966,1967],{"class":166,"line":239},[164,1968,1184],{},[164,1970,1971],{"class":166,"line":245},[164,1972,212],{},[164,1974,1975],{"class":166,"line":251},[164,1976,1977],{},"    \u002F\u002F wait for instance change\n",[164,1979,1980],{"class":166,"line":257},[164,1981,1982],{},"    select {\n",[164,1984,1985],{"class":166,"line":263},[164,1986,1987],{},"    case e := \u003C-ch:\n",[164,1989,1990],{"class":166,"line":269},[164,1991,1992],{},"        c.JSONMap(map[string]interface{}{\n",[164,1994,1995],{"class":166,"line":275},[164,1996,1997],{},"            \"data\": e,\n",[164,1999,2000],{"class":166,"line":281},[164,2001,2002],{},"            \"error\": map[ecode.Code]interface{}{\n",[164,2004,2005],{"class":166,"line":286},[164,2006,2007],{},"                ecode.NothingFound: miss,\n",[164,2009,2010],{"class":166,"line":292},[164,2011,2012],{},"            },\n",[164,2014,2015],{"class":166,"line":298},[164,2016,2017],{},"        }, nil)\n",[164,2019,2020],{"class":166,"line":304},[164,2021,2022],{},"        if !new {\n",[164,2024,2025],{"class":166,"line":310},[164,2026,2027],{},"            dis.DelConns(arg) \u002F\u002F broadcast will delete all connections of appid\n",[164,2029,2030],{"class":166,"line":316},[164,2031,272],{},[164,2033,2034],{"class":166,"line":322},[164,2035,1184],{},[164,2037,2038],{"class":166,"line":327},[164,2039,2040],{},"    case \u003C-time.After(_pollWaitSecond):\n",[164,2042,2043],{"class":166,"line":484},[164,2044,2045],{},"    case \u003C-c.Done():\n",[164,2047,2048],{"class":166,"line":489},[164,2049,212],{},[164,2051,2052],{"class":166,"line":495},[164,2053,2054],{},"    c.JSON(nil, ecode.NotModified)\n",[164,2056,2057],{"class":166,"line":672},[164,2058,2059],{},"    dis.DelConns(arg)\n",[164,2061,2062],{"class":166,"line":678},[164,2063,330],{},[14,2065,1284],{},[14,2067,2068],{},"dis.Polls:",[154,2070,2072],{"className":156,"code":2071,"language":158,"meta":159,"style":159},"\u002F\u002F Polls hangs request and then write instances when that has changes, or return NotModified.\nfunc (d *Discovery) Polls(c context.Context, arg *model.ArgPolls) (ch chan map[string]*model.InstanceInfo, new bool, miss []string, err error) {\n    return d.registry.Polls(arg)\n}\n",[161,2073,2074,2079,2084,2089],{"__ignoreMap":159},[164,2075,2076],{"class":166,"line":167},[164,2077,2078],{},"\u002F\u002F Polls hangs request and then write instances when that has changes, or return NotModified.\n",[164,2080,2081],{"class":166,"line":173},[164,2082,2083],{},"func (d *Discovery) Polls(c context.Context, arg *model.ArgPolls) (ch chan map[string]*model.InstanceInfo, new bool, miss []string, err error) {\n",[164,2085,2086],{"class":166,"line":179},[164,2087,2088],{},"    return d.registry.Polls(arg)\n",[164,2090,2091],{"class":166,"line":185},[164,2092,330],{},[14,2094,1338],{},[14,2096,2097],{},"registry.Polls:",[154,2099,2101],{"className":156,"code":2100,"language":158,"meta":159,"style":159},"\u002F\u002F Polls hangs request and then write instances when that has changes, or return NotModified.\nfunc (r *Registry) Polls(arg *model.ArgPolls) (ch chan map[string]*model.InstanceInfo, new bool, miss []string, err error) {\n    var (\n        ins = make(map[string]*model.InstanceInfo, len(arg.AppID))\n    )\n    if len(arg.AppID) != len(arg.LatestTimestamp) {\n        arg.LatestTimestamp = make([]int64, len(arg.AppID))\n    }\n    for i := range arg.AppID {\n        in, err := r.Fetch(arg.Zone, arg.Env, arg.AppID[i], arg.LatestTimestamp[i], model.InstanceStatusUP)\n        if err == ecode.NothingFound {\n            miss = append(miss, arg.AppID[i])\n            log.Error(\"Polls zone(%s) env(%s) appid(%s) error(%v)\", arg.Zone, arg.Env, arg.AppID[i], err)\n            continue\n        }\n        if err == nil {\n            ins[arg.AppID[i]] = in\n            new = true\n        }\n    }\n    if new {\n        ch = make(chan map[string]*model.InstanceInfo, 1)\n        ch \u003C- ins\n        return\n    }\n    for i := range arg.AppID {\n        k := pollKey(arg.Env, arg.AppID[i])\n        r.cLock.Lock()\n        if _, ok := r.conns[k]; !ok {\n            r.conns[k] = &hosts{hosts: make(map[string]*conn, 1)}\n        }\n        hosts := r.conns[k]\n        r.cLock.Unlock()\n\n        hosts.hclock.Lock()\n         connection, ok := hosts.hosts[arg.Hostname]\n        if !ok {\n            if ch == nil {\n                ch = make(chan map[string]*model.InstanceInfo, 5) \u002F\u002F NOTE: there maybe have more than one connection on the same hostname!!!\n            }\n            connection = newConn(ch, arg.LatestTimestamp[i], arg)\n            log.Info(\"Polls from(%s) new connection(%d)\", arg.Hostname, connection.count)\n        } else {\n            connection.count++ \u002F\u002F NOTE: there maybe have more than one connection on the same hostname!!!\n            if ch == nil {\n                ch = connection.ch\n            }\n            log.Info(\"Polls from(%s) reuse connection(%d)\", arg.Hostname, connection.count)\n        }\n        hosts.hosts[arg.Hostname] = connection\n        hosts.hclock.Unlock()\n    }\n    return\n}\n",[161,2102,2103,2107,2112,2116,2121,2125,2129,2134,2138,2143,2148,2153,2158,2163,2167,2171,2176,2181,2186,2190,2194,2199,2204,2209,2213,2217,2221,2226,2231,2236,2241,2245,2250,2255,2261,2266,2271,2276,2281,2286,2290,2295,2300,2305,2310,2314,2319,2323,2328,2332,2337,2342,2346,2351],{"__ignoreMap":159},[164,2104,2105],{"class":166,"line":167},[164,2106,2078],{},[164,2108,2109],{"class":166,"line":173},[164,2110,2111],{},"func (r *Registry) Polls(arg *model.ArgPolls) (ch chan map[string]*model.InstanceInfo, new bool, miss []string, err error) {\n",[164,2113,2114],{"class":166,"line":179},[164,2115,804],{},[164,2117,2118],{"class":166,"line":185},[164,2119,2120],{},"        ins = make(map[string]*model.InstanceInfo, len(arg.AppID))\n",[164,2122,2123],{"class":166,"line":191},[164,2124,814],{},[164,2126,2127],{"class":166,"line":197},[164,2128,1937],{},[164,2130,2131],{"class":166,"line":203},[164,2132,2133],{},"        arg.LatestTimestamp = make([]int64, len(arg.AppID))\n",[164,2135,2136],{"class":166,"line":209},[164,2137,212],{},[164,2139,2140],{"class":166,"line":215},[164,2141,2142],{},"    for i := range arg.AppID {\n",[164,2144,2145],{"class":166,"line":221},[164,2146,2147],{},"        in, err := r.Fetch(arg.Zone, arg.Env, arg.AppID[i], arg.LatestTimestamp[i], model.InstanceStatusUP)\n",[164,2149,2150],{"class":166,"line":227},[164,2151,2152],{},"        if err == ecode.NothingFound {\n",[164,2154,2155],{"class":166,"line":233},[164,2156,2157],{},"            miss = append(miss, arg.AppID[i])\n",[164,2159,2160],{"class":166,"line":239},[164,2161,2162],{},"            log.Error(\"Polls zone(%s) env(%s) appid(%s) error(%v)\", arg.Zone, arg.Env, arg.AppID[i], err)\n",[164,2164,2165],{"class":166,"line":245},[164,2166,378],{},[164,2168,2169],{"class":166,"line":251},[164,2170,272],{},[164,2172,2173],{"class":166,"line":257},[164,2174,2175],{},"        if err == nil {\n",[164,2177,2178],{"class":166,"line":263},[164,2179,2180],{},"            ins[arg.AppID[i]] = in\n",[164,2182,2183],{"class":166,"line":269},[164,2184,2185],{},"            new = true\n",[164,2187,2188],{"class":166,"line":275},[164,2189,272],{},[164,2191,2192],{"class":166,"line":281},[164,2193,212],{},[164,2195,2196],{"class":166,"line":286},[164,2197,2198],{},"    if new {\n",[164,2200,2201],{"class":166,"line":292},[164,2202,2203],{},"        ch = make(chan map[string]*model.InstanceInfo, 1)\n",[164,2205,2206],{"class":166,"line":298},[164,2207,2208],{},"        ch \u003C- ins\n",[164,2210,2211],{"class":166,"line":304},[164,2212,1184],{},[164,2214,2215],{"class":166,"line":310},[164,2216,212],{},[164,2218,2219],{"class":166,"line":316},[164,2220,2142],{},[164,2222,2223],{"class":166,"line":322},[164,2224,2225],{},"        k := pollKey(arg.Env, arg.AppID[i])\n",[164,2227,2228],{"class":166,"line":327},[164,2229,2230],{},"        r.cLock.Lock()\n",[164,2232,2233],{"class":166,"line":484},[164,2234,2235],{},"        if _, ok := r.conns[k]; !ok {\n",[164,2237,2238],{"class":166,"line":489},[164,2239,2240],{},"            r.conns[k] = &hosts{hosts: make(map[string]*conn, 1)}\n",[164,2242,2243],{"class":166,"line":495},[164,2244,272],{},[164,2246,2247],{"class":166,"line":672},[164,2248,2249],{},"        hosts := r.conns[k]\n",[164,2251,2252],{"class":166,"line":678},[164,2253,2254],{},"        r.cLock.Unlock()\n",[164,2256,2257],{"class":166,"line":684},[164,2258,2260],{"emptyLinePlaceholder":2259},true,"\n",[164,2262,2263],{"class":166,"line":690},[164,2264,2265],{},"        hosts.hclock.Lock()\n",[164,2267,2268],{"class":166,"line":695},[164,2269,2270],{},"         connection, ok := hosts.hosts[arg.Hostname]\n",[164,2272,2273],{"class":166,"line":701},[164,2274,2275],{},"        if !ok {\n",[164,2277,2278],{"class":166,"line":707},[164,2279,2280],{},"            if ch == nil {\n",[164,2282,2283],{"class":166,"line":713},[164,2284,2285],{},"                ch = make(chan map[string]*model.InstanceInfo, 5) \u002F\u002F NOTE: there maybe have more than one connection on the same hostname!!!\n",[164,2287,2288],{"class":166,"line":718},[164,2289,472],{},[164,2291,2292],{"class":166,"line":723},[164,2293,2294],{},"            connection = newConn(ch, arg.LatestTimestamp[i], arg)\n",[164,2296,2297],{"class":166,"line":728},[164,2298,2299],{},"            log.Info(\"Polls from(%s) new connection(%d)\", arg.Hostname, connection.count)\n",[164,2301,2302],{"class":166,"line":733},[164,2303,2304],{},"        } else {\n",[164,2306,2307],{"class":166,"line":739},[164,2308,2309],{},"            connection.count++ \u002F\u002F NOTE: there maybe have more than one connection on the same hostname!!!\n",[164,2311,2312],{"class":166,"line":745},[164,2313,2280],{},[164,2315,2316],{"class":166,"line":750},[164,2317,2318],{},"                ch = connection.ch\n",[164,2320,2321],{"class":166,"line":756},[164,2322,472],{},[164,2324,2325],{"class":166,"line":761},[164,2326,2327],{},"            log.Info(\"Polls from(%s) reuse connection(%d)\", arg.Hostname, connection.count)\n",[164,2329,2330],{"class":166,"line":766},[164,2331,272],{},[164,2333,2334],{"class":166,"line":772},[164,2335,2336],{},"        hosts.hosts[arg.Hostname] = connection\n",[164,2338,2339],{"class":166,"line":778},[164,2340,2341],{},"        hosts.hclock.Unlock()\n",[164,2343,2344],{"class":166,"line":1044},[164,2345,212],{},[164,2347,2349],{"class":166,"line":2348},53,[164,2350,1394],{},[164,2352,2354],{"class":166,"line":2353},54,[164,2355,330],{},[14,2357,2358],{},"broadcast方法：",[154,2360,2362],{"className":156,"code":2361,"language":158,"meta":159,"style":159},"\u002F\u002F broadcast on poll by chan.\n\u002F\u002F NOTE: make sure free poll before update appid latest timestamp.\nfunc (r *Registry) broadcast(env, appid string) {\n    key := pollKey(env, appid)\n    r.cLock.Lock()\n    conns, ok := r.conns[key]\n    if !ok {\n        r.cLock.Unlock()\n        return\n    }\n    delete(r.conns, key)\n    r.cLock.Unlock()\n    conns.hclock.RLock()\n    for _, conn := range conns.hosts {\n        ii, err := r.Fetch(conn.arg.Zone, env, appid, 0, model.InstanceStatusUP) \u002F\u002F TODO(felix): latesttime!=0 increase\n        if err != nil {\n            \u002F\u002F may be not found ,just continue until next poll return err.\n            log.Error(\"get appid:%s env:%s zone:%s err:%v\", appid, env, conn.arg.Zone, err)\n            continue\n        }\n        for i := 0; i \u003C conn.count; i++ {\n            select {\n            case conn.ch \u003C- map[string]*model.InstanceInfo{appid: ii}: \u002F\u002F NOTE: if chan is full, means no poller.\n                log.Info(\"broadcast to(%s) success(%d)\", conn.arg.Hostname, i+1)\n            case \u003C-time.After(time.Millisecond * 500):\n                log.Info(\"broadcast to(%s) failed(%d) maybe chan full\", conn.arg.Hostname, i+1)\n            }\n        }\n    }\n    conns.hclock.RUnlock()\n}\n",[161,2363,2364,2369,2374,2379,2384,2389,2394,2399,2403,2407,2411,2416,2421,2426,2431,2436,2441,2446,2451,2455,2459,2464,2468,2473,2478,2483,2488,2492,2496,2500,2505],{"__ignoreMap":159},[164,2365,2366],{"class":166,"line":167},[164,2367,2368],{},"\u002F\u002F broadcast on poll by chan.\n",[164,2370,2371],{"class":166,"line":173},[164,2372,2373],{},"\u002F\u002F NOTE: make sure free poll before update appid latest timestamp.\n",[164,2375,2376],{"class":166,"line":179},[164,2377,2378],{},"func (r *Registry) broadcast(env, appid string) {\n",[164,2380,2381],{"class":166,"line":185},[164,2382,2383],{},"    key := pollKey(env, appid)\n",[164,2385,2386],{"class":166,"line":191},[164,2387,2388],{},"    r.cLock.Lock()\n",[164,2390,2391],{"class":166,"line":197},[164,2392,2393],{},"    conns, ok := r.conns[key]\n",[164,2395,2396],{"class":166,"line":203},[164,2397,2398],{},"    if !ok {\n",[164,2400,2401],{"class":166,"line":209},[164,2402,2254],{},[164,2404,2405],{"class":166,"line":215},[164,2406,1184],{},[164,2408,2409],{"class":166,"line":221},[164,2410,212],{},[164,2412,2413],{"class":166,"line":227},[164,2414,2415],{},"    delete(r.conns, key)\n",[164,2417,2418],{"class":166,"line":233},[164,2419,2420],{},"    r.cLock.Unlock()\n",[164,2422,2423],{"class":166,"line":239},[164,2424,2425],{},"    conns.hclock.RLock()\n",[164,2427,2428],{"class":166,"line":245},[164,2429,2430],{},"    for _, conn := range conns.hosts {\n",[164,2432,2433],{"class":166,"line":251},[164,2434,2435],{},"        ii, err := r.Fetch(conn.arg.Zone, env, appid, 0, model.InstanceStatusUP) \u002F\u002F TODO(felix): latesttime!=0 increase\n",[164,2437,2438],{"class":166,"line":257},[164,2439,2440],{},"        if err != nil {\n",[164,2442,2443],{"class":166,"line":263},[164,2444,2445],{},"            \u002F\u002F may be not found ,just continue until next poll return err.\n",[164,2447,2448],{"class":166,"line":269},[164,2449,2450],{},"            log.Error(\"get appid:%s env:%s zone:%s err:%v\", appid, env, conn.arg.Zone, err)\n",[164,2452,2453],{"class":166,"line":275},[164,2454,378],{},[164,2456,2457],{"class":166,"line":281},[164,2458,272],{},[164,2460,2461],{"class":166,"line":286},[164,2462,2463],{},"        for i := 0; i \u003C conn.count; i++ {\n",[164,2465,2466],{"class":166,"line":292},[164,2467,639],{},[164,2469,2470],{"class":166,"line":298},[164,2471,2472],{},"            case conn.ch \u003C- map[string]*model.InstanceInfo{appid: ii}: \u002F\u002F NOTE: if chan is full, means no poller.\n",[164,2474,2475],{"class":166,"line":304},[164,2476,2477],{},"                log.Info(\"broadcast to(%s) success(%d)\", conn.arg.Hostname, i+1)\n",[164,2479,2480],{"class":166,"line":310},[164,2481,2482],{},"            case \u003C-time.After(time.Millisecond * 500):\n",[164,2484,2485],{"class":166,"line":316},[164,2486,2487],{},"                log.Info(\"broadcast to(%s) failed(%d) maybe chan full\", conn.arg.Hostname, i+1)\n",[164,2489,2490],{"class":166,"line":322},[164,2491,472],{},[164,2493,2494],{"class":166,"line":327},[164,2495,272],{},[164,2497,2498],{"class":166,"line":484},[164,2499,212],{},[164,2501,2502],{"class":166,"line":489},[164,2503,2504],{},"    conns.hclock.RUnlock()\n",[164,2506,2507],{"class":166,"line":495},[164,2508,330],{},[14,2510,1401],{},[137,2512,2514],{"id":2513},"_3节点剔除与闪断保护","3.节点剔除与闪断保护",[14,2516,2517],{},[20,2518],{"alt":2519,"src":2520},"image-20210112100126073","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112100126073.png",[14,2522,2523],{},"1.正常情况下，一个服务实例（instance）一分钟内会有两次renew操作",[14,2525,2526],{},"2.最大保护时间为1个小时",[14,2528,2529],{},"剔除无效节点代码：",[154,2531,2533],{"className":156,"code":2532,"language":158,"meta":159,"style":159},"func (r *Registry) evict() {\n    protect := r.gd.ok()\n    \u002F\u002F We collect first all expired items, to evict them in random order. For large eviction sets,\n    \u002F\u002F if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,\n    \u002F\u002F the impact should be evenly distributed across all applications.\n    var eis []*model.Instance\n    var registrySize int\n    \u002F\u002F all projects\n    ass := r.allapp()\n    for _, as := range ass {\n        for _, a := range as.App(\"\") {\n            registrySize += a.Len()\n            is := a.Instances()\n            for _, i := range is {\n                delta := time.Now().UnixNano() - i.RenewTimestamp\n                if (!protect && delta > _evictThreshold) || delta > _evictCeiling {\n                    eis = append(eis, i)\n                }\n            }\n        }\n    }\n    \u002F\u002F To compensate for GC pauses or drifting local time, we need to use current registry size as a base for\n    \u002F\u002F triggering self-preservation. Without that we would wipe out full registry.\n    eCnt := len(eis)\n    registrySizeThreshold := int(float64(registrySize) * _percentThreshold)\n    evictionLimit := registrySize - registrySizeThreshold\n    if eCnt > evictionLimit {\n        eCnt = evictionLimit\n    }\n    if eCnt == 0 {\n        return\n    }\n    \u002F\u002F小于0.15 * 总数全部剔除,如果大于则剔除0.15 * 总数个\n      for i := 0; i \u003C eCnt; i++ {\n        \u002F\u002F Pick a random item (Knuth shuffle algorithm)\n        next := i + rand.Intn(len(eis)-i)\n        eis[i], eis[next] = eis[next], eis[i]\n        ei := eis[i]\n        r.cancel(ei.Zone, ei.Env, ei.AppID, ei.Hostname, time.Now().UnixNano())\n    }\n}\n",[161,2534,2535,2540,2545,2550,2555,2560,2565,2570,2575,2580,2585,2590,2595,2600,2604,2609,2614,2619,2623,2627,2631,2635,2640,2645,2650,2655,2660,2665,2670,2674,2679,2683,2687,2692,2697,2702,2707,2712,2717,2722,2726],{"__ignoreMap":159},[164,2536,2537],{"class":166,"line":167},[164,2538,2539],{},"func (r *Registry) evict() {\n",[164,2541,2542],{"class":166,"line":173},[164,2543,2544],{},"    protect := r.gd.ok()\n",[164,2546,2547],{"class":166,"line":179},[164,2548,2549],{},"    \u002F\u002F We collect first all expired items, to evict them in random order. For large eviction sets,\n",[164,2551,2552],{"class":166,"line":185},[164,2553,2554],{},"    \u002F\u002F if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it,\n",[164,2556,2557],{"class":166,"line":191},[164,2558,2559],{},"    \u002F\u002F the impact should be evenly distributed across all applications.\n",[164,2561,2562],{"class":166,"line":197},[164,2563,2564],{},"    var eis []*model.Instance\n",[164,2566,2567],{"class":166,"line":203},[164,2568,2569],{},"    var registrySize int\n",[164,2571,2572],{"class":166,"line":209},[164,2573,2574],{},"    \u002F\u002F all projects\n",[164,2576,2577],{"class":166,"line":215},[164,2578,2579],{},"    ass := r.allapp()\n",[164,2581,2582],{"class":166,"line":221},[164,2583,2584],{},"    for _, as := range ass {\n",[164,2586,2587],{"class":166,"line":227},[164,2588,2589],{},"        for _, a := range as.App(\"\") {\n",[164,2591,2592],{"class":166,"line":233},[164,2593,2594],{},"            registrySize += a.Len()\n",[164,2596,2597],{"class":166,"line":239},[164,2598,2599],{},"            is := a.Instances()\n",[164,2601,2602],{"class":166,"line":245},[164,2603,462],{},[164,2605,2606],{"class":166,"line":251},[164,2607,2608],{},"                delta := time.Now().UnixNano() - i.RenewTimestamp\n",[164,2610,2611],{"class":166,"line":257},[164,2612,2613],{},"                if (!protect && delta > _evictThreshold) || delta > _evictCeiling {\n",[164,2615,2616],{"class":166,"line":263},[164,2617,2618],{},"                    eis = append(eis, i)\n",[164,2620,2621],{"class":166,"line":269},[164,2622,675],{},[164,2624,2625],{"class":166,"line":275},[164,2626,472],{},[164,2628,2629],{"class":166,"line":281},[164,2630,272],{},[164,2632,2633],{"class":166,"line":286},[164,2634,212],{},[164,2636,2637],{"class":166,"line":292},[164,2638,2639],{},"    \u002F\u002F To compensate for GC pauses or drifting local time, we need to use current registry size as a base for\n",[164,2641,2642],{"class":166,"line":298},[164,2643,2644],{},"    \u002F\u002F triggering self-preservation. Without that we would wipe out full registry.\n",[164,2646,2647],{"class":166,"line":304},[164,2648,2649],{},"    eCnt := len(eis)\n",[164,2651,2652],{"class":166,"line":310},[164,2653,2654],{},"    registrySizeThreshold := int(float64(registrySize) * _percentThreshold)\n",[164,2656,2657],{"class":166,"line":316},[164,2658,2659],{},"    evictionLimit := registrySize - registrySizeThreshold\n",[164,2661,2662],{"class":166,"line":322},[164,2663,2664],{},"    if eCnt > evictionLimit {\n",[164,2666,2667],{"class":166,"line":327},[164,2668,2669],{},"        eCnt = evictionLimit\n",[164,2671,2672],{"class":166,"line":484},[164,2673,212],{},[164,2675,2676],{"class":166,"line":489},[164,2677,2678],{},"    if eCnt == 0 {\n",[164,2680,2681],{"class":166,"line":495},[164,2682,1184],{},[164,2684,2685],{"class":166,"line":672},[164,2686,212],{},[164,2688,2689],{"class":166,"line":678},[164,2690,2691],{},"    \u002F\u002F小于0.15 * 总数全部剔除,如果大于则剔除0.15 * 总数个\n",[164,2693,2694],{"class":166,"line":684},[164,2695,2696],{},"      for i := 0; i \u003C eCnt; i++ {\n",[164,2698,2699],{"class":166,"line":690},[164,2700,2701],{},"        \u002F\u002F Pick a random item (Knuth shuffle algorithm)\n",[164,2703,2704],{"class":166,"line":695},[164,2705,2706],{},"        next := i + rand.Intn(len(eis)-i)\n",[164,2708,2709],{"class":166,"line":701},[164,2710,2711],{},"        eis[i], eis[next] = eis[next], eis[i]\n",[164,2713,2714],{"class":166,"line":707},[164,2715,2716],{},"        ei := eis[i]\n",[164,2718,2719],{"class":166,"line":713},[164,2720,2721],{},"        r.cancel(ei.Zone, ei.Env, ei.AppID, ei.Hostname, time.Now().UnixNano())\n",[164,2723,2724],{"class":166,"line":718},[164,2725,212],{},[164,2727,2728],{"class":166,"line":723},[164,2729,330],{},[14,2731,1401],{},[14,2733,2734],{},"每分钟renew操作累加、重置、闪断保护等功能：registry\u002Fguard.go",[137,2736,2738],{"id":2737},"_4流量调度","4.流量调度",[14,2740,2741],{},[20,2742],{"alt":2743,"src":2744},"image-20210112100329124","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112100329124.png",[14,2746,2747],{},"1.通过调度信息，重新计算对应zone中各实例权重值",[14,2749,2750],{},"2.新的权重 = 调度权重 * 原始zone权重之积 * 原实例权重 \u002F 原始zone权重之和",[14,2752,2753],{},"权重计算代码：",[154,2755,2757],{"className":156,"code":2756,"language":158,"meta":159,"style":159},"func (insInf *InstancesInfo) UseScheduler(zone string) (inss []*Instance) {\n    var scheduler struct {\n        zone    []string\n        weights []int64\n    }\n    var oriWeights []int64\n    for _, sch := range insInf.Scheduler {\n        if sch.Src == zone {\n            for zone, schWeight := range sch.Dst {\n                if zins, ok := insInf.Instances[zone]; ok {\n                    var totalWeight int64\n                    for _, ins := range zins {\n                        var weight int64\n                        if weight, _ = strconv.ParseInt(ins.Metadata[MetaWeight], 10, 64); weight \u003C= 0 {\n                            weight = 10\n                        }\n                        totalWeight += weight\n                    }\n                    oriWeights = append(oriWeights, totalWeight)\n                    inss = append(inss, zins...)\n                }\n                scheduler.weights = append(scheduler.weights, schWeight)\n                scheduler.zone = append(scheduler.zone, zone)\n            }\n        }\n    }\n    \u002F\u002F调度Dst为空\n     if len(inss) == 0 {\n        var ok bool\n        if inss, ok = insInf.Instances[zone]; ok {\n            return\n        }\n        for _, v := range insInf.Instances {\n            inss = append(inss, v...)\n        }\n        return\n    }\n    var comMulti int64 = 1\n    for _, weight := range oriWeights {\n        \u002F\u002F1. 原始权重之积\n        comMulti *= weight\n    }\n    var fixWeight = make(map[string]int64, len(scheduler.weights))\n    for i, zone := range scheduler.zone {\n        \u002F\u002F2. 调度权重 * 原始权重之积 \u002F 原始zone权重之和\n        fixWeight[zone] = scheduler.weights[i] * comMulti \u002F oriWeights[i]\n    }\n    for _, ins := range inss {\n        var weight int64\n        if weight, _ = strconv.ParseInt(ins.Metadata[MetaWeight], 10, 64); weight \u003C= 0 {\n            weight = 10\n        }\n        if fix, ok := fixWeight[ins.Zone]; ok {\n            \u002F\u002F3. 步骤二结果*原实例权重\n            weight = weight * fix\n        }\n        ins.Metadata[MetaWeight] = strconv.FormatInt(weight, 10)\n    }\n    return\n}\n",[161,2758,2759,2764,2769,2774,2779,2783,2788,2793,2798,2803,2808,2813,2818,2823,2828,2833,2837,2842,2846,2851,2856,2860,2865,2870,2874,2878,2882,2887,2892,2897,2902,2906,2910,2915,2920,2924,2928,2932,2937,2942,2947,2952,2956,2961,2966,2971,2976,2980,2985,2990,2995,3000,3004,3009,3014,3020,3025,3031,3036,3041],{"__ignoreMap":159},[164,2760,2761],{"class":166,"line":167},[164,2762,2763],{},"func (insInf *InstancesInfo) UseScheduler(zone string) (inss []*Instance) {\n",[164,2765,2766],{"class":166,"line":173},[164,2767,2768],{},"    var scheduler struct {\n",[164,2770,2771],{"class":166,"line":179},[164,2772,2773],{},"        zone    []string\n",[164,2775,2776],{"class":166,"line":185},[164,2777,2778],{},"        weights []int64\n",[164,2780,2781],{"class":166,"line":191},[164,2782,212],{},[164,2784,2785],{"class":166,"line":197},[164,2786,2787],{},"    var oriWeights []int64\n",[164,2789,2790],{"class":166,"line":203},[164,2791,2792],{},"    for _, sch := range insInf.Scheduler {\n",[164,2794,2795],{"class":166,"line":209},[164,2796,2797],{},"        if sch.Src == zone {\n",[164,2799,2800],{"class":166,"line":215},[164,2801,2802],{},"            for zone, schWeight := range sch.Dst {\n",[164,2804,2805],{"class":166,"line":221},[164,2806,2807],{},"                if zins, ok := insInf.Instances[zone]; ok {\n",[164,2809,2810],{"class":166,"line":227},[164,2811,2812],{},"                    var totalWeight int64\n",[164,2814,2815],{"class":166,"line":233},[164,2816,2817],{},"                    for _, ins := range zins {\n",[164,2819,2820],{"class":166,"line":239},[164,2821,2822],{},"                        var weight int64\n",[164,2824,2825],{"class":166,"line":245},[164,2826,2827],{},"                        if weight, _ = strconv.ParseInt(ins.Metadata[MetaWeight], 10, 64); weight \u003C= 0 {\n",[164,2829,2830],{"class":166,"line":251},[164,2831,2832],{},"                            weight = 10\n",[164,2834,2835],{"class":166,"line":257},[164,2836,975],{},[164,2838,2839],{"class":166,"line":263},[164,2840,2841],{},"                        totalWeight += weight\n",[164,2843,2844],{"class":166,"line":269},[164,2845,980],{},[164,2847,2848],{"class":166,"line":275},[164,2849,2850],{},"                    oriWeights = append(oriWeights, totalWeight)\n",[164,2852,2853],{"class":166,"line":281},[164,2854,2855],{},"                    inss = append(inss, zins...)\n",[164,2857,2858],{"class":166,"line":286},[164,2859,675],{},[164,2861,2862],{"class":166,"line":292},[164,2863,2864],{},"                scheduler.weights = append(scheduler.weights, schWeight)\n",[164,2866,2867],{"class":166,"line":298},[164,2868,2869],{},"                scheduler.zone = append(scheduler.zone, zone)\n",[164,2871,2872],{"class":166,"line":304},[164,2873,472],{},[164,2875,2876],{"class":166,"line":310},[164,2877,272],{},[164,2879,2880],{"class":166,"line":316},[164,2881,212],{},[164,2883,2884],{"class":166,"line":322},[164,2885,2886],{},"    \u002F\u002F调度Dst为空\n",[164,2888,2889],{"class":166,"line":327},[164,2890,2891],{},"     if len(inss) == 0 {\n",[164,2893,2894],{"class":166,"line":484},[164,2895,2896],{},"        var ok bool\n",[164,2898,2899],{"class":166,"line":489},[164,2900,2901],{},"        if inss, ok = insInf.Instances[zone]; ok {\n",[164,2903,2904],{"class":166,"line":495},[164,2905,901],{},[164,2907,2908],{"class":166,"line":672},[164,2909,272],{},[164,2911,2912],{"class":166,"line":678},[164,2913,2914],{},"        for _, v := range insInf.Instances {\n",[164,2916,2917],{"class":166,"line":684},[164,2918,2919],{},"            inss = append(inss, v...)\n",[164,2921,2922],{"class":166,"line":690},[164,2923,272],{},[164,2925,2926],{"class":166,"line":695},[164,2927,1184],{},[164,2929,2930],{"class":166,"line":701},[164,2931,212],{},[164,2933,2934],{"class":166,"line":707},[164,2935,2936],{},"    var comMulti int64 = 1\n",[164,2938,2939],{"class":166,"line":713},[164,2940,2941],{},"    for _, weight := range oriWeights {\n",[164,2943,2944],{"class":166,"line":718},[164,2945,2946],{},"        \u002F\u002F1. 原始权重之积\n",[164,2948,2949],{"class":166,"line":723},[164,2950,2951],{},"        comMulti *= weight\n",[164,2953,2954],{"class":166,"line":728},[164,2955,212],{},[164,2957,2958],{"class":166,"line":733},[164,2959,2960],{},"    var fixWeight = make(map[string]int64, len(scheduler.weights))\n",[164,2962,2963],{"class":166,"line":739},[164,2964,2965],{},"    for i, zone := range scheduler.zone {\n",[164,2967,2968],{"class":166,"line":745},[164,2969,2970],{},"        \u002F\u002F2. 调度权重 * 原始权重之积 \u002F 原始zone权重之和\n",[164,2972,2973],{"class":166,"line":750},[164,2974,2975],{},"        fixWeight[zone] = scheduler.weights[i] * comMulti \u002F oriWeights[i]\n",[164,2977,2978],{"class":166,"line":756},[164,2979,212],{},[164,2981,2982],{"class":166,"line":761},[164,2983,2984],{},"    for _, ins := range inss {\n",[164,2986,2987],{"class":166,"line":766},[164,2988,2989],{},"        var weight int64\n",[164,2991,2992],{"class":166,"line":772},[164,2993,2994],{},"        if weight, _ = strconv.ParseInt(ins.Metadata[MetaWeight], 10, 64); weight \u003C= 0 {\n",[164,2996,2997],{"class":166,"line":778},[164,2998,2999],{},"            weight = 10\n",[164,3001,3002],{"class":166,"line":1044},[164,3003,272],{},[164,3005,3006],{"class":166,"line":2348},[164,3007,3008],{},"        if fix, ok := fixWeight[ins.Zone]; ok {\n",[164,3010,3011],{"class":166,"line":2353},[164,3012,3013],{},"            \u002F\u002F3. 步骤二结果*原实例权重\n",[164,3015,3017],{"class":166,"line":3016},55,[164,3018,3019],{},"            weight = weight * fix\n",[164,3021,3023],{"class":166,"line":3022},56,[164,3024,272],{},[164,3026,3028],{"class":166,"line":3027},57,[164,3029,3030],{},"        ins.Metadata[MetaWeight] = strconv.FormatInt(weight, 10)\n",[164,3032,3034],{"class":166,"line":3033},58,[164,3035,212],{},[164,3037,3039],{"class":166,"line":3038},59,[164,3040,1394],{},[164,3042,3044],{"class":166,"line":3043},60,[164,3045,330],{},[14,3047,3048],{},"文件：naming\u002Fnaming.go",[137,3050,3052],{"id":3051},"_5client-sdk","5.Client SDK",[14,3054,3055],{},[20,3056],{"alt":3057,"src":3058},"image-20210112100515016","\u002Fblog-assets\u002FService-Registry-Discovery\u002Fimage-20210112100515016.png",[14,3060,3061],{},"目录naming中为Client SDK的代码，这里就不再一一列出。",[14,3063,3064],{},"有关grpc的负载均衡、名称解析，可以查看博文：",[14,3066,3067],{},[49,3068,3071],{"href":3069,"rel":3070},"https:\u002F\u002Fwww.zhizhi123.com\u002Fblog\u002F2020\u002F12\u002F10\u002Fgrpc-name-resolution\u002F",[53],"【译】gRPC名称解析",[14,3073,3074],{},[49,3075,3078],{"href":3076,"rel":3077},"https:\u002F\u002Fwww.zhizhi123.com\u002Fblog\u002F2020\u002F09\u002F15\u002Fgrpc-load-balancing\u002F",[53],"【译】gRPC负载均衡(gRPC博客)",[14,3080,3081],{},[49,3082,3085],{"href":3083,"rel":3084},"https:\u002F\u002Fwww.zhizhi123.com\u002Fblog\u002F2020\u002F09\u002F14\u002Fload-balancing-in-grpc\u002F",[53],"【译】gRPC负载均衡",[10,3087,3088],{"id":3088},"参考资料",[14,3090,3091,3092],{},"1.",[49,3093,3096],{"href":3094,"rel":3095},"https:\u002F\u002Fmedium.com\u002Fknerd\u002Feureka-why-you-shouldnt-use-zookeeper-for-service-discovery-4932c5c7e764",[53],"Eureka! Why You Shouldn’t Use ZooKeeper for Service Discovery",[14,3098,3099,3100,3105],{},"2.",[49,3101,3104],{"href":3102,"rel":3103},"https:\u002F\u002Fwww.shangmayuan.com\u002Fa\u002F6acf84bf44754ec782f4cb77.html",[53],"bilibili\u002Fdiscovery ",[49,3106,3108],{"href":3102,"rel":3107},[53],"介绍与源代码分析 （一）",[14,3110,3111,3112,3116],{},"3.",[49,3113,3104],{"href":3114,"rel":3115},"https:\u002F\u002Fwww.codetd.com\u002Farticle\u002F8631911",[53],[49,3117,3119],{"href":3114,"rel":3118},[53],"介绍与源代码分析 （二）",[3121,3122,3123],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":159,"searchDepth":173,"depth":173,"links":3125},[3126,3127,3128,3129],{"id":12,"depth":179,"text":12},{"id":26,"depth":179,"text":26},{"id":58,"depth":179,"text":59},{"id":3088,"depth":179,"text":3088},[3131],"源码分析","2021-01-11","什么是注册中心 注册中心可以说是 一个“通讯录”，它记录了服务和服务地址的映射关系。在服务启动时，服务会注册到这里，当服务需要调用其它服务时，就到这里找到服务的地址，进行调用。 常见的注册中心及对比 业界有许多成熟的注册中心实现，下图中对一些常见的注册中心做一对比： ZooKeeper是一款经典的服",false,"md","Service-Registry-Discovery",{"excerpt":3138},{"type":7,"value":3139},[3140,3142,3144,3148,3150,3152,3156,3158,3160,3162],[10,3141,12],{"id":12},[14,3143,16],{},[14,3145,3146],{},[20,3147],{"alt":22,"src":23},[10,3149,26],{"id":26},[14,3151,29],{},[14,3153,3154],{},[20,3155],{"alt":34,"src":35},[14,3157,38],{},[14,3159,41],{},[14,3161,44],{},[14,3163,47,3164,55],{},[49,3165,54],{"href":51,"rel":3166},[53],"\u002Fblog\u002F2021\u002F01\u002F11\u002Fservice-registry-discovery",{"title":5,"description":3133},"blog\u002F2021\u002F01\u002F11\u002FService-Registry-Discovery",[3171],"微服务",null,"18pd3MHDf5hfb2hllglqM5JW9qiXeCQQaFRxXfRyTxk",1783807996125]