SpringCloud微服务系列- 服务间通信之服务治理

SpringCloud微服务系列- 服务间通信之服务治理

  一、服务治理

  1. 为什么需要服务治理?

  在没有进行服务治理前,服务之间的通信是通过服务间直接相互调用来实现的。

  2. 在复杂的微服务系统中,服务间直接相互调用会产生什么问题?

  微服务系统中服务众多,这样会导致服务间的相互调用非常不便,因为要记住提供服务的IP地址、名称、端口号等。这时就需要中间代理,通过中间代理来完成调用。

  3. 服务治理的解决方案

  服务治理的责任:

  1)服务注册 - 服务提供方自报家门

  2)服务发现 - 服务消费者拉取注册数据

  3)心跳检测 - 服务续约和服务剔除,一套由服务提供方和注册中心配合完成的去伪存真的过程

  4)服务下线 - 服务提供方发起主动下线

  服务治理技术选型: Eureka、Consul、Nacos、Zookeeper

  二、服务治理-Eureka

  1. Eureka的介绍

  SpringCloud 封装了Netfix公司开发的Eureka模块来实现服务治理。

  Spring Cloud Eureka是Netfix 开发的注册发现组件,本身是一个基于REST的服务,提供注册与发现,同时还提供了负载均衡、故障转移等能力。

  Eureka有3个角色:

  服务中心: Eureka Server 它提供服务的注册和发现功能,即实现服务的治理。

  服务提供者: Service Provider。 它将自身服务注册到Eureka Server中,以便"服务消费者"能够通过服务器端提供的服务清单(注册服务列表)来调用它。

  服务消费者: Service Consumer。服务消费者。它从Eureka获取"已注册的服务列表",从而消费服务。

  三、Eureka的使用

  1. 搭建单机Eureka注册中心

  pom.xml

1     <dependencies>
2         <!--服务注册发现Eureka-->
3         <dependency>
4             <groupId>org.springframework.cloud</groupId>
5             <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
6         </dependency>
7     </dependencies>

  application.yml 

 1 server:
 2   port: 7001
 3 eureka:
 4   instance:
 5     # eureka服务端实例名字
 6     hostname: eureka7001.com
 7   client:
 8     # 表示是否将自己注册到eureka 服务上
 9     register-with-eureka: false
10     # 表示是否从eureka server 获取注册的服务信息
11     fetch-registry: false
12     # 设置与Eureka server.交互的地址查询服务和注册服务都需要依赖这个地址
13     service-url:
14       #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
15       defaultZone: http://localhost:7001/eureka/
16   server:
17     # 关闭服务保护机制
18     enable-self-preservation: false

  主启动类:

1 @EnableEurekaServer
2 @SpringBootApplication
3 @Slf4j
4 public class EurekaServerMain7001 {
5     public static void main(String[] args) {
6         SpringApplication.run(EurekaServerMain7001.class, args);
7         log.info("************ Eureka服务7001 启动成功 *****");
8     }
9 }

  2)创建服务提供者

  创建cloud-provider-payment8001模块

  application.yml

 1 server:
 2   port: 8001
 3 eureka:
 4   client:
 5     service-url:
 6       # 单机eureka 服务 地址
 7       defaultZone: http://localhost:7001/eureka/
 8   instance:
 9     instance-id: cloud-payment-provider8001
10 spring:
11   application:
12     # 设置应用名字
13     name: cloud-payment-provider

  主启动类:

 1 /**
 2  * 主启动类-把自己注册到eureka server上
 3  */
 4 @EnableEurekaClient
 5 @SpringBootApplication
 6 @Slf4j
 7 public class PaymentMain8001 {
 8     public static void main(String[] args) {
 9         SpringApplication.run(PaymentMain8001.class, args);
10         log.info("******  PaymentMain8001 服务启动成功 ****");
11     }
12

   业务逻辑:

 1 @RestController
 2 @RequestMapping("payment")
 3 public class PayController {
 4 
 5     /**
 6      * 测试服务调用
 7      * @return
 8      */
 9     @GetMapping("index")
10     public String index(){
11         return "payment success";
12     }
13 }

   2. Eureka集群的搭建(高可用eureka注册中心)

   在微服务架构这样的分布式环境中,我们需要充分考虑发生故障的情况,所以在生产环境中必须对各个组件进行高可用部署,对于微服务如此,对于服务注册中心也一样。

   Spring-Cloud为基础的微服务架构,所有的微服务都需要注册到注册中心,如果这个注册中心阻塞或者崩了,那么整个系统都无法继续正常提供服务,所以,这里就需要对注
册中心搭建,高可用(HA)集群。

   Eureka Server的设计一开始就考虑了高可用问题,在Eureka的服务治理设计中,所有节点即是服务提供方,也是服务消费方,服务注册中心也不例外。

   1) 服务中心节点7001

   application.yml 

 1 server:
 2   port: 7001
 3 eureka:
 4   instance:
 5     # eureka服务端实例名字
 6     hostname: eureka7001.com
 7   client:
 8     # 表示是否将自己注册到eureka 服务上
 9     register-with-eureka: false
