Spring Cloud Zuul 1.5.x 使用 Sentinel的网关流控

文章目录

  • 1. Maven 配置
  • 2. application.properties
  • 3. 自定义异常提示
  • 4. 配置动态规则
  • 5. 示例规则

1. Maven 配置



    4.0.0

    com.fengxuechao.examples
    zull-sentinel-integrate-demo
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        1.5.13.RELEASE
         
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Edgware.SR5
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                1.5.1.RELEASE
                pom
                import
            
        
    

    
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        
        
            com.alibaba.cloud
            spring-cloud-alibaba-sentinel-gateway
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-zuul
        
        
        
            org.springframework.cloud
            spring-cloud-starter-consul-discovery
        
        
            org.projectlombok
            lombok
            compile
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        
        
            redis.clients
            jedis
        
        
            org.apache.commons
            commons-lang3
            3.9
        
        
            commons-io
            commons-io
            2.6
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    8
                    8
                
            
        
    


2. application.properties

仅显示有关 sentinel 的配置,其它与本文目的无关的省略

# sentinel
spring.cloud.sentinel.enabled=true
## sentinel dashboard【可选配置】
spring.cloud.sentinel.transport.dashboard=192.168.200.19:8090
spring.cloud.sentinel.transport.port=8919
spring.cloud.sentinel.transport.client-ip=192.168.120.81
## sentinel 过滤器顺序配置 【必须配置】
spring.cloud.sentinel.zuul.order.pre=10000
spring.cloud.sentinel.zuul.order.post=1000
spring.cloud.sentinel.zuul.order.error=-1
## sentinel 基于文件的动态规则数据源配置 【DEMO】
#spring.cloud.sentinel.datasource.ds.file.file=classpath:sentinel/rules/gw_flow.json
#spring.cloud.sentinel.datasource.ds.file.rule-type=gw_flow
spring.cloud.sentinel.log.dir=D:\\develop\\idea-workspace\\zull-sentinel-integrate-demo\\logs\\

3. 自定义异常提示

Sentinel 默认的异常提示是这样的:

{
  "code": 429,
  "message": "Sentinel block exception",
  "route": ""
}
  • 相关类
    • com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.DefaultBlockFallbackProvider
    • com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse
    • com.alibaba.cloud.sentinel.zuul.handler.FallBackProviderHandler
    • com.alibaba.cloud.sentinel.zuul.SentinelZuulAutoConfiguration
    • com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulPreFilter

可以发现其默认格式不符合已有系统的返回格式,所以我们需要自定义。自定义所需的类如下:

import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse;

/**
 * 自定义返回限流返回对象
 *
 * @author fengxuechao
 * @version 0.1
 * @date 2019/11/20
 */
public class CustomBlockResponse extends BlockResponse {

    public CustomBlockResponse(int code, String message, String route) {
        super(code, message, route);
    }

    /**
     * 必须重写,才能够自定义限流返回消息
     *
     * @return
     */
    @Override
    public String toString() {
        return "{\"errorCode\":\"" + this.getCode() + "\", \"errorMsg\":\"" + this.getMessage() + "\"}";
    }
}

/**
 * 自定义限流的 fallback
 *
 * @author fengxuechao
 * @version 0.1
 * @date 2019/11/20
 */
public class CustomZuulBlockFallbackProvider implements ZuulBlockFallbackProvider {
    /**
     * The route this fallback will be used for.
     *
     * @return The route the fallback will be used for.
     */
    @Override
    public String getRoute() {
        return "*";
    }

    /**
     * Provides a fallback response based on the cause of the failed execution.
     *
     * @param route The route the fallback is for
     * @param cause cause of the main method failure, may be null
     * @return the fallback response
     */
    @Override
    public CustomBlockResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof BlockException) {
            return new CustomBlockResponse(429, "Sentinel block exception", route);
        } else {
            return new CustomBlockResponse(500, "System Error", route);
        }
    }
}

package com.fengxuechao.examples.zuul.sentinel;

/**
 * 流量控制配置类
 *
 * @author fengxuechao
 * @version 0.1
 * @date 2019/11/15
 * @see 网关限流
 * @see 动态规则扩展
 */
@Slf4j
@Configuration
@EnableConfigurationProperties({CustomSentinelProperties.class})
public class SentinelGatewayFlowConfig implements InitializingBean {

