Sentinel規則配置使用持久化(基於Redis)

  • 本次引用版本 1.7.2   目前最新版本爲1.8.0  git地址
  • 從 Sentinel 1.4.0 開始,sentinel抽取出了接口用於向遠程配置中心推送規則以及拉取規則:1. DynamicRuleProvider<T>: 拉取規則     2.DynamicRulePublisher<T>: 推送規則

  •  用戶需實現 DynamicRuleProvider 和 DynamicRulePublisher 接口,即可實現應用維度推送

第一步,啓動redis 從redis官網下載redis

第二步:sentinel-dashboard 配置修改(或者直接下載已經改好的jar )

在: com.alibaba.csp.sentinel.dashboard.rule 目錄下添加新的包redis用於存放以下幾個類

DegradeRuleRedisProvider.java

package com.alibaba.csp.sentinel.dashboard.rule.redis;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @package: com.alibaba.csp.sentinel.dashboard.rule.redis
 * @title: FlowRuleRedisProvider
 * @projectName: sentinel-parent
 * @author zhuhongqiang3
 * @date:  2020-05-18 10:30
 * @version: V1.0
 * @retrun com.alibaba.csp.sentinel.dashboard.rule.redis.sentinel-parent
 */
@Component("degradeRuleRedisProvider")
public class DegradeRuleRedisProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {
    @Autowired
    private RedisUtil redisConfigUtil;

    @Override
    public List<DegradeRuleEntity> getRules(String appName) throws Exception {
        String rules = redisConfigUtil.getString(RuleConsts.RULE_DEGRADE + appName);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return JSONObject.parseArray(rules,DegradeRuleEntity.class);
    }

}
package com.alibaba.csp.sentinel.dashboard.rule.redis;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @package: com.alibaba.csp.sentinel.dashboard.rule.redis
 * @title: FlowRuleRedisPublisher
 * @projectName: sentinel-parent
 * @author zhuhongqiang3
 * @date:  2020-05-18 10:30
 * @version: V1.0
 * @retrun com.alibaba.csp.sentinel.dashboard.rule.redis.sentinel-parent
 */
@Component("degradeRuleRedisPublisher")
public class DegradeRuleRedisPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {

    @Autowired
    private RedisUtil redisConfigUtil;

    @Override
    public void publish(String app, List<DegradeRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        String strs = JSON.toJSONString(rules);
        redisConfigUtil.setString(RuleConsts.RULE_DEGRADE + app, strs);
    }

}

   

package com.alibaba.csp.sentinel.dashboard.rule.redis;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @package: com.alibaba.csp.sentinel.dashboard.rule.redis
 * @title: FlowRuleRedisProvider
 * @projectName: sentinel-parent
 * @description: //TODO
 * @author zhuhongqiang3
 * @date:  2020-05-18 10:30
 * @version: V1.0
 * @retrun com.alibaba.csp.sentinel.dashboard.rule.redis.sentinel-parent
 */
@Component("flowRuleRedisProvider")
public class FlowRuleRedisProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
    @Autowired
    private RedisUtil redisConfigUtil;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String rules = redisConfigUtil.getString(RuleConsts.RULE_FLOW + appName);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return JSONObject.parseArray(rules,FlowRuleEntity.class);
    }

}
package com.alibaba.csp.sentinel.dashboard.rule.redis;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @package: com.alibaba.csp.sentinel.dashboard.rule.redis
 * @title: FlowRuleRedisPublisher
 * @projectName: sentinel-parent
 * @description: //TODO
 * @author zhuhongqiang3
 * @date:  2020-05-18 10:30
 * @version: V1.0
 * @retrun com.alibaba.csp.sentinel.dashboard.rule.redis.sentinel-parent
 */
@Component("flowRuleRedisPublisher")
public class FlowRuleRedisPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private RedisUtil redisConfigUtil;

    @Override
    public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(app, "app name cannot be empty");
        if (rules == null) {
            return;
        }
        String strs = JSON.toJSONString(rules);
        redisConfigUtil.setString(RuleConsts.RULE_FLOW + app, strs);
    }
}
package com.alibaba.csp.sentinel.dashboard.rule.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author zhuhongqiang3
 * @package: com.alibaba.csp.sentinel.dashboard.rule.redis
 * @title: RedisUtil
 * @projectName: sentinel-parent
 * @date: 2020-05-18 10:33
 * @version: V1.0
 * @retrun com.alibaba.csp.sentinel.dashboard.rule.redis.sentinel-parent
 */
