服务治理:Spring Cloud Eureka
服务治理可以说是微服务架构中最为核心和基础的模块, 它主要用来实现各个微服务实例的自动化注册与发现
服务注册:在服务治理框架中, 通常都会构建一个注册中心, 每个服务单元向注册中心登记自己提供的服务, 将主机与端口号、 版本号、 通信协议等一些附加信息告知注册中心, 注册中心按服务名分类组织服务清单。
服务名 | 位置 |
---|---|
服务A | 192.168.0.100:8000,192.168.0.101:8000 |
服务B | 192.168.0.100:9000,192.168.0.101:9000,192.168.0.102:9000 |
服务发现:服务间的调用不再通过指定具体的实例地址来实现, 而是通过向 服务名 发起请求调用实现。调用方需要向服务注册中心咨询服务, 并获取所有服务的实例清单, 以实现对具体服务实例的访问。
Eueka服务端,我们也称为服务注册中心。它同其他服务注册中心一样,支持高可用配置
Eueka客户端,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式,嵌入在客户端应用程序的代码中。Eueka客户端向注册中心注册自身提供的服务并周期性地发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注册的服务信息并把它们缓存到本地并周期性地刷新服务状态。
搭建服务注册中心
引入Eureka-Server依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
</parent>
<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-test</artifactId>
<scope>test</scope>
</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注解启动一个注册中心
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
application.yml文件
eureka.client.fetch-registry:检索服务实例
eureka.client.register-with-eureka:将自己作为客户端注册
eureka.client.service-url.defaultZone:指定注册中心地址
server:
port: 1111
eureka:
instance:
hostname: ljh1
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://ljh2:2222/eureka/
spring:
profiles: ljh1
application:
name: ljh
---
server:
port: 2222
eureka:
instance:
hostname: ljh2
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://ljh1:1111/eureka/
spring:
profiles: ljh2
application:
name: ljh
注册服务提供者
spring.application.name:指定服务名
eureka.client.service-url.defaultZone:指定注册中心地址
server:
port: 8080
spring:
application:
name: hello-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://ljh1:1111/eureka/,http://ljh2:2222/eureka/
@RestController
public class HelloController {
@Autowired
private DiscoveryClient client;
@RequestMapping("/hello")
public String hello(){
ServiceInstance instance = client.getLocalServiceInstance();
System.out.println(instance.getHost()+" "+instance.getPort());
return "hello world";
}
}
@EnableDiscoveryClient:激活 Eureka 中的DiscoveryClient 实现
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
服务发现与消费
服务发现的任务由Eureka的客户端完成,而服务消费的任务由Ribbon完成。Ribbon是一个基与HTTP和TCP的客户端负载均衡器。
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</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>
application.yml
server:
port: 7081
spring:
application:
name: consumer1
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://ljh1:1111/eureka/,http://ljh2:2222/eureka/
@EnableDiscoveryClient:将该应用注册为Eureka客户端
@LoadBalanced:开启服务端负载均衡
@SpringBootApplication
@EnableDiscoveryClient
public class Application {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
通过RestTemplate指定服务名进行接口调用
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/consumer")
public String consumerHello(){
String result = restTemplate.getForEntity("http://hello-service/hello", String.class).getBody();
return result;
}
}
Eureka详解
基础架构
服务注册中心: Eureka提供的服务端, 提供服务注册与发现的功能
服务提供者:提供服务的应用, 可以是 Spring Boot 应用, 也可以是其他技术平台且遵循 Eureka通信机制的应用
服务消费者:消费者应用从服务注册中心获取服务列表, 从而使消费者可以知道去何处调用其所需要的服务
很多时候, 客户端既是服务提供者也是服务消费者
服务治理机制
服务提供者
服务注册
“服务提供者”在启动的时候会通过发送REST请求的方式将自己注册到Eureka Server 上, 同时带上了自身服务的一些元数据信息。Eureka Server接收到这个REST请求之后,将元数据信息存储在一个双层结构Map中, 其中第一层的key是服务名, 第二层的key是具体服务的实例名。
服务同步
配置Eureka Server高可用时,当服务提供者发送注册请求到一个服务注册中心时, 它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步 。通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心中的任意一台获取到
服务续约
在注册完服务之后,服务提供者会维护一个心跳用来持续告诉Eureka Server: “我还活着”,以防止Eureka Server的 “剔除任务 ”将该服务实例从服务列表中排除出去,我们称该操作为服务续约(Renew)。
关千服务续约有两个重要属性,我们可以关注并根据需要来进行调整:
#参数用于定义服务续约任务的调用间隔时间,默认为30秒。
eureka.instance.lease-renewal-interval-in-seconds=30
#用于定义服务失效的时间,默认为90秒
eureka.instance.lease-expiration-duration-in-seconds=90
服务消费者
获取服务
当我们启动服务消费者的时候,它会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。Eureka Server会维护一份只读的服务清单来返回给客户端,同时该缓存清单会每隔30秒更新一次
eureka.client.registry-fetch-interval-seconds=30
服务调用
服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息
在ribbon中会默认采用轮询的方式进行调用,从而实现客户端的负载均衡
对于访问实例的选择,Eureka中有Region和Zone的概念,一个Region中可以包含多个Zone
每个服务客户端需要被注册到一个Zone中, 所以每个客户端对应一个Region和一个Zone。在进行服务调用的时候,优先访问同处一个Zone中的服务提供方, 若访问不到,就访问其他的Zone
服务下线
在系统运行过程中必然会面临关闭或重启服务的某个实例的情况。当服务实例进行正常的关闭操作时, 它会触发一个服务下线的REST请求给Eureka Server。服务端在接收到请求之后, 将该服务状态置为下线(DOWN), 并把
该下线事件传播出去
服务注册中心
失效剔除
有些时候, 我们的服务实例并不一定会正常下线, 可能由于内存溢出、 网络故障等原
因使得服务不能正常工作, 而服务注册中心并未收到 “服务下线”的请求。为了从服务列表中将这些无法提供服务的实例剔除, EurekaServer在启动的时候会创建一个定时任务,默认每隔一段时间将当前清单中超时没有续约的服务剔除出去
自我保护
当我们在本地调试基于Eurka的程序时, 基本上都会碰到这样一个问题, 在服务注册中心的信息面板中出现红色警告信息。该警告就是触发了EurkaSrvr的自我保护机制。Eureka Server 在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%。Eureka Server会将当前的实例注册信息保护起来, 让这些实例不会过期, 尽可能保护这些注册信息。 但是, 在这段保护期间内实例若出现问题, 那么客户端很容易拿到实际已经不存在的服务实例, 会出现调用失败的清况, 所以客户端必须要有容错机制, 比如可以使用请求重试、 断路器等机制
常见设置
服务端设置
eureka:
server:
enable-self-preservation: false #服务端关闭保护模式
eviction-interval-timer-in-ms: 5000 #服务端每隔多少毫秒检查剔除客户端实例
客户端配置(服务端提供者)
eureka:
instance:
lease-renewal-interval-in-seconds: 5 #客户端每隔5秒续约服务,表明它任然还活着,默认30秒
lease-expiration-duration-in-seconds: 10 # 服务端间隔多久没收到心跳将此实例删除,默认90秒
客户端配置(服务消费者)
eureka:
client:
registry-fetch-interval-seconds: 10 #客户端每隔10秒刷新下服务提供者信息,默认30秒
logging:
level:
com.netflix: debug