zoukankan      html  css  js  c++  java
  • Spring Cloud之Hystrix

      在微服务架构中,存在那么多的服务单元,若一个单元出现故障(由于网络原因或者自身原因),就很容易因依赖关系而引发故障的蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构更加不稳定。为了解决这样的问题,产生了断路器等一系列的服务保护机制。(A服务调用B服务,B服务由于自身处理逻辑等原因造成响应缓慢,会导致A服务线程被挂起,以等待B服务执行,在高并发情况下,这些挂起的线程会导致后面调用A服务的请求被阻塞,最终导致A服务也不可用)。

      加入断路器后,当服务不可用时,通过断路器的故障监控,会直接执行回调函数,直接返回一串字符串,而不是等待响应超时,这样就不会使得线程调用故障服务被长时间占用不释放,从而避免了故障在分布式系统中的蔓延。

        本节内容在上节内容基础上,阅读此节之前,先看上节Spring Cloud之Eureka、Ribbon

    一、无断路器示例

      启动上节的eureka-server、service-hello(8081/8082)、ribbon-consumer工程

      在未加入断路器之前,关闭8081实例,发送GET请求到http://localhost:9000/ribbon-consumer,轮询8081/8082,当轮询到8081后(因为8081实例被关)会得到下面输出:

    二、加入断路器示例

      在ribbon-consumer工程的pom.xml引入下面依赖

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

      在ribbon-consumer工程的 主类ConsumerApplication中使用@EnableCircuitBreaker注解开启断路器功能

    package com.stonegeek;
    
    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.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    /**
     * Created by StoneGeek on 2018/5/28.
     * 博客地址:http://www.cnblogs.com/sxkgeek
     */
    @EnableCircuitBreaker
    @EnableDiscoveryClient
    @SpringBootApplication
    public class RibbonConsumerApplication {
    
        @Bean
        @LoadBalanced
        RestTemplate restTemplate(){
            return new RestTemplate();
        }
        public static void main(String[] args) {
            SpringApplication.run(RibbonConsumerApplication.class, args);
        }
    }

      改造服务消费方式,新增HelloService类,注入RestTemplate实例,然后将在ConsumerController中对RestTemplate的使用迁移到helloService函数中,最后,在helloService函数上增加@HystrixCommand注解来指定回调方法:

    package com.stonegeek.service;
    
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.logging.Logger;
    
    /**
     * Created by StoneGeek on 2018/5/29.
     * 博客地址:http://www.cnblogs.com/sxkgeek
     */
    @Service
    public class HelloService {
        private final Logger logger =Logger.getLogger(String.valueOf(getClass()));
        @Autowired
        RestTemplate restTemplate;
    
        @HystrixCommand(fallbackMethod = "helloFallback")
        public String helloService(){
            //加logger更清晰的看出执行时间
            long start =System.currentTimeMillis();
            String result=restTemplate.getForEntity("http://SERVICE-HELLO/hello",String.class).getBody();
            long end=System.currentTimeMillis();
            logger.info("Spend time:"+(end-start));
            return result;
        }
    
        public String helloFallback(){
            return "error";
        }
    
    }

      修改RibbonConsumerApplication.class

    package com.stonegeek.controller;
    
    import com.stonegeek.service.HelloService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    /**
     * Created by StoneGeek on 2018/5/28.
     * 博客地址:http://www.cnblogs.com/sxkgeek
     */
    @RestController
    public class ConsumerController {
        /**
         * @Author: StoneGeek
         * @Date: 2018/5/29
         * @Description:之前不加断路器代码
         */
    //    @Autowired
    //    RestTemplate restTemplate;
    //
    //    @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
    //    public String helloConsumer(){
    //        return restTemplate.getForEntity("http://SERVICE-HELLO/hello",String.class).getBody();
    //    }
    
        /**
         * @Author: StoneGeek
         * @Date: 2018/5/29
         * @Description: 加了断路器代码
         */
    
        @Autowired
        HelloService helloService;
    
        @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET)
        public String helloConsumer(){
            return helloService.helloService();
        }
    
    }

      此时重新来验证一下断路器实现的服务回调逻辑,此时断开8081实例,当服务消费者轮询到8081时,不再是之前的错误内容,Hystrix服务回调生效

    三、模拟服务阻塞来验证断路器回调

      Hystrix默认超时时间是2000毫秒

      我们对service-hello的/hello接口做一些修改(重点是Thread.sleep()函数的使用):

    package com.stonegeek.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.logging.Logger;
    
    /**
     * Created by StoneGeek on 2018/5/27.
     * 博客地址:http://www.cnblogs.com/sxkgeek
     */
    @RestController
    public class HelloController {
        private final Logger logger =Logger.getLogger(String.valueOf(getClass()));
        @Autowired
        private DiscoveryClient client;
    
        /**
         * @Author: StoneGeek
         * @Date: 2018/5/29 
         * @Description:不加线服务塞的代码
         */
    //    @RequestMapping(value = "hello",method = RequestMethod.GET)
    //    public String  index(){
    //        ServiceInstance instance=client.getLocalServiceInstance();
    //        logger.info("/hello, host:"+instance.getHost()+", service_id:"+instance.getServiceId()+"service_port:"+instance.getPort());
    //        return "hello world ";
    //    }
    
        /**
         * @Author: StoneGeek
         * @Date: 2018/5/29
         * @Description: 加了Thread.sleep(3000)的服务阻塞代码,由于Hystrix默认超时时间为2000毫秒,
         * 所以这里采用了0至3000的随机数以让处理过程有一定概率发生超时来触发断路器
         */
    
        @RequestMapping(value = "/hello",method = RequestMethod.GET)
        public String  index() throws InterruptedException {
            ServiceInstance instance=client.getLocalServiceInstance();
            int sleepTime=new Random().nextInt(3000);
            logger.info("sleepTime:"+sleepTime);
            Thread.sleep(sleepTime);
            logger.info("sleepTime:"+sleepTime+"/hello, host:"+instance.getHost()+", service_id:"+instance.getServiceId()+"service_port:"+instance.getPort());
            return "hello world ";
        }
    }

       重新启动service-hello和ribbon-consumer模块,连续访问http://localhost:9000/ribbon-consumer几次,当RIBBON-CONSUMER的控制台输出的Spend time大于2000的时候,网页就会返回error,即服务消费者因调用的服务超时从而触发熔断请求,并调用回调逻辑返回结果

      此时Spring Could Hystrix的断路器就配置完成了!!!

      

  • 相关阅读:
    C++中的函数
    C++基本语句
    面向对象程序设计
    数据结构中的算法
    数据结构开篇
    条件编译
    文件包含
    简单的宏替换
    系统启动过程
    parted 命令学习
  • 原文地址:https://www.cnblogs.com/sxkgeek/p/9105951.html
Copyright © 2011-2022 走看看