• Spring Cloud之服务雪崩(一)


    什么是服务雪崩?

    参考: <<重新定义spring cloud>>

    代码:https://gitee.com/08081/hello-springcloud/tree/springcloud-fallback/

    在微服务中,我们是服务于服务之间调用,当在微服务突然有大量的请求过来,一个服务瘫痪之后,后面的服务的请求积压,这就造成了服务雪崩!

    一个服务瘫痪,另外调用这个服务的服务就会超时,导致整个服务群瘫痪.

    造成雪崩的原因可以归结为以下三点:

    1. 服务提供者不可用 (硬件故障,程序bug 缓存击穿,用户大量请求)

    2. 重试加大流量(用户重试,代码逻辑重试)

    3. 服务调用者不可用(同步等待造成的资源耗尽)

    最终的结果就是一个服务不可用,导致一系列的服务不可用,这种后果无法预料

    如何解决在灾难性雪崩效应?

    1.降级: 超时降级资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据,实现一个fallback方法,当请求出现异常之后,调用这个方法

    2.隔离(线程池隔离和信号量隔离): 限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用

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

    4.缓存:提供请求缓存

    5.提供请求合并

    解决方案之一服务降级

     先说遇到的坑吧

    1. 坑1 mapping 报错.feign调用服务端的时候说实例已经实例过了

    当时报错的时候错误信息没有记录.具体参考的是这篇文章 感谢作者.https://my.oschina.net/u/2000675/blog/2244769

    //@FeignClient(name = "book-service",fallback = BookServiceFallback.class,path = "/")

    2.坑2 在写好fallback方法之后,一直调用fallback方法,不调用原来的方法.不知为什么hystrix的默认时间一直1秒钟

    尝试配置了很多次.最终结合官网解决:

    feign:
      hystrix:
        enabled: true
        #局部配置 这个必须加
      client:
        config:
          default:
            connectTimeout: 5000
            readTimeout: 5000
            loggerLevel: basic
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 2000

    3.坑3: 使用fallbackFactory报错

    要是因为我返回空的list还去添加

    代码

    如何下载代码运行案例?

    1.到码云下下载项目

    2.运行ch3-eureka-ribbon 下的eureka-server

    3.运行ch4-fegin下的ch4-book-service

    4.运行ch6-hystrix下的book-consumer-hystrix

    我创建了一个子项目book-consumer-hystrix

    pom文件如下:

    spring:
      application:
        name: book-hystrix
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:8888/eureka/
      instance:
        prefer-ip-address: true
    server:
      port: 8002
    feign:
      hystrix:
        enabled: true
        #局部配置
      client:
        config:
          default:
            connectTimeout: 5000
            readTimeout: 5000
            loggerLevel: basic
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 5000
    View Code

    启动类:

    /**
     * Created by xiaodao
     * date: 2019/7/18
     */
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    @EnableHystrix
    public class BookConsumerHystrixApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(BookConsumerHystrixApplication.class,args);
        }
    }
    View Code

     feignClient调用类:

    注意坑2.的解决方案:必须加path

    /**
     * Created by xiaodao
     * date: 2019/7/17
     */
    @Component
    //@FeignClient(name = "book-service",fallback = BookServiceFallback.class,path = "/")
    @FeignClient(name = "book-service",fallbackFactory = WebFeignFallbackFactory.class,path = "/")
    public interface BookService extends BookApi {
    }

    controller 调用类:

    /**
     * Created by xiaodao
     * date: 2019/7/17
     */
    @RestController
    @RequestMapping("/book")
    @AllArgsConstructor
    public class BookController {
    
    
        private BookService bookService;
    
        @GetMapping("/findList")
        public List<Book> findList(){
          return   bookService.findList();
        }
    
    
    }

    fallback的俩个类

    一个是正常的fallback

    另一个是打印错误日志的fallbackFactory

    /**
     * Created by xiaodao
     * date: 2019/7/18
     */
    @Component
    public class BookServiceFallback implements BookService {
        public List<Book> findList() {
            return Collections.emptyList();
        }
    }
    /**
     * Created by xiaodao
     * date: 2019/7/18
     * 这个fallback工厂类为了查看回退的原因的异常信息
     */
    @Component
    public class WebFeignFallbackFactory implements FallbackFactory<BookService> {
        private static final Logger LOGGER = LoggerFactory.getLogger(WebFeignFallbackFactory.class);
    
        @Override
        public BookService create(Throwable throwable) {
    
            return new BookService(){
                // 日志最好放在各个fallback方法中,而不要直接放在create方法中
                @Override
                public List<Book> findList() {
                    WebFeignFallbackFactory.LOGGER.info("fallback; reason was:", throwable);
    
                    Book book = Book.builder().id(1000).name("异常回退!").build();
                    List<Book> list = new ArrayList<>();
                    list.add(book);
                    return list;
    
                };
            };
    
        }
    }

    以下4种情况触发fallback调用

    1.方法抛出非HystrixBadRequestException异常

    2.方法调用超时

    3.熔断器开启拦截调用

    4.线程池/信号量/队列是否跑满

    解决方案之一服务请求缓存

    hystrix 有俩个缺点:

    1.不支持集群

    2.不支持第三方缓存

    所以我们做集成第三方缓存,并且可以支持集群.所以我们选用redis, 在书上,和网上的案例很多都是基于hystrixCommand 来实现的,我们来来模仿真实案例来实现.

    ch6下创建俩个项目

    1.book-consumer-hystrix-cache

    2.book-service-cache

    启动eurekaserver服务

    代码

    book-consumer-hystrix-cache服务

    controller 添加了俩个方法

    @RestController
    @RequestMapping("/book-consumer")
    @AllArgsConstructor
    public class BookController {
    
    
        private BookService bookService;
    
        @GetMapping("/findList")
        public List<Book> findList(){
          return   bookService.findList();
        }
    
        @GetMapping("get/{id}")
        public Book get(@PathVariable(value = "id")  Integer id ){
            return bookService.get(id);
        }
    
        @GetMapping("del/{id}")
        public Book del(@PathVariable(value = "id") Integer id ){
            return bookService.del(id);
        }
    
    
    }
    View Code

    service:

    @FeignClient(name = "book-service-cache",path = "/")
    public interface BookService extends BookApi {
    }

    book-service-cache 服务

    pom文件 依赖

          <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>

    bootstrap.yml配置了redis的连接池

    spring:
      application:
        name: book-service-cache
      #redis的索引默认是0
      redis:
        database: 1
        host: localhost
        port: 6379
        password:
        lettuce:
          #负数表示没有限制
          pool:
            max-active: 100
            #最大空闲连接
            max-idle: 10
            #连接池最大阻塞等待时间(使用负数表示没有限制)
            max-wait: -1ms
            #连接池最小空闲连接
            min-idle: 0
    
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:8888/eureka/
      instance:
        prefer-ip-address: true
    server:
      port: 8000
    View Code

    bookServiceImpl

    /**
     * Created by xiaodao
     * date: 2019/7/17
     */
    
    @RestController
    @RequestMapping
    @CacheConfig(cacheNames = {"xiaodao.book"})
    public class BookServiceImpl implements BookApi {
    //    @GetMapping(value = "list")
        @Override
        public List<Book> findList() {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Book book =Book.builder().id(1).name("第一本书").build();
            System.out.println(book);
           List<Book> list =  new  CopyOnWriteArrayList<>();
           list.add(book);
           return list;
        }
    
    
        @Cacheable(key = "'get-' + #id")
        @Override
        public Book get(Integer id) {
            System.out.println("-----查询------"+id+"-------");
            return Book.builder().id(id).name("使用缓存").build();
        }
    
        @CacheEvict(key = "'get-'+ #id")
        @Override
        public Book del(Integer id) {
            System.out.println("------删除-----"+id+"-------");
            return Book.builder().id(id).name("使用缓存").build();
        }
    
    
    }
    View Code

    还有book-api也需要改造一下.

    就是加了俩个接口.

    public interface BookApi {
    
        @GetMapping(value = "/book/list")
        List<Book> findList();
        @GetMapping(value = "/book/get/{id}")
        Book get(@PathVariable(value = "id")  Integer id );
    
        @GetMapping(value = "/book/del/{id}")
        Book del(@PathVariable(value = "id") Integer id );
    
    
    
    }
    View Code

    上面基本就是缓存方案的所有代码的所有代码

    贴一张注册中心的图:

    http://localhost:8002/book-consumer/get/100

    {"id":100,"name":"使用缓存"}

    在后台看到无论请求多少遍.只有第一遍,会加载后去就会到缓存中查找

    ------删除-----100-------
    ------删除-----100-------
    ------删除-----100-------
    -----查询------100-------

    删除的时候回调用多次,这样我们就实现了请求缓存

  • 相关阅读:
    TF中的自定义正则项
    [工具]多线程下载 axel
    [算法]kv-memory 表示dense特征
    [code]tensorflow分桶
    [code]Keras API的用法记录
    vim显示下划线不高亮问题
    [代码] kv2sparse
    [代码]并发执行python的例子
    AI算法手册
    [工具]soundflower
  • 原文地址:https://www.cnblogs.com/bj-xiaodao/p/11203573.html
Copyright © 2020-2023  润新知