10     # 表示是否从eureka server 获取注册的服务信息
11     fetch-registry: false
12     # 设置与Eureka server.交互的地址查询服务和注册服务都需要依赖这个地址
13     service-url:
14       #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
15       defaultZone: http://localhost:7002/eureka/
16   server:
17     # 关闭服务保护机制
18     enable-self-preservation: false

  2)服务中心节点7002

  在已有7001的节点上,增加节点7002

  application.yml

 1 server:
 2   port: 7002
 3 eureka:
 4   instance:
 5     # eureka服务端实例名字
 6     hostname: eureka7002.com
 7   client:
 8     # 表示是否将自己注册到eureka 服务上
 9     register-with-eureka: false
10     # 表示是否从eureka server 获取注册的服务信息
11     fetch-registry: false
12     # 设置与Eureka server.交互的地址查询服务和注册服务都需要依赖这个地址
13     service-url:
14       #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
15       defaultZone: http://localhost:7001/eureka/
16   server:
17     # 关闭服务保护机制
18     enable-self-preservation: false

  2)创建服务提供者

  cloud-provider-payment8001

  application.yml

 1 server:
 2   port: 8001
 3 eureka:
 4   client:
 5     service-url:
 6       defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
 7   instance:
 8     instance-id: cloud-payment-provider8001
 9 spring:
10   application:
11     # 设置应用名字
12     name: cloud-payment-provider

   3. 创建服务消费者

   cloud-consumer-order80

   application.yml

 1 server:
 2   port: 80
 3 eureka:
 4   client:
 5     service-url:
 6       # 指定单机eureka 服务 地址
 7       #defaultZone: http://localhost:7001/eureka/
 8       defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/
 9   instance:
