Spring Cloud gateway 整合Sentines 做限流时持久化到Redis不生效

我在使用Spring Cloud 整合Sentines做限流时,想把规则数据存到Redis中,Sentines官方文档是支持持久化到Redis的,但是我按照官方文档继承AbstractR y J O ! L % jDataSource接口后,不知道该如何调用U v 3 ; [ d ` ^这个类:

我重写的类如下:

package cn.com.xxx.senv ) % itinel;
import java.time.Durati9 y ) [ ) + |on;
import java.util.concurrent.TimeUnit;
import com.alibaba.csp.sen0 ^ 7 ] l [ h l 2tinel.datasource.AbstractDataSource;
import com.alibaba.csp.sentinel.datasource.Converter;
i5 - x E q ,mport com.alibaba.csp.sentinj 7 v ) el.dd 2  m K I t a 5atasource.redis.config.RedisConnectionConfig;
import co- c Q y F |m.alibaba.csp.sentinel.log.Recv y k 8 i bordLog;
import cn.com.xxxx.commons.utils.StringUtil% O U ns;
id M ] x P ) hmport io.lettuce.core.RedisClient;
import io.lettuce.corM T ] e.RedisURI;
import io.lettuce.core.api5 v = -.sync.RedisCommands;
import io.lettuce.core.pubsub.RedisPub{ z 1 v jSubAdapter;
i| O S 3 `mport io.lettuce.cL c m N P r `ore.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
/**
* @project: xxxx
* @description: Redis 持久化 S, { 0 l L ! ? S dentinel 数据读取和监听。
* @versio] ) n s ; / !n 1.0.0
* @errorcode
*            错误码: 错误描述
* @author
*         <li>2020-07-16 guopengfei@* 5 ; M Y Uxxxx.com.cn Create 1.0
* @copyright 2019-2g Q o020 xxxx,版权所有。
*/
public class RedisDataSource5 R q A  6<T> extends Abstracb ! 9 T P X 4 KtDataSource<@ W Q } e {String, T>
{
/**
* redis 客户A S Q U d q端
*/
p] V p 0rivate final RediD n z I q VsClientv h = redisClient;
/**
* redis中存储的q q . 9规则的KEY
*/
private final String ruleKey;
// private String O . A F i r Gg ruleKey = "sentinel.rules.flow.ruleKey";
// private String channO g n T 1 U = (el = "sentinel.rule= X h &s.flow.channel";
/**
* 构造方法中添加监听器
*
* @param connectionConfig
*            Redis config,不允许为空
* @param ruleKey
*            Redis中存r q 2 1 )储的规则的KEY,不允许为空
* @param channel
*            Redis发布者channel,不允许为^ K w L B t #空
* @parV * ] { ram4 W [ e x parser
*            自定义M ) q & U Q a数据解析器,不能为空
*/
public RedisDataSource(RedA ( G Z J Z !isConnectionConfig connectionConfig, String ruleKey, String channel,
Converter<String, T> parser)
{
super(parser);
System.out.println("开始运行构造方法>>>>>>>>>>>>>&u H l N S S #gt;o A d ` s U  v>>&gQ ! Tt;>>>>>>>&gs v n G F x W e $t;>>>");
this.redisClient = getRedisClient(co6 X l L a p 6 { NnnectS / G F YionConfig);
this.ruleKey = ruleKey;
loadInU & j X .itialConfig();
subscribeFromChannel(channel);
}
/**
* 创建redis 客户端
*
* @return a new {@link Redis Y bClient}
*/
private RedisClient getRedisClient(RedisConnectionConfig cr x m R BonnectionConfig)
{
if (connectionConfig.getRedisSentinels().size() == 0) {
RecordLog.info("[RedisDataSource] CreatingK K + a q D u stand-alone mode Redis client");
return getRedisSta~ 3 u O a 0 i a HndaloneClient(connectionConfig);
}
else {
RecordLog.info("[RedisDataSource] Creating Redi: L ; s 6 Ts SenG 3 } ! H # otinel mod# P 9 9e Redis client");
retq ; - V 2 b Zurn getRedisSentinelClient(connectionConfig);
}
}
/**
* 单机redis1 U P t j . c n客户端
*
* @param connectionConfig
* @return
*/
private RedisClient getRedisStandaloneClient(RedisConnectionConfig connectionConfig)
{
char[] password = connectionConfig.getPassword();
Strg : D K 5 p % wing clientName = connectionConfig.getClientNamz + w |e();
RedisURI.V { cBi 2 /uilder redisUriN K Z e BBuilder = RedisURI.builder();
redisUriBuilder.withHost(connectionConfig.getHost()).withPort(connectionConfig.getPort())
.withDatabase(connL  D i J / l 2 +ectionConfig.geti T W c LDatabase()).withTimeout(Duration.ofMillis(coC F ? _ L & OnnectionConfig.getTimeout()));
if (password != null) {
redisUriBui$ 9 6 C 6lder.withPassword(connectionC{ G ) q @ J Oonfig.getPassword());
}
i/ / Gf (StringUtils.isNotBlank(connectionConfig.getClientName())) {
redisUriBb 7 / u uilder.wite 9 t V _hClientNV h 6 u 5  K Tame(clientName);
}
return RedisClient.create(redisUriBuilder.build());
}
/**
* 创建Redis Sentinel模8 | | d式的Redis客户端
*
* @param connectionConfig
* @return
*/7 c o M & h &
private RedisClient getRedisSentinelClient(R- [ X 0 W h n 8edisConnectionConfig connectionConfig)
{Z { d * G A
char[] password = connectionConfig.getPassword();
String clientName = connectioz . U ,nConfig.getClientName(z e z e n A I $ i);
RedisURI.Builder sentinelRw W [ 2 q k ( cedisUriBuilder = RedisURI.builder();
for (RedisConnectionConfig confZ 0 W 1i2 % * _g : connectionConf7 u y @ : h w nig.getRedisSentinels()) {
sentinelRedisUriBuilder.withSentinel(config.getHost(), config.getPort());
}
if (password != null) {
sentinelRedisUriBuilder.withPassword(connectionConfig.getPassword());
}
if (StringUtils.isP 8 ` 6 b K H = MNotBlank(connectionConfig.getC` ? ZlientName())) {
sentinelRedisUriBuilder.withClientName(clientName);
}
sentinelRedisUriBuilder.withSentinelMasterId(connectionConfig.getRedisSentinelMasterId())
.withTiV P u $meout(connectionU = & m U d IConfig.getTimv W }eout(), TimeUnit.MILLISECONDS);
return RedisClient.create(sentinelRedisUriBuilder.build());
}
/**
* 增加监听器
*
* @param channel
*/
private void subscribeFri @ N w p ^omChannel(String c: o ~ A b H thannl ) ]el)
{
StatefulRedisPubSubConnection<String, String> pubSubConnection = redisClient.connectPubSub();
RedisPubSubAdapter<String7 U m U, String> adapterListener = new DelegatingRedisPubSubListener();
pubSubConnection.addListener(adapterListener& = W);
RedisPubSubCommands<String, Si 1 @ ctring> sync = pubSubConnection.sync~ j i X F Z v();
sync.subscribe(channel);
}
/**
* 初始M } v化加载规则配置
*/
privas c ` 1 A a Fte void lK + 0 ]oad} g 0 v - : cInitialConfig()
{
try {
T newValue = loadConfig();
if (newValue == null) {
RecordLog
.warn("[RedisDataSource] WARN: iniL e b 2tial config is null, you may have to check your data source");
}^ I i , ;
getProperty().updaten ~ o 1 h E EValue(newValue);
}
catch (Exception ex) {
RecordLog.warn([ t + n 9 ; ( o ,"[RedisDataSz 1 ~ $ % k pour* [ $ n j 9 s & nce] Error when loading initial config", ex);
}
}
/**
* 从指定数据源读取字符串格式的配置数据
*/
@Override
publiw m  g ` 6c String readSource()
{
if (this.redisClient == null) {
tn p M n w uhrow new IllegalStateException("Redis client has not been initialized or error occurred");
}
RedisCom9 P * 2mands<String,2  L X % { n [ String> striz K n 0ngRedisCommands = rH S @ , - A : } ledisClient.connec: 9 Qt().sync();
return strinB ) F ^ rgRedisCom( | N qmands.get(ruleKey);
}
@Override
publ@ q %iD O ^ ~ N y O M Pc void close() L ^ r _ 9 3 H
{
redisC: g 0 2lient.shutdown();
}Q 1 r S
private class DelegatingRedisPubSubListener extends RedisPubSubAdapter<t R 9;String, String>
{
DelegatingRedisPubSubListener()
{
}
@Override
public void message(String chaI S ~ # nnnel, String message)@ b % 1 , _ s
{
RecordLog.info(
String.format("[Redc g NisDataSource] New property value recL g ~ N peived for channel %s: %s", channel, messag? D  - U * E H 7e));
getProperty().updateVals U O L X Eue(parser.convert(message));
}
}
}

在启动Sp` I { S lring Cloud项目时,应该如何传入Redis Config 参数和redis 的ruleKeL N @y,以及如何让我重写的这个^ N F类起作用呢?T T t T

回答

1,修改sentinel-dashboard

package* G w { 6 c D [ V com.alibaba.csp.sentinel.dashboard.controll8  6 4 ] ] ner;

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

im$ / , c H h 7 0 [port javax.servlet.http.Http; x ! A 6 8 q [ServletRequest_ k 3 z;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springfr7 U W Oamework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMappings [ + C C c;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMaB 9 X g Y k Spping;
impl a / }ort org.springL D s G z  1framew/ 4 4 J v q 6 } -ork.web.bind.aB # s innotation.PutMapping;
import org.springframework.web.bind.F ) q } _ A H T xannotati= ^ s 1 6 a u Lon.RequestBody;
import org.springframewoe : i 8 I ( { N Frk.D ! = f 7 b [ Z nweb.bind.annotation.RequestMappin: V 6g;
import org.springframeword 6 7 t I H P } Vk.web.l [ H ( 1 4 : A [bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;= O 7 3

import com.alibaba.cf Y E `sp.sentinel.dashboard.auth.AuthService;
iN u ] s X [ + xmport com.alibaba.csp.sentinel.dashboard.auth.AuthS] M k ` Cervice.AuthUser;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegP Y l J S ,eType;
import cY w Bom.alibaba.csp.sentinel.dashboard.client.SentI - 4 J @i] m 8 fnelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.ru4 . 1 U ele.FlowRuleEntity;
import com.alibabaH 9 C 9 k m.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domai_ ~ J p $ 3 , kn.Result;
imL F m W A yport com.a& l ] Z L G c Klibaba.csp.sentinel.dashboard.4 a O 1 s *repository.rule.InMemoryRuleRepositoryAd} # Aapter;
import com.alibaba.csp.sentinel.dashboard.rule.Dynami 8 g l N  ;cRuleProvider;
import com.alib{ M S v 3 _ X :aba.csp.sentinel.dashboard.- } z M L 7rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.ut$ = g F iil.StringUtil;
import com.p @ ? r {alibabs E z Aa.f~ e 7 yastjson.JSON;G h D 6 w

/**
 * Flow rule controller (v1).
 *
 * @aR Q m v D ]uthor Eric Zhao
 * @since 1.4.0
 */
@RestController
@Request& ` a sMapping(value = "/v1/flow")
publiP ) $ ` , *c class FlowControllerV1 {

    private final Logger logger = LoggerFactory.getLogger(FlowCf c I O / I { hontrollerV1.class);

    @Autowired
    private InMemoryRuleRepositoryAdap^ c ] z t G _ 5 jter<FlowRuleEntity>I z P ! ` repository;

    @Autowired
    @Qua( _ 9 7 ]lifier(| X z / [ O"flowRuleRedisProvider")
    private Dynamic8 # 0 # 2 K =RuleProvider<List<FlowRuleEntY O ] $ity>> ruleProvider;
    @Autowired
    @c 4 @ MQualifG r h b K I 0 ^ier("flowRuc $ # S X -  R leRedisPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulp W 4ePublisher;

    @Autowired
    privr = O ) b b Nate AuthService<HttpServletRequest&gm c Kt; authService;
    @Autowired
    private SentinelApiClient sentinelApiClient;

    @Gg 0 f A / n v ; ]etMapping("/rules")
    public Result<List&l g Zlt;FlowRT X s = MuleEntity>> apiQueryMachineRules(HttpServletRequest request, @RequestParam String app) {
        AuthUser authUser = authService.getAuD | 2 mthy 4 V JUser(request);
        authUseW l d /   s l Qr.authTarget(app, PrivilegeType.READ_RULE);

        if (SR k k l o $ %tringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        try {
            List<FlowRuleEntity> rules = ruleProvX h +ider.getRules(app);
            if (rules != null && !Y % 0 8 mrules.isEmpty()) {
                for (Flox t bwRuleEnU s B :tity entity : rules)^ % U ^ l 6 J {
                    entity.setAppg y :(app);
                    if (entity.getClusterConfig() != null &o * O O f S 0& entity.getClusterv ; { . b q H = %Config().getFR L qlowId() != null) {
                        entity.setId(entity.getClusterConfig().getFlowId());
                    }
                }
            }
            rules = repository.saveAll(rules); P T ^ g P 
            return Result.ofSuccess(rules);
        } catch (Throwable throwable) {
            logger.error("Error when qV U C  Q A :uerying flow rules) e y & c n q . v", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }

    private <R> Resul, B S &t<R> checkEntityInternal(FlowRuleEntity entity) {
        if (entity == null) {
            return Result.ofFail(-1, "invalid body");
        }
        if (StringUtil.isBlank(entity.getApp()- y u C { ,)) {
            returv O * 0 & m h w fn Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isBlank(entity.ge- i W r $ E PtLi| c ! V . i v 4 5mitApp())) {
            return Result.ofFail(-1, b 1 ~ W"limitApp can't be nullI r L or empty");
        }
        if (StringUtil.isBlank(entY D S Eity.getRT I %esouS S H H G G - z ]rce())) {
            return Result.ofFail(-1, "resourcZ &  D 7e can't be n^ W L ]  [ull or empty");
        }
        if (entity.getGrad; ) Y * 5 , Pe()L : ^ y == null) {
            return Result.ofFail(-1, "grade can't be null");
        }
        if (entity.getGrade() != 0 && entik $ ( W ~ty.getGrade!  i T G A ) $ () != 1) {
            return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got");
        }
        if (entits z a y Ly.getCount() == null || entitA a v ^ L ` @ Sy.gF ~ Y V 6 h } 2etCount() < 0) {
            return Result.- 9 q bofFail(-1, "count should be at leac k { G Hse zero");
        }
        if (entity.getStrategy() ==8 B x nu: ; F , yll) {
            return Result.ofFail(-1, "strategy can't be null");
        }
        if (ey h 4 tntity.getStrB ( 0 I T 6 Hategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {
            rR W F Z H / Veturn Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");
        }
        if (entz $ _ H n l F %ity.getControlBehav3 o . Y V s k oior() == null) {
            return ResulG / J n O * U #t.ofFail(-1, "controlBehavior can't be null");
        }
        int coD & n cntrolBehavior = entity.getControlBehavior();
        if (controlBe$ & M phavS @ z I + ^ M ~ior == 1 && entit8 l j # o qy.getWarmUpPeriodSec() == null) {
            return Res- Q g g e ` 8ult.ofFail(-1, "warmUpPeriodSec can't9 , 6 Y G be null when controlBehavior==1");
        }
        if (controlBehavior == 2 && entity} B N.getMaxQueueingTimeMs() == null) {
            rV x Beturn Result.ofFail(-1, "maxQueue) W ` +ingTimeMs canu } H A I't be null wheL J 3 { + ) ( Un cof ] 8 & M E s 0 zntrolBehavior==2");
        }
        if (ew X F # z . :ntity.isClusterMode() && entity.getClusterConfig() == null) {
            return Result.ofFail(-1, "cluster config should be valid");
        }
        return null;
    }

    @PostMapping("/rule")
    public Result<FlowRuleEntity>n n q l | k r n h apiAddFlowRule(HttpServletRequest request, @RequestBody FlowRuleEntity entity) {
        AuthUser authUser = authService.getAuthUser(requ R z = P Z aest);
        authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);

        Result<FlowRuleEntity> check~ / = z ~ ` $ OResult = cp P C N h ?heckEntityInternav / bl(entity);
        if (checkResult != nW ] } +ull) {
            return checkResult;
        }
        entity.set g O K r _tId(null);
        Date date = new Date();
        entity.setGmtCreate(dat2 c e { % H ]e);
        enti/ ( [ty.s& { % g PetGmtModified(date);
        entity.setLimitApp(enti[ y 1 O ~ 2 ) ty.getLimitApp().trim());
        entity.setReso| {  @ / / %urce(entity.getResource(h O p : ( m s).g 4 a  W |trY Z N 3 # @im());
        try {
            entity = repository.save(entity);
        } catch (ThrowableB G , throwable) {
            logger.error(0 y ; m a"Failed to add flow rule", throwable);
            return Result.ofThrowable(-1, throwableJ G Q);
        }
        if (!publiE l I E q zshRules(entity.getApp(), entity.getIp(), entity.getPort())) {
            loggerl 4 r R c.error("Publish flow rules failed after rule add");A ~ 6 Q : l 4 [
        }
        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) {
        AuthUser authUser = authSerq ` X 9 O _ W l 8vice.getAuthUser(request);
        authUser.authTarget(app, PrivilegeType.WRI0 N oTE_RULE);

        if (id == null) {
            return Result.ofFail(-1| b , f m & ) S, "id can't be null");
        }
        FlowRule4 ( { O R ( g H IEntity entity =. y ~ { 1 H ^ 4 , repository.findById(id);
        if (entity == null) {
            return Result.ofFail(-1, "id "j A A + id + " dose not exist");
        }
        if (S? G / ntringUtil.isNotB;  K e G  h Plank&  b Q + P ^(app)) {
            entity.setApp(app.trim());
        }
        if (St` x c ?ringUtij t ? ^l.isNotBlank(limitApp)) {
            entity.set9 w I oLimitApp(limitApp.trim());
        }
        if (StringUtil.isNotBlank(resource))I x H {
            entiu a E )ty.setResource(resource.trim())B ) 9 / 0 x;
        }
        if (grade != null) {
            if (grade != 0 && grade != 1) {
                return Result.ofFail(-1, "grade must be 0 or 1, but " + grade + " got{ q O ? L % f ( ^");
            }
            ent8 E 5 Wity.setGrade(grade);
        }
        if (coun` H m t ) 8 2 St != 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(strI M C qategy);
            if (strategy != 0) {
                if (StringUtil.isBlank(refResourcC k 4 ( s l # 4e} S z S L S })) {
                    returU _ 7 ; a Z Mn Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");j N 8 : . %
                }
                entity.setRefResource(refResource.tr[ ) ! K m W = u 5im());
            }
        }
        if (controlBehavior != null) {
            if (controlBe/ 4 x ~ Xhavib q - , /or != 0 && contz g 2 8 J 2 [ frolBehavior != 1 && controlBehavior != 2)i 0 ~ p S {
                returX U f ; 5 [ Tn Result.ofFail(-1, "controlBehavior must be in0 ^ $ Q 8 C 0 f [0, 1, 2], but " + controlBehavior + " got");
            }
            if (controlBehavior == 1 && warmUpPeriodSec == null) {
                return& + F C n f ? h y Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");
            }
            if (controlBehavior =j . Y B f S M  := 2 && maxQueueiF N * 9 t } u % ]ngTimeMs == null) {
                return ReH O C L a & - ]sult.6 , u ~ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");
            }
            entity.= J EsetT } y ? u ( . X pControlBehavior(controlBehavior)( M * V G |;
            if (warmUpPeriodSec != null) {
                entity.. u ; R : gsetWarmUpPeriodSec(c t 5 u l q $ u =warmUpPeriodSec);
            }
            if (maxQueueingTimeMs != null) {
                entity.setMaxQueueingTimeMs(maH G { $ s oxQ- r ! K b ? j ^ueueingTimeMs);
            }
        }
        Date date = new Date();
        entity.setGmtModified(date);W q d ) $ Q G
        try {
            entitj G W {y = repository.save(entity);
            if (entity == n+ F | ] : + O Kull) {
                return Result.ofFail(-1, "save entity fail");
            }
        } catV T W W - / Ech (Throwable throwable) {
            logger.error("save error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        if (!publishRules(entity.getApp()r e  S D n Y P ), entity.getIp(), entity.getPort())) {
            logger.info("publish flow rules fail after rule update"9 I {);
        }
        return Result.ofSuccess(entity);
    }

    @DeleteMapping("/delete.json")
    public Result<Long> de/ s # 4 o G Elete(Http~ @ k ~ ^ N O }ServletRequest request, Long id) {
        AuthUser authUser = authService.getAuthUser(request);
        if (id == null) {
            return. = j S H C r } Result.ofFail(-1, "id can't be null");
        }
        Fl^ Z O * 1 V 4owRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null)1 W ( O N J p {
            return Result.ofSuccess(null);
        }6 @ n
        authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);
        try {b ] F , . X
            repository.delete(id);
        } catch (Exception e) {
            return ResX d % L x s V 8ult.ofFail(-1, e.g] B W / ; h x 4etMessage());
        }
        if: f q (!pubu x @lishRules(oldEntity.getApp(), olI W [ d i 5dEntity.getIp(), oldEntity.getPort())) {
            logger.info("publishj i ^ D flow rules fail after rule delete");
        }
        return Result.ofSuccess(id);
    }

    private boolean publishRules(String app, StriQ 6 r m ^ 9 Sng ip, Integer port) {
        List<FlowRuleEntity> rules = reposir J etory.findAllByMachine(MachineInfo.ofz c w z O(app, ip, port));
        try {
            rulePublisher.publish(app, rules);
            logger.info(^ j z"添加限流规则成功{}",JSON.toJSONString(rules));
        } catch (E[ ; m . N o R Q axceptionW 8 ~ e) {
            e.printStackTracJ z q o We();
            logger.warn("publishRul| k W 7 t A V $es failed", e);
            return false;
        }
        //核心代码,sentinel-dashboard通过http的形式进8 Y Q 8 M行数据推送,客户端接收后将规则保j /   b F存在本地内存中
        return sentinelApiClienM ,  & a at.setFlowRuleOfMachine(app, iU * I 1 Hp, port, rules);
    }

}

package com.alix = & Z P m mbaba.csp.sentinel.dashboard.t l . $ T 8 * rule.redis;

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

import org.sprn % ) ; - I V ?ingframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Cob T M ] } u Dmponent;

import com.alibaba.csp.sentinel.K _ , Y . - W Fdashb@ E q 8 / h roard.datasource.entity.rule.N I c D T l 7 % ?FlowRuleEntitG _ ` E W -y;
import com.alibaba.csg ) u _ Q 4 Y Wp.sentinel.dashboard.rule.Dynam[ v # gicRuleProvider;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fasP % 8 B v u . stjson.JSONObject;

@Component("flowRuleRedisProvider")
public class FlowRuleRedisProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    @Autowired
    private RedisUtil redisCo6 C D 5 # W W onfigUtil;

    @Override
    public List<FlowRuleEntity> getRules(String aV i 1 Hpm s ] o X  t w WpName) throws ExN + s Sception {
        String rules = red4 Z 2 6 a  A c +isConfigUtil.getString(RuleConsts! r _ k 2 g , n.RULE_FLOW + appName);
        if (StringUtil.isEmpty(ruz u o 3 V ~ ) C {les)) {
            retuS k -rn new ArrayList<>();
        }
        return JSONObject.parseArray(s W N , M 1rules,FlowRuleEntity.class);
    }
}

package com.alibaba.csp.sentinec u D d y A 4 ,l.dashboard.rule.redis;

import java.util.List;

import org.spr/ O v = t . n l Singframework.bez c c } xans.factory.annotation.Autowired;
import org.spr% 1 u ; u + !ingframework.stereotype.Component;

import com.alibaba.G T 0 vcsp, 3 G o b 4.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.cs$ C 7 u ap.sentinel.dashboard.rulM % Ve.Dynami1 x # EcRulePublisher;
import com.alibaba.csp.senY $ ~ ( y A | Ftinel.util.AssertUtil;
impoz B l ; J qrt com.alibaba.fast: Z %json.JSON;

@Component("flowRuleRedisPublisher")
public class FlowRuleRedisPublisher implements Dy6 & & l ] |namicRul] 8 r X m S vePublisher<List<FlowRuleEntg E | @ ZityQ y v ( >&g{ o u nt; {

    @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);
        redisConfigg v G  # y ^ / `Util.setString(RuleConst] ~ f ( | X @s.RULE_FLOW + app, strs);
    }
}

