微服务与Nacos概述-4

限流规则配置

每次服务重启后 之前配置的限流规则就会被清空因为是内存态的规则对象,所以就要用到Sentinel一个特性ReadableDataSource 获取文件、数据库或者配置中心是限流规则

依赖:spring-cloud-alibaba-sentinel-datasource

通过文件读取限流规则

spring.cloud.sentinel.datasource.ds1.file.file=classpath:flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

在resources新建一个文件 比如flowrule.json 添加限流规则

[
    {
        "resource": "resource",
        "controlBehavior": 0,
        "count": 1,
        "grade": 1,
        "limitApp": "default",
        "strategy": 0
    }
]
@RestController
public class TestController {
    @GetMapping("/test1")
    @SentinelResource(value = "resource")
    public String sayHello(String name){
        if(name==null || name.trim().length()<1)
        throw new IllegalArgumentException("参数为空!");
        return "Hello "+name+"!";
    }
}

一条限流规则主要有:

  • resource资源名,即限流规则的作用对象

  • count限流阈值

  • grade限流阈值类型,0表示线程,1表示QPS

  • limitApp流控针对的调用来源,若为 default 则不区分调用来源

  • strategy调用关系限流策略,0表示直接,1表示关联,2表示链路

  • controlBehavior流量控制效果,0表示快速失败,1表示Warm Up,2表示排队等待

隔离降级

限流是一种预防措施,虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。

  • 线程隔离就是调用者在调用服务提供者时,给每个调用的请求分配独立线程池,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽

  • 熔断降级是在调用方这边加入断路器,统计对服务提供者的调用,如果调用的失败比例过高,则熔断该业务,不允许访问该服务的提供者了。

不管是线程隔离还是熔断降级,都是对客户端调用方的保护。需要在调用方发起远程调用时做线程隔离、或者服务熔断。

RestTemplate整合Sentinel

Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。

  • @SentinelRestTemplate 注解的属性支持限流(blockHandler, blockHandlerClass)和降级(fallback, fallbackClass)的处理。

SentinelRestTemplate用于sentinel 集成 RestTemplate。可以添加在 RestTemplate上,全局的限流容错处理,优先级低于局部限流容错注解。例如 @SentinelRestTemplate(blockHandler ="handleException", blockHandlerClass = ExceptionUtil.class, fallback = "fallback",fallbackClass = ExceptionUtil.class)

  • blockHandler限流策略 (方法名,方法必须是静态的)

  • blockHandlerClass 限流方法类

  • fallback 熔断降级策略(方法名,方法必须是静态的)

  • fallbackClass 熔断降级类

@Bean
@LoadBalance
@SentinelRestTemplate
public RestTemplate restTemplate(){
    return new RestTemplate();
}

对应实现,static修饰参数类型不能出错

public class ExceptionUtil {
    // 服务流量控制处理
    public static ClientHttpResponse handleException(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException exception)
    {
        exception.printStackTrace();
        return new SentinelClientHttpResponse( JSON.toJSONString(new
Product(1, "服务流量控制处理-托底数据")));
    }
    // 服务熔断降级处理
    public static ClientHttpResponse fallback(HttpRequest request,byte[]
body, ClientHttpRequestExecution execution, BlockException exception) {
        exception.printStackTrace();
        return new SentinelClientHttpResponse( JSON.toJSONString(new
Product(1, "服务熔断降级处理-托底数据")));
    }
}

FeignClient整合Sentinel

SpringCloud中微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。

在启动类上面加个注解 @EnableFeignClients

修改OrderService的application.yml文件,开启Feign的Sentinel功能 feign.sentinel.enabled=true开启feign对sentinel的支持

方法1:定义对应FeignClient接口的实现类,提供对应的处理

@Component
public class UserClientImpl implements UserClient {
    @Override
    public JsonResult getAllUsers() {
        return JsonResult.failure(103,"加载失败...");
    }
}

然后在FeignClient上添加注解说明

@FeignClient(value="user-provider",fallback = UserClientImpl.class)
public interface UserClient {
    @GetMapping("/users")
    public JsonResult getAllUsers();
}

方法2:自定义工厂 [推荐使用]

@Component
public class MyFeignFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable cause) {
        return new UserClient() {
            @Override
            public JsonResult getAllUsers() {
                return JsonResult.failure(1031,"加载失败...");
            }
        };
    }
}

在配置类中添加配置

@Bean
public MyFeignFactory myClientFallbackFactory(){
    return new MyFeignFactory();
}

在FeignClient接口上添加配置使用MyFeignFactory

@FeignClient(value="user-provider",fallbackFactory = MyFeignFactory.class)
public interface UserClient {
    @GetMapping("/users")
    public JsonResult getAllUsers();
}

失败降级逻辑

业务失败后不能直接报错,而应该返回用户一个友好提示或者默认结果,这个就是失败降级逻辑。可以给FeignClient编写失败后的降级逻辑。

  • 方式一FallbackClass无法对远程调用的异常做处理

  • 方式二FallbackFactory可以对远程调用的异常做处理

定义类实现FallbackFactory

@Slf4j
public class UserClientFallbackFactory implements
FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public User findById(Long id) {
                log.error("查询用户异常", throwable);
                return new User();
            }
        };
    }
}

项目中的DefaultFeignConfiguration类中将UserClientFallbackFactory注册为一个Bean

@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
    return new UserClientFallbackFactory();
}

项目中的UserClient接口中使用UserClientFallbackFactory

@FeignClient(value = "userservice", fallbackFactory =
UserClientFallbackFactory.class) public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

在需要被保护的方法上使用@SentinelResource注解进行熔断配置。与Hystrix不同的是,Sentinel对抛出异常和熔断降级做了更加细致的区分,通过 blockHandler 指定熔断降级方法,通过 fallback 指定 触发异常执行的降级方法

@GetMapping("/buy/{id}")
@SentinelResource(value="order",blockHandler = "orderblockHandler",fallback
= "orderfallback")
public Product order(@PathVariable Long id) {
    return restTemplate.getForObject("http://shop-
service/product/product/1", Product.class);
}
//降级方法
public Product orderblockHandler(Long id) {
    Product product = new Product();
    product.setId(-1l);
    product.setProductName("触发熔断降级方法");
    return product;
}
//降级方法
public Product orderfallback(Long id) {
    Product product = new Product();
    product.setId(-1l);
    product.setProductName("触发抛出异常方法");
    return product;
}

隔离降级总结

Sentinel支持的雪崩解决方案:线程隔离的仓壁模式、降级熔断

你可能感兴趣的:(微服务,架构)