10     # 实例名字
11     instance-id: cloud-order-consumer80
12 spring:
13   application:
14     # 设置应用名字
15     name: cloud-order-consumer

   主启动类启动后,Eureka UI界面为:

   

   6. 微服务通信实例

   这里先补充个知识点:RestTemplate

   RestTemplate是从Spring3.0开始支持的一个HTTP请求工具,它提供了常见的REST请求方案的模版,例如GET请求、POST请求、PUT请求、DELETE请求以及一些通用的请求执行方法exchange以及execute。

   RestTemplateConfig.class配置文件:

 1 /**
 2  * RestTemplate 放入Spring Ioc容器中
 3  */
 4 @Configuration
 5 public class RestTemplateConfig {
 6 
 7     @Bean
 8     public RestTemplate restTemplate(){
 9         return new RestTemplate();
10     }
11 }

    服务调用:

 1 @RestController
 2 @RequestMapping("order")
 3 public class OrderController {
 4 
 5     /**
 6      * 服务发现
 7      */
 8     @Autowired
 9     private DiscoveryClient discoveryClient;
10 
11     /**
12      * Http请求工具
13      */
14     @Autowired
15     private RestTemplate restTemplate;
16 
17 
18     /**
19      * 服务发现获取服务列表清单
20      *
21      * @return
22      */
23     @GetMapping("discovery")
24     public Object testDiscoveryClient() {
25         // 获取所有服务列表清单
26         for (String service : discoveryClient.getServices()) {
27             System.out.println(service);
28         }
29 
30         return this.discoveryClient;
31     }
32 
33     /**
34      * 测试服务调用
35      *
36      * @return
37      */
38     @GetMapping("index")
39     public String index() {
40         // 服务生产者名字
41         String hostname = "CLOUD-PAYMENT-PROVIDER";
42 
43         // 远程调用方法具体URL
44         String url = "/payment/index";
45 
46         // 1. 服务发现中获取服务生产者实例
47         List<ServiceInstance> instances = discoveryClient.getInstances(hostname);
48 
49         // 2.获取到具体实例 服务生产者实例
50         ServiceInstance serviceInstance = instances.get(0);
51 
52         System.out.println(serviceInstance.getUri());
53         System.out.println(serviceInstance.getPort());
54         System.out.println(serviceInstance.getHost());
55         System.out.println(serviceInstance.getServiceId());
56         System.out.println("===========================");
57 
58         // 3. 发起了远程调用
59         String rest = restTemplate.getForObject(serviceInstance.getUri() + url, String.class);
60         return rest;
61     }
62 }

   在web浏览器上访问url:localhost/order/index  就是一次完整的微服务通信

   三、Eureka注册中心UI界面

   1. System Status (重点)

  

   参数说明:

   1) Environment:环境,默认为test,该参数在实际使用过程中,可以不用更改

   2) Data center:数据中心,使用的是默认的是"default"

   3) Current time:当前的系统时间

   4) Uptime: Eureka 服务已经运行了多少时间

   5) Lease expiration enabled(服务自我保护)

   是否启用租约过期。自我保护机制关闭时该值默认是true,自我保护机制开启之后为false。

   自我保护机制开启时(false),此时Eureka Server会保护注册表中的信息,不再注销任何服务实例。在关闭自我保护机制后(true),Eureka Server将不再保护注册表信息,而是会根据实际情况注销不可用的服务实例。

   6) Renews threshold

   每分钟最少续约数,Eureka Server期望每分钟收到客户端实例续约的总数。

   7) Renews(last min)

   最后一分钟的续约数量(不含当前,1分钟更新一次), Eureka Server 最后1分钟收到客户端实例续约的总数。

   红字提醒

   系统在三种情况下会出现红色加粗的字体提示:

   a. 在配置上,自我保护机制关闭

   RENEWALS ARE LESSER THAN THE THRESHOLD. THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

   b. 自我保护机制开启了

   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.

   c. 在配置上,自我保护机制关闭了,但是一分钟内的续约数没有达到85% , 可能发生了网络分区,会有如下提示 

   THE SELF PRESERVATION MODE IS TURNED OFF.THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

   2. Eureka 集群

   

   3. 注册到Eureka服务上的实例信息  

   

   参数说明:

   1)Application:服务名称。配置的spring.application.name属性

   2)AMls: 字符串N/A + 实例的数量 

   3)Availability Zones: 实例的数量

   4)Status: 实例的状态+eureka.instance.instance-id的值。

   其中status参数: 

   实例的状态分为UP、DOWN、STARTING、OUT OF SERVICE、UNKNOWN

   UP:服务正常运行,特殊情况当进入自我保护模式,所有的服务依然是UP状态,所以需要做好熔断重试等容错机制应对灾难性网络出错情况

   OUT OF SERVICE: 不再提供服务,其他的Eureka Client将调用不到该服务,一般有人为的调用接口设置的,如:强制下线。

   UNKNOWN: 未知状态

   STARTING: 表示服务正在启动中

   DOWN: 表示服务已经宕机,无法继续提供服务

   4. Eureka服务端信息 

   

   ipAddr :eureka服务端ip地址

   status: eureka服务端状态

   四、Eureka与Zookeeper进行比较

   当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。

   也就是说,服务注册功能对可用性的要求要高于一致性。

   1)Zookeeper CP架构 保证一致性

   注意:Zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30-120s且
选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪 

   2)Eureka AP架构 保证可用性

   Eureka看在设计时就优先保证可用性。

   五、服务自保和服务剔除机制

   注册中心在同一时刻,只能使用服务剔除和服务自保中的其中一种机制

   1. 服务剔除

   服务剔除把服务节点果断剔除,即使你的续约请求晚了一步也被剔除了。

   2. 服务自保

   服务自保把当前所有节点保留,一个都不能少。

   在实际应用中,并不是所有无心跳的服务都不可用,也许因为短暂的网络抖动等原因,导致服务节点与注册中心之间续约不上。

   服务自保模式往往是为了应对短暂的网络环境问题,在理想情况下服务节点的续约成功率应该接近100%,如果突然发生网络问题,比如一部分机房无法连接到注册中心,这时候续约成功率有可能大幅降低。

   但考虑到Eureka采用客户端的服务发现模式,客户端中存有所有节点的地址,如果服务节点只是因为网络原因无法续约但其自身服务是可用的,那么客户端仍然可以成功发起调用请求。

   这样就避免了被服务剔除给错杀。

 

posted @ 2024-06-28 12:32  欢乐豆123  阅读(70)  评论(0编辑  收藏  举报