学习 spring-cloud-aibaba第五篇,服务容错 Sentinel 下篇


特别声明:整理自慕课网大目师兄的微服务视频,链接:https://coding.imooc.com/learn/list/358.html
前情提要:接续服务容错 Sentinel 上篇

7.热点参数规则限流

热点即经常访问的数据
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
在这里插入图片描述

7.1 针对第二个传参限流

规则:如果第二个参数有值,QPS达到阈值,限流;如果第二个参数没传值,不限流

  • 准备代码
    @SentinelResource注解的作用是把参数纳入统计
@RestController
@Slf4j
@RequestMapping("/hot")
public class HotParamController {

    @GetMapping("basic")
    // 把方法的参数纳入统计
    @SentinelResource("basic")
    public String basic(String a,String b){
        log.info("basic......");
        return a + ":" + b;
    }

7.2 针对参数具体值的限流

在这里插入图片描述
热点规则 ------> 点击编辑(新增的时候是没有的)

8.系统规则限流

  • 应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 和线程数四个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
  • 系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效
    在这里插入图片描述
    在这里插入图片描述

8.1 四种阈值类型

8.1.1 Load

8.1.1.1 释义

  • 当系统load1(一分钟的负载)超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护
  • 系统容量:系统的 maxQps * minRt 计算得出
    maxQps:秒级统计最大的QP
    minRt:秒级统计的最小响应时间
  • 阈值:一般是CPU cores * 2.5
  • 仅对Linux/Unix-like机器生效

8.1.1.2 查看linux系统的load

uptime

在这里插入图片描述
红框里的三个数分别是1分钟内的平均负载,5分钟内的平均负载,15分钟内的平均负载

8.1.2 RT

所有入口流量的平均RT

8.1.3 线程数

所有入口流量的并发线程数

8.1.4 QPS

所有入口流量的QPS

9.授权规则限流

针对应用级别的黑白名单,参数的意思也都很好理解
在这里插入图片描述

10.sentinel控制台如何监控微服务数据?

11.常用的自定义参数

11.1 服务方常用的自定义参数

在这里插入图片描述

11.2 sentinel控制台常用的自定义参数

在这里插入图片描述
在这里插入图片描述

  • 自定义端口示例
java -jar -DServer.port=8090 sentinel-dashboard-1.6.3.jar

在这里插入图片描述
端口已改成8090
在这里插入图片描述

12.Feign,RestTemplate整合Sentinel

Sentinel不仅可以控制本服务的资源,还可以限流Feign和RestTemplate对外的请求

12.1 RestTemplate整合Sentinel

  • 添加注解@SentinelRestTemplate
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
  • sentinel控制台添加限流规则,簇点链路这边可以看到restTemplate发送的请求了,添加注解之前是没有的
    在这里插入图片描述
  • 关闭注解@SentinelRestTemplate,默认是开启的
resttemplate:
  sentinel:
    # 关闭 @SentinelRestTemplate 作用
    enabled: false

12.2 Feign整合Sentinel

  • 写配置
feign:
  sentinel:
    # IDE不会提示,但是有效
    enabled: true
  • 使用feign,sentinel这边已经可以看到 GET:http://user-center/reciteHis/testAno 资源了
    在这里插入图片描述
    如果spring-cloud-alibaba是0.9.0版本并且你的Feign是用继承模式实现的,那么会报 NullPointerException 错误,把spring-cloud-alibaba升级到2.1.0.RELEASE就没有了,我踩了这个坑…

13.Sentinel 扩展

13.1 Sentinel规则错误提示优化

之前,我测试的时候,限流规则,降级规则,热点规则等待,返回的都是一句错误消息,或者干脆报异常,无法区别到底违反了哪个规则?这一节的扩展可以优化这个问题

13.1.1 普通spring mvc资源的错误提示优化

  • ContentUrlBlockHandler 实现 UrlBlockHandler,并加上@Component
package com.zengchen.content.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zengchen.content.common.ResponseVO;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class ContentUrlBlockHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        ResponseVO sentinelErrorMsg = null;
        // 根据异常类型区分违反哪种规则
        if(e instanceof FlowException){// 限流异常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(100)
                    .msg("被限流规则阻挡")
                    .build();
        }else if(e instanceof DegradeException){ // 降级异常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(101)
                    .msg("被降级规则阻挡")
                    .build();
        }else if(e instanceof ParamFlowException){ // 热点参数异常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(102)
                    .msg("被热点参数规则阻挡")
                    .build();
        }else if(e instanceof SystemBlockException){ // 系统规则异常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(103)
                    .msg("被系统规则阻挡")
                    .build();
        }else if(e instanceof AuthorityException){ // 认证异常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(104)
                    .msg("被授权规则阻挡")
                    .build();
        }else{
            sentinelErrorMsg = ResponseVO.builder()
                    .code(999)
                    .msg("Unknown")
                    .build();
        }
        httpServletResponse.setStatus(500);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        new ObjectMapper().writeValue(httpServletResponse.getWriter(),sentinelErrorMsg);
    }
}

