7、Spring Cloud07——断路器01

为什么需要断路器

在开发中,如果是前后端不分的话,很少使用断路器,但是在微服务中,断路器是一个非常重要的工具,因为在微服务里面,整个系统出错的概率远远高于单机服务,在微服务中可能有很多个单机服务,每个单机服务正常运行整个微服务系统才能正常运行,随着服务数量的增加,系统能正常运行的概率越低。采用微服务这个问题是无法避免的,我们能做的就是当一个微服务系统中的某一个单机服务出现问题后,希望它不要影响到整个服务的正常运行,这就是断路器里面提供的功能,主要有服务降级,缓存、容错等等。

断路器还能解决服务雪崩问题,服务雪崩就是微服务中的调用链,比如说A模块调用B模块,B模块调用C模块
,现在假如C模块出现了问题,B模块调不通就卡住了,然后A又去调用B,但是B需要从C那里拿到数据才能返回给A,在高并发的环境下本来B没有问题,现在把B也拖出问题了,如果这时候有人再调用A,那么A也会出问题这就叫服务雪崩,也叫做故障蔓延。

这些问题我们都要去避免,最重要的避免手段就是Hystrix,实际上Hystrix是一个即将下线的产品了,他也是Spring Cloud Netfilex公司提供的产品,Hystrix即将停止维护。但还是先学习一下Hystrix的用法。

服务容错

使用Hystrix,首先在我的consumer模块中引入Hystrix的依赖

 <!-- Hystrix断路器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

然后再consumer的启动类中加入@EnableCircuitBreaker注解,开启断路器:
在这里插入图片描述创建一个BookService:

@Service
public class BookSerivce {
    @Autowired
            @Qualifier("restTemplateTwo")
    RestTemplate restTemplateTwo;

// @HystrixCommand(fallbackMethod = "error")调用hello方法失败后就去调用error方法,不会卡在这里
    @HystrixCommand(fallbackMethod = "error")
    public String hello(){
        return restTemplateTwo.getForObject("http://provider/hello",String.class);
    }

    //在这里定义error方法,方法名字要和@HystrixCommand中一样,返回值要和原本要调用的方法(hello)一样
    public String error(){
         return "error";
    }
}

在BookController中加入如下代码:

   @Autowired
    BookSerivce bookSerivce;
    @GetMapping("/test3")
    public String test3(){
      return   bookSerivce.hello();
    }

此时访问test3如下:
在这里插入图片描述在这里插入图片描述
然后现在关掉其中一个服务,出现的结果就是这样:
在这里插入图片描述在这里插入图片描述这是因为配置了 @HystrixCommand(fallbackMethod = “error”),如果没有配置,在默认90秒的心跳时间内都会显示错误页面,这样配置就可以在eureka知道它挂掉之前,显示自己定义的页面,而不是错误页面。

自定义Hystrix请求命令

上面使用Hystrix的方式是基于注解的方式,除了这个方式,我们还可以使用定义类的方式使用Hystrix。

定义一个BookCommand类继承自HystrixCommand:

public class BookCommand extends HystrixCommand<Book> {

    //RestTemplate因为并不需要注入到Spring容器里面,所以不能添加@Autowired
    //一会儿通过构造方法,手动的传进来
    RestTemplate restTemplate;

    //构造方法
    public BookCommand(RestTemplate restTemplate) {
        super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")));
        this.restTemplate = restTemplate;
    }

    @Override
    protected Book run() throws Exception {
        //在run方法中定义的是真正会执行的东西,很像线程的定义,在外面调用的时候不能直接调用run方法
        //如果直接调用run方法,那它就变成了一个普通的方法了,就不具备容错的功能了
        return this.restTemplate.getForObject("http://provider/book?id={1}",Book.class,99);

 //请求失败在这儿处理,相当于注解中的error,这个方法名是固定的
    @Override
    protected Book getFallback() {
        Book book = new Book();
        book.setId(-1L);
        return book;
    }
}

然后BookController中写个方法来调用它:

   @GetMapping("/test4")
    public void test4() throws ExecutionException, InterruptedException {
        BookCommand bc = new BookCommand(restTemplateTwo);
        //execute()方法表示立马执行 bookCommand
        //注意new一个bookCommand只能让它execute()或者queue()一次
        Book b1 = bc.execute();
        BookCommand bc2 = new BookCommand(restTemplateTwo);
        //queue()表示让请求先入队
        Future<Book> q = bc2.queue();
        //调用get()方法时才会真正去执行
        Book b2 = q.get();
        System.out.println(b1);
        System.out.println(b2);
    }

运行结果如下:
在这里插入图片描述如果这时候关掉一个服务,那么这两个里面必然有一个会调用失败,调用失败就会自动降级:
在这里插入图片描述
一个使用使用了降级的数据,一个使用了正常的数据。

在实际开发中,比如说去调用provider,如果没有调通,我可能去查询某个数据,没调通的话可能还会有一个备选的方案,本来是从数据库中查询数据,现在数据库没调通,比如可以从redis里面先拿一个数据顶上。极有可能在error方法中调用redis里面的数据,或者调用备用的数据库里面的东西。这个数据库的特点就是调用的数据可能没有刚才的原本的默认数据库里面的数据准确,但是它很容易获取到。加入在这里面又发送了另一个请求,这个请求也有可能失败,所以在这里不仅叫容错,也叫服务降级。

那么现在来看一看服务降级:
在这里插入图片描述当1调用失败就去2中,当2调用失败就是3中,以此类推,这是在注解中的服务降级。

在自定义的BookCommand中的服务降级是怎么实现的呢?在这个自定义类中,重写的方法就只有一个,要想实现服务降级,就只能定义多个BookCommand或者在以下方法中new一个BookCommand,然后传参数进来。
在这里插入图片描述
刚刚我们用自定义类的方式实现了让请求先入队,再执行,现在再来看一下用注解的方式实现请求先入队再执行

首先在BookService里面定义一个方法:

@HystrixCommand
    public Future<Book> hello2(){
        return new AsyncResult<Book>() {
            @Override
            public Book invoke() {
                return restTemplateTwo.getForObject("http://provider/book?id={id}",Book.class,100);
            }
        };
    }

然后在BookController中调用:

@GetMapping("/test5")
    public void test5() throws ExecutionException, InterruptedException {
        Future<Book> bookFuture = bookSerivce.hello2();
        Book book = bookFuture.get();
        System.out.println(book);
    }

这就是用注解的方式让请求先入队,然后再实现它:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值