@Component
public class RedisUtil {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplate;
    }

    /**
     * 存放string類型
     *
     * @param key
     * @param data
     * @param timeout
     */
    public void setString(String key, String data, Long timeout) {
        try {
            stringRedisTemplate.opsForValue().set(key, data, 7, TimeUnit.DAYS);
            if (timeout != null) {
                stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 存放string類型
     *
     * @param key
     * @param data
     */
    public void setString(String key, String data) {
        setString(key, data, null);
    }

    /**
     * 根據key查詢string類型
     *
     * @param key
     * @return
     */
    public String getString(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return value;
    }

    /**
     * 根據對應的key刪除key
     *
     * @param key
     */
    public Boolean delKey(String key) {
        return stringRedisTemplate.delete(key);
    }
}
package com.alibaba.csp.sentinel.dashboard.rule.redis;
/**
 * @package: com.alibaba.csp.sentinel.dashboard.rule.redis
 * @title: RuleConsts
 * @projectName: sentinel-parent
 * @description: //TODO
 * @author zhuhongqiang3
 * @date:  2020-05-18 10:33
 * @version: V1.0
 * @retrun com.alibaba.csp.sentinel.dashboard.rule.redis.sentinel-parent
 */
public class RuleConsts {

    //流控規則key前綴
    public static final String RULE_FLOW = "sentinel_rule_flow_";
    //限流規則key前綴
    public static final String RULE_DEGRADE = "sentinel_rule_degrade_";
    //系統規則key前綴
    public static final String RULE_SYSTEM = "sentinel_rule_system_";

}
  •  

替換com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1.java 內容改成以下的代碼

package com.alibaba.csp.sentinel.dashboard.controller;

import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;

/**
 * Flow rule controller.
 *
 * @author leyou
 * @author Eric Zhao
 */
@RestController
@RequestMapping(value = "/v1/flow")
public class FlowControllerV1_Mark {

    private final Logger logger = LoggerFactory.getLogger(FlowControllerV1_Mark.class);

    @Autowired
    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;

    @Autowired
    @Qualifier("flowRuleRedisProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleRedisPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
    @Autowired
    private AuthService<HttpServletRequest> authService;

    @Autowired
    private SentinelApiClient sentinelApiClient;

    @GetMapping("/rules")
    public Result<List<FlowRuleEntity>> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app) {
        logger.info("flow觸發rules()>>>>>>>>>>");
        AuthUser authUser = authService.getAuthUser(request);
        authUser.authTarget(app, PrivilegeType.READ_RULE);

        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        try {
            List<FlowRuleEntity> rules = ruleProvider.getRules(app);
            if (rules != null && !rules.isEmpty()) {
                for (FlowRuleEntity entity : rules) {
                    entity.setApp(app);
                    if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {
                        entity.setId(entity.getClusterConfig().getFlowId());
                    }
                }
            }
            rules = repository.saveAll(rules);
            return Result.ofSuccess(rules);
        } catch (Throwable throwable) {
            logger.error("Error when querying flow rules", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }

    private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {
        if (entity == null) {
            return Result.ofFail(-1, "invalid body");
        }
        if (StringUtil.isBlank(entity.getApp())) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isBlank(entity.getLimitApp())) {
            return Result.ofFail(-1, "limitApp can't be null or empty");
        }
        if (StringUtil.isBlank(entity.getResource())) {
            return Result.ofFail(-1, "resource can't be null or empty");
        }
        if (entity.getGrade() == null) {
            return Result.ofFail(-1, "grade can't be null");
        }
        if (entity.getGrade() != 0 && entity.getGrade() != 1) {
            return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got");
        }
        if (entity.getCount() == null || entity.getCount() < 0) {
            return Result.ofFail(-1, "count should be at lease zero");
        }
        if (entity.getStrategy() == null) {
            return Result.ofFail(-1, "strategy can't be null");
        }
        if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {
            return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");
        }
        if (entity.getControlBehavior() == null) {
            return Result.ofFail(-1, "controlBehavior can't be null");
        }
        int controlBehavior = entity.getControlBehavior();
        if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {
            return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");
        }
        if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {
            return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");
        }
        if (entity.isClusterMode() && entity.getClusterConfig() == null) {
            return Result.ofFail(-1, "cluster config should be valid");
        }
        return null;
    }

    @PostMapping("/rule")
    public Result<FlowRuleEntity> apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {
        logger.info("flow觸發rule()>>>>>>>>>>");
        AuthUser authUser = authService.getAuthUser(request);
        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);

        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
        if (checkResult != null) {
            return checkResult;
        }
        entity.setId(null);
        Date date = new Date();
        entity.setGmtCreate(date);
        entity.setGmtModified(date);
        entity.setLimitApp(entity.getLimitApp().trim());
        entity.setResource(entity.getResource().trim());
        try {
            entity = repository.save(entity);
        } catch (Throwable throwable) {
            logger.error("Failed to add flow rule", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
            logger.error("Publish flow rules failed after rule add");
        }
        return Result.ofSuccess(entity);
    }

    @PutMapping("/save.json")
    public Result<FlowRuleEntity> updateIfNotNull(HttpServletRequest request, Long id, String app,
                                                  String limitApp, String resource, Integer grade,
                                                  Double count, Integer strategy, String refResource,
                                                  Integer controlBehavior, Integer warmUpPeriodSec,
                                                  Integer maxQueueingTimeMs) {

        logger.info("flow觸發save.json()>>>>>>>>>>");
        AuthUser authUser = authService.getAuthUser(request);
        authUser.authTarget(app, PrivilegeType.WRITE_RULE);

        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }
        FlowRuleEntity entity = repository.findById(id);
        if (entity == null) {
            return Result.ofFail(-1, "id " + id + " dose not exist");
        }
        if (StringUtil.isNotBlank(app)) {
            entity.setApp(app.trim());
        }
        if (StringUtil.isNotBlank(limitApp)) {
            entity.setLimitApp(limitApp.trim());
        }
        if (StringUtil.isNotBlank(resource)) {
            entity.setResource(resource.trim());
        }
        if (grade != null) {
            if (grade != 0 && grade != 1) {
                return Result.ofFail(-1, "grade must be 0 or 1, but " + grade + " got");
            }
            entity.setGrade(grade);
        }
        if (count != null) {
            entity.setCount(count);
        }
        if (strategy != null) {
            if (strategy != 0 && strategy != 1 && strategy != 2) {
                return Result.ofFail(-1, "strategy must be in [0, 1, 2], but " + strategy + " got");
            }
            entity.setStrategy(strategy);
            if (strategy != 0) {
                if (StringUtil.isBlank(refResource)) {
                    return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");
                }
                entity.setRefResource(refResource.trim());
            }
        }
        if (controlBehavior != null) {
            if (controlBehavior != 0 && controlBehavior != 1 && controlBehavior != 2) {
                return Result.ofFail(-1, "controlBehavior must be in [0, 1, 2], but " + controlBehavior + " got");
            }
            if (controlBehavior == 1 && warmUpPeriodSec == null) {
                return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");
            }
            if (controlBehavior == 2 && maxQueueingTimeMs == null) {
                return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");
            }
            entity.setControlBehavior(controlBehavior);
            if (warmUpPeriodSec != null) {
                entity.setWarmUpPeriodSec(warmUpPeriodSec);
            }
            if (maxQueueingTimeMs != null) {
                entity.setMaxQueueingTimeMs(maxQueueingTimeMs);
            }
        }
        Date date = new Date();
        entity.setGmtModified(date);
        try {
            entity = repository.save(entity);
            if (entity == null) {
                return Result.ofFail(-1, "save entity fail");
            }
        } catch (Throwable throwable) {
            logger.error("save error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
            logger.info("publish flow rules fail after rule update");
        }
        return Result.ofSuccess(entity);
    }

    @DeleteMapping("/delete.json")
    public Result<Long> delete(HttpServletRequest request, Long id) {
        logger.info("flow觸發delete.json()>>>>>>>>>>");
        AuthUser authUser = authService.getAuthUser(request);
        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }
        FlowRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null) {
            return Result.ofSuccess(null);
        }
        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);
        try {
            repository.delete(id);
        } catch (Exception e) {
            return Result.ofFail(-1, e.getMessage());
        }
        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
            logger.info("publish flow rules fail after rule delete");
        }
        return Result.ofSuccess(id);
    }

    private boolean publishRules(String app, String ip, Integer port) {
        List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
        try {
            rulePublisher.publish(app, rules);
            logger.info("flow添加限流規則成功{}", JSON.toJSONString(rules));
        } catch (Exception e) {
            e.printStackTrace();
            logger.warn("publishRules failed", e);
            return false;
        }
        //核心代碼,sentinel-dashboard通過http的形式進行數據推送,客戶端接收後將規則保存在本地內存中
        return sentinelApiClient.setFlowRuleOfMachine(app, ip, port, rules);
    }

}

 替換com.alibaba.csp.sentinel.dashboard.controller.DegradeController.java爲以下代碼

