hystrix实现服务降级的3种方式

1、hystrix是什么


Hystrix是一款开源的容错插件,具有依赖隔离,系统容错降级等功能,这也是其最重要的两种用途,还有请求合并等功能

2、为什么要进行隔离


在实际工作中,尤其是分布式、微服务越来越普遍的今天,一个服务经常需要调用其他的服务,即RPC调用,而调用最多的方式还是通过http请求进行调用,这里面就有一个问题了,如果调用过程中,因为网络等原因,造成某个服务调用超时,如果没有熔断机制,此处的调用链路将会一直阻塞在这里,在高并发的环境下,如果许多个请求都卡在这里的话,服务器不得不为此分配更多的线程来处理源源不断涌入的请求

更恐怖的是,如果这是一个多级调用,即此处的服务的调用结果还被其他服务调用了,这就形成了所谓的雪崩效应,后果将不堪设想

因此,需要某种机制,在一定的异常接口调用出现的时候,能够自动发现这种异常,并快速进行服务降级,这就是hystrix要发挥的作用,常见的降级处理方式包括,超时处理、线程池隔离和信号量隔离,下面就对这几种方式做简单的说明

1、导入依赖

 

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <hystrix.version>1.5.12</hystrix.version>
    </properties>

    <dependencies>

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

       <!-- <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>-->

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

        <!-- hysrtix -->
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
            <version>${hystrix.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-metrics-event-stream</artifactId>
            <version>${hystrix.version}</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
            <version>${hystrix.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.netflix.hystrix/hystrix-core -->
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
            <version>1.5.12</version>
        </dependency>

        <dependency>
            <groupId>org.databene</groupId>
            <artifactId>contiperf</artifactId>
            <version>2.3.4</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!-- 指定maven版本 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


2、配置bean组件


由于我这里使用的是springboot的方式,如果想要整合springboot使用,需要添加一个bean 的配置,网上也有通过配置文件的方式,那也是可以的

@Configuration
public class HystrixConfig {

    @Bean
    public HystrixCommandAspect hystrixCommandAspect(){
        return new HystrixCommandAspect();
    }

}


3、创建一个接口,模拟远程调用其他服务接口

 

   @GetMapping("/createOrder")
    public String createOrder(){
       return orderService.createOrder();
    }


接口代码很简单,就是远程获取一个订单号,下面看实现类,首先看第一种方式,即接口超时的处理,hystrix提供了超时降级熔断机制,即通过添加注解的方式,当这个注解配置在某个方法上面的时候,就会对这个方法生效,当然也可以直接写在接口上,超时配置代码如下,

方式1,超时降级

@Autowired
private RestTemplate restTemplate;

@HystrixCommand(
     commandKey = "createOrder",
     commandProperties = {
        @HystrixProperty(name="execution.timeout.enabled", value="true"),
        @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000"),
       },
     fallbackMethod = "createOrderFallbackMethod4Timeout"
    )
public String createOrder() {
       String orderNo = restTemplate.getForObject("http://localhost:8087/getUserOrder",String.class);
        return "下单成功,订单号是:" + orderNo;
    }


这里我们设置超时时间为3秒,在注解上有一个叫做fallbackMethod 的,这个里面对应的方法就是当达到超时时间时走的方法,如下,

//超时降级的方法
   

public String createOrderFallbackMethod4Timeout() throws Exception {
        System.err.println("-------超时降级策略执行------------");
        return "hysrtix timeout !";
    }


下面我们来测试一下,在另外一个工程中,我这里提供了一个非常简单的接口,用以测试调用,

@RestController
public class UserInfoController {

    @GetMapping("/getUserOrder")
    public String getUserOrder(){
        return UUID.randomUUID().toString().substring(0,6);
    }

}


启动两个demo工程,为了模拟效果,我们将被调用的接口设置休眠5秒

    

@GetMapping("/getUserOrder")
    public String getUserOrder() throws Exception{
        Thread.sleep(5000);
        return UUID.randomUUID().toString().substring(0,6);
    }


启动完毕,浏览器访问:http://localhost:8083/order/createOrder,从返回结果可以看到,超时降级策略执行了,

超时降级也可在类级别定义降级执行方法

 

1、在类上方声明注解@DefaultProperties(defaultFallback = "defaultFallback")//默认服务降级,这里采用默认的服务降级,defaultFallback属性表示要编写的方法(服务降级的提示)
2、在方法上声明注解@HystrixCommand即可
 

方法层定义超时时间

 @HystrixCommand(commandProperties = {
                    @HystrixProperty(name="execution.timeout.enabled", value="true"),
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
    })

默认降级执行方法 

 public ResponseEntity<String> defaultFallback(){
        return ResponseEntity.ok().body("服务访问超时,请稍后访问~~");
    }

 

方式2,线程池隔离


所谓线程池隔离,就是每个过来的请求,系统会单独将这个请求的执行放在一个线程或线程池里,这个根据自己的需求进行配置,以后,相同的请求不管有多少打进来,都会在指定数量的线程池内进行,因此不会出现诸如线程资源耗尽的情况,这种隔离方式在某些场景下是非常有用的,下面来看具体的代码,

线程池隔离这里同样采用注解的方式,只是注解里面的相关配置发生了变化,这里我们仍以请求创建订单号为例,

//    限流策略:线程池方式
   

@HystrixCommand(
            commandKey = "createOrder",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")
            },
            threadPoolKey = "createOrderThreadPool",
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "3"),
                    @HystrixProperty(name = "maxQueueSize", value = "5"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "7")
            },
            fallbackMethod = "createOrderFallbackMethod4Thread"
    )
    public String createOrder() {
        String orderNo = restTemplate.getForObject("http://localhost:8087/getUserOrder", String.class);
        return "下单成功,订单号是:" + orderNo;
    }


