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-------

刪除的時候回調用多次,這樣我們就實現了請求緩存


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM