hystrix服务降级 服务熔断

1.hystrix是什么

image-20200811094738112

hystrix官宣停更

2.能做什么

image-20200811094805133

3.重要概念

3.1 服务降级

image-20200811094908169

3.2 服务熔断

image-20200811094935864

3.3服务限流

image-20200811095000972

4.服务降级 案例

4.1流程

创建一个单机版eureka服务注册中心 一个支付微服务(消费端) 和 一个订单微服务(客户端)

支付微服务注册进eureka 并且 订单微服务 通过openfeign来调用支付微服务

4.2创建eureka服务注册中心

pom

(版本控制都在父项目中,springboot版本 2.2.2.RELEASE springcloud版本 Hoxton.SR1 其他的自行选择)

 <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

yml

server:
  port: 7001

eureka:
  instance:
    hostname: localhost #eureka服务端的实例名称
  client:
    register-with-eureka: false     #false表示不向注册中心注册自己。
    fetch-registry: false     #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      #设置与Eureka server 交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://localhost:7001/eureka/ #单机版 指向自己

主启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * @author Xkuna
 * @date 2020/7/22 15:32.
 */

@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class, args) ;
    }
}

4.3创建支付微服务

4.3.1创建项目基础

pom

 <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

yml

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

主启动类

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

/**
 * @author Xkuna
 * @date 2020/8/9 18:20.
 */
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //启动熔断降级
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class, args) ;
    }
}
4.3.2 创建service 层 并且 完成服务降级配置

服务降级配置 无非是给 service层方法发生服务降级时 配置一个“兜底”的方法 以节省访问发生错误时的时间和资源消耗

在这里我们简写 service层 直接写 实现类

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author Xkuna
 * @date 2020/8/9 18:21.
 */
@Service
public class PaymentService {
    public String paymentInfo_ok(Integer id){
        return "线程池:  "+Thread.currentThread().getName()+"  paymentInfo_OK,id:  "+id+"\t"+"O(∩_∩)O哈哈~" ;
    }
    //fallbackMethod 就是发生服务降级时的 “兜底”方法
    //commandProperties 是相关配置 是个数组
    //HystrixProperty 是具体的单个配置 在这里我们配置的是超时时间 值为 3000 ms
    @HystrixCommand(fallbackMethod = "paymentInfoHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_timeout(Integer id){
//        int num = 10 / 0 ;
        try {
            TimeUnit.SECONDS.sleep(5); //设置暂停 5s
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:  "+Thread.currentThread().getName()+" id:  "+id+"\t"+"O(∩_∩)O哈哈~" ;
    }
	//服务降级 “兜底”方法
    public String paymentInfoHandler(Integer id) {
        return "线程池:  "+Thread.currentThread().getName()+",  8001系统访问超时!! id:  "+id+"\t"+"O(∩_∩)O哈哈~" ;
    }
}
4.3.3 创建controller
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.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springboot.service.PaymentService;

import javax.annotation.Resource;

/**
 * @author Xkuna
 * @date 2020/8/9 18:24.
 */
@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService ;

    @Value("${server.port}")
    private String port ;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_ok(@PathVariable("id") Integer id){
        String rs = paymentService.paymentInfo_ok(id);
        log.info(rs);
        return rs ;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id){
        String rs = paymentService.paymentInfo_timeout(id);
        log.info(rs);
        return rs ;
    }

}
4.3.4 测试

我们首先启动 eureka7001 在启动 支付微服务8001

然后我们访问 正常的接口

http://localhost:8001/payment/hystrix/ok/1

image-20200811101410173

没有问题

我们接着访问设置了 超时的接口

image-20200811101502903

**发现 我们访问到的是 服务降级后的结果 **

支付微服务 hystrix 服务降级搭建成功

4.4创建订单微服务

4.4.1 搭建项目基础

pom


 <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般基础通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

yml

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka/
#开启openfeign 服务熔断
feign:
  hystrix:
    enabled: true
#开启openfeign 服务熔断 必须配置 hystrix超时时间 Hystrix的熔断时间必须大于Ribbon的 ( ConnectTimeout + ReadTimeout )
hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 10000

#开启openfeign服务熔断 必须配置 ribbon超时时间
ribbon:
  ConnectTimeout: 5000
  ReadTimeout: 5000

主启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author Xkuna
 * @date 2020/8/9 19:45.
 */