    @Autowired
    @Qualifier("sentinel-json-gw-flow-converter")
    private JsonConverter jsonConverter;

    @Autowired
    private JedisCluster jedisCluster;

    @Autowired
    private CustomSentinelProperties customSentinelProperties;

    /**
     * 自定义限流返回消息
     * 与业务的返回消息保持一致
     *
     * @return CustomZuulBlockFallbackProvider
     * @see CustomBlockResponse CustomBlockResponse 自定义 BlockResponse,重写 toString() 自定义返回消息
     * @see FallBackProviderHandler FallBackProviderHandler 实现了接口 SmartInitializingSingleton,故此利用 Spring Bean 生命周期原理将默认的 ZuulBlockFallbackProvider 替换为自定义的返回限流处理
     * @see SentinelZuulPreFilter SentinelZuulPreFilter 捕获 BlockException, 设置限流返回消息,也就是 CustomBlockResponse
     */
    @Bean
    public CustomZuulBlockFallbackProvider customZuulBlockFallbackProvider() {
        return new CustomZuulBlockFallbackProvider();
    }

    /**
     * bean 创建完成后执行
     * 1. 注册sentinel网关流控, 但是网关流控不能应用集群流控
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        String gwFlowKey = customSentinelProperties.getGwFlowKey();
        String gwFlowChanel = customSentinelProperties.getGwFlowChanel();
        JedisPullDataSource redisDataSource = new JedisPullDataSource<>(jsonConverter, jedisCluster, gwFlowKey, gwFlowChanel);
        // 网关流控,不适配我们现有的业务,应使用集群流控
        GatewayRuleManager.register2Property(redisDataSource.getProperty());
    }
}

4. 配置动态规则

我发现 Sentinel 的动态规则 Redis 扩展使用的客户端是 io.lettuce:lettuce-core 并且没有支持 Redis 集群,而业务使用的是 Redis 集群,同时用的是 redis.clients:jedis 。所以需要自定义动态规则数据源的实现。

我选择继承 com.alibaba.csp.sentinel.datasource.AutoRefreshDataSource ,代码如下:

package com.fengxuechao.examples.zuul.sentinel.datasource;

/**
 * @author fengxuechao
 * @version 0.1
 * @date 2019/11/22
 */
public class JedisPullDataSource extends AutoRefreshDataSource {

    private final JedisCluster jedisCluster;

    private final String ruleKey;

    /**
     * Constructor of {@code JedisClusterDataSource}.
     *
     * @param jedisCluster JedisCluster
     * @param ruleKey      data key in Redis
     * @param channel      channel to subscribe in Redis
     * @param parser       customized data parser, cannot be empty
     */
    public JedisPullDataSource(Converter parser, JedisCluster jedisCluster, String ruleKey, String channel) {
        super(parser);
        AssertUtil.notNull(jedisCluster, "JedisCluster can not be null");
        AssertUtil.notEmpty(ruleKey, "Redis ruleKey can not be empty");
        AssertUtil.notEmpty(channel, "Redis subscribe channel can not be empty");
        this.jedisCluster = jedisCluster;
        this.ruleKey = ruleKey;
        loadInitialConfig();
    }

    private void loadInitialConfig() {
        try {
            T newValue = loadConfig();
            if (newValue == null) {
                RecordLog.warn("[RedisDataSource] WARN: initial config is null, you may have to check your data source");
            }
            getProperty().updateValue(newValue);
        } catch (Exception ex) {
            RecordLog.warn("[RedisDataSource] Error when loading initial config", ex);
        }
    }

    /**
     * Read original data from the data source.
     *
     * @return the original data.
     * @throws Exception IO or other error occurs
     */
    @Override
    public String readSource() throws Exception {
        if (this.jedisCluster == null) {
            throw new IllegalStateException("JedisCluster has not been initialized or error occurred");
        }
        return jedisCluster.get(ruleKey);
    }
}

5. 示例规则

应用网关流控的关键代码已有,如下所示为限流规则:

[
    {
        "resource": "capacity-group-protocol-adaptor-consumer",
        "count": 1,
        "intervalSec": 20,
        "paramItem": {
            "parseStrategy": 3,
            "matchStrategy": 0,
            "fieldName": "accessToken",
            "pattern": "325144132CC3BC7C433CD64C0BE98CC8"
        }
    }
]

你可能感兴趣的:(流量控制,springcloud)