一、Eureka 的简单使用
本文不是初学开始的,在有点基础上的是查漏补缺。
起言:服务治理概念
Spring Cloud
提供了多种组件发现的支持,例如 Eureka、Consul、Zookeeper等。
一个服务治理组件应该具有以下三个功能:
服务注册表
服务治理组件的核心,它用来记录各个微服务的信息,比如说微服务的名称、IP、端口等。服务注册组件提供查询 API 和管理 API,查询 API 用来查询可用的微服务实例,管理 API用于服务的注册和注销。
服务注册与发现
- 服务注册是指微服务在启动时,将自己的信息注册到服务治理组件上的过程。
- 服务发现是指查询可用微服务列表及其网络地址的机制。
服务检查
服务治理组件使用一定机制定时检测已注册的服务,是否在线,或者是否无法连接等,若发现无法访问,就会从服务注册表中移除该实例,进行下线操作。
一、What's Eureka
Eureka
是 Netflix
开源的服务发现组件,本身是一个基于 REST 的服务。它包含 Server 和 Client 两部分。Spring Cloud 将它继承自了子项目 Spring Cloud Netflix
中。以下六点内容取自周立的《Spring Cloud 与 Docker 微服务架构与实战》第二版,对 Eureka的一些特性描述得还不错,先来看看:
Eureka Server
提供了服务发现的能力,微服务启动时,会向Eureka
发送自己的信息(ip、port、微服务名称等),Eureka Server
会将这些信息存储起来;Eureka Client
是一个 Java 客户端,用于简化与Eureka
的交互。只需要几行配置文件、注解就可以实现注册到Eureka
上;- 微服务启动后,会周期性(默认30 s)向
Eureka Server
发送心跳以续约自己的“租期”; - 如果
Eureka Server
在一定的时间内没有收到某个微服务实例的心跳, Eureka Server 将注销该实例(默认为90 s); - 默认情况下,
Eureka Server
同时也是Eureka Client
。多个Eureka Server
实例相互之间通过复制的方式来实现服务注册表中数据的同步; Eureka Client
会缓存服务注册表中的信息。这种方式有一定的优势——首先,不需要每次都请求查询Eureka Server
,从而降低了Eureka Server
的压力;其次,即使 Eureka Server 所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者并完成调用。
综上所述:Eureka 通过心跳检测机制、客户端缓存机制,提高了系统的灵活性,可伸缩性和可用性。
二、How to use Eureka
2.1 Maven 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
注意:E 版本 之前(包括E版本)是引入spring-cloud-starter-eureka-server
。
2.2 关于配置文件(重要)
我在本地新建了一个Spring Boot 工程,在 appication.yml
里面配置如下:
# server参数
server:
port: 8761
tomcat:
uri-encoding: utf-8
# spring 参数
spring:
application:
name: pro-eureka
# Eureka 参数
eureka:
instance:
# 实例的主机名称
hostname: localhost
client:
# 不要向注册中心 Eureka 注册它自己
register-with-eureka: false
# 是否从 Eureka Server 获取注册信息,默认为 true,当前是单点的 Eureka Server,不需要同步其他节点的数据,所以false
fetch-registry: false
service-url:
# 指定服务注册中心地址,这里指向了本服务,假如多个地址,直接使用逗号分隔
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2.3 主类配置
然后在主类上添加@EnableEurekaServer
注解。这样子就可以简单启动一个 Eureka 了。
需要注意的是,最后的defaultZone
参数不写时,默认就是http://localhost:8761/eureka
。
三、微服务注册
3.1 Maven 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2 配置文件(重要)
server:
port: 9001
tomcat:
uri-encoding: UTF-8
spring:
application:
name: pro-product
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
# 启用ip配置,如果不配置该属性或将该属性配置成 false,则表示注册微服务所在操作系统的 hostname 到 Eureka Server
prefer-ip-address: true
# 实例名称 最后呈现地址:ip:2000
instance-id: ${spring.cloud.client.ip-address}:${server.port}
最终在 Eureka 界面看到的将是:
3.3 主类配置
在主类上添加 @EnableEurekaClient
注解即可。
package com.kjgym.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* 注意这里也可使用 @EnableEurekaClient
* 但由于 spring cloud 是灵活的,注册中心支持 eureka、consul、zookeeper等
* 若写了具体的注册中心注解,则当替换成其他注册中心时,又需要替换成对应的注解了。
* 所以 直接使用 @EnableDiscoveryClient 启动发现。
* 这样在替换注册中心时,只需要替换相关依赖即可。
* @author kangjia
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
注意:
- 在
Spring Cloud Edgware
以及更高的版本中,只需要添加相关依赖,即可实现自动注册。- 如果不想将服务注册到 Eureka Server ,可以设置
spring.cloud.service-registry.auto-registration.enabled=false
或者是直接在主类上添加@EnableDiscoveryClient(autoRegister = false)
。
四、Eureka 的自我保护机制
当出现以下文字:
默认情况下,如果Eureka Server
在一定时间内没有接收到某个微服务实例的心跳,Eureka Server
将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,这就可能变得非常危险了,因为微服务本身是健康的,此时本不应该注销这个微服务。
Eureka Server
通过“自我保护模式”来解决这个问题,当Eureka Server
节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server
就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server
节点会自动退出自我保护模式。
自我保护模式是一种对网络异常的安全保护措施。使用自我保护模式,而让Eureka集群更加的健壮、稳定。
开发阶段可以通过配置:eureka.server.enable-self-preservation=false
关闭自我保护模式。
生产阶段,理应以默认值进行配置。
五、给 Eureka 一个用户认证
5.1 Maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
5.2 配置文件
# server参数
server:
port: 8761
tomcat:
uri-encoding: utf-8
# spring 参数
spring:
application:
name: pro-eureka
security:
user:
password: 123456
name: kangjia
# Eureka 参数
eureka:
instance:
hostname: localhost
client:
# 不要向 Eureka 注册它自己
register-with-eureka: false
# 不去检索其他的服务,因为注册中心本身职责就是维护服务实例,它不需要去检索其它服务
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
5.3 Java 代码
package com.kjgym.peureka;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
*
* @author kangjia
*/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
注意:
Spring Cloud Finchley
及更高版本必须添加这一段,在Edgware
以及更早的版本中无需这一步骤。
5.3 启动项目
浏览器输入 http://localhost:8761
,则自动跳转到如下页面:
输入上文 在配置文件里配置的用户名和密码,即可登录。
注意:
如不设置这段内容,账号默认是
user
,密码是一个随机值,该值会在启动时打印出来。
5.4 向带认证 Eureka 注册客户端
客户端的配置要改成 defaultZone: http://kangjia:123456@localhost:8761/eureka/
,否则注册不了。
六、Eureka 元数据
官网译文:值得花费一些时间来了解Eureka元数据的工作原理,因此您可以在平台上使用有意义的方式使用它。有用于信息的标准元数据,例如主机名,IP地址,端口号,状态页和运行状况检查。这些将发布在服务注册表中,并由客户端用于以直接方式联系服务。可以将其他元数据添加到中的实例注册中
eureka.instance.metadataMap
,并且可以在远程客户端中访问此元数据。通常,除非让客户端知道元数据的含义,否则其他元数据不会更改客户端的行为。在本文档后面将介绍几种特殊情况,其中Spring Cloud已经为元数据映射分配了含义。
6.1 实例:在上文的 Eureka Client 配置文件中修改:
server:
port: 9001
tomcat:
uri-encoding: UTF-8
spring:
application:
name: pro-product
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
# 启用ip配置 这样在注册中心列表中看见的是以ip+端口呈现的
prefer-ip-address: true
# 实例名称 最后呈现地址:ip:2000
instance-id: ${spring.cloud.client.ip-address}:${server.port}
metadata-map:
myData: 我是可怜的自定数据
新建 DemoController.java
:
server:
port: 9001
tomcat:
uri-encoding: UTF-8
spring:
application:
name: pro-product
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
# 启用ip配置 这样在注册中心列表中看见的是以ip+端口呈现的
prefer-ip-address: true
# 实例名称 最后呈现地址:ip:2000
instance-id: ${spring.cloud.client.ip-address}:${server.port}
metadata-map:
myData: 我是可怜的自定数据
6.2 新建一个 Java 类:
package com.kjgym.product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author kangjia@xxx.com
* @date 2019/12/16 17:33
*/
@RestController
public class TestController {
@Autowired
DiscoveryClient discoveryClient;
@GetMapping("/hello")
public List<ServiceInstance> sayHello() {
return discoveryClient.getInstances("pro-product");
}
}
6.3 启动项目,在浏览器访问 http://localhost:9001/hello
[
{
"host": "10.0.8.194",
"port": 9001,
"metadata": {
"management.port": "9001",
"myData": "我是可怜的自定数据"
},
"secure": false,
"uri": "http://10.0.8.194:9001",
"serviceId": "PRO-PRODUCT",
"instanceId": "10.0.8.194:9001",
"instanceInfo": {
"instanceId": "10.0.8.194:9001",
"app": "PRO-PRODUCT",
"appGroupName": null,
"ipAddr": "10.0.8.194",
"sid": "na",
"homePageUrl": "http://10.0.8.194:9001/",
"statusPageUrl": "http://10.0.8.194:9001/actuator/info",
"healthCheckUrl": "http://10.0.8.194:9001/actuator/health",
"secureHealthCheckUrl": null,
"vipAddress": "pro-product",
"secureVipAddress": "pro-product",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "10.0.8.194",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1576564038125,
"lastRenewalTimestamp": 1576564188072,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1576564038126
},
"isCoordinatingDiscoveryServer": false,
"metadata": {
"management.port": "9001",
"myData": "我是可怜的自定数据"
},
"lastUpdatedTimestamp": 1576564038126,
"lastDirtyTimestamp": 1576564038063,
"actionType": "ADDED",
"asgName": null
},
"scheme": null
}
]
可以看到,自定义的数据也出现在了结果 JSON 里。
七、Eureka 的 REST 接口
八、排除 Jersey 依赖
默认情况下,EurekaClient使用Jersey进行HTTP通信。如果希望避免来自Jersey的依赖关系,可以将其从依赖关系中排除。Spring Cloud基于Spring自动配置传输客户端RestTemplate
。以下示例显示排除了Jersey:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</exclusion>
</exclusions>
</dependency>
九、为什么注册服务这么慢
成为实例还涉及到注册表的周期性心跳(通过客户端serviceUrl
),默认持续时间为30秒。直到实例,服务器和客户端在其本地缓存中都具有相同的元数据后,客户端才能发现该服务(因此可能需要3个心跳)。您可以通过设置更改周期eureka.instance.leaseRenewalIntervalInSeconds
。将其设置为小于30的值可加快使客户端连接到其他服务的过程。在生产中,最好使用默认值,因为服务器中的内部计算对租约续订期进行了假设。