内容简介:声明:本文对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相应的路径下就行了。
注册:对于同一组服务,配置一个统一的前缀(如图上的”/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, }) //... }
关于详细的代码,可以参见:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java Servlet&JSP经典实例
(美)佩里 / 朱涛江、邹红霞、林琪 / 中国电力出版社 / 2005-7 / 86.00元
本书将用于帮助指导Java web开发人员的日常任务,提供典型的web相关问题的快速解决方案。本书集中介绍了如何用Java初始化某些与web相关的任务,而不是教会读者如何使用Java语言,或者事无巨细地解释servlet和JSP API。书中包含了大量关于复杂的日常开发任务的技巧,这些技巧涵盖了许多与Servlet 2.4和JSP 2.0规范相关联的新特性,包括ServletRequestList......一起来看看 《Java Servlet&JSP经典实例》 这本书的介绍吧!