package com.alibaba.csp.sentinel.dashboard.controller;

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;
import java.util.List;

/**
 * @author leyou
 */
@Controller
@RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE)
public class DegradeController {

    private final Logger logger = LoggerFactory.getLogger(DegradeController.class);

    @Autowired
    private InMemDegradeRuleStore repository;
    @Autowired
    private SentinelApiClient sentinelApiClient;
    @Autowired
    @Qualifier("degradeRuleRedisProvider")
    private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("degradeRuleRedisPublisher")
    private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;

    @ResponseBody
    @RequestMapping("/rules.json")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result<List<DegradeRuleEntity>> queryMachineRules(String app, String ip, Integer port) {

        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isEmpty(ip)) {
            return Result.ofFail(-1, "ip can't be null or empty");
        }
        if (port == null) {
            return Result.ofFail(-1, "port can't be null");
        }
        try {
            List<DegradeRuleEntity> rules = ruleProvider.getRules(app);
            rules = repository.saveAll(rules);
            return Result.ofSuccess(rules);
        } catch (Throwable throwable) {
            logger.error("queryApps error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }

    @ResponseBody
    @RequestMapping("/new.json")
    @AuthAction(PrivilegeType.WRITE_RULE)
    public Result<DegradeRuleEntity> add(String app, String ip, Integer port, String limitApp, String resource,
                                         Double count, Integer timeWindow, Integer grade) {
        if (StringUtil.isBlank(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isBlank(ip)) {
            return Result.ofFail(-1, "ip can't be null or empty");
        }
        if (port == null) {
            return Result.ofFail(-1, "port can't be null");
        }
        if (StringUtil.isBlank(limitApp)) {
            return Result.ofFail(-1, "limitApp can't be null or empty");
        }
        if (StringUtil.isBlank(resource)) {
            return Result.ofFail(-1, "resource can't be null or empty");
        }
        if (count == null) {
            return Result.ofFail(-1, "count can't be null");
        }
        if (timeWindow == null) {
            return Result.ofFail(-1, "timeWindow can't be null");
        }
        if (grade == null) {
            return Result.ofFail(-1, "grade can't be null");
        }
        if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
            return Result.ofFail(-1, "Invalid grade: " + grade);
        }
        DegradeRuleEntity entity = new DegradeRuleEntity();
        entity.setApp(app.trim());
        entity.setIp(ip.trim());
        entity.setPort(port);
        entity.setLimitApp(limitApp.trim());
        entity.setResource(resource.trim());
        entity.setCount(count);
        entity.setTimeWindow(timeWindow);
        entity.setGrade(grade);
        Date date = new Date();
        entity.setGmtCreate(date);
        entity.setGmtModified(date);
        try {
            entity = repository.save(entity);
        } catch (Throwable throwable) {
            logger.error("add error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(app, ip, port)) {
            logger.info("publish degrade rules fail after rule add");
        }
        return Result.ofSuccess(entity);
    }

    @ResponseBody
    @RequestMapping("/save.json")
    @AuthAction(PrivilegeType.WRITE_RULE)
    public Result<DegradeRuleEntity> updateIfNotNull(Long id, String app, String limitApp, String resource,
                                                     Double count, Integer timeWindow, Integer grade) {
        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }
        if (grade != null) {
            if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
                return Result.ofFail(-1, "Invalid grade: " + grade);
            }
        }
        DegradeRuleEntity entity = repository.findById(id);
        if (entity == null) {
            return Result.ofFail(-1, "id " + id + " dose not exist");
        }

        if (StringUtil.isNotBlank(app)) {
            entity.setApp(app.trim());
        }

        if (StringUtil.isNotBlank(limitApp)) {
            entity.setLimitApp(limitApp.trim());
        }
        if (StringUtil.isNotBlank(resource)) {
            entity.setResource(resource.trim());
        }
        if (count != null) {
            entity.setCount(count);
        }
        if (timeWindow != null) {
            entity.setTimeWindow(timeWindow);
        }
        if (grade != null) {
            entity.setGrade(grade);
        }
        Date date = new Date();
        entity.setGmtModified(date);
        try {
            entity = repository.save(entity);
        } catch (Throwable throwable) {
            logger.error("save error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
            logger.info("publish degrade rules fail after rule update");
        }
        return Result.ofSuccess(entity);
    }

    @ResponseBody
    @RequestMapping("/delete.json")
    @AuthAction(PrivilegeType.DELETE_RULE)
    public Result<Long> delete(Long id) {
        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }

        DegradeRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null) {
            return Result.ofSuccess(null);
        }

        try {
            repository.delete(id);
        } catch (Throwable throwable) {
            logger.error("delete error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
            logger.info("publish degrade rules fail after rule delete");
        }
        return Result.ofSuccess(id);
    }

    private boolean publishRules(String app, String ip, Integer port) {
        List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
        try {
            rulePublisher.publish(app, rules);
            logger.info("Degrade添加規則成功{}", JSON.toJSONString(rules));
        } catch (Exception e) {
            e.printStackTrace();
            logger.warn("publishRules failed", e);
            return false;
        }
        //核心代碼,sentinel-dashboard通過http的形式進行數據推送,客戶端接收後將規則保存在本地內存中
        return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
    }
}

 

第三步: 調用端配置修改

添加pom依賴 sentinel.version爲1.7.2

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-parameter-flow-control</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-extension</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-annotation-aspectj</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-transport-simple-http</artifactId>
            <version>${sentinel.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-web-servlet</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-cluster-server-default</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-cluster-client-default</artifactId>
            <version>${sentinel.version}</version>
        </dependency>
        <!--sentinel end-->

  

 

修改配置文件,加入redis信息

server:
    port: 9090

spring:
  application:
    name: sentinelApp
  redis:
    host: 127.0.0.1
    port: 6379
    password:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080

   添加初始化redis信息配置類

package com.example.sentinel_demo.config;

import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource;
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
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.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.List;


/**
 * @author zhuhongqiang3
 * @package: com.example.sentinel_demo.config
 * @title: RedisDataSourceConfig
 * @projectName: sentinel_demo
 * @description: //TODO
 * @date: 2020-05-18 10:36
 * @version: V1.0
 * @retrun com.example.sentinel_demo.config.sentinel_demo
 */
@Component
public class RedisDataSourceConfig implements ApplicationRunner {
    private static final Logger log = LoggerFactory.getLogger(RedisDataSourceConfig.class);

    @Value("${spring.redis.host}")
    public String redisHost;

    @Value("${spring.redis.port}")
    public int redisPort;

    @Value("${spring.redis.password}")
    public String redisPass;

    //限流規則key前綴
    public final String RULE_FLOW = "sentinel_rule_flow_";
    public final String RULE_FLOW_CHANNEL = "sentinel_rule_flow_channel";

    //降級規則key前綴
    public final String RULE_DEGRADE = "sentinel_rule_degrade_";
    public final String RULE_DEGRADE_CHANNEL = "sentinel_rule_degrade_channel";

    //系統規則key前綴
    public final String RULE_SYSTEM = "sentinel_rule_system_";
    public final String RULE_SYSTEM_CHANNEL = "sentinel_rule_system_channel";

    /**
     * ApplicationRunner
     * 該接口的方法會在服務啓動之後被立即執行
     * 主要用來做一些初始化的工作
     * 但是該方法的運行是在SpringApplication.run(…​) 執行完畢之前執行
     */
    @Override
    public void run(ApplicationArguments args) {
        log.info(">>>>>>>>>執行sentinel規則初始化 start。。。");
        RedisConnectionConfig config = RedisConnectionConfig.builder().withHost(redisHost).withPort(redisPort).withPassword(redisPass).build();

        Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
        });
        String ruleAppName = RULE_FLOW + SentinelConfig.getAppName();
        log.info("ruleAppName>>>" + ruleAppName);
        ReadableDataSource<String, List<FlowRule>> redisDataSourceFlow = new RedisDataSource<>(config, ruleAppName, RULE_FLOW_CHANNEL, parser);
        FlowRuleManager.register2Property(redisDataSourceFlow.getProperty());

        Converter<String, List<DegradeRule>> parserDegrade = source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
        });
        ReadableDataSource<String, List<DegradeRule>> redisDataSourceDegrade = new RedisDataSource<>(config, RULE_DEGRADE + SentinelConfig.getAppName(), RULE_DEGRADE_CHANNEL, parserDegrade);
        DegradeRuleManager.register2Property(redisDataSourceDegrade.getProperty());

        Converter<String, List<SystemRule>> parserSystem = source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() {
        });
        ReadableDataSource<String, List<SystemRule>> redisDataSourceSystem = new RedisDataSource<>(config, RULE_SYSTEM + SentinelConfig.getAppName(), RULE_SYSTEM_CHANNEL, parserSystem);
        SystemRuleManager.register2Property(redisDataSourceSystem.getProperty());
        log.info(">>>>>>>>>執行sentinel規則初始化 end。。。");
    }
}

     以下爲測試類

