栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Hystrix学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Hystrix学习

降级

降级是指,当请求超时、资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是快速失败直接返回托底数据,保证服务链条的完整,避免服务雪崩。

解决服务雪崩效应,都是避免客户端请求服务端时,出现服务调用错误或网络问题。所有的处理手法都是在客户端中实现。

超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。保证服务出现问题整个项目还可以继续运行。

本文示例是在OpenFeign教程上做修改,链接地址:https://blog.csdn.net/liwenyang1992/article/details/126167210

客户端添加依赖:

        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-hystrix
            2.2.10.RELEASE
        

注意:需要在启动类添加@EnableHystrix注解才能使hystrix生效。

降级示例

下面示例介绍通过@HystrixCommand注解实现降级方法:

package com.lwy.it.service;

import com.lwy.it.feign.BookFeignService;
import com.lwy.it.vo.BookVO;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class ClientBookService {
    @Autowired
    private BookFeignService bookFeignService;

    
    @HystrixCommand(fallbackMethod = "getBookFallback")
    public BookVO getBook(int id) {
        BookVO bookVO = bookFeignService.getBookById(id);
        return bookVO;
    }

    
    private BookVO getBookFallback(int id) {
        log.info("执行回调方法,参数:{}", id);
        return new BookVO();
    }

}

下面再提供一个Controller方法用于测试验证。

import com.lwy.it.service.ClientBookService;
import com.lwy.it.vo.BookVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ClientBookController {

    @Autowired
    private ClientBookService bookService;

    @GetMapping("/hello")
    public BookVO hello() {
        // return bookService.getBook(0); 由于服务端没有这条数据,会导致500异常,会调用降级方法
        return bookService.getBook(1);
    }

}

思考:如果使用try catch包住异常是否会执行备用方法(不会,failed and fallback failed)

import com.lwy.it.feign.BookFeignService;
import com.lwy.it.vo.BookVO;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class ClientBookService {

    @Autowired
    private BookFeignService bookFeignService;

    
    @HystrixCommand(fallbackMethod = "getBookFallback")
    public BookVO getBook(int id) {
        BookVO bookVO = null;
        try {
            bookVO = bookFeignService.getBookById(id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bookVO;
    }

    
    private BookVO getBookFallback(int id) {
        log.info("执行回调方法,参数:{}", id);
        return new BookVO();
    }
}
熔断

当一定时间内,异常请求比例(请求超时、网络故障、服务异常等)达到阈值时,启动熔断器,熔断器一旦启动,则会停止调用具体服务逻辑,通过fallback快速返回托底数据,保证服务链路的完整。

熔断有自动恢复机制,如:当熔断启动后,每隔5秒,尝试将新的请求发给服务提供方,如果可正常执行并返回结果,则关闭熔断器,服务恢复。如果仍然调用失败,则继续返回托底数据,熔断器持续开启状态。

降级与熔断的区别为:降级是出错了返回托底数据,而熔断是出错后如果开启了熔断将会一定时间不再访问原方法。熔断机制相当于电路的跳闸功能。例如:我们可以配置熔断策略为当请求错误比例在10s内>50%时,该服务将进入熔断状态,后续请求将都会进入fallback。通俗理解:熔断就是具有特定条件的降级,当出现熔断时在设定的时间内容就不再请求应用服务了。所以在代码上熔断和降级都是一个注解@HystrixCommand。

当失败率(如因网络故障/超时造成的失败率高)达到阀值自动触发降级,熔断器触发的快速失败会进行快速恢复。

保证:服务出现问题整个项目还可以继续运行。

熔断的实现是在调用远程服务的方法上增加@HystrixCommand注解。当注解配置满足则开启或关闭熔断器。

熔断示例:

import com.lwy.it.feign.BookFeignService;
import com.lwy.it.vo.BookVO;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class ClientBookService {
    @Autowired
    private BookFeignService bookFeignService;

    
    @HystrixCommand(fallbackMethod = "getBookFallback", commandProperties = {
            // 开启熔断策略
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value = "true"),
            // 设置单位时间内请求超时超出5次则触发熔断策略
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "5"),
            // 设置单位时间为20秒
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "20000"),
            // 设置容器策略开启后,5秒后尝试再次请求远程服务
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000"),
            // 设置阈值为50%,即出现错误的请求百分比达到50%,则触发熔断策略
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
            // 不强制开启熔断策略
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_FORCE_OPEN, value = "false"),
            // 不强制关闭熔断策略
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_FORCE_CLOSED, value = "false")
    })
    public BookVO getBook(int id) {
        BookVO bookVO = bookFeignService.getBookById(id);
        return bookVO;
    }

    
    private BookVO getBookFallback(int id) {
        log.info("执行回调方法,参数:{}", id);
        BookVO bookVO = new BookVO();
        bookVO.setBookDescription("降级方法返回结果");
        return bookVO;
    }

}

