spring-cloud微服务之【服务降级】【Hystrix】--持续更新

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

为什么需要服务降级?

在分布式架构服务中,服务的调用链路非常的长,这样就会导致服务与服务之间的连接关系越来越复杂。为了保证服务的稳定运行,防止(服务雪崩)雪崩效应。

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

瞬间把服务器的CPU或者内存直接打满。导致服务器内存溢出或者宕机的情况。那么这个时候就需要一种链路中断或者做好服务降级。

Hystrix是什么东东?

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

Hystrix能够提供什么样的功能?

  • 服务降级 fallback
  • 服务熔断 break
  • 服务限流 flowlimit

访问github地址查看如何使用: github.com/Netflix/Hystrix/wiki/How-To-Use

项目实战

pom.xml引入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-client</artifactId>
        <groupId>com.wolf.boy</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-cloud-consumer-hystrix-client</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--额外增加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.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

然后对应的服务注册中心还是连接到Eureka,所有引入了eureka-client客户端

yml配置

  • 服务调用者
############对应hystrix并没有增加额外的配置信息######
#下面这些配置信息都是上一章openFeign引入的
server:
  port: 7773
spring:
  application:
    name: spring-cloud-consumer-hystrix-client

#设置openFeign的超时时间
ribbon:
  ReadTimeout: 1000
  ConnectTimeout: 1000

#Eureka服务提供者配置
eureka:
  client:
    #false表示不向注册中心注册自己(你自己就是一个eureka服务,没必要把自己注册进去了)
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    #这个主要结合discoveryClient使用
    fetch-registry: true
    service-url:
      #单机版
      defaultZone: http://localhost:9999/eureka

#用于服务降级,在注解@FeignClient中添加fallbackFactory属性值
feign:
  hystrix:
    enabled: true #开启对hystrix的支持

新增配置

feign:
  hystrix:
    enabled: true #开启对hystrix的支持
  • 服务提供者
############对应hystrix并没有增加额外的配置信息######
#下面这些配置信息都是上一章openFeign引入的
server:
  port: 7773
spring:
  application:
    name: spring-cloud-consumer-hystrix-client

#设置openFeign的超时时间
ribbon:
  ReadTimeout: 1000
  ConnectTimeout: 1000

#Eureka服务提供者配置
eureka:
  client:
    #false表示不向注册中心注册自己(你自己就是一个eureka服务,没必要把自己注册进去了)
    register-with-eureka: false
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    #这个主要结合discoveryClient使用
    fetch-registry: true
    service-url:
      #单机版
      defaultZone: http://localhost:9999/eureka

主启动类

  • 客户端
@SpringBootApplication
@EnableEurekaClient
//启用feign客户端
@EnableFeignClients
//开启熔断
@EnableHystrix
public class RunClientMain {
    public static void main(String[] args) {
        SpringApplication.run(RunClientMain.class,args);
    }
}
  • 服务提供者
@SpringBootApplication
@EnableEurekaClient
//启用feign客户端
@EnableFeignClients
//开启回路
@EnableCircuitBreaker
public class RunClientMain {
    public static void main(String[] args) {
        SpringApplication.run(RunClientMain.class,args);
    }
}

服务降级代码

  • 简单版
在客户端的controller代码里面


