注册中心是微服务构架的通信录,记录服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。注册中心一般有三种角色,他们之间相互交互,如图:
这里我们讲解一下CAP原则:
CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。要么AP,要么CP,要么AC,但是不存在CAP(AC情况好像很少)。
- 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
- 可用性(A):保证每个请求不管成功或者失败都有响应。
- 分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。
取舍策略
CAP 三个特性只能满足其中两个,那么取舍的策略就共有三种:
总结
现如今,对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,节点只会越来越多,所以节点故障、网络故障是常态,因此分区容错性也就成为了一个分布式系统必然要面对的问题。那么就只能在 C 和 A 之间进行取舍。但对于传统的项目就可能有所不同,拿银行的转账系统来说,涉及到金钱的对于数据一致性不能做出一丝的让步,C 必须保证,出现网络故障的话,宁可停止服务,可以在 A 和 P 之间做取舍。而互联网非金融项目普遍都是基于 AP 模式。
总而言之,没有最好的策略,好的系统应该是根据业务场景来进行架构设计的,只有适合的才是最好的。
了解了什么是注册中心,那么我们继续谈谈,为什么需要注册中心。在分布式系统中,我们不仅仅是需要在注册中心找到服务和服务地址的映射关系这么简单,我们还需要考虑更多更复杂的问题:
这些问题的解决都依赖于注册中心。简单看,注册中心的功能有点类似于 DNS 服务器或者负载均衡器,而实际上,注册中心作为微服务的基础组件,可能要更加复杂,也需要更多的灵活性和时效性。所以我们还需要学习更多 Spring Cloud 微服务组件协同完成应用开发。
注册中心解决了以下问题:
Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 Spring Cloud Netflix 中,实现 Spring Cloud 的服务注册与发现,同时还提供了负载均衡、故障转移等能力。
Eureka Server
通过 Register、Get、Renew 等接口提供服务的注册和发现。
Service Provider(Eureka Client)
服务提供方,把自身的服务实例注册到 Eureka Server 中。同时拉取服务列表,消费其他服务。
Service Consumer(Eureka Client)
服务调用方,通过 Eureka Server 获取服务列表,消费服务。
导入Eureka服务端、web模块依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
# 是否将自己注册到注册中心,默认为 true
register-with-eureka: false
# 是否从注册中心获取服务注册信息,默认为 true
fetchRegistry: false
# 注册中心对外暴露的注册地址
service-url:
defaultZone: http://localhost:7001/eureka
@SpringBootApplication
//Eureka服务端注解
@EnableEurekaServer
public class Eureka7001 {
public static void main(String[] args) {
SpringApplication.run(Eureka7001.class,args);
}
}
访问http://localhost:7001
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
server:
port: 8003
spring:
application:
name: provider8003
eureka:
client:
# 是否将自己注册到注册中心,默认为 true
register-with-eureka: true
# 是否从注册中心获取服务注册信息,默认为 true
fetchRegistry: true
service-url:
# 注册中心对外暴露的注册地址
defaultZone: http://localhost:7001/eureka
@SpringBootApplication
//指明该服务为Eureka客户端
@EnableEurekaClient
public class Provider8003 {
public static void main(String[] args) {
SpringApplication.run(Provider8003.class,args);
}
}
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "Hello,Eureka";
}
}
启动访问http://localhost:8003/hello
可以发现注册中心已经注册该服务
注意:这行红字先不用管,这里是Eureka的自我保护机制,后续章节七会讲。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
dependencies>
server:
port: 8004
spring:
application:
name: Consumer8004
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
开启Eureka客户端
@SpringBootApplication
@EnableEurekaClient
public class Consumer8004 {
public static void main(String[] args) {
SpringApplication.run(Consumer8004.class,args);
}
}
@Configuration
public class ResConfig {
@Bean
//负载均衡注解
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
①PROVIDER8003:服务提供者的名称,其在注册中心上注册,可以通过该名称找到对应ip地址;
②restTemplate.getForObject(”远程调用服务ip地址“,“调用服务方法返回类型”)
@RestController
public class HelloController {
//调用服务地址
private final static String url="http://PROVIDER8003";
@Autowired
RestTemplate restTemplate;
@GetMapping("consumer")
public String hello(){
return restTemplate.getForObject(url+"/hello",String.class);
}
}
①启动Eureka7001
②启动服务提供者Provider8003
③启动服务消费者Consumner8004
④访问http://loaclhost:8004/hello
结果如下:
还记得第四章搭建服务提供者后访问Eureka网页时出现的那两行红色警示吗,那个就是eureka的自我保护。
一般情况下,服务在 Eureka 上注册后,会每 30 秒发送心跳包,Eureka 通过心跳来判断服务是否健康,同时会定期删除超过 90 秒没有发送心跳的服务。
有两种情况会导致 Eureka Server 收不到微服务的心跳:
自我保护模式
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,同时提示一个警告。这种算法叫做 Eureka Server 的自我保护模式。
注册中心配置自我保护
eureka:
server:
enable-self-preservation: false # true:开启自我保护模式,false:关闭自我保护模式
eviction-interval-timer-in-ms: 60000 # 清理间隔(单位:毫秒,默认是 60*1000)