微服务开发(12)--Hystrix的使用

分布式系统遇到的问题

  • 我们在使用分布式架构模式的情况下,会遇到服务雪崩等问题,先来明确几个概念
  • 服务扇出

在分布式系统中,存在服务A调用服务B,而服务B又去调用服务C,服务D,这样的调用过程就是服务扇出

  • 服务雪崩或者级联故障

在我们某一条扇出的服务调用链路中有一个服务,由于响应时间过程或者抛出异常,导致服务调用者老被占用越来越多的资源,从而导致整个系统崩溃,整个的过程就叫做服务雪崩或者级联故障

在这里插入图片描述

如何解决服务雪崩?
  • 解决方案:

(1)超时机制

  • 在我们没有使用Hystrix的时候,我们会设置restTemplate的超时机制,然后捕获异常,全局处理该异常

(2)舱壁隔离

在这里插入图片描述

  • 拿船来举例,现代的轮船会分为很多的舱室,舱室之间用钢板焊死,彼此隔离。这样即使有某个/某些船舱进水,也不会影响其他的舱室,浮力够的话,船就不会沉。
  • 软件工程可以这么理解:M类使用线程池1,N类使用线程池2,彼此的线程池不同,并且为每个类分配线程池的大小,例如:coreSize=10。举个例子来说,M类调用B服务,N类调用C服务,如果M类和N类使用相同的线程池,那么如果B服务挂了,M类调用B服务的接口并发又很高,你又没有任何保护措施,你的服务很可能就会被M类拖死。而如果M类有自己的线程池,N类也有自己的线程池,如果B服务挂了,M类项顶多就是将自己的线程池沾满,不会影响N类的线程池—于是N类依然能正常工作
  • 思路:不要把鸡蛋放在一个篮子里,你有你的线程池,我有我的线程池,你的和我的没关系,我的和你的也没关系

(3)熔断器

  • 现实世界的断路器大家肯定都很了解,每个人家里都会有断路器。断路器实时监控电路的情况,如果发现电路电流异常,就会跳闸,从而防止电路被烧毁
  • 软件世界的断路器可以这么理解,实时监测应用,如果发现在一定时间内失败的次数/失败率达到一定的阈值,就“跳闸”,断路器打开,此时,请求直接返回,而不去调用原本调用的逻辑
  • 跳闸一段时间后(例如15s后),断路器就会进入半开状态,这是一个瞬间态,此时允许一次请求调用正常的业务逻辑,如果成功,则断路器关闭,应用恢复正常,如果还是不成功,断路器继续回到打开状态,过段时间后再次进入半开状态并尝试打开,通过“跳闸”,应用可以保护自己,而且避免浪费资源,而通过半开的设计,可以实现应用的“自我修复”。
  • 注意:
  1. 服务熔断是条件,服务降级是手段(熔断就是直接给一个错误,降级就是服务返回自己的异常封装响应)
  2. 我们只需要对高频接口实现降级,并不需要对每一个接口降级

什么是Hystrix?

  • Hystrix(豪猪)是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统,服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。

Hystrix能做什么?

  • hystrix可以提供最基础的服务的熔断和降级。
  • 服务的熔断是条件,降级是实现的手段,熔断就是直接报错,不进行调用,而降级是在熔断的基础上,自己调用自己本地的方法,返回客户一个自己定义的响应(比如:淘宝的店家不在,稍后再来等,都是服务的降级)