ResponseVO

package com.zengchen.content.common;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * http请求返回的最外层对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseVO<T> {

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 提示信息
     */
    private String msg;

    /**
     * 具体内容
     */
    private T data;

    public static ResponseVO success(Object data){
        ResponseVO responseVO = new ResponseVO();
        responseVO.setCode(0);
        responseVO.setData(data);
        responseVO.setMsg("OK");
        return responseVO;
    }

    public static ResponseVO fail(Integer code,String msg){
        ResponseVO responseVO = new ResponseVO();
        responseVO.setCode(code);
        responseVO.setMsg(msg);
        return responseVO;
    }

}

  • 测试
    限流规则
    在这里插入图片描述
    降级规则
    在这里插入图片描述
    热点参数规则
    失败了,程序并没有走进ContentUrlBlockHandler,因为热点参数测试方法上添加了@SentinelResource注解,有这个注解就不能使用ContentUrlBlockHandler来做错误页优化,下小节会讲


    系统规则
    在这里插入图片描述
    授权规则
    在这里插入图片描述

13.1.2 使用@SentinelResource注解资源的错误提示优化

主要使用@SentinelResource注解的blockHandlerblockHandlerClassfallbackfallbackClass四个属性

13.1.2.1 属性详解

  • blockHandler
    值是方法名,资源受到规则限制,会调用此方法
    方法要求:访问控制 public,返回值与资源方法一直,参数是资源方法参数 + BlockException e
  • blockHandlerClass
    blockHandler方法所在位置于资源方法不在一个类时,用这个属性指定
  • fallback
    值是方法名,资源方法发生异常时,会调用此方法
    方法要求:访问控制 public,返回值与资源方法一直,参数是资源方法参数 + Throwable e(版本1.6.0之和才有Throwable参数)
  • fallbackClass
    fallback方法所在位置于资源方法不在一个类时,用这个属性指定

13.1.2.2 示例代码

  • 资源方法:
@RestController
@Slf4j
public class SentinelResourceController {

    @GetMapping("/test-blockHandler")
    @SentinelResource(value = "blockHandler",
            blockHandler = "block",blockHandlerClass = SentinelResourceBlockHandler.class ,
            fallback = "fallBack",fallbackClass = SentinelResourceFallbackHandler.class)
    public String testBlockHandler(String a,String b) throws InterruptedException {
        throw new IllegalArgumentException();
    }

}
  • SentinelResourceBlockHandler
@Slf4j
public class SentinelResourceBlockHandler {

    public static  String block(String a,String b, BlockException e){
        String errorMsg = "Unknown";
        // 根据异常类型区分违反哪种规则
        if(e instanceof FlowException){// 限流异常
            errorMsg = "被限流规则阻挡";
        }else if(e instanceof DegradeException){ // 降级异常
            errorMsg = "被降级规则阻挡";
        }else if(e instanceof ParamFlowException){ // 热点参数异常
            errorMsg = "被热点参数规则阻挡";
        }else if(e instanceof SystemBlockException){ // 系统规则异常
            errorMsg = "被系统规则阻挡";
        }else if(e instanceof AuthorityException){ // 认证异常
            errorMsg = "被授权规则阻挡";
        }
        log.warn("block method,{}",errorMsg ,e);
        return errorMsg;
    }


}
  • SentinelResourceFallbackHandler
@Slf4j
public class SentinelResourceFallbackHandler {

    public static String fallBack(String a,String b,Throwable e){
        log.warn("fallBack method",e);
        return "出现异常了";
    }
}

13.1.3 Feign 通讯资源的错误提示优化

还记得@FeignClient注解吗?也有fallback,fallbackFactory属性
只能配置一个总的,因为都是一个feignClient,入口统一了

  • 代码
    SentinelFeignFallbackFactory