使用jmeter工具进行10分钟压测验证,通过开启和关闭服务,可以更好的观察效果。

请求合并

什么情况下使用请求合并?

在微服务架构中,我们将一个项目拆分成多个独立的项目,这些独立的项目通过远程调用来互相配合工作。但是,在高并发场景下,通信次数的增加会导致总的通信时间增加。同时,线程池的资源也是有限的,在高并发环境会导致有大量的线程处于等待状态,进而导致响应延迟,为了解决这些问题,我们可以使用Hystrix的请求合并。

请求合并的缺点?

设置请求合并之后,本来一个请求可能5ms就搞定了,但是现在必须再等10ms看看还有没有其他的请求一起的,这样一个请求的耗时就从5ms增加到15ms了。不过,如果我们要发起的命令本身就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候的时间窗的时间消耗就显得微不足道了,另外高并发也是请求合并的一个非常重要的场景。

添加@HystrixCollapser注解

被@HystrixCollapser注解标注的方法,返回类型必须为Future,使用异步方法,否则无法进行请求合并。

batchMethod:合并请求的方法,方法只能接收一个参数。如果你需要传递多个参数,那么请将它们封装成一个类参数。

scope:请求方式。分别为REQUEST(默认)、GLOBAL。REQUEST范围支队一个request请求内的多次服务请求进行合并;GLOBAL是多个应用中的所有线程的请求中的多次服务请求进行合并。

注意:请求合并需要服务端同步进行修改

隔离

隔离分为线程池隔离和信号量隔离。通过判断线程池或信号量是否已满,超出容量的请求直接降级,从而达到限流的作用。

线程池隔离

为什么使用线程池隔离

没有线程池隔离的时候可能因为某个接口的高并发导致其它接口不可用。

使用线程池隔离,不同接口有自己独立的线程池。

即使某个线程池都被占用,也不影响其它线程。

Hystrix采用Bulkhead Partition舱壁隔离技术。舱壁隔离指的是船体内部分为多个隔舱,一旦其中某几个隔舱发生破损进水,水流不会在其它舱壁中流动,从而保证船舱依然具有足够的浮力和稳定性,降低沉船危险。

优点:

  • 任何一个服务都会被隔离在自己的线程池内,即使自己的线程池资源填满也不会影响其它服务。
  • 当依赖的服务重新恢复时,可通过清理线程池,瞬间恢复服务的调用。但是如果是tomcat线程池被填满,再恢复就会很麻烦。
  • 每个都是独立线程池,一定程度上解决了高并发问题。
  • 由于线程池中线程个数是有限制的,所以也解决了限流问题。

缺点:

  • 增加了CPU开销。因为不仅仅有Tomcat的线程池,还需要有Hystrix线程池。
  • 每个操作都是独立的线程,就有排队、调度和上线文切换等问题。
    
    @HystrixCommand(groupKey = "", commandKey = "", threadPoolKey = "", threadPoolProperties = {
            @HystrixProperty(name = HystrixPropertiesManager.CORE_SIZE, value = "8"),
            @HystrixProperty(name = HystrixPropertiesManager.MAX_QUEUE_SIZE, value = "64"),
            @HystrixProperty(name = HystrixPropertiesManager.KEEP_ALIVE_TIME_MINUTES, value = "1"),
            @HystrixProperty(name = HystrixPropertiesManager.QUEUE_SIZE_REJECTION_THRESHOLD, value = "100"),
    })
    public BookVO thread() {
        BookVO bookVO = new BookVO();
        bookVO.setBookDescription(Thread.currentThread().getName());
        return bookVO;
    }

