etcd与service-registration-discovery

栏目: 后端 · 发布时间: 5年前

内容简介:声明:本文对etcd的原理,实现细节,性能等均不考虑,仅将etcd作为一个分布式的K-V存储组件。本文提价代码均在:etcd, 分布式Key-Value存储工具。详细资料就具体场景而言:我们的生产环境中使用了一个代理网关服务器,用于转发移动端和PC端的API请求,并完成其他功能。所有的服务实例配置都是硬编码在网关程序中,顶多就是抽离出来成了一个配置文件。这样做的缺点很明显:“非动态”。也就意味着,一旦有服务Down掉,那么用户访问则可能异常,甚至导致整个服务的崩溃;其次,需要对服务进行扩容的情况下,则需要

声明:本文对etcd的原理,实现细节,性能等均不考虑,仅将etcd作为一个分布式的K-V存储组件。本文提价代码均在: github.com/yeqown/server-common/tree/master/framework/etcd

一个核心

etcd, 分布式Key-Value存储工具。详细资料 由此去

两个对象

  • 服务提供者(在测试环境中,我定义为单独的服务实例),也就是服务的提供者,需要向其他服务暴露自己的ip和端口,方便调用。
  • 服务调用者(同样地,在测试环境中我定义为反向代理网关程序),也就是服务的调用者,需要获取到 可使用 地服务地址并调用。

关于服务注册与发现

就具体场景而言:我们的生产环境中使用了一个代理网关服务器,用于转发移动端和PC端的API请求,并完成其他功能。所有的服务实例配置都是硬编码在网关程序中,顶多就是抽离出来成了一个配置文件。这样做的缺点很明显:“非动态”。也就意味着,一旦有服务Down掉,那么用户访问则可能异常,甚至导致整个服务的崩溃;其次,需要对服务进行扩容的情况下,则需要先进行服务部署再更新网关程序,步骤繁琐且容易出错。

那么如果我们设计成为如下图的样子:

etcd与service-registration-discovery

对于新添加的服务实例,只需要启动新的服务,并注册到etcd相应的路径下就行了。

注册:对于同一组服务,配置一个统一的前缀(如图上的”/specServer”),不同实例使用ID加以区分。

将现行服务改造成为上述模式需要解决的问题:

  • etcd 配置安装
  • 网关程序改造(监听etcd的节点夹子/prefix;适配动态的服务实例调用)
  • 服务实例改造(注册服务实例到etcd;心跳更新;其他配套设施,异常退出删除注册信息)

etcd安装配置在github.com已经非常详细了。在这里贴一下我在本地测试时候启动的脚本(这部分是从etcd-demo获取到的,做了针对端口的改动):

#!/bin/bash

# For each machine
TOKEN=token-01
CLUSTER_STATE=new
NAME_1=machine1
NAME_2=machine2
NAME_3=machine3
HOST_1=127.0.0.1
HOST_2=127.0.0.1
HOST_3=127.0.0.1
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2381,${NAME_3}=http://${HOST_3}:2382

# For machine 1
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
etcd --data-dir=machine1.etcd --name ${THIS_NAME} \
	--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
	--advertise-client-urls http://${THIS_IP}:2377 --listen-client-urls http://${THIS_IP}:2377 \
	--initial-cluster ${CLUSTER} \
	--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} &

# For machine 2
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
etcd --data-dir=machine2.etcd --name ${THIS_NAME} \
	--initial-advertise-peer-urls http://${THIS_IP}:2381 --listen-peer-urls http://${THIS_IP}:2381 \
	--advertise-client-urls http://${THIS_IP}:2378 --listen-client-urls http://${THIS_IP}:2378 \
	--initial-cluster ${CLUSTER} \
	--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} & 

# For machine 3
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
etcd --data-dir=machine3.etcd --name ${THIS_NAME} \
	--initial-advertise-peer-urls http://${THIS_IP}:2382 --listen-peer-urls http://${THIS_IP}:2382 \
	--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
	--initial-cluster ${CLUSTER} \
	--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN} &

对于程序的改造,鉴于服务较多且etcd操作流程大体一致,便简单包装了一下,项目地址见文首位置。

1.对于调用方使用示例如下:

// etcdtest/gw.go

func main() {
    // ...
    endpoints := []string{
        "http://127.0.0.1:2377",
        "http://127.0.0.1:2379",
        "http://127.0.0.1:2378",
    }
    // 连接etcd获取KeysAPI
    kapi, err := etcd.Connect(endpoints...)
    if err != nil {
        fmt.Println(err)
        os.Exit(2)
    }

    // debug more, more log ~
    etcd.OpenDebug(true)

    // etcd watch, 监听/prefix目录下的改动(“expire;set;update;delete”)
    // 如:set {Key: /prefix/srv_3457, CreatedIndex: 1155, ModifiedIndex: 1155, TTL: 12}
    // 并更新watcher.members, 维持最新的节点状态和数量
    watcher = etcd.NewWatcher(kapi, "prefix")
    go watcher.Watch()
    // ...
}

func ServeHTTP() {
    // ...
    srvs := watcher.RangeMember() // 获取所有可用的服务节点
    // ...
}

2.对于请求提供方,使用示例如下:

// etcdtest/server.go

func main() {
    // ...
    endpoints := []string{
        "http://127.0.0.1:2377",
        "http://127.0.0.1:2379",
        "http://127.0.0.1:2378",
    }
    etcd.OpenDebug(true)
    kapi, err := etcd.Connect(endpoints...)
    if err != nil {
        fmt.Errorf(err.Error())
        os.Exit(2)
    }

    // 根据服务生成一个provider, 用于生成K:V
    provider := etcd.NewProvider(
        fmt.Sprintf("srv_%d", *port),              // name
        fmt.Sprintf("http://127.0.0.1:%d", *port), // addr
    )

    ctx, cancel := context.WithCancel(context.Background())
    // 每10s设置一个TTL=12s的 “/prefix/id”:“http://host:port” 的的键值对
    // 10s和12s是写死的,没有考虑动态~~,后续考虑升级,目前仅仅是测试。
    go provider.Heartbeat(ctx, kapi, &etcd.ProvideOptions{
        NamePrefix: "prefix",
        SetOpts:    nil,
    })
    //...
}

关于详细的代码,可以参见:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Art of Computer Programming, Volume 2

The Art of Computer Programming, Volume 2

Knuth, Donald E. / Addison-Wesley Professional / 1997-11-04 / USD 79.99

Finally, after a wait of more than thirty-five years, the first part of Volume 4 is at last ready for publication. Check out the boxed set that brings together Volumes 1 - 4A in one elegant case, and ......一起来看看 《The Art of Computer Programming, Volume 2》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器