@SpringBootApplication
@EnableFeignClients //开启openfeign
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args) ;
    }
}
4.4.2 创建service业务类 (openfeign 及 解决支付微服务宕机关闭问题)

踩坑~

——————————————————————————

订单服务 调用 支付微服务的超时接口,需要注意一下三点

1. 开启openfeign的熔断

在yml添加

image-20200811121206750

2.配置ribbon超时时间

因为 ribbon默认调用服务 超时时间为 1000ms

支付服务 超时接口 服务降级 时限为 3000 ms

image-20200811121602728

所以当我们访问 支付服务超时接口时 会超时报错

修改为ribbon超时时间 > 支付服务超时接口降级时间

在yml添加

image-20200811121322493

3.配置 熔断时间

image-20200811121904610

如此, 完成支付微服务的yml配置, 便在订单服务调用 支付微服务时, 若支付微服务发生了服务降级,那么订单服务也会获取到 支付微服务的服务降级信息了。

上面的yml配置 已经在搭建基础时配置完成了

——————————————————————————

创建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.PathVariable;

/**
 * @author Xkuna
 * @date 2020/8/9 19:46.
 */
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class)
public interface PaymentServiceClient {
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_ok(@PathVariable("id") Integer id) ;

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id) ;
}

我们可以看到与之前的openfeign服务调用有一点不同

image-20200811122624224

多了一个fallback,这个是用来做什么的呢?

我们现在思考一个问题:支付服务8001 我们完成了服务降级,如果订单服务访问支付服务时,支付服务发生了服务降级,订单服务可以获取到 服务降级的信息,

但是 如果订单服务访问支付服务时, 支付服务宕机或者关机了呢?

订单服务还能获取到 支付服务返回的 服务降级信息吗?

肯定获取不到,所以此时 支付服务要自己为防止这种情况的发生,我们需要 添加一个服务降级处理的实现类

实现类实现 该openfeign调用接口即可

import org.springframework.stereotype.Component;

/**
 * @author Xkuna
 * @date 2020/8/10 10:09.
 */
@Component //必须注入ioc容器 否则 openfeign调用接口的fallbcak获取不到
public class PaymentFallbackService implements PaymentServiceClient{
    @Override
    public String paymentInfo_ok(Integer id) {
        return "80调用8001失败 (┬_┬)" ;
    }

    @Override
    public String paymentInfo_timeout(Integer id) {
        return "80调用8001失败 (┬_┬)" ;
    }
}

如此再给openfeign接口 添加fallback 即可

image-20200811123503664

4.4.3创建controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springcloud.service.PaymentServiceClient;

import javax.annotation.Resource;

/**
 * @author Xkuna
 * @date 2020/8/9 19:50.
 */
@RestController
@Slf4j
public class OrderController {
    @Resource
    private PaymentServiceClient paymentServiceClient ;

    @RequestMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_ok(@PathVariable("id") Integer id) {
        return paymentServiceClient.paymentInfo_ok(id) ;
    }

    @RequestMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        return paymentServiceClient.paymentInfo_timeout(id) ;
    }
}
4.4.5测试

我们的eureka7001 和 支付微服务8001 已经启动

接下来启动我们的 订单微服务80

访问正常接口 http://localhost/consumer/payment/hystrix/ok/1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pyVYATwB-1597123879668)(C:\Users\xkuna\AppData\Roaming\Typora\typora-user-images\image-20200811123759828.png)]

服务调用成功

访问超时接口 http://localhost/consumer/payment/hystrix/timeout/1

image-20200811123901976

浏览器转了一会儿圈 访问到了支付微服务的服务降级信息 这个也ok

接下来我们将 支付微服务 关闭 再测试

上面两个接口

image-20200811124108924

说明我们 配置订单服务调用支付服务的 fallback也成功了

4.5服务降级 全局配置

支付微服务中

如果一个方法调用失败 要配置一个fallback方法 代码相对臃肿

此时 我们可以使用 全局配置

修改 支付服务的service

在类上面添加@DefaultProperties 及其配置

需要配置服务降级的方法上面添加@HystrixCommand即可

注意fallback方法去掉参数

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 org.springframework.stereotype.Service;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author Xkuna
 * @date 2020/8/9 18:21.
 */