package com.example.sentinel_demo.controller;

import com.example.sentinel_demo.service.impl.TestServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @author zhuhongqiang3
 * @package: com.example.sentinel_demo.controller
 * @title: TestContoller
 * @projectName: sentinel_demo
 * @description: //TODO
 * @date: 2020-03-27 14:19
 * @version: V1.0
 * @retrun com.example.sentinel_demo.controller.sentinel_demo
 */
@RestController
public class TestContoller {
    private static final Logger logger = LoggerFactory.getLogger(TestContoller.class);
    @Autowired
    private TestServiceImpl service;

    /**
     * 功能描述: <br>
     * 〈限流測試〉
     *
     * @param: [name]
     * @return: java.lang.String
     * @author: zhuhongqiang3
     * @date: 2020/3/30 10:47
     */
    @GetMapping(value = "/hello")
    public String apiHello(@RequestParam(value = "name") String name) {
        return service.sayHello(name);
    }

    /**
     * 功能描述: <br>
     * 〈降級〉
     *
     * @param: [name]
     * @return: java.lang.String
     * @author: zhuhongqiang3
     * @date: 2020/3/30 10:50
     */
    @GetMapping(value = "/down")
    public String down(Integer name) {
        System.out.println(new Date());
        return service.downRule(name);
    }
}
package com.example.sentinel_demo.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.sentinel_demo.util.ExceptionUtil;
import org.springframework.stereotype.Service;

