springcloud gateway使用sentinel进行限流

pom.xml中引入sentinel依赖

首先在父工程中引入alibaba依赖管理


    <properties>
        <spring-cloud.version>Finchley.SR2</spring-cloud.version>        
        <alibaba-cloud.version>2.0.0.RELEASE</alibaba-cloud.version>
        ...
    </properties>
    
...

<!--依赖管理,用于管理spring-cloud的依赖,其中Finchley.RELEASE是版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>           
            <!-- 阿里 spring-cloud 依赖 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${alibaba-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

然后在gateway微服务项目中引入 sentinel

        <!-- 基于sentinel限流 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
        </dependency>

application.yml配置

server:
  port: 7005

spring:
  profiles:
    active: @profileActive@
  application:
      name: zmg-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
      # zmg系统服务模块
      - id: zmg-sys
        uri: lb://zmg-sys
        order: 2001
        predicates:
        - Path=/api/zmg/sys/**
        filters:
        - StripPrefix=3
        - adminAuthorize=true

eureka:
  instance:
    statusPageUrlPath: /actuator/info
    healthCheckUrlPath: /actuator/health
    home-page-url-path: /
    # docker 部署开启后将IP修改为部署所在服务器的外网IP
    prefer-ip-address: true
    ip-address: 127.0.0.1
    instance-id: zmg-gateway
  client:
    serviceUrl:
      #            defaultZone: http://localhost:7001/eureka/
      # docker 部署开启
      defaultZone: http://${EUREKA_HOST:localhost}:${EUREKA_PORT:7001}/eureka/
    client:
      healthcheck:
        enabled: true

SentinelConfiguration.java 配置类

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.zmg.commons.command.support.ResultSupport;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Description:
 *
 * @author jacky
 * @create 2019/11/01
 **/
@Configuration
public class SentinelConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public SentinelConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                 ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initGatewayRules();
        initBlockHandler();
    }

    /**
     * 初始化限流规则
     */
    private void initGatewayRules() {
        /*GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,
        可以针对不同 route 或自定义的 API 分组进行限流,
        支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
        */
        Set<GatewayFlowRule> rules = new HashSet<>();
        /*设置限流规则
        resource: 资源名称,这里为路由router的ID
        resourceMode: 路由模式
        count: QPS即每秒钟允许的调用次数
        intervalSec: 每隔多少时间统计一次汇总数据,统计时间窗口,单位是秒,默认是 1 秒。
        */
        rules.add(new GatewayFlowRule("zmg-sys")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                .setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 自定义限流异常处理
     * ResultSupport 为自定义的消息封装类,代码略
     */
    private void initBlockHandler() {
        GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange,
                                                      Throwable throwable) {
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(
                                ResultSupport.error("GW001", "系统访问量过大,限流啦!")));
            }
        });
    }
}

测试

正常请求1次,没有问题
正常访问

连续请求1次以上,报错了,限流器起作用了。
在这里插入图片描述

延伸

流控参数当然不能直接硬编码到代码中,可以通过sentinel控制台进行配置调整,或者用zookeeper进行统一管理推送给sentinel。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值