微服务中处理雪崩的方案

一、什么是雪崩效应

二、雪落而不雪崩的解决方案

隔离

熔断

降级

限流

超时

三、容错

容错的三个核心思想

  • 保证自己不被上游服务压垮
  • 保证自己不被下游服务拖垮
  • 保证自己不受外界环境影响

四、解决方案(Sentinel)

Sentinel规则

1. 流控规则

流量控制,其原理是监控流量的QPS(每秒查询率)或者并发线程数等指标
1.1流控模式

  • 直接(默认)

  • 关联(当关联的资源达到限流条件时,开启限流(适用于资源让步))

  • 链路(当从某个接口的过来的资源达到限流时,开启限流,有点类似于针对来源配置项,区别在针对来源配置项针对上级微服务,链路流控针对于上级接口,也就是粒度最细
    如果要使链路规则生效,则版本需要2.1.1之后,并且增加配置

    spring.cloud.sentinel.web-context-unify=false
    

1.2 流控效果

  • 快速失败
  • warm up(预热)
  • 排队等待

2. 降级规则

降级规则指的就是在满足什么条件的时候对服务进行降级

  • 慢调用比例
    RT:平均响应时间
    时间窗口:降级持续时间
    当平均响应时间 大于 输入的值的时候,进入预降级状态,如果1秒内持续进入的请求响应时间依然大于输入的值,则进入降级状态,降级时间为时间窗口输入的值
  • 异常比例
    当资源的每秒异常总数占总量的比例超过阈值后,就会进入降级状态
  • 异常数
    当1分钟之内的异常数大于异常数,则进入降级状态,注意 时间窗口需要大于60秒

3. 热点规则

热点参数流控规则是一种更细粒度的规则,它允许将规则具体细化到具体的参数上

4. 授权规则

自定义授权规则,继承 RequestOriginParse,自定义授权。

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;

import javax.servlet.http.HttpServletRequest;

/**
 * 自定义授权规则
 *
 * @author mrhy
 * @date 2020/10/3 11:25 下午
 * Copyright (C), 2018-2020
 */
@Configuration
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
//        判断来源或者指定带有的规则
        String origin = request.getHeader("origin");
        if (StringUtils.isEmpty(origin)){
            throw new RuntimeException("origin is not empty");
        }
        return origin;
    }
}

5. 系统规则

(不推荐)

自定义异常界面

继承UrlBlockHandler(Spring cloud alibaba 2.2.0 realease 之前)

继承 BlockExceptionHandler(Spring cloud alibaba 2.2.0 realease 之后)

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
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.alibaba.fastjson.JSON;
import com.mrhy.wisdomcommon.common.ObjectResponse;
import com.mrhy.wisdomcommon.common.OperationFlag;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;

import javax.print.attribute.standard.Media;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义sentinel界面
 *
 * @author mrhy
 * @date 2020/10/3 11:34 下午
 * Copyright (C), 2018-2020
 */
@Configuration
public class ExceptionHandlerPage implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        ObjectResponse response = null;
        if (e instanceof FlowException) {
            response = new ObjectResponse(OperationFlag.INTERFACE_LIMITING.getReturnCode(), OperationFlag.INTERFACE_LIMITING.getDescription());
        } else if (e instanceof DegradeException) {
            response = new ObjectResponse(OperationFlag.SERVICE_DEGRADATION.getReturnCode(), OperationFlag.SERVICE_DEGRADATION.getDescription());
        } else if (e instanceof ParamFlowException) {
            response = new ObjectResponse(OperationFlag.HOT_PARAMETER_LIMITING.getReturnCode(), OperationFlag.HOT_PARAMETER_LIMITING.getDescription());
        } else if (e instanceof SystemBlockException) {
            response = new ObjectResponse(OperationFlag.TRIGGER_SYSTEM_PROTECT_ROLE.getReturnCode(), OperationFlag.TRIGGER_SYSTEM_PROTECT_ROLE.getDescription());
        } else if (e instanceof AuthorityException) {
            response = new ObjectResponse(OperationFlag.AUTHORIZATION_RULES.getReturnCode(), OperationFlag.AUTHORIZATION_RULES.getDescription());
        }
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
        httpServletResponse.getWriter().write(JSON.toJSONString(response));
    }
}

