SpringCloud 2020笔记
代码链接:码云
- hosts文件添加以下内容
修改hosts文件后不重启电脑的方法
一、Eureka梳理
- 服务注册
- 服务调用
- 负载均衡
1、注册中心
① pom文件
<!-- 服务注册中心的服务端 eureka-server -->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
② yml文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com # eureka服务端的实例名称
client:
# false 表示不向注册中心注册自己
register-with-eureka: false
# false 表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址
# http://${eureka.instance.hostname}:${server.port}
defaultZone: http://eureka7002.com:7002/eureka/
③ 启动类
2、服务提供方
① pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
② yml文件
eureka:
client:
# 表示将自己注册到eureka注册中心
register-with-eureka: true
# 是否从Eureka Server中抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合Ribbon负载均衡
fetch-registry: true
service-url:
# defaultZone: http://localhost:7001/eureka/ 单机
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
instance:
instance-id: payment8001
prefer-ip-address: true # 展示ip
③ 启动类
3、服务消费方
① pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
② yml
eureka:
client:
# true表示将自己注册到eureka注册中心
register-with-eureka: false
# 是否从Eureka Server中抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合Ribbon负载均衡
fetch-registry: true
service-url:
# defaultZone: http://localhost:7001/eureka/ 单机
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
③ 启动类
④ 负载均衡
4、服务发现,在服务提供方添加
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@RequestMapping("/paymentController")
@RestController
public class PaymentController {
@Value(value = "${server.port}")
private String serverPort;
@Resource
private DiscoveryClient discoveryClient;
/**
* 获取服务信息
* @return
*/
@GetMapping(value = "/discovery")
public Object discovery(){
// 获取eureka所有服务
List<String> services = discoveryClient.getServices();
for(String service : services){
log.info("service==>" + service);
}
// 根据服务id获取对应的服务列表: spring.application.name
List<ServiceInstance> serviceInstancesList = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for(ServiceInstance instance : serviceInstancesList){
log.info(instance.getUri() + "\t" + instance.getHost() + "\t" + instance.getPort());
}
return this.discoveryClient;
}
}
5、Eureka自我保护
- 默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区发生故障(延时、卡顿、拥挤)时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了—因为微服务本身其实是健康的,此时本不应该注销该微服务。Eureka通过“自我保护模式”来解决这个问题—当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个阶段就会进入自我保护模式。
- 关闭自我保护(Eureka Server端)
Eureka默认开启自我保护,可通过配置关闭eureka.server.enable-self-preservation = false
- 客户端(服务提供者)设置心跳时间
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认30秒)
eureka.instance.lease-renewal-interval-in-seconds = 1
# Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认90秒),超时Eureka Server将剔除该服务
eureka.instance.lease-expiration-duration-in-seconds = 2
二、Zookeeper & Consul
1、Zookeeper
(1) zookeeper作为注册中心,在linux中安装
Zookeeper和Conusl作为注册中心集成到SpringBoot中相对Eureka较为简单,只需要将Zookeeper、Consul安装好,在代码中将服务提供方注册到zookeeper、consul即可
- 安装环境
使用VMWare Workstation 虚拟机安装
系统:CentOS-6.8-x86_64-bin-DVD1.iso
JDK:jdk-8u301-linux-x64.tar.gz
Zookeeper:zookeeper-3.4.13.tar.gz
- 安装教程
教程
(2)服务提供方
① pom文件
添加zookeeper所需依赖
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的zookeeper3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.13版本(与虚拟机中安装的zookeeper版本保持一致)-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
② yml文件
服务名、zookeeper地址
# 服务别名,将provider注册到zookeeper
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
# zookeeper地址
connect-string: 192.168.21.128:2181
③ 启动类
添加注解:@EnableDiscoveryClient
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Description
* @Author xzkui
* @Date 2021/9/2 18:30
**/
@SpringBootApplication
@EnableDiscoveryClient // 当使用consul或zookeeper作为注册中心时,该注解用于向注册中心注册服务
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class, args);
}
}
④ 最终效果
服务方启动后,会注册到zookeeper中,可在虚拟机中查看
使用 ls / 查看zookeeper所有节点,发现图中有一个services节点
查看services节点 ls /services,其中cloud-provider-payment即注册到zookeeper的微服务
(3) 服务消费方
① pom文件(与服务提供方添加的依赖一致)
添加zookeeper所需依赖
<!-- SpringBoot整合zookeeper客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!--先排除自带的zookeeper3.5.3-->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.13版本(与虚拟机中安装的zookeeper版本保持一致)-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
② yml文件
添加zookeeper配置
# 服务别名,将provider注册到zookeeper
spring:
application:
name: cloud-consumer-order
cloud:
zookeeper:
connect-string: 192.168.21.128:2181
③ 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Description
* @Author xzkui
* @Date 2021/9/2 19:15
**/
@SpringBootApplication
@EnableDiscoveryClient
public class OrderMainZk80 {
public static void main(String[] args) {
SpringApplication.run(OrderMainZk80.class, args);
}
}
④ 最终效果
查看步骤参考服务提供方
2、Consul
(1) consul作为注册中心,在windows下安装
-
下载下来的是一个压缩包,解压后只有一个.exe文件
-
解压后在.exe文件所在目录进入cmd,输入:consul agent -dev 启动consul,启动成功打印如下:
-
在浏览器访问consul界面查看服务
默认地址:localhost:8500
(2)服务提供方
① pom文件
添加consul所需依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
② yml文件
spring:
application:
name: consul-provider-payment
# consul 服务注册地址
cloud:
consul:
host: localhost
port: 8500
discovery:
③ 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Description
* @Author xzkui
* @Date 2021/9/2 19:48
**/
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMainConsul8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMainConsul8006.class, args);
}
}
④ 最终效果
启动服务后可在consul管理界面查看
(3)服务消费方
① pom文件
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
② yml文件
spring:
application:
name: consul-consumer-order
# consul 服务注册地址
cloud:
consul:
host: localhost
port: 8500
discovery:
③ 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Description
* @Author xzkui
* @Date 2021/9/2 19:59
**/
@SpringBootApplication
@EnableDiscoveryClient
public class ConsulConsumerMain80 {
public static void main(String[] args) {
SpringApplication.run(ConsulConsumerMain80.class, args);
}
}
④ 最终效果
在consul管理界面查看
3、注册一个RestTemplate,用于服务调用、负载均衡
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @Description
* @Author xzkui
* @Date 2021/9/2 20:02
**/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
三、OpenFeign
Open Feign作为服务消费方调用服务提供方相关接口(此处使用eureka作为服务注册中心)
1、服务消费方pom文件添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、服务消费方yml配置文件添加配置
server:
port: 80
spring:
application:
name: cloud-feign-order-service
eureka:
client:
# true表示将自己注册到eureka注册中心
register-with-eureka: false
service-url:
# defaultZone: http://localhost:7001/eureka/ 单机
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下, 两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
logging:
level:
# feign日志以 debug 级别监控 com.atguigu.springcloud.service.FeignOrderService 接口
com.atguigu.springcloud.service.FeignOrderService: debug
3、服务消费方启动类
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // 启动feign
public class FeignOrderMain80 {
public static void main(String[] args) {
SpringApplication.run(FeignOrderMain80.class, args);
}
}
4、添加feign接口
import com.atguigu.springcloud.config.FeignFallBack;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @Description
* @Date 2021/9/4 19:08
* @Author xzkui
**/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE", fallback = FeignFallBack.class)
@RequestMapping("/paymentController")
public interface FeignOrderService {
/**
* 添加一条数据
* @param payment
* @return
*/
@PostMapping(value = "/create")
int create(Payment payment);
/**
* 根据id查找数据
* @param id
* @return
*/
@GetMapping(value = "/getPaymentById")
CommonResult getPaymentById(@RequestParam("id") Long id);
}
5、controller代码
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.service.FeignOrderService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Description
* @Date 2021/9/4 19:12
* @Author xzkui
**/
@RestController
@RequestMapping("/feignOrderController")
public class FeignOrderController {
@Resource
private FeignOrderService feignOrderService;
@GetMapping(value = "/getPaymentById")
public CommonResult getPaymentById(@RequestParam("id") Long id){
return feignOrderService.getPaymentById(id);
}
}
6、配置open feign日志
(1)全局
package com.atguigu.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description
* @Date 2021/9/4 19:56
* @Author xzkui
**/
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
(2)针对某个类,在配置文件中配置
logging:
level:
# feign日志以 debug 级别监控 com.atguigu.springcloud.service.FeignOrderService 接口
com.atguigu.springcloud.service.FeignOrderService: debug
7、open feign 超时机制
OpenFeign默认等待时间是1秒,超过1秒,直接报错
(1)设置超时时间,修改配置文件:
因为OpenFeign的底层是ribbon进行负载均衡,所以它的超时时间是由ribbon控制
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下, 两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
四、Hystrix
- 服务降级:某个服务被调用时出现异常或者超时,返回一个友好结果提示,避免用户一直等待;
- 服务熔断:
- 当某个服务出现异常或者超时次数达到了设定的断路要求时,将拒绝所有请求该服务的请求,断路器打开,并进入一个休眠期,期间使用服务降级返回一个友好结果提示;
- 休眠期到期后,断路器进入半开状态,会释放少量请求到原来的服务,如果请求正常,断路器将会关闭,如果请求再次出现异常,则断路器再次打开,并进入休眠期;
- 服务限流
1、服务降级
- 服务降级一般都在服务消费方进行配置
- 消费方调用的服务超时,进行服务降级
- 消费方调用的服务在调用过程中宕机,进行服务降级
- 消费方调用的服务返回数据正常,消费方出现故障或者超时,进行服务降级
- eureka作为注册中心、openfeign进行服务调用、hystrix服务降级
(1)pom文件
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
(2)yml文件
eureka:
client:
# true表示将自己注册到eureka注册中心
register-with-eureka: false
service-url:
# defaultZone: http://localhost:7001/eureka/ 单机
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
(3)启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @Description
* @Date 2021/9/5 19:35
* @Author xzkui
**/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class ConsumerFeignHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(ConsumerFeignHystrixMain80.class, args);
}
}
(4)openfeign服务调用
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @Description
* @Date 2021/9/4 19:08
* @Author xzkui
**/
@Component
@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX-SERVICE")
@RequestMapping("/hystrixProviderController")
public interface FeignOrderService {
/**
* hystrix正确调用接口
* @param id
* @return
*/
@GetMapping("/paymentOkInfo")
String paymentOkInfo(@RequestParam("id") Integer id);
/**
* hystrix超时接口
* @param id
* @return
*/
@GetMapping("/paymentTimeOutInfo")
String paymentTimeOutInfo(@RequestParam("id") Integer id);
/**
* 服务熔断
* @param id
* @return
*/
@GetMapping("/paymentCircuitBreaker")
String paymentCircuitBreaker(@RequestParam("id") Integer id);
}
(5)controller
import com.atguigu.springcloud.service.FeignOrderService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description
* @Date 2021/9/5 19:37
* @Author xzkui
**/
@RestController
@Slf4j
@RequestMapping("/feignHystrixController")
@DefaultProperties(defaultFallback = "globalFallbackMethod")
public class OrderFeignHystrixController {
@Value("${server.port}")
private String serverPort;
@Resource
private FeignOrderService feignOrderService;
@GetMapping("/paymentOkInfo")
@HystrixCommand(fallbackMethod = "fallBackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentOkInfo(@RequestParam("id") Integer id){
String result = feignOrderService.paymentOkInfo(id);
return result;
}
@GetMapping("/paymentTimeOutInfo")
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentTimeOutInfo(@RequestParam("id") Integer id){
int a = id / 0;
String result = feignOrderService.paymentTimeOutInfo(id);
return result;
}
public String fallBackMethod(@RequestParam("id") Integer id){
return id + "fallback at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
public String globalFallbackMethod(){
return "this is global fallback method !";
}
/**
* 服务熔断
* @param id
* @return
*/
@GetMapping("/paymentCircuitBreaker")
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentCircuitBreaker(@RequestParam("id") Integer id){
return feignOrderService.paymentCircuitBreaker(id);
}
}
(6)服务降级注解
① @HystrixCommand
- 作用于用户所调用的方法
- fallbackMethod 指定在服务降级时调用的方法;
- commandProperties 可用于配置服务降级触发条件等配置;
② @DefaultProperties
- 作用于类,用于指定全局默认的服务降级出发后执行的fallbackmethod;
- 未添加@HystrixCommand注解的接口不会调用全局指定的fallbackmethod
③ 区别
- 如果使用fallbackmethod指定了方法则不会执行全局默认的fallbackmethod
- 使用了@HystrixCommand注解但未指定fallbackmethod则会执行全局默认的fallbackmethod
2、服务熔断
- 服务熔断一般作用与服务提供方,是应对雪崩效应的一种微服务链路保护机制
- 类比与保险丝,当失败的调用达到一定的阈值时,会触发熔断机制 ;
- 熔断机制触发后,进入休眠期,拒绝所有访问请求,并使用服务降级返回提示;
- 休眠期过后,释放少量请求(半开状态),如果请求正常,关闭熔断机制,反之重新触发熔断机制;
- eureka作为服务注册、hystrix服务熔断
(1)pom文件
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(2)yml文件
spring:
application:
name: cloud-payment-hystrix-service # 微服务名称
eureka:
client:
# 表示将自己注册到eureka注册中心
register-with-eureka: true
# 是否从Eureka Server中抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合Ribbon负载均衡
fetch-registry: true
service-url:
# defaultZone: http://localhost:7001/eureka/ 单机
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
(3)启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @Description
* @Date 2021/9/5 17:40
* @Author xzkui
**/
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
// 开启断路器功能
@EnableCircuitBreaker
public class HystrixPaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixPaymentMain8001.class, args);
}
}
(4)服务熔断使用
① controller
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.service.HystrixPaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Description
* @Date 2021/9/5 17:45
* @Author xzkui
**/
@RestController
@Slf4j
@RequestMapping("/hystrixProviderController")
public class HystrixProviderController {
@Resource
private HystrixPaymentService hystrixPaymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/paymentOkInfo")
public String paymentOkInfo(@RequestParam("id") Integer id){
String result = hystrixPaymentService.paymentOk(id);
log.info("result ==> " + result);
return result;
}
@GetMapping("/paymentTimeOutInfo")
public String paymentTimeOutInfo(@RequestParam("id") Integer id){
String result = hystrixPaymentService.paymentTimeOut(id);
log.info("result ==> " + result);
return result;
}
@GetMapping("/paymentCircuitBreaker")
public String paymentCircuitBreaker(@RequestParam("id") Integer id){
return hystrixPaymentService.paymentCircuitBreaker(id);
}
}
② Service
/**
* 服务熔断:此处的配置是,10请求中,超出60%的请求出现问题,则会熔断,熔断后即使请求正常也不会正常返回
* 开启 -> 半开 -> 关闭
* @return
*/
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否开启熔断器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60") // 失败率达到多少后跳闸60%
})
@Override
public String paymentCircuitBreaker(Integer id){
if(id > 0){
throw new RuntimeException("test");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreakerFallback(Integer id){
System.out.println("该服务已熔断,开启服务降级!!");
return "该服务已熔断,开启服务降级!!";
}
(5)服务熔断注解
- 注解使用与服务降级基本一致,不同的是需要通过配置属性打开熔断器,并配置触发条件
- 如何查看属性:通过查看
HystrixCommandProperties
,是一个抽象类,记录了需要配置的属性
(6)熔断类型
- 熔断打开:请求不在进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时间,则进入半熔断状态;
- 熔断关闭:熔断关闭后,服务正常调用;
- 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则,则认为服务恢复正常,关闭熔断;
(7)整体流程
1、请求进来,首先查询缓存,如果缓存有,直接返回
如果缓存没有,--->2
2、查看断路器是否开启,如果开启的,Hystrix直接将请求转发到降级返回,然后返回;如果断路器是关闭的,判断线程池等资源是否已经满了,如果已经满了,也会走降级方法;如果资源没有满,判断我们使用的什么类型的Hystrix,决定调用构造方法还是run方法,然后处理请求;
3、然后Hystrix将本次请求的结果信息汇报给断路器,因为断路器此时可能是开启的 (因为断路器开启也是可以接收请求的);断路器收到信息,判断是否符合开启或关闭断路器的条件,
4、如果本次请求处理失败,又会进入降级方法; 如果处理成功,判断处理是否超时,如果超时了,也进入降级方法,最后,没有超时,则本次请求处理成功,将结果返回给controller