1.概念
Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的 需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。同时,服务提供方与Eureka之间通过“心跳” 机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。这就实现了服务的自动注册、发现、状态监控。
原理图
Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
服务提供者:启动后向Eureka注册自己信息(地址,提供什么服务)
服务消费者:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定 期更新
心跳(续约):微服务定期通过http方式向Eureka刷新自己的状态
Eureka主要包含两个组件:EurekaServer和EurekaClient
EurekaServer提供服务注册,各个微服务节点通过配置启动后,会在EurekaServer 中进行注册。这样EurekaServer 中的服务注册表 将会存储所有可用服务的节点信息,服务节点信息可以在eureka控制面板上看得到。
EurekaClient通过注册中心进行访问
通过EurekaClient这个客户端,我们可以和EurekaServer进行交互。EurekaClient内部内置了一个默认 使用轮询算法的负载均衡器。在应用启动之后,将会向EurekaServer发送心跳(默认时间周期是30秒)。 如果EurekaServer在多个心跳周期内没有收到某个节点的心跳,EurekaServer将会从服务注册表中把 这个服务节点移除,默认时间是90秒。
2.搭建Eureka中心
首先在pom文件中导入依赖
<dependencies><!--eureka服务端依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><!--web启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency></dependencies>
编写application.yml文件
server:port: 7001
# 配置eureka服务端
eureka:client:register-with-eureka: false # 禁止自己注册自己fetch-registry: false # 禁止抓取注册中心中的服务信息service-url:defaultZone: http://localhost:7001/eureka/ # eureka服务端的地址
编写启动类
@SpringBootApplication
@EnableEurekaServer //标识当前服务是eureka服务端
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class,args);}
}
现在启动项目,访问7001端口,查看eureka服务端是否搭建成功:
我们访问到了eureka注册中心的控制台面板,说明我们的eureka注册中心搭建成功。
接下来就是对相应的服务提供方和服务消费方进行相应的配置。
首先是服务消费方
在pom文件中加上
<!--eureka-client--><dependency><groupId>org.springframework.cloud groupId><artifactId>spring-cloud-starter-netflix-eureka-client artifactId><dependency>
在它的启动类上添加开启服务发现的注解:@EnableDiscoverClient
yml文件中要要加入
eureka:client:# 表示当前微服务是否向Eureka注册中心注册自己,设置为true即开启注册功能register-with-eureka: true # 表示客户端是否从Eureka注册中心获取服务注册列表,设置为true可获取fetch-registry: true service-url:# defaultZone指定Eureka注册中心的地址,这里设置为本地7001端口的Eureka服务地址defaultZone: http://localhost:7001/eureka/
接下来是服务消费方
导入依赖
<!--eureka-client--><dependency><groupId>org.springframework.cloud groupId><artifactId>spring-cloud-starter-netflix-eureka-client artifactId><dependency>
然后在它的启动类上添加开启服务发现的注解:@EnableDiscoverClient
yml上加入
eureka:client:# 表示当前微服务是否向Eureka注册中心注册自己,设置为true即开启注册功能register-with-eureka: true # 表示客户端是否从Eureka注册中心获取服务注册列表,设置为true可获取fetch-registry: true service-url:# defaultZone指定Eureka注册中心的地址,这里设置为本地7001端口的Eureka服务地址defaultZone: http://localhost:7001/eureka/
之后启动项目,注册中心就会多了两个
3.eureka集群
Eureka Server即服务的注册中心,在刚才的案例中,我们只有一个EurekaServer,但是如果这个 EurekaServer挂掉了会影响整个应用。事实上EurekaServer也可以是一个集群,形成高可用的Eureka中 心,一个Eureka服务中心挂掉了还有其他的服务注册中心。
多个Eureka Server之间也会互相注册为服务,当服务提供者注册到Eureka Server集群中的某个节点时, 该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到Eureka Server集群中的任意一个节点,都可以获取到完整的服务列表信息。
现在我们动手搭建高可用的EurekaServer,Eureka 集群的原理,就是 相互注册,互相守望。
和之前的搭建一样,我们只要把端口改了就行,这里我们改成7002
但是两个注册中心的defaultZone要改为对方的
就是进行互相注册
然后服务消费方和提供方的配置文件也要进行修改,也就是defaultZone要把两个注册中心的URL都写上就行了,用逗号隔开
4.服务集群
对服务提供方进行集群搭建
我们的微服务目前也只有一台服务,如果在生产环境中挂掉了,也不能对外继续提供服务,所以我们的服 务也可以进行集群。在这里我们以服务提供方为例。搭建两台服务提供方,另一台服务提供方的端口号为 8002。
yml部分内容
server:port: 8001spring:application:name: service-provider # 配置服务名称
控制面板如下
对服务消费方进行集群搭建
当我们有了两台服务提供方以后,我们需要改造服务消费端的服务调用方式。那么现在问题来了,我们应 该访问两台服务提供方的哪一台服务呢?以前在消费方指定访问的是8001端口的微服务。现在我们需要 修改访问提供方的访问规则,规则就是通过服务提供方的名称进行访问,不再通过服务提供方的ip+端口 进行访问。
我们修改服务消费方的控制器层代码:
// 表明这是一个 RESTful 风格的控制器,用于处理 HTTP 请求并返回 JSON 或 XML 等数据
@RestController
// 定义该控制器处理的请求的基础路径,所有该控制器下的请求都会以 "consumer" 开头
@RequestMapping("consumer")
// 抑制所有编译器警告,通常不建议滥用,仅在特定场景下使用
@SuppressWarnings("all")
public class PaymentController {// 使用 Spring 的依赖注入机制,将 RestTemplate 对象注入到当前控制器中// RestTemplate 是 Spring 提供的用于发送 HTTP 请求的工具类@AutowiredRestTemplate restTemplate;/*** 根据 ID 查询支付信息的方法* @param id 要查询的支付信息的 ID* @return 包含支付信息的结果对象*/@RequestMapping("findById/{id}")public Result<Payment> findById(@PathVariable("id") Long id) {// 通过服务实例名称进行访问,构建请求的 URL// 这里的 "SERVICE-PROVIDER" 是服务提供者的实例名称,在服务注册中心中注册的名称// 拼接具体的请求路径和参数String url = "http://SERVICE-PROVIDER/provider/findById?id=" + id;// 使用 RestTemplate 发送 GET 请求到指定的 URL,并将响应结果映射为 Result 类型的对象Result result = restTemplate.getForObject(url, Result.class);// 返回查询结果return result;}
}
但此时必不能直接启动,因为仅仅根据代码中的url是不能知道调用哪台服务提供方的机器,所以我们要进行负载均衡的实现,实现它就是要实现负载均衡器。
@ConfigurationpublicclassMyConfig {@Bean@LoadBalanced
开启负载均衡的访问
publicRestTemplaterestTemplate(){returnnewRestTemplate();}}
6.服务发现
有的时候,我们需要盘点一下我们eureka注册中心上到底有哪些服务实例,我们需要获取这些详细服务的 各种细节(比如IP地址 服务端口号等),这个时候我们就需要使用一个新的注解来开启服务发现功能,这个 注解就是EnableDiscoveryClient。对于注册进eureka里面的微服务,可以通过服务发现来获得该服务 的信息
首先:在微服务的启动类上面添加EnableDiscoveryClient注解。开启服务发现功能。
然后,消费方的控制器中定义如下逻辑
// 标识该类为 REST 风格的控制器,会将方法的返回值直接作为 HTTP 响应体返回
@RestController
// 为该控制器下的所有请求映射添加公共的请求路径前缀 "consumer"
@RequestMapping("consumer")
// 使用 Lombok 提供的 @Slf4j 注解,自动生成一个日志记录器对象 log,方便进行日志记录
@Slf4j
// 抑制编译器产生的所有警告信息,使用时需谨慎,可能会掩盖一些潜在问题
@SuppressWarnings("all")
public class PaymentController {// 使用 @Resource 注解注入 DiscoveryClient 对象// DiscoveryClient 是 Spring Cloud 提供的用于与服务注册中心交互的客户端,可用于获取服务注册信息@Resourceprivate DiscoveryClient discoveryClient;/*** 获取服务发现信息的接口* @return 返回 DiscoveryClient 对象,可用于前端进一步了解服务发现的情况*/@GetMapping("/customer/discovery")public Object discovery() {// 通过 DiscoveryClient 获取服务注册中心中所有已注册的服务名称列表List<String> services = discoveryClient.getServices();// 遍历服务名称列表,并使用日志记录每个服务的名称for (String service : services) {log.info("service:" + service);}// 根据服务提供方的名称获取对应的服务实例信息// 这里假定服务提供方在服务注册中心注册的名称是 "service-provider"List<ServiceInstance> instances = discoveryClient.getInstances("service-provider");// 遍历服务实例列表,获取每个服务实例的详细信息for (ServiceInstance instance : instances) {// 获取服务实例的主机地址String host = instance.getHost();// 获取服务实例的端口号int port = instance.getPort();// 获取服务实例的服务 IDString serviceId = instance.getServiceId();// 使用日志记录每个服务实例的主机地址、端口号和服务 IDlog.info("host:" + host + " port:" + port + " serviceId:" + serviceId);}// 返回 DiscoveryClient 对象,前端可以根据该对象获取更多服务发现相关的信息return this.discoveryClient;}
}
7.Eureka的自我保护机制
Eureka服务端会检查最近15分钟内所有Eureka 实例正常心跳占比,如果低于85%就会触发自我保护机 制。触发了保护机制,Eureka将暂时把这些失效的服务保护起来,不让其过期,但这些服务也并不是永远 不会过期。Eureka在启动完成后,每隔60秒会检查一次服务健康状态,如果这些被保护起来失效的服务 过一段时间后(默认90秒)还是没有恢复,就会把这些服务剔除。如果在此期间服务恢复了并且实例心跳 占比高于85%时,就会自动关闭自我保护机制。
为什么会有自我保护机制? Eureka服务端为了防止Eureka客户端本身是可以正常访问的,但是由于网路通信故障等原因,造成 Eureka服务端失去于客户端的连接,从而形成的不可用。
因为网络通信是可能恢复的,但是Eureka客户端只会在启动时才去服务端注册。如果因为网络的原因而剔 除了客户端,将造成客户端无法再注册到服务端。
如何选择关闭还是开启自我保护机制?Eureka服务端默认情况下是会开启自我保护机制的。但我们在不同 环境应该选择是否开启保护机制。一般情况下,我们会选择在开发环境下关闭自我保护机制,而在生产环 境下启动自我保护机制。
开发环境下,我们启动的服务数量较少而且会经常修改重启。如果开启自我保护机制,很容易触发 Eureka客户端心跳占比低于85%的情况。使得Eureka不会剔除我们的服务,从而在我们访问的时候,会 访问到可能已经失效的服务,导致请求失败,影响我们的开发。
在生产环境下,我们启动的服务多且不会反复启动修改。环境也相对稳定,影响服务正常运行的人为情况 较少。适合开启自我保护机制,让Eureka进行管理。
如何关闭自我保护机制:
在Eureka服务中心进行配置:
eureka:server:#服务端是否开启自我保护机制(默认true)enable-self-preservation: false#扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒eviction-interval-timer-in-ms: 2000
在Eureka客户端进行配置:
eureka:client:service-url:defaultZone: http://localhost:7001/eureka, http//localhost:7002/eurekainstance:#客户端向注册中心发送心跳的时间间隔,(默认30秒)lease-renewal-interval-in-seconds: 1#Eureka注册中心(服务端)在收到客户端心跳之后,等待下一次心跳的超时时间,如果在这个时间内没有收到下次心跳,则移除该客户端。(默认90秒)lease-expiration-duration-in-seconds: 2