状态码

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 操作符
 *
 * @author mrhy
 */
@AllArgsConstructor
@Getter
public enum OperationFlag {
    /**
     * 成功
     */
    SUCCESS(0, "操作成功"),
    /**
     * 失败
     */
    FAIL(-1, "操作失败"),
    /**
     * 参数违法
     */
    ILLEGAL_ARGUMENT(-2, "参数违法"),
    /**
     * 未登录
     */
    NOT_LOGIN(401, "未登录"),
    /**
     * 服务降级
     */
    SERVICE_DEGRADATION(100, "服务降级"),
    /**
     * 接口限流
     */
    INTERFACE_LIMITING(101, "接口限流"),
    /**
     * 热电参数限流
     */
    HOT_PARAMETER_LIMITING(102, "热点参数限流"),
    /**
     * 触发系统保护规则
     */
    TRIGGER_SYSTEM_PROTECT_ROLE(103, "触发系统保护规则"),
    /**
     * 授权规则不通过
     */
    AUTHORIZATION_RULES(104, "授权规则不通过"),
    ;

    private final Integer returnCode;

    private final String description;
}

/**
 * 响应实体类
 * @author mrhy
 */
public class ObjectResponse {
    private Integer returnCode;
    private String description;
    private Object result;

    public ObjectResponse() {
        this.returnCode = OperationFlag.SUCCESS.getReturnCode();
        this.description = OperationFlag.SUCCESS.getDescription();
    }

    public ObjectResponse(Integer returnCode) {
        this(returnCode, (String) null);
    }

    public ObjectResponse(Object result) {
        this.returnCode = OperationFlag.SUCCESS.getReturnCode();
        this.description = OperationFlag.SUCCESS.getDescription();
        this.result = result;
    }

    public ObjectResponse(Integer returnCode, String description) {
        this(returnCode, description, (Object) null);
    }

    public ObjectResponse(Integer returnCode, String description, Object result) {
        this.returnCode = returnCode;
        this.description = description;
        this.result = result;
    }

    public Integer getReturnCode() {
        return returnCode;
    }

    public void setReturnCode(Integer returnCode) {
        this.returnCode = returnCode;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }
}

将配置同步到nacos

引入依赖

     <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

配置application

## ds指的数据源,可以随便写
## server-addr 指的是nacos地址
spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848 
## 对应配置中的data-id
spring.cloud.sentinel.datasource.ds.nacos.data-id=sentinel-wisdom-web
## 对应配置中的group-id
spring.cloud.sentinel.datasource.ds.nacos.group-id=DEFAULT_GROUP
## 对应文件类型
spring.cloud.sentinel.datasource.ds.nacos.data-type=json
## 降级规则
spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow

配置nacos(配置内容)

[
    {
        "resource": "test", // 资源名称 
        "limitApp": "default", //来源应用
        "grade": 1,//阈值类型 0 线程数 1QPS
        "count": 5,// 单机阈值
        "strategy": 0,// 流控模式 0 直接 1 关联 2 链路
        "controlBehavior": 0,// 流控效果 0 快速失败 1 warmup 2 排队等待
        "clusterMode": false // 是否是集群模式
    }
]

参考网站sentinel github 网站

将配置持久化到本地

编写配置类

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * sentinel 配置持久化到本地
 *
 * @author mrhy
 * @date 2020/10/4 10:17 上午
 * Copyright (C), 2018-2020
 */
@Configuration
public class FilePersistence implements InitFunc {
    private final String APPLICATION_NAME = "wisdom-web";

    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + APPLICATION_NAME;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);

        // 注册一个可读数据源,用来定时读取本地的json文件,更新到规则缓存中
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS =
                new FileRefreshableDataSource<>(flowRulePath, flowRuleListParser);
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}


在resource 下 新建这个目录

META-INF.services

在这个目录下新建文件

com.alibaba.csp.sentinel.init.InitFunc

添加内容

上面写的java文件的路径 copy reference

posted @ 2020-12-24 17:19  小憨马  阅读(304)  评论(0)    收藏  举报