@RequestMapping("consumer/openFeign/getTimeout")
//使用hystrix,并且设置了fallback降级对应调用的方法,然后配置了一个请求超时的设置
@HystrixCommand(fallbackMethod = "fallbackMethod",commandProperties = {
		@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public JsonResult getUserTimeOut() {
	JsonResult obj = demoService.getUserTimeOut();
	System.out.println(obj);
	return obj;
}

JsonResult fallbackMethod() {
	return new JsonResult(-1, "接口响应超时,服务降级处理,返回默认提示");
}
  • 如果一个controller里面有很多方法都想用默认的,怎么办;这个时候需要有一个通用的或者默认的降级代码
@RestController
@RequestMapping("/user/V2/")
@DefaultProperties(defaultFallback = "fallbackMethod")
public class DemoController2 {

    @Autowired
    private DemoService demoService;

    @RequestMapping("consumer/openFeign/get")
    public JsonResult getOpenFeignUser() {
        JsonResult obj = demoService.getUser();
        System.out.println(obj);
        return obj;
    }

    @RequestMapping("consumer/openFeign/getTimeout")
    @HystrixCommand
    public JsonResult getUserTimeOut() {
        JsonResult obj = demoService.getUserTimeOut();
        System.out.println(obj);
        return obj;
    }

    JsonResult fallbackMethod() {
        return new JsonResult(-1, "接口响应超时,服务降级处理,返回默认提示");
    }
}

变化点
1、在请求的类上加入注解
@DefaultProperties(defaultFallback = "fallbackMethod")
并设置降级调用方法
2、在原controller里面的方法上,只需要设置@HystrixCommand即可,无需配置其他参数。
  • 对openFeign设置的接口的服务,设置默认的降级方法
@Component
//@FeignClient(value = "SPRING-CLOUD-EUREKA-PROVIDER-SERVER-01")
@FeignClient(value = "SPRING-CLOUD-EUREKA-PROVIDER-SERVER-01",fallback = DemoServiceFallBack.class)
public interface DemoService {

    /**
     * 通过springMvc的方式,指定服务提供者访问地址
     *
     *
     * @return
     */
    @RequestMapping("/user/get")
    public JsonResult getUser();

    @RequestMapping("/user/get/timeout")

    public JsonResult getUserTimeOut();

}


@Component
public class DemoServiceFallBack implements DemoService {

    @Override
    public JsonResult getUser() {
        return new JsonResult(0, "非常抱歉,基于DemoServiceFallBack实现,没有拿到默认内容");
    }

    @Override
    public JsonResult getUserTimeOut() {
        return new JsonResult(0, "非常抱歉,基于DemoServiceFallBack实现,没有拿到默认内容");
    }
}

服务熔断

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

代码讲解

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

/**
     * 熔断
     *
     * @return
     */
    @RequestMapping("get/timeoutRd")
    //使用hystrix,并且设置了fallback降级对应调用的方法,然后配置了一个请求超时的设置
    @HystrixCommand(fallbackMethod = "fallbackMethodRd", commandProperties = {
            //为什么配置信息该这么配置:看HystrixCommandProperties这个类
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启熔断服务
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//失败率达到多少后,进入熔断
    })
    public JsonResult getUserTimeOutRd(String val) {
        if (val == null) {
            long sleepTime = 3L;
            try {
                TimeUnit.SECONDS.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            Integer i = Integer.parseInt(val);
        }

        return new JsonResult(0, "从服务端口:" + serverPort + "获取用户信息!开始使用熔断策略,响应你" + val);
    }


    JsonResult fallbackMethodRd(String val) {
        return new JsonResult(-1, "从服务端口:" + serverPort + "服务端处理业务处理繁忙,返回默认!" + val + "格式有误");
    }

如果请求路径

http://localhost:8881/user/get/timeoutRd?val=ddss 不携带val值,那么请求conroller就拿到val=ddss,然后转int就出错了。如果错误率达到60%的时候,就熔断。这个时候就算给予正确的

http://localhost:8881/user/get/timeoutRd?val=1 请求,客户端输出的效果还是熔断效果。因为这个时候已经发生了熔断

http://localhost:8881/user/get/timeoutRd?val=ddss  把这个请求一直多来几次,让失败率大于60%,然后在访问http://localhost:8881/user/get/timeoutRd?val=1 

输出:

{"code":-1,"msg":null,"result":"从服务端口:8881服务端处理业务处理繁忙,返回默认!1格式有误"}
{"code":-1,"msg":null,"result":"从服务端口:8881服务端处理业务处理繁忙,返回默认!ddss格式有误"}

过几秒后,在访问:http://localhost:8881/user/get/timeoutRd?val=1 

{"code":0,"msg":null,"result":"从服务端口:8881获取用户信息!开始使用熔断策略,响应你1"}

看效果就是正确的响应。熔断后自动完成恢复!

结论:

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

所有配置说明:

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

spring-cloud微服务之【服务降级】【Hystrix】--持续更新

 

 

 

 

 

 

 

 

 

 

上一篇:业务项目自定义响应包装类和请求类


下一篇:新!Shiro自定义异常无法被捕获总是抛出AuthenticationException解决方案