天天看点

在阿里云容器服务上开发基于Docker的Spring Cloud微服务应用(三)

本文为阿里云容器服务spring cloud应用开发系列文章的第三篇。

三、服务发现(本文)

传统应用由于服务数目相对固定,服务所在的节点也相对固定,动态发现的需求不强烈。服务发现的通常做法是通过dns域名解析,或ip地址找到相关服务。云环境中,服务的数目可能随时变化,服务所在的节点和侦听的端口也无法预先知道,所以在云应用的微服务架构中,服务发现是必须具备的一项能力。

通常我们可以采用zookeeper、etcd或consul等开源产品来实现服务的注册与发现。spring cloud中提供了两种方式:一是consul,另外一个是netflix的eureka。本文讨论了如何采用eureka进行服务发现,以及如何将eureka及相关服务容器化。

eureka server可以通过在程序中annotation声明的方式生成,详细步骤见下文。如果想直接用构建好的镜像,可以使用本文提供的示例:

如果使用已有的镜像可以忽略本步。

首先,我们需要在在build.gradle中引入java项目对eureka的依赖。

在application.yml配置eureka的参数。内容如下:

在测试环境中(profile没有指定),eureka侦听8761端口,<code>serviceurl.defaultzone</code>中只有一个eureka服务实例。

在随后的cloud profile中,<code>serviceurl.defaultzone</code>除了有本机的eureka server,还通过<code>additional_eureka_server_list</code>环境变量指明其他eureka server节点信息。eureka server之间的内容互相同步,eureka client只需注册到其中任意一个节点即可。

运行eureka服务有两种方式,一种是通过docker命令行执行,另外一种是在docker-compose文件中声明,并执行docker-compose启动。

命令行方式可以是从gradle中执行

通过java命令启动单独一个eureka server,效果和上面的gradle命令一样

通过java命令启动,profile为cloud,并通过环境变量指定了eureka集群中其他的节点地址。

由于在开发实际的应用过程中往往需要启动多个容器,所以通过脚本的方式启动服务非常不方便,所以建议通过在docker-compose文件中声明启动容器时的环境变量配置eureka启动时的行为:

如果有多个eureka server需要在<code>additional_eureka_server_list</code>环境变量中指明,每个eureka 的url之间用逗号分隔即可。

springboot应用编译生成的jar包包含了所有依赖包以及app server,这种包也叫“超级jar包“,英文 uber jar。编译好的jar包在dockercompose文件中指明,即可由docker引擎生成容器镜像。

如何编译uber jar就不详细介绍了,我们只要记住执行如下命令即可:

编译生成的jar包在build/libs目录:

最后,写一个dockerfile,定义如何生成一个eureka server镜像:

其中<code>add build/libs/*.jar app.jar</code>表示把构建好的jar包复制到镜像中。由于<code>build/libs</code>目录下只有一个jar包,所以这行命令实际上是将jar包复制到镜像的根目录下,并命名为<code>app.jar</code>。这个dockerfile可以用户所有springboot编译出来的应用。

<code>run echo $(date) &gt; /image_built_at</code>表示把构建镜像的日期打入镜像以利于未来的运维,读者可以根据自己的需求修改或删除这一行。

运行如下命令生成镜像,并上传到你自己的镜像仓库账号下:

运行上述命令生成的eureka server镜像有两个tag:<code>latest</code>和创建时间,例如<code>20160627-092112</code>。你可以根据自己的实际需求定义tag。

登录到镜像仓库并上传镜像的命令如下:

进入创建容器仓库界面,输入仓库名,并选择<code>本地仓库</code>

在阿里云容器服务上开发基于Docker的Spring Cloud微服务应用(三)

创建成功后就可以随时上传镜像了。

除eureka server之外所有需要注册到eureka的应用都要通过eureka client注册到eureka server和获取其他应用的访问信息。

引入eureka client也是通过annotation的机制完成。

其中<code>@enablediscoveryclient</code>和前面eureka server的注解功能一样,注意,client不能声明enableeurekaserver。

在bootstrap.yml中为应用命名,该名字用于向eureka注册,所有用同样名字注册的应用都视为同一个应用(或服务)的不同实例,所以一定要注意应用名在一个eureka集群的上下文中是唯一的。

eureka client所属应用也是通过<code>build.gradle</code>文件引入eureka依赖,<code>application.yml</code>配置eureka,程序的编译、运行和构建容器镜像和eureka server类似,这里就不再重复了。

通过在<code>bootstrap.yml</code>中声明<code>spring.application.name</code>,应用启动时会自动完成向eureka server的注册。那么,如何进行服务发现呢,我们在<code>foobar</code>的代码中看一下。

resttemplate的声明需要加上<code>@autowired</code>和<code>@loadbalanced</code>两个注解。在随后对<code>resttemplate.getforobject</code>的调用传入服务的url。

其中<code>bar</code>服务名,<code>/message</code>为希望调用的url path。 <code>resttemplate.getforobject</code>从eureka中获得<code>bar</code>服务的所有实例,并根据内置的负载均衡算法选择一个实例调用并返回结果。

在<code>aliyuncs-springcloud-demo</code>目录下可以看到如下内容:

<code>common</code>目录下是公用服务,包括服务发现的discovery server(eureka),和智能路由gateway(zuul)。

<code>services</code>目录下是所有应用的业务逻辑所在的服务,它们都注册到discovery server,服务的发现也通过discovery server。

注册和发现的机制在前面已经描述过了,所有应用(包括discovery server和gatewa也)的docker镜像构建都非常类似,在各自的目录下都可以找到相应的文件。

手工进入每个目录并执行编译和上传的命令非常繁琐,也容易出错,所以在示例中提供了脚本来构建和上传所有服务的镜像。

编译、打包所有服务,并创建容器镜像(不上传镜像)的命令:

上传所有镜像,注意,如果还没创建镜像仓库,首先去创建,步骤见eureka server一节。

本文介绍了如何通过eureka进行服务注册和发现,如何镜像化等内容。最后简单介绍了如何构建示例代码中的所有服务镜像。

后续的文章我们会一起来看spring cloud和阿里云容器服务的其它特性。