@Component
@Slf4j
public class SentinelFeignFallbackFactory implements FallbackFactory<UserCenterFeignClient> {

    @Override
    public UserCenterFeignClient create(Throwable throwable) {
        return new UserCenterFeignClient() {
            @Override
            public Page<ReciteHisOT> memberRctHisAno() {
                log.warn("memberRctHisAno FallbackFactory");
                return null;
            }
        };
    }
}

UserCenterFeignClientfallbackFactory

@FeignClient(name = "user-center", fallbackFactory = SentinelFeignFallbackFactory.class)
public interface UserCenterFeignClient
        extends UserCenterFeignClientService
{

    /**
     * FeignClient的name + GetMapping的value
     * 相当于 http://user-center/reciteHis/testAno
     * 和RestTemplate里写的url一模一样
     * @return Page
     */
//    @GetMapping(value = "/reciteHis/testAno")
//    Page<ReciteHisOT> memberRctHisAno();
}

配置个限流规则,触发规则时,进入fallbackFactory
在这里插入图片描述

13.1.4 RestTemplate 通讯资源的错误提示优化

还记得RestTemplate怎么和sentinel整合的吗?也是一个注解@SentinelRestTemplate,这个注解和@SentinelResource一样,有blockHandlerblockHandlerClassfallbackfallbackClass四个属性
也只能配置一个总的,因为都是一个入口
不写代码了,反正我用Feign

13.2 Sentinel 实现区分来源

授权规则的黑白名单需要来源,流控规则也有针对来源的属性,但是这个来源是怎么来的呢?sentinel提供了RequestOriginParser接口,写个类实现这个接口,唯一方法的返回值就是来源

  • 代码
@Component
public class ContentRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
//        String origin = httpServletRequest.getHeader("origin");
        String origin = httpServletRequest.getParameter("origin"); // 放在参数中方便测试
        if(StringUtils.isBlank(origin)){
            throw new IllegalArgumentException("origin is blank!");
        }
        return origin;
    }
}
  • 测试授权规则
    在这里插入图片描述
    在这里插入图片描述

13.3 Sentinel 对Restful Url的支持

问题说明:如下图,仅仅id不一样,这规则怎么配置?
在这里插入图片描述
sentinelUrlCleaner会帮助我们解决这个问题

  • restful接口DownController
    @GetMapping("restful/{id}")
    public String restful(@PathVariable Integer id) {
        log.info("restful start with {}", id);
        return "" + id;
    }

    @GetMapping("restful/{id}/test")
    public String restfulTest(@PathVariable Integer id) {
        log.info("restfulTest start with {}", id);
        return "test " + id;
    }
  • UrlCleaner
@Component
@Slf4j
public class ContentUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String path) {
        log.info("ContentUrlCleaner start with {}", path);
        String[] paths = path.split("/");
        path = Arrays.stream(paths).map(string -> {
            if (NumberUtils.isNumber(string)) {
                return "{number}";
            }
            return string;
        }).reduce((a, b) -> {
            return a + "/" + b;
        }).orElse("");
        log.info("ContentUrlCleaner end with {}", path);
        return path;
    }
}
  • sentinel控制台,簇点链路,url已经改变
    在这里插入图片描述

14.Sentinel 规则持久化

现在sentinel有个致命缺陷,服务重启或者sentinel重启,配置的规则都会丢失,如果是生产上,这还了得?这种事绝对不能发生。下面介绍三种解决办法

14.1 拉模式

参考大目老师手记:https://www.imooc.com/article/289402,生产环境不建议用这个,因为使用之后后患不小

14.2 推模式

14.3 阿里 ahas

免费的可以接入5个节点,专业的收费
开通地址:https://ahas.console.aliyun.com
开通说明:https://help.aliyun.com/document_detail/90323.html
在这里插入图片描述
在这里插入图片描述

14.3.1 加依赖

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>spring-boot-starter-ahas-sentinel-client</artifactId>
   <version>1.3.5</version>
</dependency>

在这里插入图片描述

14.3.2 添加埋点

springcloud-alibaba已经帮我们做过了,省略

14.3.3 配置启动参数

这里选择公网,才有license
在这里插入图片描述
我的ahas.license就不暴露出来了
在这里插入图片描述
注释掉

#      transport:
#        # 指定 sentinel 控制台地址
#        dashboard: localhost:8080

14.3.4 重启应用

随便访问一个接口,比如:http://localhost:8081/down/restful/2/test,再刷新阿里的控制台,就可以看到了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章