@Service
//全局配置服务降级
@DefaultProperties(defaultFallback = "paymentInfoHandler",commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public class PaymentService {
    public String paymentInfo_ok(Integer id){
        return "线程池:  "+Thread.currentThread().getName()+"  paymentInfo_OK,id:  "+id+"\t"+"O(∩_∩)O哈哈~" ;
    }
    //fallbackMethod 就是发生服务降级时的 “兜底”方法
    //commandProperties 是相关配置 是个数组
    //HystrixProperty 是具体的单个配置 在这里我们配置的是超时时间 值为 3000 ms
    @HystrixCommand
    public String paymentInfo_timeout(Integer id){
//        int num = 10 / 0 ;
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:  "+Thread.currentThread().getName()+" id:  "+id+"\t"+"O(∩_∩)O哈哈~" ;
    }

    public String paymentInfoHandler() {
        return "线程池:  "+Thread.currentThread().getName()+",  8001系统访问超时!!  "+"\t"+"O(∩_∩)O哈哈~" ;
    }

}

此时我们重启 eureka注册中心 支付微服务 订单微服务

订单服务访问 支付微服务 超时接口 http://localhost/consumer/payment/hystrix/timeout/1

image-20200811125642584

获取到了 支付服务的服务降级信息 全局配置成功~~

如果我们给个别方法配置了单独的 服务降级 那么 发生服务降级时, 调用的是 单独配置的fallback

5.服务熔断 案例

5.1 修改 支付微服务service

添加

这里我们设置发生服务降级的条件是 id <0 抛出异常

本地上出现 访问超时 从而发生服务降级 的可能性很小

 //----断路器
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", 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"),// 失败率达到多少后跳闸
    })
    public String paymentCircuitBreaker(Integer id) {
        if(id < 0){
            throw new RuntimeException("*****id < 0") ;
        }
        String uuid = UUID.randomUUID().toString().replace("-", "") ;
        return Thread.currentThread().getName() + "\t 调用成功, 流水号:" + uuid ;
    }

    public String paymentCircuitBreaker_fallback(Integer id) {
        return "id不能小于0" ;
    }

5.2 修改 支付服务controller

 //====服务熔断
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentCircuitBreaker(id);
        log.info("****result: "+result);
        return result;
    }

5.3 测试

在这里我们就不写 订单服务 来调用 支付服务的熔断接口了( 跟上面的一样 openfeign接口 写调用方法 实现类实现fallback controller写个接口就行了)

我们直接用支付微服务测试

重启 支付微服务8001

访问 http://localhost:8001/payment/circuit/1

image-20200811130905506

访问成功 多刷新几次 也是没毛病的

访问 http://localhost:8001/payment/circuit/-1

image-20200811131004106

访问成功 返回服务降级信息

我们接着快速多次刷新访问 http://localhost:8001/payment/circuit/-1

然后立马访问 http://localhost:8001/payment/circuit/1

image-20200811131257754

发现 还是返回了 服务降级信息

过一会 在访问 http://localhost:8001/payment/circuit/1

image-20200811131339916

访问正常了

如此,hystrix服务熔断成功~~

6. hystrix的仪表盘

6.1创建项目基础

pom

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

yml

server:
  port: 9001

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * @author Xkuna
 * @date 2020/8/10 20:46.
 */
@SpringBootApplication
@EnableHystrixDashboard//启动hystrix仪表盘
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class, args) ;
    }
}

6.2 当前版本改动

当前版本需要在被监控的服务 主启动类 添加 指定的监控路径

我们在这里监控的是 支付微服务8001

所以修改 支付微服务8001 主启动类

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

/**
 * @author Xkuna
 * @date 2020/8/9 18:20.
 */
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class, args) ;
    }
    /**
     *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
     *只要在自己的项目里配置上下面的servlet就可以了
     */
    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

6.3 测试

启动 仪表盘 9001

浏览器打开 http://localhost:9001/hystrix

image-20200811132120599

image-20200811132332300

image-20200811132450513

快速多次刷新访问 http://localhost:8001/payment/circuit/-1

然后多次访问 http://localhost:8001/payment/circuit/1

image-20200811132611075

7.结束语

到这里 hystrix的服务降级 服务熔断 还有仪表盘 就介绍完了

很可惜的是hystrix停更了 所以 新的技术将逐步替换它

阿里爸爸 的 sentinel是不二之选

真的是学不完啊~~😢 😢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值