目录
1.Eureka工作机制
Eureka工作原理
Eureka包含哪两个组件?
Eureka Server:
Eureka Client:
2.服务提供者与服务消费者
Eureka Client有哪两种角色?
服务提供者:
服务消费者:
服务消费者是如何调用服务提供者的?
3.第一个Eureka应用
①搭建Eureka-server
1、创建项目,引入依赖
2、添加Eureka的相关配置
3、在项目启动类添加@EnableEurekaServer注解
②搭建服务提供者
1、创建项目,引入依赖
2、添加Eureka的相关配置
3、在项目启动类添加@EnableEurekaClient注解
③搭建服务消费者
1、创建项目,引入依赖
2、添加Eureka的相关配置
3、在项目启动类添加@EnableEurekaClient或@EnableDiscoveryClient 注解
4.Eureka 高可用配置
5.心跳机制
自定义心跳时间:
自定义Eureka Server刷新服务列表,将无效服务剔除的时间间隔:
自我保护机制
Eureka Server如何判断是否开启自我保护?
如何关闭自我保护机制?
其他配置
Spring Cloud框架下的服务发现Eureka包含两个组件,分别是:Eureka Server与Eureka Client。
Eureka Server,也称为服务注册中心。各个服务启动后,会在Eureka Server中进行注册,这样Eureka Server的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client也称为服务(服务实例)。作为一个Java客户端,用于简化与Eureka Server的交互。Eureka Client内置一个使用轮询负载算法的负载均衡器。服务启动后,Eureka Client将会向Eureka Server发送心跳更新服务,如果Eureka Server在多个心跳周期内没有接收到某个服务的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
为了更好地理解Eureka 组件的工作原理,接下来通过一张图来描述。如下图所示。
Eureka Client注册在Eureka Server,Eureka Client中的服务Service是通过REST调用的。Eureka Client还具有缓存功能,它能够从Eureka Server查询当前注册的服务信息并缓存到本地,这样即使Eureka Server宕机,Eureka Client依然可以利用缓存中的信息调用服务。
Eureka Client存在两种角色,分别是服务提供者和服务消费者。
服务提供者( Eureka Client )启动后,会通过REST请求将自己注册在Eureka Server,并维护一个心跳(默认30秒发送一次心跳)进行服务续约,告诉Eureka Server“我还活着”,防止Eureka Server将该服务从服务列表剔除。
用于获取Eureka Server注册的服务清单,并且该服务清单默认每隔30秒更新一次。服务消费者获取到服务清单后,能够根据自己的需求决定调用哪个服务,默认采用轮询方式调用,从而实现Eureka Client的负载均衡。
接下来,通过一张图描述Eureka Server与服务提供者、服务消费者之间的关系,如下图所示。
服务提供者和服务消费者都属于Eureka Client,它们都会将自己的信息通过REST API形式提交给Eureka Server。服务消费者注册后,还会获取一份服务注册列表,该列表包含了所有向Eureka Server注册的服务信息。获取到服务注册信息后,服务消费者就会根据服务提供者的IP地址,通过HTTP远程调用服务提供者的服务。
使用Spring Initializr方式创建一个名称为eureka-server的Spring Boot项目,在pom.xml文件中添加Eureka Server依赖。
在pom文件中添加的Eureka Server依赖如下:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
在全局配置文件application.yml中添加Eureka的相关配置信息。(注:项目自动生成的配置文件为application.properties,我们直接将后缀名改掉就行。这两个文件格式都可以,只是内部的内容格式不同。)
application.yml:
server:
port: 7000 # 服务器端口号7000
spring:
application: #端口号名称配置
name: eureka-server
eureka:
client:
fetch-registry: false # 表示是否向Eureka Server注册
register-with-eureka: false # 表示是否从Eureka Server获取注册信息
service-url:
defaultZone:
http://${eureka.instance.hostname}:${server.port}/eureka/ #设置服务注册中心地址
instance:
hostname: localhost
在项目启动类EurekaServerApplication上添加@EnableEurekaServer注解开启Eureka Server功能。
@SpringBootApplication
@EnableEurekaServer
public class EurekeServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekeServerApplication.class, args);
}
}
启动eureka-server项目,在浏览器中访问http://localhost:7000/。效果如下图所示。
使用Spring Initializr方式创建一个名称为eureka-provider的Spring Boot项目,添加Web、Eureka Client依赖。
pom.xml
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
2.1.7.RELEASE
在全局配置文件application.yml中添加Eureka的相关配置信息。
server:
port: 7006
spring:
application:
name: eureka-provider
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka/
instance:
hostname: localhost
在项目启动类EurekaProviderApplication上添加@EnableEurekaClient注解开启Eureka Client功能。
@SpringBootApplication
@EnableEurekaClient
public class EurekaServerProvide01Application {
public static void main(String[] args) {
SpringApplication.run(EurekaServerProvide01Application.class, args);
}
}
保证Eureka Server启动的状态下,启动eureka-provider,在浏览器中访问Eureka Server的主界面http://localhost:7000/,效果如下图所示。
使用Spring Initializr方式创建一个名称为eureka-consumer的Spring Boot项目,添加Web、Eureka Client依赖。
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
2.1.7.RELEASE
在全局配置文件application.yml中添加Eureka的相关配置信息。
server:
port: 7002
spring:
application:
name: eureka-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka/
instance:
hostname: localhost
注:在启动类中,使用了@EnableDiscoveryClient 注解来修改启动类,该注解使得服务调用者有能力去 Eureka 中发现服务,需要注意的是@EnableEurekaClient 注解已经包含了 @EnableDiscoveryClient 的功能,也就是说,一个 Eureka 客户端,本身就具有发现服务的能力。
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaServerInvokeApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerInvokeApplication.class, args);
}
}
保证Eureka Server启动的状态下,启动eureka-consumer。在浏览器访问http://localhost:7000/,说明服务提供者和服务消费者已经成功注册到Eureka Server中。效果如下图所示。
在微服务架构这样的分布式环境中,我们需要充分考虑发生故障的情况,所以在生产环境中必须对各个组件进行高可用部署,对于微服务如此,对于服务注册中心也一样。但是到本节为止,我们一直都在使用单节点的服务注册中心,这在生产环境中显然并不合适,我们需要构建高可用的服务注册中心以增强系统的可用性。 Eureka Server 的设计一开始就考虑了高可用问题,在 Eureka 的服务治理设计中,所有节点即是服务提供方,也是服务消费方,服务注册中心也不例外。
Eureka Server 的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。下面我们就来尝试搭建高可用服务注册中心的集群。可以在本章第1节中实现的服务注册中心的基础之上进行扩展,构建一个双节点的服务注册中心集群。
本例将会运行两个服务器实例、两个服务提供者实例,然后服务调用者请求服务,第一个 Eureka 应用,使用的是浏览器访问 Eureka 的服务调用者,而改造后,为了能看到负载均衡的效果,会编写一个 HttpClient 的 REST 客户端访问服务调用者发布的服务。
新建模块 eureka-cloud-server,并添加依赖
在 Eureka 项目中用 springboot 创建 eureka-cloud-server 模块,勾选 Spring Cloud Discovery->Eureka Server ,修改相应的版本和上面一致。
@SpringBootApplication
@EnableEurekaServer
public class EurekaCloudServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaCloudServerApplication.class).run(args);
}
}
配置文件如下
server:
port: 8761
spring:
application:
name: eureka-cloud-server
profiles: slave1
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8762/eureka/
--------------------------------------------------
server:
port: 8762
spring:
application:
name: eureka-cloud-server
profiles: slave2
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
配置了两个 profiles,名称分别为 slave1 和 slave2。
在 slave1 中,配置了应用端口为 8761,主机名为 slave1,当使用 salve1 这个 profiles 来启动服务器时,将会向http://slave2:8762/eureka/注册自己。
使用salve2来启动服务器,会向 http://slave1:8761/eureka/ 注册自己。
注意:第一个启动的服务器会抛出异常,异常原因我们前已经讲述,抛出的异常可不必理会。
当 Eureka 配置高可用后,服务提供者也需要进行相应的调整。eureka-service-provider 复制出来,并改名为 eureka-cloud-provider。修改配置文件,将服务提供者注册到两个服务器中
spring:
application:
name: eureka-cloud-provider
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
修改控制器,可以看到是请求的 url
@RestController
public class FirstController {
@RequestMapping(value = "/person/{personId}")
public Map findPerson(@PathVariable Integer personId, HttpServletRequest request) {
HashMap map = new HashMap<>();
map.put("id", personId);
map.put("name", "张三");
map.put("age", 18);
map.put("url", request.getRequestURL().toString());
return map;
}
}
调整服务调用者,将 eureka-service-invoker 复制并改名为 eureka-cloud-invoker。本例中的服务调用者只需启动一个实例,因此修改下配置文件即可使用
server:
port: 9000
spring:
application:
name: eureka-cloud-invoker
eureka:
client:
serviceUrl:
defaultZone: http://slave1:8761/eureka/,http://slave2:8761/eureka/
调用类修改一下服务名
@RestController
@Configuration
public class InvokerController {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@RequestMapping(value = "/router/{personId}")
public String router(@PathVariable Integer personId) {
RestTemplate restTpl = getRestTemplate();
// 根据应用名称调用服务
String json = restTpl.getForObject(
"http://eureka-cloud-provider/person/" + personId, String.class);
return json;
}
}
启动调用者服务后即可完成浏览器测试,但是建议使用 HttpClient 进行测试
org.apache.httpcomponents
httpclient
4.5.2
main方法如下
public static void main(String[] args) throws Exception {
// 创建默认的HttpClient
CloseableHttpClient httpclient = HttpClients.createDefault();
// 调用6次服务并输出结果
for(int i = 0; i < 6; i++) {
// 调用 GET 方法请求服务
HttpGet httpget = new HttpGet("http://localhost:9000/router/"+i);
// 获取响应
HttpResponse response = httpclient.execute(httpget);
// 根据 响应解析出字符串
System.out.println(EntityUtils.toString(response.getEntity()));
}
}
完成编写后,按以下顺序启动各个组件:
启动了整个集群后,运行TestHttpClient,可以看到输出如下
{"name":"张三","id":3,"age":18,"url":"http://localhost:8886/person/0"}
{"name":"张三","id":3,"age":18,"url":"http://localhost:8888/person/1"}
{"name":"张三","id":3,"age":18,"url":"http://localhost:8886/person/2"}
{"name":"张三","id":3,"age":18,"url":"http://localhost:8888/person/3"}
{"name":"张三","id":3,"age":18,"url":"http://localhost:8886/person/4"}
{"name":"张三","id":3,"age":18,"url":"http://localhost:8888/person/5"}
根据输出结果可知,8888 与 8886端口分别被请求了 3 次,可见已经达到负载均衡的目的
Eureka是通过心跳的方式实现对各个服务实例的健康检测的。在Eureka的服务续约与剔除机制下,客户端的健康状态从注册到注册中心开始都会处于UP状态,除非心跳中止一段时间后,服务注册中心将其剔除。心跳机制以有效检查客户端进程是否正常运作,接下来,我们将对Eureka心跳机制常用配置进行讲解
服务启动后,Eureka Client实例将会向Eureka Server发送周期性的心跳,默认是每30秒发送一次,可通过修改Eureka Client实例的eureka.instance. leaseRenwalIntervalInSeconds属性来改变发送周期性心跳的默认时间。如下所示。
eureka:
instance:
leaseRenewalIntervalInSeconds: 5 #设置心跳时间为5秒
Eureka Server如果在一定期限内没有接收到Eureka Client实例的心跳,那么会将该实例从注册中心剔除掉,其他客户端将无法访问这个实例。这个期限默认值为90秒。可以通过eureka.instance.leaseExpriationDurationInSeconds属性修改默认值,示例代码如下:
eureka:
instance:
leaseRenewalIntervalInSeconds: 5
leaseExpirationDurationInSeconds: 10 #设置每隔10秒刷新服务列表,将 无效服务剔除
Eureka的自我保护机制是为了防止误杀服务。如果Eureka Server注册中心发生故障,那么Eureka Client服务就有可能不能正常续约,虽然这时Eureka Client服务是正常的,但注册中心依然会将超过90秒未续约的服务剔除,造成误杀服务的情况。
Eureka Server通过判断是否存在大量续约失败的服务,从而确定是否开启自我保护。默认情况下,Eureka Server配置的自我保护阀值是0.85,如果Eureka Server运行期间根据心跳比例接收到的服务续约低于阀值,则Eureka Server开启自我保护,不再剔除注册列表的信息。
如果在Eureka Server保护期间,可能会发生服务下线的情况,此时Eureka Server维护的服务清单就不那么准确了,此时可以关闭保护保护机制,确保服务中心不可用的服务实例被及时剔除。 默认情况下,Eureka Server自我保护是开启的,如果需要关闭,则可以在配置文件中添加如下代码:
eureka:
server:
enable-self-preservation: false
参数名称 |
参数说明 |
enable |
启动Eureka客户端,默认值true |
registryFetchIntervalSeconds |
从Eureka服务端获取注册信息的间隔时间,单位为秒,默认值30 |
fetchRegistry |
是否从Eureka服务端获取注册信息,默认值false |
eurekaServerReadTimeoutSeconds |
读取Eureka Server信息的超时时间,单位为秒,默认值8 |
initialInstanceInfoReolicationInstervalSeconds |
初始化实例信息到Eureka服务端的间隔时间,单位为秒,默认值40 |
instanceInfoReolicationInstervalSeconds |
更新实例信息的变化到Eureka服务端的间隔时间,单位为秒,默认值30 |
preferIpAddress |
是否优先使用IP地址作为主机名的标识,默认值false |
leaseRenewalIntervalInSeconds |
Eureka客户端向服务端发送心跳的时间间隔,单位为秒,默认值30 |
leaseExpirationDurationInSeconds |
Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,如果超出时间后,服务端会将该服务实例从服务清单中剔除,从而禁止服务调用请求被发送到该实例上,默认值90 |
appname |
服务名,默认取spring.application.name的配置值,如果没有则为unknown |
hostname |
主机名,不配置时将根据操作系统的主机名来获取 |