Eureka高可用注册中心
高可用注册中心
高可用的必要性
在微服务架构这样的分布式环境中,开发者需要充分考虑故障发生的情况,所以在生产环境中必须对各个组件进行高可用部署,对于微服务如此,对于服务注册中心也一样。在前面咋们使用的都是单节点的服务注册中心,这在生产环境中显然是不合适的,因此我们需要构建高可用的服务注册中心以增强系统的可用性。
Eureka Server从设计时就考虑了高可用的问题,在Eureka的服务治理设计中,所有的节点既是服务提供方,也是服务消费方,注册中心也不例外。在前面搭建单节点的配置中,我们设置过下面两个参数,用于禁止服务注册中心自己注册自己:
1 | eureka: |
Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,进而达到高可用的效果。
接下来尝试搭建高可用的服务注册中心集群,这里选择在前面实现的单节点服务注册中心的基础上进行扩展,使之变成一个双节点的服务注册中心集群。
双节点服务注册中心实现
第一步,修改eureka-server服务注册中心的配置文件。修改eureka-server服务注册中心的application.yml配置文件为如下所示:
1 | # 双节点的Eureka Server |
第二步,修改hosts文件。开发者需要修改windows系统下的hosts文件,在该文件中添加对peer1和peer2的转换,让上面配置的host形式的serviceUrl能在本地正确访问到。该文件所在路径为C:\Windows\System32\drivers\etc
,往里面新增如下配置:
1 | 127.0.0.1 peer1 peer2 |
第三步,将项目打包成jar包。打包命令为mvn clean package
,之后在target目录下生产一个jar包,然后使用如下命令来启动这两个节点:
1 | start /min java -jar target/eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 |
第四步,访问链接。之后访问peer1的注册中心http://peer1:1111/
如下图所示,可以看到DS Replicas中已经有peer2节点的信息,同时细心的你可能也注意到在registered-replicas中已经存在peer2节点的eureka-server了:
同理当你访问peer2的注册中心http://peer2:1112/
也可以看到DS Replicas中已经有peer1节点的信息。
这样一个简易的服务注册中心集群就搭建完成,但是里面还没有服务注册进去,将下来尝试将前面的eureka-client项目注册进去。
首先需要将eureka-client项目的application.yml配置文件中的服务注册地址修改为如下所示:
1 | spring: |
可以看到这里主要是对eureka.client.service-url.defaultZone
的属性值做了修改,将注册中心指向了之前搭建的peer1和peer2,这一点需要注意。
接着启动eureka-client项目,通过访问http://peer1:1111/
和http://peer2:1112/
链接,可以观察到hello-service服务同时被注册到peer1和peer2上,如下所示:
若此时断开peer1,由于hello-service服务同时也向peer2注册,因此在peer2上的其他服务依旧能访问到hello-service,进而实现了服务注册中心的高可用。
当然了,如果开发者不想使用主机名称来定义注册中心的地址,而是想使用IP地址的方式,这也是允许的,只不过需要在application.yml配置文件中增加如下配置参数:
1 | eureka: |
请注意该值默认为false,需要开发者自己打开。
服务发现与消费
服务发现与消费介绍
通过上面的介绍和动手实践,我们已经搭建起微服务架构中的核心组件—服务注册中心(包含单节点和高可用模式)。同时还将之前的eureka-client项目进行了改造,通过简单的配置,使得该程序注册到Eureka注册中心上,成为该服务治理体系下的一个服务,且名称为hello-service。也就是说到现在为止,我们已经有了服务注册中心和服务提供者,就差一个服务消费者就形成了一个完整的提供-消费模式。这个服务消费者主要完成两个目标:发现服务和消费服务。也就是说服务的发现和消费实际上是两个行为,这两个行为需要由不同的对象来完成,其中服务的发现由Eureka客户端来完成,而服务的消费由Ribbon来完成。
Ribbon是一个基于HTTP和TCP的客户端负载均衡器,它可以在通过客户端中配置的ribbonServerList服务端列表去轮询访问,以达到负载均衡的作用。当我们将Ribbon和Eureka一起使用时,Ribbon的服务实例清单RibbonServerList会被DiscoveryEnableNIWSServerList重写,扩展成从Eureka注册中心获取服务端列表。同时它也会使用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。在本篇笔者并不对Ribbon做详细介绍,开发者只需要理解它在Eureka服务发现的基础上实现了一套对服务实例的选择策略,进而实现了对服务的消费。关于Ribbon的学习,笔者会在后续文章中进行介绍。
接下来将通过一个简单的例子,来看看如何在Eureka的服务治理体系下如何实现服务的发现与消费。
服务发现与消费实现
第一步,启动服务注册中心。在进行服务发现与消费之前,需要启动之前的服务注册中心eureka-server,为了测试方便,我这里就直接启动一个单节点项目,启动结果如下所示:
第二步,启动多个hello-service服务。为了验证Ribbon的客户端负载均衡功能,这里通过java -jar
命令来启动两个不同端口的hello-service服务,当然了前提是先将hello-service服务打包为jar包(注意此处的hello-service就是eureka-client项目):
1 | start /min java -jar target/eureka-client-0.0.1-SNAPSHOT.jar --server.port=8081 |
第三步,确认服务已经注册。在启动两个hello-service服务之后,访问服务注册中心,可以从Eureka的信息面板中看到名为HELLO-SERVICE的服务中出现了两个实例单元,这两个实例就是前面使用命令行启动的8081和8082端口的服务:
第四步,创建消费者。服务注册中心和服务注册都有了,接下来开始创建消费者。按照图示操作,新建一个SpringBoot工程,名称为ribbon-consumer,并在pom.xml文件中添加如下依赖:
1 | <?xml version="1.0" encoding="UTF-8"?> |
其实这个依赖和之前的hello-service服务没有任何区别,因为spring-cloud-starter-netflix-eureka-client
中已经包含了spring-cloud-starter-netflix-ribbon
,开发者可以在pom.xml同级目录下使用mvn dependency:tree
命令来分析项目jar包的引用关系:
第五步,配置启动类。在这一步中,开发者需要做两件事情:标识ribbon-consumer为客户端应用和提供RestTemplate的Bean。首先完成第一小步,在RibbonConsumerApplication这个项目入口类上添加@EnableDiscoveryClient
注解让该应用注册为Eureka客户端应用,以获得服务发现的能力。接下来完成第二小步,提供RestTemplate的Bean。RestTemplate可以帮助我们发起一个GET或者POST请求,这个笔者会在后续进行介绍,这里只需要提供一个RestTemplate的Spring Bean实例即可,同时在上面添加@LoadBalanced
注解来开启客户端负载均衡,相应的代码如下:
1 | @SpringBootApplication |
第六步,创建controller。在项目目录中新建一个controller包,并在里面新建一个ConsumerController类,注意该类需要提供一个/ribbon-consumer
接口,用于获取服务信息。其实这里所谓的/ribbon-consumer
接口就是一个方法的访问链接是/ribbon-consumer
罢了,而这个方法名称随意,此处命名为helloConsumer,然后在该方法中可以导入之前创建的RestTemplate,通过它来实现对HELLO-SERVICE服务提供的/hello
接口进行调用。可以看到这里访问的地址是服务名HELLO-SERVICE,而不是一个具体的地址,在服务治理框架中,这是一个非常重要的特性,也符合本文开头对于服务治理的解释:
1 | @RestController |
第七步,配置服务注册中心位置。接下来就是在ribbon-consumer项目中配置Eureka服务注册中心的地址,主要这个地址需要和前面的HELLO-SERVICE保持一致,否则是无法发现服务的,同时设置该消费者的端口为9000或者其他,但是不能与之前使用的端口产生冲突即可:
1 | spring: |
第八步,启动ribbon-consumer服务。在完成了上述配置后,接下来启动ribbon-consumer服务,然后查看Eureka注册中心的信息面板,可以看到除了之前的HELLO-SERVICE之外,刚才实现的RIBBON-COMSUMER服务也出现了:
第九步,发起请求。接下来开发者可以在浏览器地址栏中输入http://peer1:9000/ribbon-consumer/
,可以看到页面返回hello,world!:
同时开发者也可以在ribbon-consumer应用的控制台中看到如下信息:
可以看到Ribbon输出了当前客户端维护的HELLO-SERVICE的服务列表情况,其中包含了各个实例的位置,Ribbon就是按照此信息进行轮询访问,以实现基于客户端的负载均衡。此外还输出了一些其他有用的信息,如对各个实例的请求总数量、第一次连接信息、上一次连接信息、总的请求失败数量等。
那么现在有人可能要问题,这个hello,world!究竟是哪个HELLO-SERVICE提供的呢?前面提供了8081和8082这两个HELLO-SERVICE服务,其实开发者可以多次发起/ribbon-consumer
请求并通过观察启动的两个HELLO-SERVICE服务的控制台,可以看到两个控制台会交替输出下面的日志:
1 | /hello,host: 192.168.73.1,service_id: HELLO-SERVICE |
这个是之前我们在HelloController中对服务信息的输出配置,开发者可以通过判断当前ribbon-consumer对HELLO-SERVICE的调用来判断Ribbon是否进行了负载均衡。