/**
 * @author zhuhongqiang3
 * @package: com.example.sentinel_demo.service.impl
 * @title: TestServiceImpl
 * @projectName: sentinel_demo
 * @description: //TODO
 * @date: 2020-03-30 10:34
 * @version: V1.0
 * @retrun com.example.sentinel_demo.service.impl.sentinel_demo
 */
@Service
public class TestServiceImpl {

    /**
     * 功能描述: <br>
     * 〈@SentinelRestTemplate 註解的限流(blockHandler, blockHandlerClass)和降級(fallback, fallbackClass)屬性不強制填寫。〉
     * @param:
     * @return:
     * @author: zhuhongqiang3
     * @date: 2020/3/30 14:50
     */
    /**
     * 功能描述: <br>
     * 〈限流策略〉
     *
     * @param: [name]
     * @return: java.lang.String
     * @author: zhuhongqiang3
     * @date: 2020/3/30 10:49
     */
    @SentinelResource(value = "helloRule", blockHandler = "degradeMethod")
    public String sayHello(String name) {
        return "Hello, " + name;
    }

    /**
     * 限流後應用
     *
     * @return
     */
    public String degradeMethod(String name, BlockException blockException) {
        return "請求被限流,觸發限流規則=" + blockException.getRule().getResource();
    }