更多学习:https://www.cnblogs.com/seifon/p/9921774.html

信号量隔离

java.util.concurrent.Semaphore用来控制可同时并发的线程数,通过构造方法指定内部虚拟许可的数量。每次线程执行操作时先通过acquire方法获得许可,执行完毕之后再通过release方法释放许可。如果没有可用的许可,acquire方法将一直阻塞,直到其它线程释放许可。

如果采用信号量隔离技术,每接收一个请求,都是服务自身线程去直接调用依赖服务。信号量就相当于一道关卡,每个线程通过关卡后,信号量数量减去1,当为0时不再允许线程通过,而是直接执行fallback逻辑并返回,说白了仅仅做了一个限流。

同一个方法不能即是线程池隔离又是信号量隔离。

代码示例:

    
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS, value = "10"),
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT, value = "FALSE"),
            @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10"),
            @HystrixProperty(name = HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "10")
            // 超过信号量数量执行降级方法
    }, fallbackMethod = "semaphoreFallback")
    public BookVO semaphore() {
        BookVO bookVO = new BookVO();
        bookVO.setBookDescription("信号量隔离");
        // 为了展示效果,睡眠800ms
        try {
            Thread.sleep(800L);
        } catch (InterruptedException exception) {
            log.error("Exception:{}", exception);
        }
        log.info("执行了信号量隔离方法");
        return bookVO;
    }

    public BookVO semaphoreFallback() {
        BookVO bookVO = new BookVO();
        bookVO.setBookDescription("信号量隔离Fallback方法");
        log.info("执行了信号量隔离Fallback方法");
        return bookVO;
    }

编写Controller,通过jmeter调用

    @GetMapping("/semaphore")
    public BookVO semaphore() {
        return bookService.semaphore();
    }
OpenFeign降级处理
# 开启OpenFeign的Hystrix配置
feign.circuitbreaker.enabled=true

通过fallback参数指定

import com.lwy.it.config.FeignConfiguration;
import com.lwy.it.vo.BookVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;



@FeignClient(name = "demo-server", path = "/server/book", configuration = FeignConfiguration.class, fallback = BookFeignServiceFallback.class)
public interface BookFeignService {

    // 声明需要调用rest接口对应的方法
    @GetMapping("/list")
    List getAllBooks();

    @GetMapping("/{bookId}")
    BookVO getBookById(@PathVariable("bookId") Integer id);

    
    @GetMapping("/login")
    String login(@RequestParam("username") String username, @RequestParam("password") Integer password);

    @PutMapping("/saveBook")
    BookVO saveBook(@RequestBody BookVO bookVO);

    @GetMapping("/param")
    BookVO getBook(@RequestParam int bookId, @RequestParam String bookName, @RequestParam String bookDescription, @RequestParam double bookPrice);
}
import com.lwy.it.vo.BookVO;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;


@Component
public class BookFeignServiceFallback implements BookFeignService {
    @Override
    public List getAllBooks() {
        return Collections.emptyList();
    }

    @Override
    public BookVO getBookById(Integer id) {
        return new BookVO();
    }

    @Override
    public String login(String username, Integer password) {
        return "OK";
    }

    @Override
    public BookVO saveBook(BookVO bookVO) {
        return bookVO;
    }

    @Override
    public BookVO getBook(int bookId, String bookName, String bookDescription, double bookPrice) {
        BookVO bookVO = new BookVO();
        bookVO.setBookId(bookId);
        bookVO.setBookName(bookName);
        bookVO.setBookPrice(bookPrice);
        bookVO.setBookDescription(bookDescription);
        return bookVO;
    }
}

编写Controller调用,分别开启和关闭服务提供方进行验证测试

    @GetMapping("/all")
    public List getAllBooks() {
        return bookFeignService.getAllBooks();
    }
请求缓存

服务A调用服务B,如果在A中添加请求缓存,第一次请求后走缓存了,就不再访问服务B了,即使出现大量请求时,也不会对B产生高负载。

请求缓存可以使用Spring Cache实现。减少对应用服务的调用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1039697.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号