Java实现非注解版本的Hystrix(TODO
  1. 包裹请求
    (1)创建工程,工程结构图:
    (2)导入依赖

。。。。

  1. 跳闸机制
    2.1 Hystrix的默认配置跳闸父阈值
  • 宕机跳闸
    (1)启动服务注册中心以及服务消费者(不启动服务的提供者模拟宕机)
  • 超时跳闸
  • 使用Ribbon+Hystrix来进行调用,需要调用超时时间(设置全局超时时间)
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
    注意:如果这里没有和Fegn联用,则无需配置Ribbon的超时时间
  • 指定设置接口的超时设置
    hystrix.command.queryUserInfoByName.execution.isolation.thread.timeoutInMilliseconds=10

(1)在服务提供方的被调用方法中设置睡眠时间,来达到超时

  • 异常跳闸
    (1)查询一个不存在的id,就是让服务的提供方主动抛出一个异常对其进行测试

2.2 跳闸机制的三转换图
在这里插入图片描述

  • 当熔断器开关关闭的时候,请求允许通过熔断器,如果当前健康状况高于设定的阈值,开关就继续保持关闭状态,如果当前健康状况低于设定的阈值的时候,开关则切换成打开状态
  • 当熔断器开关打开的时候,请求禁止通过直接返回
  • 当熔断器开关处于打开状态的时候,经过一段时间后(这段时间中真实服务器无法访问,都是直接返回一个错误的或者用户自定义的响应),熔断器会自动进入半开状态,这时候熔断器只能够允许一个请求通过,当该请求调用成功的时候,熔断器恢复关闭的状态,若该请求失败的话,熔断器继续保持打开的状态,接下来的请求被禁止通过(继续半开状态)。
  • 熔断器的核心配置:
    在这里插入图片描述

Feign整合Hystrix

  • Feign默认是对Hystrix不支持的(Dalston版本之后),需要开启配置,因此使用Feign集成Hystrix的时候,需要手动开启,否则断路器不会生效
#开启feign支持hystrix就将其设置为true 默认是关闭的(将其设置为true是为了测试局部关闭,在config类中配置)
feign:
  hystrix:
    enabled: true

搭建工程----FeignAPI

  • 该工程用来抽象出Fegin所需要的service
  • 之前已经搭建过了,这里只贴出核心代码
package com.xiyou.api.api;

import org.springframework.web.bind.annotation.*;

/**
 * Feign的Service接口
 * @author 92823
 */
public interface FeignApiService {
    /**
     *
     * @return
     */
    @RequestMapping(value = "/test/{id}", method = RequestMethod.GET)
    public String test(@PathVariable("id") String id);

    /**
     *
     * @param cookie
     * @return
     */
    @RequestMapping(value = "/testCookie", method = RequestMethod.GET)
    public String testCookie(@RequestHeader("Cookie") String cookie);

    /**
     *
     * @param token
     * @return
     */
    @RequestMapping(value = "/testToken", method = RequestMethod.GET)
    public String testToken(@RequestHeader("Token") String token);
}

  • 打包

搭建工程my-feign-hystrix

  • 该工程就是用来实现远程通信的,并且集成了hystrix
  • 代码架构:
    在这里插入图片描述
    (1)pom依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <dependency>
            <groupId>com.xiyou</groupId>
            <artifactId>my-feign-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </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>

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

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

这里导入了刚刚我们自己打包的feign-api

(2)配置文件yml

server:
  port: 8006
# 注册到eureka服务端的服务名称
spring:
  application:
    name: my-feign-hystrix

eureka:
  client:
    # 指明eureka服务端的地址
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    instance-id: my-feign-hystrix-8006
    # 点击具体的微服务,右下角显示微服务的IP
    prefer-ip-address: true

#开启feign支持hystrix就将其设置为true 默认是关闭的(将其设置为true是为了测试局部关闭,在config类中配置)
feign:
  hystrix:
    enabled: true

(3)主启动类

package com.xiyou.hystrix;

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.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author 92823
 */
@SpringBootApplication
@EnableCircuitBreaker
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.xiyou.hystrix.service")
public class FeignHystrixApplication {
    public static void main(String[] args) {
        SpringApplication.run(FeignHystrixApplication.class, args);
    }
}

(4)FeignController

package com.xiyou.hystrix.controller;

import com.xiyou.hystrix.service.FeignHystrixService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 92823
 */
@RestController
public class FeignHystrixController {

    @Autowired
    private FeignHystrixService feignHystrixService;

    @GetMapping("/feign/{id}")
    public String test(@PathVariable("id") String id) {
        String test = feignHystrixService.test(id);
        return test;
    }
}

(5)service

package com.xiyou.hystrix.service;

import com.xiyou.api.api.FeignApiService;
import com.xiyou.hystrix.config.FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @author 92823
 */
@FeignClient(name = "my-provider", fallbackFactory = FeignApiFallBackFactory.class)
public interface FeignHystrixService extends FeignApiService {
}

这里利用了fallbackFactory来实现hystrix的集成,实现降级服务
name属性就是要调用的目标服务的Eureka中的服务的名(applicationName)

(6)降级服务的类

package com.xiyou.hystrix.service;

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

/**
 * 错误处理的方法
 * @author 92823
 */
@Component
public class FeignApiFallBackFactory implements FallbackFactory<FeignHystrixService> {

    @Override
    public FeignHystrixService create(Throwable throwable) {
        return new FeignHystrixService(){
            @Override
            public String test(String id) {
                return "test方法的回退方法.....";
            }

            @Override
            public String testCookie(String cookie) {
                return null;
            }

            @Override
            public String testToken(String token) {
                return null;
            }
        };
    }
}

下面的相关知识点的配置,都是在上面的基础上进行的,所以下面只给出核心的代码

Hystrix调用的超时重试设置

(1)导入maven的依赖

        <dependency>
			<groupId>org.springframework.retry</groupId>
			<artifactId>spring-retry</artifactId>
		</dependency>

(2)加入yml配置文件

server:
  port: 8006
# 注册到eureka服务端的服务名称
spring:
  application:
    name: my-feign-hystrix

  # 默认就是开启重试的机制
  cloud:
    loadbalancer:
      retry:
        enabled: true

eureka:
  client:
    # 指明eureka服务端的地址
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    instance-id: my-feign-hystrix-8006
    # 点击具体的微服务,右下角显示微服务的IP
    prefer-ip-address: true

#开启feign支持hystrix 默认是关闭的
feign:
  hystrix:
    enabled: true

# ribbon的超时机制,若用feign+hystrix则需要配置这个
ribbon:
  ReadTimeout: 4000
  ConnectTimeout: 4000
  # 出现异常的时候当前实例重试的次数
  MaxAutoRetries: 1
  # 切换实例重试的次数
  MaxAutoRetriesNextServer: 1
  # 对所有的操作进行重试 值可以是true(所有的进行重试),get, post, put, delete
  OkToRetryOnAllOperations: true


# 默认值是1s
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            # 这个的时间应该大于(MaxAutoRetries+MaxAutoRetriesNextServer+本次调用这一次) * ribbon的超时时间
            # 所以这里是:(1+1+1)* (4000+4000)= 24000
            timeoutInMilliseconds: 24000


# 局部配置
# 微服务实例名称.ribbon.ConnectTimeout=250
# 微服务实例名称.ribbon.ReadTimeout=1000
# 微服务实例名称.ribbon.OkToRetryOnAllOperations=true
# 微服务实例名称.ribbon.MaxAutoRetriesNextServer=2
# 微服务实例名称.ribbon.MaxAutoRetries=1
  • 这里注意一点,因为我们利用的是Feign和Hystrix,所以设置超时时间的时候,不止需要设置hystrix的,还需要设置ribbon的。我们设置的hystrix的超时时间需要大于(MaxAutoRetries+MaxAutoRetriesNextServer+本次调用这一次) * ribbon的超时时间
    在这里插入图片描述
    关闭Feign对Hystrix的支持
  1. 全局关闭
  • 默认我们就是全局关闭的,yml中配置即可

#开启feign支持hystrix就将其设置为true 默认是关闭的
feign:
  hystrix:
    enabled: false

  1. 局部关闭

(1)写一个配置类,在配置中写一个FeignBuilder类

package com.xiyou.hystrix.config;

import feign.Feign;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

/**
 * @author 92823
 * 不能写@Configuration 否则全局生效
 */
public class FeignConfig {
    @Scope("prototype")
    @Bean
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}

这里我们在类上面写@Configuration注解,否则改配置又成全局生效了
(2)修改Feign的调用Service加入Configuration属性

package com.xiyou.hystrix.service;

import com.xiyou.api.api.FeignApiService;
import com.xiyou.hystrix.config.FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * 测试局部关闭feign对hystrix的支持
 * @author 92823
 */
@FeignClient(name = "my-provider", fallbackFactory = FeignApiFallBackFactory.class, configuration = FeignConfig.class)
public interface FeignHystrixService1 extends FeignApiService {
}

(3)全局开启Feign对Hystrix的支持

#开启feign支持hystrix就将其设置为true 默认是关闭的
feign:
  hystrix:
    enabled: true

  • 测试结果:
    (1)不指定config使用的是全局的配置,那么由于超时会出现服务的降级
    (2)指定config,由于自己配置的FeignBuilder,导致不支持服务降级,直接报错(该报错就由SpringBoot默认处理了,不是自己的服务降级)

关闭熔断

(1)全局关闭

hystrix: 
	command: 
		default: 
			circuitBreaker: 
				enabled: false

(2)局部关闭

hystrix: 
	command: 
		<HystrixCommandKey>: 
			circuitBreaker: 
				enabled: false
  • 其中< HystrixCommandKey> 是一个变量,具体的值就是@FeignClient标注的类#方法名(参数类型)
  • 如:
    在这里插入图片描述

设置超时

(1)全局关闭

# 全局设置超时:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 1000

(2)局部关闭

# 局部设置超时:
hystrix.command.<HystrixCommandKey>.execution.isolation.thread.timeoutInMilliseconds: 1000

关闭超时

(1)全局关闭

# 全局关闭:
hystrix.command.default.execution.timeout.enabled: false

(2)局部关闭

# 局部关闭:
hystrix.command.<HystrixCommandKey>.execution.timeout.enabled: false

hystrix整合监控点

  1. 单纯的引入hystrix.stream的监控端点

(1)导入maven依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

(2)加入注解

  • 在主启动类上加入@EnableCircuitBreaker注解

(3)加入监控端点的配置

  • SpringCloud的E版本是不需要加入的,但是E版本之后,就必须手写过滤器来加入监控端点的配置
package com.xiyou.hystrix.config;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 加入的端点监控配置
 * @author 92823
 */
@Configuration
public class HystrixDashboardConfig {
    @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;
    }
}

(4)访问http://localhost:端口号/hystrix.stream

  • 该接口访问出来的接口全部都是json格式的数据,很难使用
  1. Hystrix+DashBoard监控

(1)重新创建一个新的工程:
在这里插入图片描述
(2)pom文件依赖

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

(3)yml配置文件没有做其他的配置
(4)主启动类

package com.xiyou.dashnoard;

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

/**
 * @author 92823
 */
@SpringBootApplication
@EnableHystrixDashboard
public class DashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(DashboardApplication.class, args);
    }
}

  • 此时访问的时候先访问当前工程的http://localhost:XXXX/hystrix
  • 然后在输入框中输入,要监控服务的监控地址:如http://localhost:8006/my-feign-hystrix.stream

注意事项
(1)访问的hystrix地址,是我们新搭建的工程的
(2)监控的地址是我们要监控的地址的ip+端口号+注册到applicationName.stream
(3)切记监控的工程也需要引入hystrix.strream的相关依赖,否则没有地址,报错(引入可以参考1. 单纯的引入hystrix.stream的监控端点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值