    /**
     * 限流後應用
     *
     * @return
     */
    public String degradeMethod2(String name, BlockException blockException) {
        return "請求被熔斷,觸發熔斷規則=" + blockException.getRule().getResource();
    }

    /**
     * 功能描述: <br>
     * 〈降級策略〉
     * 注:1.6.0 之前的版本 fallback 函數只針對降級異常(DegradeException)進行處理,不能針對業務異常進行處理
     *
     * @param: [name]
     * @return: java.lang.String
     * @author: zhuhongqiang3
     * @date: 2020/3/30 10:49
     */
    @SentinelResource(value = "downRule", fallback = "downMethod")
    public String downRule(Integer name) {
        int a = 100 / name;
        return "downRule Success, " + a;
    }

    /**
     * 降級應用
     *
     * @return
     */
    public String downMethod(Integer name, Throwable throwable) {
        throwable.printStackTrace();
        System.out.println("downMethod請求被熔斷,觸發熔斷規則=" + throwable.getMessage());
        return "downMethod請求被熔斷,觸發熔斷規則=====>>>>";
    }
}

 

至此,步驟完成, 接下來首先啓動redis, 再啓動sentinel-dashboard,最後啓動你自己的項目,就可以去試試配置是不是保存到redis當中了..至此,sentinel中的流量控制規則持久化完成

 

 

 

 

 

 

 

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