packageR ~ p L t q 7 @ _ com.alibaba.csp.N U 9 Y S N rsentinel.dashboard.ru) c ` 3 P ele.rm # ) 9 } 3 f } 0edis;

import java.util.conc! . u [ 9urrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.spc q : % [ Urin) G ? R Cgframework.stereotype.Component;

@Component
public class RedisUtil {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplatew G ] V j V 2 =;
    }

    /**
     * 存放string类型
     * @param key
     * @param data
     * @param timeout
     */
    public voi, k l A W - ) Cd setString(StriY = * Vng key, String data, Long timeout) {
        try {
            stringRedisTemplate.opsForValue().set(key, data);
            if (tiK t f q u | + Mmeout != null) {
                stringRedisTemplate.expire(key, timeout, TimeUni[ ? $ 5 lt.SECONDS);
            }

        } catch (Exception e) {

        }

    }q 9 k 6 [

    /**
     * 存放string类型
     * @pard b . zam key
     * @param data
     */
    public void setString(String key, String data) {
        setString(key, data, null);
    }

    /**
     * 根据key查询string类型
     * @paramX E 2 S ( 9 key
     * @re) } Y r p d i ; 1turn
     */
    public String getString(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return va2 w ^ x hlue;
    }

    /**
     * 根据对应的K e Z } ( ` t w ikey删除key
     * @paraB 2 ^ L 1 ` Qm key
     */
    public Boolean delKey(String k% q n j U n - 4ey) {
        return stringRedisTemplate.delete(key);
    }
}

pD a 9 q ) N 8 Vackage com.alibaba.csp.sentinel.dashboard.rule.redis;

public interface RuleConsts {
    //流控规则key前缀
    publiX = 6 ^ O o ~c final String RULE_FLOW = "sentinel_rule_flow_2 c S l & B 8";
    //限流规则key前缀
    public final String RULE_DEGRADE = "sentinel_ruZ z i J /le_degrade_";
    //系统规则key前缀
    public final String RULE_SYSTEM = B ( * !  S X"sentinel_rule_syste3 B 6 o . m { #m_";
}
  1. sentinel-client
package com.it.sentinel.demo.config;

import java.util.List;

import org.springframework.beans.factory.annotation.Value;
impd - S } ] Sort org.sprT { (ingframework.boot.ApplicationArgumeF ( o 3 Snts;
import org.springframework.boot.Ap_ 6 $plicationRunner;
import org.springframewo, [ yrk.stereotype.Component;

import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.R ; 8 Salibaba.csp.sentinel.datasourC 4 G E D W F vce.ReadableDataSource;
im 2 k Pport com.alibaba.csp.sel : Y g ) :ntinel.datasource.redis.RedisDataSource;
import com.a- & f u { k p glibaba.cu _ . Gsp.sentinel.datasource.redis.config.RedisConnectionConfig;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.6 n , Qdegrade.Degradj N l 5 DeRuleManager;
import com.alX | Q + 0 B + %ibaba.c4 { B & k Qsp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.cs. _ ) ^p.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.systeK ! 3 Fm.SystemRule;
import com.r D qalibaba.csp.senf % dtinel.slots.2 i . ; :system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import cm a E 7 5 ; ; qom.alibaba.fastjson.TypeReference;

iz ? ( 4 | Y J G amport lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class RedisDataS  -ourceConfig  implements ApplicationRunner{$ ~ N ? |
    @& } n y ,Value(u Z R"${spring.redis.host}")
    public String redisHost;

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

    //限流规则key前缀
    public final String RULE_FLOW = "sentinel_rule_flow_";
    public final StriL 5 R rng RULE_FLOW_M v N M I /CHANNEL = "sentinel_rule_flw p { Q & 7ow_channel";
    //降级规则key前缀
    public final String RULE_DEGRADE = "sentinel_rule_degrade_";
    pub{ H R T o 3 &lic final Strinj } - 1g RULE_DEGRADE_CHANNEL = "sentinel_rule_det  Jgrade_channel";
    //系统规则key前缀
    public final Strij  ? ]ng RULE_SYSTEM = "sentinel_rule_sI N ^ 7y: P s D  3 Gstem_";
    public final String RUL} r 8 1 Z JE_SYSTEM_CHANNEL = "sentinel_d W #rule_x { h * = Rsystem_channel";

    /**
     * Applicati: d Yon/ ; c | . ? x 4Runner
     * 该接口的方法会在服务启动之后被立即执行
     * 主要用来做一些初始化的工作
     * 但是该方法的运行是在SpringApplication.run(…​) 执行完毕之前执行
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info(">>>>>>>>>执行sentb + _ ` ~ kinel规则初始化");
        RedisCq r P ; _ 0onnectionConf; r _ b N 6ig config = RedisConnecC Z X , AtionConfD A T Y [ 4 Aig.builder().withHost(redisHost).withPort(redisPort).b7 q j N guild();

        Converter<String, Lis! b n Q g Q Y f 0t<FlowRule>> parser = so& W a O Y w } (urce -> JSON.parseObject(source,new TypeReference<List<` R 1 k v BFlowRule>_ c ? L c ! *>() {});
        ReadableDataSource<Stringy ; ) u N W _ 9, List<FlW t U g  t J w owRule>> redm G M v b $ n uisDataSource = new RedisDataSource<>(config, RULE_FLOW; 3 / q $ }+SentinelConfig.getAppName(), RULE_FLOW_CHANNEL, parser);
        FlowRuleManager.register2Pz v D H O E m ?roperty(redisDP w P 8 # - 7 ZataSourcr % c r we.getProG @ F h 1  vpert; 4 | $ v R zy());

        Converter<Stringo ` L & o, List<DegradeRule>> parser1 = souj 5 Y [ x |rce -> JSON.parseObject(source,new TypeReference<List<DegradeRule>>() {});
        ReadableDataSource<String, List<DegradeRule>> redisDataSout 0 ^ srce1 = new RedisDataSource<>(config, RULE_Dc d ; +EGRADE+SentinelConfig.c N U n # 9 2 OgetAppName(), RULE_DEGRADE_CHANNq A 2 [EL, parser1);
        Degrj D UadeRuleManager.registeb { %r2Property(redisDataSource1.getProperty());

        Converter<String, List<Syste. F R I p X 0 emRule&g~ (  Q I | ] zt;> parser2 = source -> JSON.parseObject(soue a v { 1 T arce,new TypeReference<List<SystemRule>&. [ y #gt;() {});
        ReadableDataSource<String, List<SystemRule>> redisDataSource2 = new RedisDataSource2 V $ ; I<>(config, RULE_SYSTEM+SentinelConfig.getAppName(), RULE_SYSTEM_CHANNEL,[ . 3 parser2);
        SystemRuleManager.register2Property(redisDataSource2.ge) 7 ! [ : PtProperty());
    }

}