资讯专栏INFORMATION COLUMN

运行在 Docker 上的微服务 - 服务发现与注册

陈伟 / 951人阅读

摘要:运行在上的微服务服务发现与注册在上一节中,我们学习了如何在上构建一个风格的微服务。接下来,我们将学习如何把运行在上的微服务暴露在服务中心上,以便客户端的调用。资源服务在关闭时需要将服务实例在服务中心进行注销操作。响应用户的终止。

运行在 Docker 上的微服务 - 服务发现与注册

tags: Docker Microservice RESTful etcd

Author: Andy Ai
Weibo: NinetyH
GitHub: https://github.com/aiyanbo/docker-restful-demo


在 上一节 中,我们学习了如何在 Docker 上构建一个 RESTful 风格的微服务。接下来,我们将学习如何把运行在 Docker 上的微服务暴露在服务中心上,以便客户端的调用。

etcd

etcd 是一个分布式,一致性的 k-v 存储系统,用于共享配置和服务发现。其特性是:

简单: 可使用 curl 访问的用户 API(HTTP+JSON)

安全: 可选的SSL客户端证书认证

快速: 单实例每秒 1000 次写操作

可靠: 使用Raft保证一致性

etcd 使用 go 语言实现,并通过 Raft 一致性算法处理日志复制以保证强一致性。

在这个章节中,我们使用 etcd 作为服务注册中心。

架构

  

注册 & 服务发现
1. etcd registry 做为服务中心,提供注册与服务发现。
2. 资源服务在准备完毕之后将服务实例注册到服务中心。
3. 客户端到服务注册中心根据服务名称获取资源服务的地址。
4. 客户端获取资源服务的地址后,调用资源服务。
5. 资源服务在关闭时需要将服务实例在服务中心进行注销操作。

服务注册 & 发现 Step0. 启动 etcd

etcd 提供了 Docker Image, 我们将使用 Docker 运行一个 etcd:

bashdocker run -d -p 4001:4001 coreos/etcd:v0.4.6 -name myetcd

测试 etcd 服务:

bash$ boot2docker ip
192.168.59.103

$ curl http://192.168.59.103:4001/v2/keys/
{"action":"get","node":{"key":"/","dir":true}}
Step1. 在 pom.xml 加入 etcd 客户端
xml
    2.7.0



    org.mousio
    etcd4j
    ${etcd4j.version}

Step2. 微服务启动完成时注册到服务中心

在这里,我们将使用到 etcd 的 put 接口:

bashcurl http://192.168.59.103:4001/v2/keys/registry/stacks/v1/$instance_id -XPUT -d value="$instance_address"

你可以在 etc api docs 里面查看更多的接口使用。

调用 mousio.etcd4j.EtcdClient 向 etcd 注册服务:

javafinal String instanceId = UUID.randomUUID().toString();
final String serviceInstanceKey = "registry/stacks/v1/" + instanceId;
final EtcdClient etcd = new EtcdClient(UriBuilder.fromUri("http://192.168.59.103/").port(4001).build());
etcd.put(serviceInstanceKey, "http://" + host + ":9998/").send();
  

Note:
注册到服务中的key有以下几个部分组成:
1. registry: 服务中心 Root
2. stacks: 服务名称
3. v1: 服务的版本
4. instanceId: 服务的实例 ID

Step3. 查看服务中心里的服务
bash$ curl http://192.168.59.103:4001/v2/keys/registry/stacks/v1

如果服务成功注册,你可以看到像下面这样的结果:

json{
  "action": "get",
  "node": {
    "key": "/registry/stacks/v1",
    "dir": true,
    "nodes": [
      {
        "key": "/registry/stacks/v1/ee07bf3e-e9d3-4d04-8bb9-48fc38f16384",
        "value": "http://172.17.0.5:9998/",
        "modifiedIndex": 26,
        "createdIndex": 26
      }
    ],
    "modifiedIndex": 26,
    "createdIndex": 26
  }
}
Step4. 微服务关闭时注销实例

资源服务实例在关闭时应该在服务中心进行注销操作,否则客户端就会拿到一个不可用的服务。

有一种简单的方式是在程序的 Runtime.shutdownHook 里面添加一个注销线程,像下面这样:

javaRuntime.getRuntime().addShutdownHook(new Thread() {
    @Override
    public void run() {
        try {
            etcd.delete(serviceInstanceKey).send();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});
暴露公共 IP

上面我们已经成功地使用 etcd 为我们的服务进行了注册跟暴露,但是我们实例在暴露自己的位置的时候却是 "value": "http://172.17.0.5:9998/"。它暴露的是 docker 内部的一个 ip,不是公共 ip 会还是会导致客户端不能访问服务。这时我们可以使用 docker 的环境变量来解决这个问题。

在 docker 中,我们需要使用 ENV 来定义一个环境变量。

# host 的默认值为 0.0.0.0
ENV host 0.0.0.0

在程序中,我们要使用 System.getenv("host") 来读取 docker 的环境变量。

在 docker 运行中,我们使用 -e 来覆盖 Dockerfilehost 的环境变量值。

shdocker run -d -p 9998:9998 -e "host=192.168.59.103" docker-restful-demo
高阶

在 JVM 中,调用 shutdown hooks 有以下几种情况:
1. 程序正常终止,最后的 non-daemon 线程退出。
2. 调用 System.exit 方法退出。
3. 响应用户的终止。例如,输入 Ctrl + C;使用 kill 命令杀死 JVM 进程(kill -9 不会);系统的全局事件:用户的注销,操作系统的 shutdown。

最底层的 java.lang.Shutdown 只运行 10 个 shutdown hook,但是使用 java.lang.Runtime.addShutdownHook 添加的的 shutdown hook 经过 java.lang.ApplicationShutdownHooks 包装后并没有限制。java.lang.ApplicationShutdownHooksjava.lang.Shutdown 的一个子 shutdown hook

在程序被上面的几种方法正常关闭下,JVM 会顺利的执行 shutdown hooks。但是在非正常情况下,例如:kill -9;系统突然断电并不会调用 shutdown hooks。那么这样就会导致资源服务已经不能提供服务了,但是由于这些原因没有在服务中心注销,同样地会让客户端拿到一个不可用的地址列表。资源服务器的网络中断也会有同样的问题。

我们可以使用一种很通用的方法解决这个问题:发送心跳包。具体的步骤为:

使用 etcd 的 TTL 接口设置key 的存活时间为 5s。

bashcurl http://192.168.59.103:4001/v2/keys/registry/stacks/v1/$instance_id -XPUT -d value="$instance_address" -d ttl=5

{
  "action": "get",
  "node": {
    "key": "/registry/stacks/v1/72f9a7ba-f100-4638-a502-1541fc7d08f1",
    "value": "http://192.168.113.86:9998/",
    "expiration": "2015-06-30T07:20:47.485980615Z",
    "ttl": 5,
    "modifiedIndex": 45,
    "createdIndex": 45
  }
}

每 5s 将服务实例的信息重新注册到 etcd。

javaetcd.put(serviceInstanceKey, "http://" + host + ":9998/").ttl(5).send();
  

Note
5s 视自己系统的情况而定。

参考资料

https://github.com/coreos/etcd
http://java.dzone.com/articles/know-jvm-series-2-shutdown
http://www.infoq.com/cn/news/2014/07/etcd-cluster-discovery
https://coreos.com/docs/cluster-management/setup/cluster-discovery/
http://blog.gopheracademy.com/advent-2013/day-06-service-discovery-wit...
http://stackoverflow.com/questions/2541597/how-to-gracefully-handle-th...

未经同意不可转载, 转载需保留原文链接与作者署名。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/26419.html

相关文章

  • 数人云|当K8S遇上微服务-京东金融PaaS平台思考实践

    摘要:平台上的微服务架构应用再来看一下我眼中的基于当前最流行的微服务架构的设计是什么样的,即我们平台上要运行的典型应用是什么样的。 showImg(https://segmentfault.com/img/remote/1460000010900878); 8月19日的数人云Container Meetup上,张龙老师做了《基于Kubernetes的PaaS平台的设计和思考》的精彩分享,分别...

    Imfan 评论0 收藏0
  • 个推基于Docker和Kubernetes的微服务实践

    摘要:个推针对服务场景,基于和搭建了微服务框架,提高了开发效率。三容器化在微服务落地实践时我们选择了,下面将详细介绍个推基于的实践。 2016年伊始Docker无比兴盛,如今Kubernetes万人瞩目。在这个无比需要创新与速度的时代,由容器、微服务、DevOps构成的云原生席卷整个IT界。个推针对Web服务场景,基于OpenResty和Node.js搭建了微服务框架,提高了开发效率。在微服...

    yibinnn 评论0 收藏0
  • 个推基于Docker和Kubernetes的微服务实践

    摘要:个推针对服务场景,基于和搭建了微服务框架,提高了开发效率。三容器化在微服务落地实践时我们选择了,下面将详细介绍个推基于的实践。 2016年伊始Docker无比兴盛,如今Kubernetes万人瞩目。在这个无比需要创新与速度的时代,由容器、微服务、DevOps构成的云原生席卷整个IT界。个推针对Web服务场景,基于OpenResty和Node.js搭建了微服务框架,提高了开发效率。在微服...

    genefy 评论0 收藏0
  • 2021 年最新基于 Spring Cloud 的微服务架构分析

    摘要:是一个相对比较新的微服务框架,年才推出的版本虽然时间最短但是相比等框架提供的全套的分布式系统解决方案。提供线程池不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务器雪崩的问题。通过互相注册的方式来进行消息同步和保证高可用。 Spring Cloud 是一个相对比较新的微服务框架,...

    cikenerd 评论0 收藏0
  • 网易容器云平台的微服务化实践(一)

    摘要:本文是网易容器云平台的微服务化实践系列文章的第一篇。网易容器云平台的前身是网易应用自动部署平台,它能够利用云提供的基础设施,实现包括构建和部署一体化在内的整个应用生命周期管理。目前网易云容器服务团队以的方式管理着微服务,每周构建部署次数。 此文已由作者冯常健授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 摘要:网易云容器平台期望能给实施了微服务架构的团队提供完...

    zhjx922 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<