用例spring_cloud_eureka
项目地址:传送门
注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
注册中心的主要作用
服务注册中心(下称注册中心)是微服务架构非常重要的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
-
服务发现:
-
服务注册/反注册:保存服务提供者和服务调用者的信息
-
服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功能
-
服务路由(可选):具有筛选整合服务提供者的能力。
-
服务配置(部门注册中心支持,不是重点):
-
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
-
配置下发:主动将配置推送给服务提供者和服务调用者
-
服务健康检测
-
检测服务提供者的健康情况
常见的注册中心
Zookeeper
zookeeper它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。简单来说zookeeper=文件系统+监听通知机制。
Eureka
Eureka是在Java语言上,基于Restful Api开发的服务注册与发现组件,Springcloud Netflix中的重要组件
Consul
Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服务软件,采用Raft算法保证服务的一致性,且支持健康检查。
Nacos Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是注册中心 + 配置中心的组合,提供简单易用的特性集,帮助我们解决微服务开发必会涉及到的服务注册与发现,服务配置,服务管理等问题。Nacos 还是 Spring Cloud Alibaba 组件之一,负责服务注册与发现
最后我们通过一张表格大致了解Eureka、Consul、Zookeeper的异同点。选择什么类型的服务注册与发现组件可以根据自身项目要求决定。
组件名 | 语言 | CAP | 一致性算法 | 服务健康检查 | 对外暴露接口 |
---|---|---|---|---|---|
Eureka | Java | AP | 无 | 可配支持 | HTTP |
Consul | Go | CP | Raft | 支持 | HTTP/DNS |
Zookeeper | Java | CP | Paxos | 支持 | 客户端 |
Nacos | Java | AP | Raft | 支持 | HTTP |
二、Eureka的概述
Eureka的基础知识
Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。
上图简要描述了Eureka的基本架构,由3个角色组成:
1、Eureka Server
-
提供服务注册和发现
2、Service Provider
-
服务提供方
-
将自身服务注册到Eureka,从而使服务消费方能够找到
3、Service Consumer
-
服务消费方
-
从Eureka获取注册服务列表,从而能够消费服务
Eureka的交互流程与原理
由图可知,Eureka包含两个组件:Eureka Server 和 Eureka Client,它们的作用如下:
-
Eureka Client是一个Java客户端,用于简化与Eureka Server的交互;
-
Eureka Server提供服务发现的能力,各个微服务启动时,会通过Eureka Client向Eureka Server进行注册自己的信息(例如网络信息),Eureka Server会存储该服务的信息;
-
微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。如果Eureka Server在一定时间内没有接收到某个微服务节点的心跳,Eureka Server将会注销该微服务节点(默认90秒);
-
每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的方式完成服务注册表的同步;
-
Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。
综上,Eureka通过心跳检测、健康检查和客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。
三、服务注册到Eureka注册中心
使用eureka的步骤
注意:在之前的订单-商品的应用项目为基础进行搭建;
1、搭建eureka server
1.1 创建工程
1.2 导入坐标
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies>
1.3 配置application.yml
#端口9000 spring: application: name: eureka-server server: port: 9000 #端口 #配置eureka server eureka: instance: hostname: localhost client: register-with-eureka: false #是否将自己注册到注册中心 fetch-registry: false #是否从eureka中获取注册信息 service-url: #配置暴露给Eureka Client的请求地址 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.4 配置启动类
package cn.hzp.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication //激活eureakaserver @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); } }
搭建好后,启动后效果:
2、将服务提供者注册到eurekaServer上
2.1 引入EurekaClient的坐标
<!--引入eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2.2 修改application.yml,添加EurekaServer的信息
配置Eureka eureka: client: service-url: defaultZone: http://localhost:9000/eureka/ #多个eurekaserver之间用,隔开 instance: prefer-ip-address: true #使用ip地址注册到Eureka Server上,而如果不配置就是机器的主机名
2.3 修改启动类,添加服务发现的支持(可选)
@SpringBootApplication @EntityScan("cn.hzp.product.domain") //激活eurekaClient--高版本不写可以,会自动根据yml配置添加此注解 //@EnableEurekaClient //@EnableDiscoveryClient public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class,args); } }
3、服务消费者通过注册中心获取服务列表,并调用
Eureka元数据:包含服务的主机名、ip、等信息,可以通过eurekaServer进行获取。用于服务间调用。
因此根据元数据订单服务可以动态的调用商品服务代码如下:
package cn.hzp.order.controller; import cn.hzp.order.domain.Product; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @RestController @RequestMapping("/order") public class OrderController {//注入restTemplate对象 @Autowired private RestTemplate restTemplate; /** * 注入DiscoveryClient : * springcloud提供的获取元数据的工具类 * 调用方法获取服务的元数据信息 * */ @Autowired private DiscoveryClient discoveryClient; @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { //已调用服务名称获取所有的元数据 List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); //获取唯一的一个元数据 ServiceInstance instance = instances.get(0); //根据元数据中的主机地址和端口号拼接请求微服务的URL //如何调用商品服务? Product product = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class); //Product product = restTemplate.getForObject("http://localhost:9001/product/"+id,Product.class); return product; } }
元数据对象:
四、Eureka中的元数据
Eureka的元数据有两种:标准元数据和自定义元数据。
-
标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。
-
自定义元数据:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这些元数据可以在远程客户端中访问。
在程序中可以使用DiscoveryClient 获取指定微服务的所有元数据信息
/** * 注入DiscoveryClient : * springcloud提供的获取元数据的工具类 * 调用方法获取服务的元数据信息 * */ @Autowired private DiscoveryClient discoveryClient; @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { //已调用服务名称获取所有的元数据 List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); //获取唯一的一个元数据 ServiceInstance instance = instances.get(0); //根据元数据中的主机地址和端口号拼接请求微服务的URL //如何调用商品服务? Product product = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class); //Product product = restTemplate.getForObject("http://localhost:9001/product/"+id,Product.class); return product; }