什么是服务治理
主要是用来实现各个微服务实例的自动化注册与发现,解决随着微服务的增加,静态配置变得越来越难以维护的问题.
服务注册
1. 维护注册
维护一个类似下面的清单
image.png
2. 服务发现
向注册中心发起咨询服务请求,以获取某个服务可用的位置清单 ,再以客户端负载均衡的方式调用.
搭建服务注册中心
- 添加POM依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-actuator</artifactId>-->
<!--</dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 通过 @EnableEurekaServer 启动一个服务注册中心
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
- 配置几个配置,如,禁用客户端注册行为
eureka.instance.hostname=localhost
# 关闭保护机制
#eureka.server.enable-self-preservation=false
# false代表不向注册中心注册自己
eureka.client.register-with-eureka=false
#false代表不需要去检索服务,因为自己就是维护服务
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
启动看一下界面
image.png注册服务提供者
- 修改pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 修改启动类,添加
@EnableDiscoveryClient
,激活Eureka的实现
@EnableDiscoveryClient
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
- 修改配置
#配置服务名称
spring.application.name=hello-service
#指定服务注册中心的位置
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
启动结果
image.png
搭建高可用注册中心
原理是搭建两台注册中心,然后分别把自己作为客户端服务向对方注册,对方就把自己当成服务.
- 添加配置
application-peer1.properties
,内容如下:
spring.application.name=eureka-server
server.port=1111
eureka.instance.hostname=peer1
eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/
2.添加配置application-peer2.properties
,内容如下:
spring.application.name=eureka-server
server.port=1112
eureka.instance.hostname=peer2
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/
- 其他的配置不变,然后我们部署两台注册中心,启动命令如下
java -jar eureka-server-ha-1.0.0.jar --spring.profiles.active=peer1
java -jar eureka-server-ha-1.0.0.jar --spring.profiles.active=peer2
- 向高可用的注册中心注册服务
修改上面的工程配置文件如下:
#eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/
-
查看结果
image.png
我们这时访问一下刚刚的接口
http://localhost:8082/hello1?name=zzj
发现没有问题.
这时再把peer2杀掉.再访问,也是没有问题的.同时在peer1的后台中可以看到如下报错:
image.png
peer1上也显示peer2也为不可用服务
image.png
peer1在不停的尝试连接peer2
至此,我们实现了注册中心的高可用.
我们再启动一下peer2,观察peer1的报错停止了.出现了以下的信息,
同时访问peer2也能访问了.
如果不想用主机名来定义注册中心,也可以使用IP地址的形式.修改配置如下:
eureka.instance.prefer-ip-address=true
默认为
false
服务的发现与消费
服务的发现是由eureka完成,消费是由Ribbon完成,Ribbon可以通过客户端配置的ribbonServerList
服务端列表去轮询以达到负载均衡的效果
ribbonServerList
被DiscoveryEnabledNIWSServerList
重写
NIWSDiscoveryPing
取代IPing
- 启动之前的eureka-server以及hello-service服务,如下:
java -jar hello-service-eureka-1.3.7.RELEASE.jar --server.port=8083
java -jar hello-service-eureka-1.3.7.RELEASE.jar --server.port=8082
image.png
image.png
创建ribbon-customer工程
- 添加依赖
<name>ribbon-consumer</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-amqp</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 添加注解
@EnableDiscoveryClient
以获得服务发现能力,@LoadBalanced
开启客户端的负载均衡,代码如下
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
3.创建Controller,实现/ribbon-consumer1接口
代码如下:
@Autowired
RestTemplate restTemplate;
@RequestMapping(value="/ribbon-consumer1",method = RequestMethod.GET)
public String helloConsumer1() {
return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
}
地址用服务名
image.png
- 修改application.properties,启动服务
spring.application.name=ribbon-consumer
server.port=9000
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/
image.png
访问
http://localhost:9000/ribbon-consumer1
会发现两个服务提供者的后台会交替显示访问日志:image.png
image.png
Eureka基础架构
image.png-
服务注册中心
- 失效剔除
- 自我保护
Eureka Server在运行期间, 会统计心跳失败的比例在15分钟之内是否低于85%(在单机调试的时候很容易满足)
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
对此可以修改eureka.server.enable-self-preservation=false
默认为true
为true
的意思是Eureka会将当前的实例信息保护起来,让这些实例不过期.
客户端对于此问题必须要有容错机制,比如:请求重试,断路器等机制
-
服务提供者
- 服务注册,启动时注册,Eureka保存在一个双层结构的Map中
- 服务同步
只要向其中一台Eureka注册,另一台就会自动同步到另外一台
peer1的发现方式
2017-12-03 17:47:13.807 INFO 7336 --- [nio-1111-exec-5] c.n.e.registry.AbstractInstanceRegistry : Registered instance RIBBON-CONSUMER/loca
lhost:ribbon-consumer:9000 with status UP (replication=false)
peer2的发现日志
2017-12-03 17:47:14.913 INFO 19480 --- [nio-1112-exec-5] c.n.e.registry.AbstractInstanceRegistry : Registered instance RIBBON-CONSUMER/loc
alhost:ribbon-consumer:9000 with status UP (replication=true)
一个 replication=false 另一个 replication=true . 说明peer2是通过peer1同步过来的
- 服务续约(renew)
#定义服务续约任务的调用间隔时间
eureka.instance.lease-renewal-interval-in-seconds=30
#定义服务失效的时间
eureka.instance.lease-expiration-duration-in-seconds=90
-
服务消费者
有Ribbon 和 Feign
很多时候,客户端即使服务提供者也是服务消费者- 获取服务
确保eureka.client.fetch-registry
为true
修改缓存清单的更新时间eureka.client.registry-fetch-interval-seconds=30
- 服务调用
Ribbon会默认采取轮询的方式进行调用
对访问实例的选择,Eureka有 Region 和 Zone 的概念- 一个Region包含多个Zone
- 在服务调用的时候,优先访问同一个Zone中的服务提供方
- 服务下线
- 获取服务
源码分析
@EnableDiscoveryClient
image.png
EurekaDiscoveryClient
EndpointUtils.getServiceUrlsFromConfig
-->eureka.client.service-url.defaultZone=
eureka.client.region=
指定regioneureka.client.availability-zones=
指定zone通过对Zone的定义,配合实际部署的物理结构,可以设计出对区域性故障的容错集群
服务注册
服务获取
服务续约
服务注册中心
InstanceRegistry.register
配置详解
- Eureka服务端的配置可查看
EurekaServerConfigBean
- Eureka客户端的配置
服务注册类配置
参数名 | 说明 | 默认值 |
---|---|---|
registryFetchIntervalSeconds | Indicates how often(in seconds) to fetch the registry information from the eureka server | 30 |
instanceInfoReplicationIntervalSeconds | Indicates how often(in seconds) to replicate instance changes to be replicated to the eureka server | 30 |
initialInstanceInfoReplicationIntervalSeconds | Indicates how long initially (in seconds) to replicate instance info to the eureka server | 40 |
eurekaServiceUrlPollIntervalSeconds | Indicates how often(in seconds) to poll for changes to eureka server information.Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it. | 300 |
eurekaServerReadTimeoutSeconds | Indicates how long to wait (in seconds) before a read from eureka server needs to timeout. | 8 |
eurekaServerConnectTimeoutSeconds | Indicates how long to wait (in seconds) before a connection to eureka server needs to timeout | 5 |
eurekaServerTotalConnections | Gets the total number of connections that is allowed from eureka client to all eureka servers. | 200 |
eurekaServerTotalConnectionsPerHost | Gets the total number of connections that is allowed from eureka client to a eureka server host. | 50 |
eurekaConnectionIdleTimeoutSeconds | Indicates how much time (in seconds) that the HTTP connections to eureka server can stay idle before it can be closed | 30 |
heartbeatExecutorThreadPoolSize | The thread pool size for the heartbeatExecutor to initialise with | 2 |
heartbeatExecutorExponentialBackOffBound | Heartbeat executor exponential back off related property. It is a maximum multiplier value for retry delay, in case where a sequence of timeouts occurred.心跳超时延迟时间的最大乘数值 | 10 |
cacheRefreshExecutorThreadPoolSize | The thread pool size for the cacheRefreshExecutor to initialise with | 2 |
cacheRefreshExecutorExponentialBackOffBound | Cache refresh executor exponential back off related property. It is a maximum multiplier value for retry delay, in case where a sequence of timeouts occurred缓存刷新重试 延迟时间的最大乘数值 | 10 |
useDnsForFetchingServiceUrls | Indicates whether the eureka client should use the DNS mechanism to fetch a list of eureka servers to talk to | false |
registerWithEureka | Indicates whether or not this instance should register its information with eureka server for discovery by others. | true |
preferSameZoneEureka | Indicates whether or not this instance should try to use the eureka server in the same zone for latency and/or other reason | true |
filterOnlyUpInstances | Indicates whether to get the applications after filtering the applications for instances with only InstanceStatus UP states | true |
fetchRegistry | Indicates whether this client should fetch eureka registry information from eureka server. | true |
EurekaClientConfigBean
服务注册中心加入安全校验
参数名 | 说明 | 默认值 |
---|---|---|
registryFetchIntervalSeconds | Indicates how often(in seconds) to fetch the registry information from the eureka server | 30 |
instanceInfoReplicationIntervalSeconds | Indicates how often(in seconds) to replicate instance changes to be replicated to the eureka server | 30 |
initialInstanceInfoReplicationIntervalSeconds | Indicates how long initially (in seconds) to replicate instance info to the eureka server | 40 |
eurekaServiceUrlPollIntervalSeconds | Indicates how often(in seconds) to poll for changes to eureka server information.Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it. | 300 |
eurekaServerReadTimeoutSeconds | Indicates how long to wait (in seconds) before a read from eureka server needs to timeout. | 8 |
eurekaServerConnectTimeoutSeconds | Indicates how long to wait (in seconds) before a connection to eureka server needs to timeout | 5 |
eurekaServerTotalConnections | Gets the total number of connections that is allowed from eureka client to all eureka servers. | 200 |
eurekaServerTotalConnectionsPerHost | Gets the total number of connections that is allowed from eureka client to a eureka server host. | 50 |
eurekaConnectionIdleTimeoutSeconds | Indicates how much time (in seconds) that the HTTP connections to eureka server can stay idle before it can be closed | 30 |
heartbeatExecutorThreadPoolSize | The thread pool size for the heartbeatExecutor to initialise with | 2 |
heartbeatExecutorExponentialBackOffBound | Heartbeat executor exponential back off related property. It is a maximum multiplier value for retry delay, in case where a sequence of timeouts occurred.心跳超时延迟时间的最大乘数值 | 10 |
cacheRefreshExecutorThreadPoolSize | The thread pool size for the cacheRefreshExecutor to initialise with | 2 |
cacheRefreshExecutorExponentialBackOffBound | Cache refresh executor exponential back off related property. It is a maximum multiplier value for retry delay, in case where a sequence of timeouts occurred缓存刷新重试 延迟时间的最大乘数值 | 10 |
useDnsForFetchingServiceUrls | Indicates whether the eureka client should use the DNS mechanism to fetch a list of eureka servers to talk to | false |
registerWithEureka | Indicates whether or not this instance should register its information with eureka server for discovery by others. | true |
preferSameZoneEureka | Indicates whether or not this instance should try to use the eureka server in the same zone for latency and/or other reason | true |
filterOnlyUpInstances | Indicates whether to get the applications after filtering the applications for instances with only InstanceStatus UP states | true |
fetchRegistry | Indicates whether this client should fetch eureka registry information from eureka server. | true |
服务实例类配置
EurekaInstanceConfigBean
以eureka.instance
开头
元数据
InstanceInfo
中的private volatile Map<String, String> metadata = new ConcurrentHashMap<String, String>()
是自定义元数据信息,其他的标准化的元数据信息
- 配置自定义元数据
#自定义元数据配置
eureka.instance.metadata-map.zone=shanghai
- 配置标准化元数据
eureka.instance.<EurekaInstanceConfigBean中的属性> = <value>
实例名配置
随机端口配置
eureka.instance.instance-id=${spring.application.name}:${random.int}
上面配置能够实现在同一台主机上,不指定端口就能启动多个实例的效果
端点配置
确保/info和/health是有效的
1.当修改了management.context-path
时需要修改端点
# 端点配置
management.context-path=/hello
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health
- 为了安全考虑
endpoints.info.path=appInfo
endpoints.health.path=healthInfo
eureka.instance.statusPageUrlPath=/${endpoints.info.path}
eureka.instance.healthCheckUrlPath=/${endpoints.health.path}
- HTTPS的端点配置
#HTTPS的端点配置
eureka.instance.status-page-url=https://${eureka.instance.hostname}/info
eureka.instance.health-check-url=https://${eureka.instance.hostname}/health
eureka.instance.home-page-url=https://${eureka.instance.hostname}
健康检测
心跳方式的健康检查不能说明服务是可用的,因为服务可能还依赖一些数据库等,所以我们需要用/health端点来实现更全面的健康维护,步骤如下:
- 添加jar包至服务提供者
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 在application.properties中添加配置
# 健康检查
#eureka.client.healthcheck.enabled=true
- 保证/health端点路径是有效的
就是确保eureka.instance.health-check-url
是有效访问的,具体可参考上面的端点配置
网友评论