提供一个熔断超时的方法,

     //线程池方式降级
   

public String createOrderFallbackMethod4Thread() throws Exception {
        System.err.println("-------超时线程池降级策略执行------------");
        return "hysrtix timeout !";
    }


这个线程池隔离的方式模拟起来稍显麻烦,我这里直接在接口里面通过线程池的方法发起100个请求进行模拟测试,代码如下,

@GetMapping("/createOrder")
    public String createOrder(){
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch countDownLatch = new CountDownLatch(requestTotal);
        Semaphore semaphore = new Semaphore(concurrentThreadNum);
        for (int i = 0; i< requestTotal; i++) {
            executorService.execute(()->{
                try {
                    String result = orderService.createOrder();
                    System.out.println(result);
                    semaphore.release();
                } catch (Exception e) {
                    System.out.println(e);
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }
       // return orderService.createOrder();
        return "success";
    }


然后启动工程,浏览器访问:http://localhost:8083/order/createOrder,通过控制台打印结果,可以看到,由于并发请求远超过我们在注解上配置的参数,因此走了线程池隔离的熔断方法,但由于这里并不会影响主线程,因此最终接口还是打印了主线程的结果,

方式3,信号量隔离

信号量的资源隔离只是起到一个开关的作用,比如,服务 A 的信号量大小为 10,那么就是说它同时只允许有 10 个 tomcat 线程来访问服务 A,其它的请求都会被拒绝,从而达到资源隔离和限流保护的作用,下面直接上代码,基本配置和上述两种相似,就是注解不同,

@HystrixCommand(
                commandKey="createOrder",
                commandProperties= {
                        @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE"),
                        @HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests", value="6")
                },
                fallbackMethod = "createOrderFallbackMethod4semaphore"
            )
    public String createOrder() {
        String orderNo = restTemplate.getForObject("http://localhost:8087/getUserOrder", String.class);
        return "下单成功,订单号是:" + orderNo;
    }


超时熔断方法

// 信号量方式降级
    public String createOrderFallbackMethod4semaphore() throws Exception {
        System.err.println("-------信号量降级策略执行------------");
        return "hysrtix timeout !";
    }


这里通过字面意义理解,我们仍然可以用上述的接口进行模拟,启动工程,浏览器访问:http://localhost:8083/order/createOrder,通过控制台可以看到,由于我们在注解的配置里面设置的是6个,即允许同时通过的是6个请求,而我们请求的时候使用的是100个,达到了阈值,触发了熔断机制,

这里提供一个比较好用的模拟并发请求的组件,项目添加如下依赖即可,然后在自己的单元测试类中即可使用,下面贴上简单的使用代码,即只需要在你的测试方法上添加 @PerfTest(invocations = 10,threads = 10)这样的注解即可,里面的参数可以动态配置,

        

        <dependency>
            <groupId>org.databene</groupId>
            <artifactId>contiperf</artifactId>
            <version>2.3.4</version>
            <scope>test</scope>
        </dependency>


 

@SpringBootTest
@ContextConfiguration(classes = HystrixTest.class)
public class HystrixTest {

    @Autowired
    private OrderService orderService;

    public ContiPerfRule contiPerfRule = new ContiPerfRule();

    @Test
    @PerfTest(invocations = 10,threads = 10)
    public void test1() {
        String result = orderService.createOrder();
        System.out.println(result);
    }


}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值