在上一章节完成了一个简单的微服务案例,下面就经过在这个案例的基础上集成 Eureka 来学习 Eureka。html
Eureka 是 Netflix 的一个子模块,也是核心模块之一。Eureka 是一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来讲是很是重要的,有了服务发现与注册,只须要使用服务的标识符,就能够访问到服务。功能相似于 dubbo的注册中心,好比 Zookeeper。java
SpringCloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册与发现,Eureka 采用了 C/S 的设计架构。web
Eureka Server 做为服务注册功能的服务器,它是服务注册中心。而系统中的其它微服务,使用 Eureka 的客户端链接到 Eureka Server 并维持心跳链接。这样系统的维护人员就能够经过 Eureka Server 来监控系统中的各个微服务是否正常运行。SpringCloud 的一些其它模块(好比 Zuul)就能够经过 Eureka Server 来发现系统中的其它微服务,并执行相关逻辑。算法
Eureka Server 包含两个组件:spring
图 1:Eureka 架构图apache
图 2:Dubbo 架构图浏览器
一、新建名为 "microservicecloud-eureka-7001" 的子工程做为 Eureka 服务端,依赖以下:安全
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>microservicecloud</artifactId> <groupId>zze.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>microservicecloud-eureka-7001</artifactId> <dependencies> <!--eureka-server服务端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!-- 修改后当即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> </project>
二、配置 Eureka :服务器
server: port: 7001 eureka: instance: hostname: localhost # eureka 服务端实例名称 client: register-with-eureka: false # 表示不向注册中心注册本身 fetch-registry: false # false 表示本身就是注册中心,不须要检索服务 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 设置与 Eureka Server 交互的地址,可用来查询注册的服务
三、编写主启动类,并使用注解开启 Eureka Server 功能:网络
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer // 开启 Eureka Server 功能,标识当前程序就是一个 Eureka Server public class Application_7001 { public static void main(String[] args) { SpringApplication.run(Application_7001.class, args); } }
四、测试:
运行主启动类,浏览器访问 http://localhost:7001/ 进入 Eureka Server 图形化页面:
一、修更名为 "microservicecloud-provider-dept-8001" 的工程的 pom 文件,添加以下 Eureka 客户端依赖:
<!--Eureka 客户端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
二、将当前工程做为 Eureka 客户端注册到 Eureka 服务,在配置文件添加以下配置:
eureka: client: # 将当前工程做为 Eureka 客户端 service-url: defaultZone: http://localhost:7001/eureka # Eureka 服务端地址
三、在主启动类添加注解标识当前工程为 Eureka 客户端:
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient // 标注当前工程为 Eureka 客户端 public class Application_8001 { public static void main(String[] args) { SpringApplication.run(Application_8001.class, args); } }
四、测试:
先启动 7001 Eureka 服务端工程,再启动 8001 Eureka 客户端工程,浏览器访问 http://localhost:7001/,能够看到 8001 客户端实例已经被注册到 Eureka 服务端:
服务发现实际上就是让 EurekaServer 端可以扫描到咱们注册的服务,默认咱们能够经过 Web UI 的方式查看哪些服务注册到了 EurekaServer,还能够经过 Eureka 客户端依赖提供的服务发现客户端获取注册到 EurekaServer 的服务信息。
一、修更名为 "microservicecloud-consumer-dept-80" 的服务工程添加以下 Eureka 客户端依赖:
<!--Eureka 客户端依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
二、在其主启动类上添加注解启用 Eureka 客户端功能:
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class Application_80 { public static void main(String[] args) { SpringApplication.run(Application_80.class, args); } }
三、修改 Controller:
package zze.springcloud.controller; 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.*; import org.springframework.web.client.RestTemplate; import zze.springcloud.entities.Dept; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/consumer/dept") public class DeptController { @Autowired private RestTemplate restTemplate; @PostMapping("/add") public boolean add(@RequestBody Dept dept) { return restTemplate.postForObject(getRestUrlPrefix("MICROSERVICECLOUD-PROVIDER-DEPT") + "/dept/add", dept,Boolean.class); } @GetMapping("/get/{id}") public Dept get(@PathVariable Long id){ return restTemplate.getForObject(getRestUrlPrefix("MICROSERVICECLOUD-PROVIDER-DEPT") + "/dept/get/" + id, Dept.class); } @GetMapping("/list") public List<Dept> list(){ return restTemplate.getForObject(getRestUrlPrefix("MICROSERVICECLOUD-PROVIDER-DEPT") + "/dept/list", List.class); } // 服务发现客户端 @Autowired private DiscoveryClient discoveryClient; /** * 经过服务名称获取到服务实例对应的 url */ private String getRestUrlPrefix(String serviceName){ List<String> services = discoveryClient.getServices(); System.out.println("---------------------------"+services); List<ServiceInstance> instances = discoveryClient.getInstances(serviceName); System.out.println(instances); // 好比只注册了一个 MICROSERVICECLOUD-PROVIDER-DEPT 微服务实例 ServiceInstance serviceInstance = instances.get(0); return String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort()); } /** * 获取全部注册到 EurekaServer 的服务信息 * @return */ @GetMapping("/discovery") public Object discovery(){ Map<String, Object> map = new HashMap<>(); // 获取全部注册到 EurekaServer 的微服务名称,对应 spring.application.name List<String> services = discoveryClient.getServices(); for (String service : services) { // 获取对应服务全部实例 List<ServiceInstance> instances = discoveryClient.getInstances(service); map.put(service, instances); } return map; } }
四、测试:
正常访问 http://localhost/consumer/dept/list:
访问 http://localhost/consumer/dept/discovery 查看全部服务信息:
即经过服务发现咱们只须要使用约定的服务名称就能够经过注册中心访问到具体服务信息。
一、新建两个子工程做为两个 EurekaServer 端,分别名为 "microservicecloud-eureka-7002"、"microservicecloud-eureka-7003",主启动类分别名为 "Application_7002"、"Application_7003",依赖同 "microservicecloud-eureka-7001"。
二、因为是单机测试,需修改本机 host 映射:
127.0.0.1 www.eurekaserver1.com
127.0.0.1 www.eurekaserver2.com
127.0.0.1 www.eurekaserver3.com
三、分别修改 700一、700二、7003 的配置文件:
server: port: 7001 eureka: instance: hostname: www.eurekaserver1.com # eureka 服务端实例名称 client: register-with-eureka: false # 表示不向注册中心注册本身 fetch-registry: false # false 表示本身就是注册中心,不须要检索服务 service-url: defaultZone: http://www.eurekaserver2.com:7002/eureka/,http://www.eurekaserver3.com:7003/eureka/
server: port: 7002 eureka: instance: hostname: www.eurekaserver2.com # eureka 服务端实例名称 client: register-with-eureka: false # 表示不向注册中心注册本身 fetch-registry: false # false 表示本身就是注册中心,不须要检索服务 service-url: defaultZone: http://www.eurekaserver1.com:7001/eureka/,http://www.eurekaserver3.com:7003/eureka/
server: port: 7003 eureka: instance: hostname: www.eurekaserver3.com # eureka 服务端实例名称 client: register-with-eureka: false # 表示不向注册中心注册本身 fetch-registry: false # false 表示本身就是注册中心,不须要检索服务 service-url: # 单机版 # defaultZone: http://${e ureka.instance.hostname}:${server.port}/eureka/ # 设置与 Eureka Server 交互的地址,可用来查询注册的服务 defaultZone: http://www.eurekaserver1.com:7001/eureka/,http://www.eurekaserver2.com:7002/eureka/
四、修改 8001 客户端 Eureka 配置,让客户端注册到多个 Eureka 服务端:
eureka: client: # 将当前工程做为 Eureka 客户端 service-url: # 单机版 # defaultZone: http://localhost:7001/eureka # Eureka 服务端地址 defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka instance: instance-id: microservicecloud-provider-dept prefer-ip-address: true # 访问路径显示 IP
五、测试:
依次启动 700一、700二、7003 EurekaServer 服务,接着再启动 8001 客户端服务,客户端服务能够正常访问。 访问 http://www.eurekaserver1.com:7001/:
访问 http://www.eurekaserver2.com:7002/:
访问 http://www.eurekaserver3.com:7003/:
由测试结果可知,客户端服务已经注册到了多个 Eureka 服务端,每一个 Eureka 服务端上又挂载了其它 Eureka 服务端的副本,Eureka 集群搭建成功。
上面咱们经过访问 Eureka 的 Web 页看到以下界面:
该界面是描述的是有哪些 Eureka 客户端实例注册到了当前 Eureka 服务端,说明以下:
Status 栏所显示的实例名称是能够点击的,它所跳转的页面为 随机域名:端口/info ,以下:
若是咱们但愿将这个地址的随机域名改成 IP 地址,则能够在配置文件中修改 eureka.instance.prefer-ip-address 属性值为 true 实现:
为方便咱们使用,Eureka 容许咱们经过点击客户端实例访问实例的详细信息,对应路径为 /info ,但咱们访问时会发现会返回 404 以下:
其实 SpringBoot 自己提供的监控功能就能够帮咱们解决这个问题,须要在客户端工程中引入监控相关依赖:
<!--监控信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
使用 maven 资源插件,让项目属性加载到项目环境变量中:
<build> <finalName>microservicecloud</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <delimiters> <delimit>$</delimit> </delimiters> </configuration> </plugin> </plugins> </build>
接着咱们就能够在客户端配置文件中添加 info 相关配置,例如:
info: host: ${java.rmi.server.hostname} port: ${server.port} app.name: microservicecloud-provider-dept-8001 build.artifactId: ${project.artifactId} build.version: ${project.version}
重启客户端程序,从新访问 /info :
当咱们在作上述测试的时候,咱们可能会发现 Eureka 页可能会显示以下红字:
这个其实就是由于 Eureka 的自我保护机制引发的。默认状况下,若是 EurekaServer 在必定时间内没有接收到某个微服务实例的心跳,EurekaServer 将会注销该实例(默认为 90 秒)。可是当网络分区故障时,微服务与 EurekaServer之间没法正常通讯,以上行为可能就变得很是危险了——由于微服务自己实际上是健康的,此时本不该该注销这个微服务。Eureka 经过“自我保护模式”来解决这个问题:当 EurekaServer 节点在短期内丢失过多的客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer 就会保护服务注册表中的信息,再也不删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障回复后,EurekaServer 节点会自动退出自我保护模式。
在自我保护模式中,EurekaServer 会保护服务注册表中的信息,不注销任何服务实例。当它收到的心跳数从新恢复到阈值以上时,该 EurekaServer 就会自动退出自我保护模式。它的设计哲学就是宁肯保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例,其目的是遵循 AP 原则。
关于 CAP 原则可参考【CAP 定理的含义】
综上,自我保护模式是一种应对网络异常的安全保护措施,它的架构哲学是宁肯同时保留全部微服务(不管微服务是健康仍是不健康),也不盲目注销任何健康的微服务。使用自我保护模式,可让 Eureka 集群更加的健壮、稳定。
在 SpringCloud Eureka 服务端工程中,能够经过 eureka.server.enable-self-preservation = false 来禁用自我保护模式。
做为服务注册中心,Eureka 比 Zookeeper 好在哪里?
著名的 CAP 理论指出,一个分布式系统不可能同时知足 C(一致性)、A(可用性)和 P(分区容错性)。因为分区容错性 P 是在分布式系统中必须保证的,所以咱们只能在 A 和 C 之间权衡。
所以 Zookeeper 保证的是 CP,而 Eureka 则是保证 AP。