springcloud gateway网关

gateway 网关

背景:

在微服务架构中,一个微服务会拆分为多个微服务,客户端如何调用微服务呢?只能在客户端记录微服务的地址进行调用。

存在的问题:
客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性
认证复杂,每个服务都需要独立认证。
存在跨域请求,在一定场景下处理相对复杂。

解决方案:api网关

所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服
务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。

一、gateway简介

1、简介

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术
开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代
Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安
全,监控和限流。

2、优点

性能强劲:是第一代网关Zuul的1.6倍
功能强大:内置了很多实用的功能,例如转发、监控、限流等
设计优雅,容易扩展

3、缺点

其实现依赖NettyWebFlux,不是传统的Servlet编程模型,学习成本高
不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
需要Spring Boot 2.0及以上的版本,才支持

4、核心概念

4.1 路由(route)

路由是网关最基础的部分,路由信息由一个ID、一个目的URL、一组断言工厂和一组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

4.2 断言(predicates)

Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自Http Request中的任何信息,比如请求头和参数等。

4.3 过滤器(filter)

一个标准的Spring webFilterSpring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

二、 gateway网关

1、快速入门
1.1 搭建gateway-server

添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

注意SpringCloud Gateway使用的web框架为webflux,和SpringMVC不兼容。所以不需要spring-boot-starter-web。

启动类

@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

添加配置文件application.yml进行配置

server:
  port: 7001 #服务端口

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          uri: http://localhost:9444
          predicates:
            - Path=/product-service/**
            # http://localhost:7001/product-service/service/demo1 →
            # http://localhost:9444/product-service/service/demo1
          filters:
            # StripPrefix=1 表示去掉第一层路径 product-service
            # http://localhost:7001/product-service/service/demo1 →
            # http://localhost:9444/service/demo1
            - StripPrefix=1
1.2 搭建客户端服务

引用eureka-client-demo1

1.3 测试

测试客户端:localhost:9444/service/demo1

{
    "code": "ok",
    "port": 9444,
    "url": "/service/demo1"
}

测试经过网关:localhost:7001/product-service/service/demo1

{
    "code": "ok",
    "port": 9444,
    "url": "/service/demo1"
}
2、路由规则及断言
2.1 内置断言

Spring Cloud Gateway 的功能很强大,前面我们只是使用了 predicates 进行了简单的条件匹配,其实
Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。在 Spring Cloud Gateway 中 Spring 利用
Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件
匹配到对应的路由。

路由规则配置

id,路由标识符,区别于其他 Route。
uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
filter,过滤器用于修改请求和响应信息。

示例:

org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory

public class AfterRoutePredicateFactory extends AbstractRoutePredicateFactory<AfterRoutePredicateFactory.Config> {

	public static final String DATETIME_KEY = "datetime";

	public AfterRoutePredicateFactory() {
		super(Config.class);
	}

	@Override
	public List<String> shortcutFieldOrder() {
		return Collections.singletonList(DATETIME_KEY);
	}

	@Override
	public Predicate<ServerWebExchange> apply(Config config) {
		ZonedDateTime datetime = config.getDatetime();
		return exchange -> {
			final ZonedDateTime now = ZonedDateTime.now();
			return now.isAfter(datetime);
		};
	}

	public static class Config {
		@NotNull
		private ZonedDateTime datetime;

		public ZonedDateTime getDatetime() {
			return datetime;
		}

		public void setDatetime(ZonedDateTime datetime) {
			this.datetime = datetime;
		}
	}

}

配置:

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: after-route
          uri: https://www.baidu.com
          predicates:
            - After=2020-10-05T19:39:37.198+08:00[Asia/Shanghai]

时间格式为:datetime

@org.junit.Test
    public void test1(){
    ZonedDateTime datetime =ZonedDateTime.now();
    System.out.println(datetime);
}

访问测试:http://localhost:7001?access_token=zengqingfa

测试时间变化为某个未来的时间:

        - id: after-route
          uri: https://www.baidu.com
          predicates:
            - After=2020-10-05T20:39:37.198+08:00[Asia/Shanghai]

重新启动测试:
无法访问。

2.2 自定义断言工厂

假设我们的应用仅仅让age在(min,max)之间的人来访问。

1、自定义断言工厂

@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }

    //用于从配置文件中获取参数值赋值到配置类中的属性上
    @Override
    public List<String> shortcutFieldOrder() {
        //这里的顺序要跟配置文件中的参数顺序一致
        return Arrays.asList("minAge", "maxAge");
    }

    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                //从serverWebExchange获取传入的参数
                String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
                if (StringUtils.isNotEmpty(ageStr)) {
                    int age = Integer.parseInt(ageStr);
                    return age > config.getMinAge() && age < config.getMaxAge();
                }
                return true;
            }
        };
    }

    /**
     * 自定义一个配置类, 用于接收配置文件中的参数
     */
    @Data
    @NoArgsConstructor
    public static class Config {
        private int minAge;
        private int maxAge;
    }
}

2、配置

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          uri: lb://eureka-client1
          predicates:
            - Path=/product-service/**
            - Age=18,60 # 限制年龄只有在18到60岁之间的人能访问
          filters:
            - StripPrefix=1

3、启动测试:

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa&age=1

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa&age=20

3、动态路由

和zuul网关类似,在SpringCloud GateWay中也支持动态路由:即自动的从注册中心中获取服务列表并

访问。

3.1 gateway-server改造

添加eureka依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

添加注解@EnableEurekaClient

@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

application.yml配置文件修改

server:
  port: 7001 #服务端口

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          #uri : uri以 lb: //开头(lb代表从注册中心获取服务)+微服务名称
          uri: lb://eureka-client1
          predicates:
            - Path=/product-service/**
          filters:
            - StripPrefix=1

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:9001/eureka
    ##是否需要将自己注册注册中心
    register-with-eureka: true
    ##是否需要检索服务信息
    fetch-registry: true
3.2 客户端不修改
3.3 测试

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa

{
    "code": "ok",
    "port": 9444,
    "url": "/service/demo1",
    "userCode": "zengqingfa2323232"
}
4、重写转发路径

在SpringCloud Gateway中,路由转发是直接将匹配的路由path直接拼接到映射路径(URI)之后,那
么在微服务开发中往往没有那么便利。这里就可以通过RewritePath机制来进行路径重写。

4.1 改造
server:
  port: 7001 #服务端口

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          #uri : uri以 lb: //开头(lb代表从注册中心获取服务)+微服务名称
          uri: lb://eureka-client1
          predicates:
            - Path=/product-service/**

直接访问:

localhost:7001/product-service/service/demo1 报错404

于路由转发规则默认转发到微服务(http://127.0.0.1:9444/product- service/product/1 )路径上,而微服务又没有 product-service 对应的映射配置。

4.2 添加RewritePath重写转发路径
server:
  port: 7001 #服务端口

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          #uri : uri以 lb: //开头(lb代表从注册中心获取服务)+微服务名称
          uri: lb://eureka-client1
          predicates:
            - Path=/product-service/**
          filters:
            - RewritePath=/product-service/(?<segment>.*), /$\{segment} #路径重写

访问:

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa 成功

通过RewritePath配置重写转发的url,将/product-service/(?.*),重写为{segment},然后转发。比如在网页上请求http://localhost:8080/product-service/product,此时会将请求转发到

http://127.0.0.1:9002/product/1( 值得注意的是在yml文档中 $ 要写成 $\ )

5、过滤器

Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通
过过滤器的形式来实现的。

5.1 过滤器的生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像Zuul的那么丰富,它只有两个:“pre” 和 “post”。

PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。

5.2 过滤器类型

Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilterGlobalFilter
GatewayFilter:应用到单个路由或者一个分组的路由上。
GlobalFilter:应用到所有的路由上。

5.3 局部过滤器

局部过滤器(GatewayFilter),是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。在
Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。

1、内置局部过滤器

每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是
Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为
AddRequestHeaderGatewayFilterFactory

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
SetStatus修改原始响应的状态码HTTP 状态码,可以是数字,也可以是字符串

SetStatus为例:

配置

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          uri: lb://eureka-client1
          predicates:
            - Path=/product-service/**
          filters:
            - StripPrefix=1
            - SetStatus=2500

测试

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa

image.png

2、自定义局部过滤器

自定义过滤器工厂

@Component
@Slf4j
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    //构造函数
    public LogGatewayFilterFactory() {
        super(LogGatewayFilterFactory.Config.class);
    }

    //读取配置文件中的参数 赋值到 配置类中
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("consoleLog", "cacheLog");
    }

    //过滤器逻辑
    @Override
    public GatewayFilter apply(LogGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if (config.isCacheLog()) {
                    log.info("cacheLog已经开启了....");
                }
                if (config.isConsoleLog()) {
                    log.info("consoleLog已经开启了....");
                }
                return chain.filter(exchange);
            }
        };
    }

    @Data
    @NoArgsConstructor
    public static class Config {
        private boolean consoleLog;
        private boolean cacheLog;
    }
}

配置

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: product-service
          uri: lb://eureka-client1
          predicates:
            - Path=/product-service/**
          filters:
            - StripPrefix=1
            - Log=true,false  #控制日志是否开启

测试

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa

2020-10-06 10:28:12.238  INFO 16052 --- [ctor-http-nio-3] c.z.g.factory.LogGatewayFilterFactory    : consoleLog已经开启了....
5.4 全局过滤器

全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户
可以自定义实现自己的Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。
Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理

1、内置全局过滤器
过滤器作用
loadBalancerClientFilter负载均衡
ForwardPathFilter路径转发
public interface GlobalFilter {

	/**
	 * Process the Web request and (optionally) delegate to the next
	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
	 * @param exchange the current server exchange
	 * @param chain provides a way to delegate to the next filter
	 * @return {@code Mono<Void>} to indicate when request processing is complete
	 */
	Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

}

负载均衡过滤器配置:lb://微服务名称

- id: product-service
  uri: lb://eureka-client1
  order: 1
2、自定义全局过滤器: 统一鉴权

开发中的鉴权逻辑:
当客户端第一次请求服务时,服务端对用户进行信息认证(登录)认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证以后每次请求,客户端都携带认证的token服务端对token进行解密,判断是否有效。

对于验证用户是否已经登录鉴权的过程可以在网关层统一检验。检验的标准就是请求中是否携带token凭证以及token的正确性。

自定义全局过滤器

@Slf4j
@Component
public class LoginFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("access_token");
        List<String> cookie = exchange.getRequest().getHeaders().get("Cookie");
        if (StringUtils.isBlank(token)) {
            log.error("token is empty...");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        } else {

        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

认证流程

自定义全局过滤器需要实现GlobalFilterOrdered接口。
在filter方法中完成过滤器的逻辑判断处理
getOrder方法指定此过滤器的优先级,返回值越大级别越低
ServerWebExchange就相当于当前请求和响应的上下文,存放着重要的请求-响应属性、请求实例和响应实例等等。一个请求中的request,response都可以通过 ServerWebExchange 获取调用 chain.filter 继续向下游执行

测试

http://localhost:7001/product-service/service/demo1

添加token参数测试:

http://localhost:7001/product-service/service/demo1?access_token=zengqingfa

{
    code: "ok",
    port: 9444,
    url: "/service/demo1",
    userCode: "zengqingfa2323232"
}
6、网关限流

网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,我们本次采用前
面学过的Sentinel组件来实现网关的限流。Sentinel支持对SpringCloud Gatway、Zuul等主流网关进
行限流·

从1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流:
route维度:即在Spring配置文件中配置的路由条目,资源名为对应的routeId
自定义API维度:用户可以利用Sentinel提供的API来自定义一些API分组

1、导入依赖
<!--限流-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
2、编写配置类

基于Sentinel 的Gateway限流是通过其提供的Filter来完成的,使用时只需注入对应的
SentinelGatewayFilter实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

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

    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    // 配置初始化的限流参数
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                new GatewayFlowRule("eureka-client1") //资源名称,对应路由id
                        .setCount(1) // 限流阈值
                        .setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
        );
        GatewayRuleManager.loadRules(rules);
    }

    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 自定义限流异常页面
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 0);
                map.put("message", "接口被限流了");
                return ServerResponse.status(HttpStatus.OK).
                        contentType(MediaType.APPLICATION_JSON_UTF8).
                        body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

}
3、配置
server:
  port: 7001 #服务端口

spring:
  application:
    name: gateway-server #指定服务名
  cloud:
    gateway:
      routes:
        - id: eureka-client
          uri: lb://eureka-client
          order: 1
          predicates:
            - Path=/eureka-client/**
          filters:
            - StripPrefix=1
        - id: eureka-client1
          uri: lb://eureka-client1
          order: 1
          predicates:
            - Path=/eureka-client1/**
          filters:
            - StripPrefix=1

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:9001/eureka
    ##是否需要将自己注册注册中心
    register-with-eureka: true
    ##是否需要检索服务信息
    fetch-registry: true
4、路由维度测试

http://localhost:7001/eureka-client1/service/demo1?access_token=zengqingfa

http://localhost:7001/eureka-client/hello?access_token=zengqingfa

5、自定义api维度

修改配置类

@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

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

    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    // 配置初始化的限流参数
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("client_api1").setCount(1).setIntervalSec(1));
        rules.add(new GatewayFlowRule("client_api2").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 自定义限流异常页面
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 0);
                map.put("message", "接口被限流了111");
                return ServerResponse.status(HttpStatus.OK).
                        contentType(MediaType.APPLICATION_JSON_UTF8).
                        body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

    //自定义API分组
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("client_api1")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 以/eureka-client/api1 开头的请求
                    add(new ApiPathPredicateItem().setPattern("/eureka-client1/eureka-client/api1/**").
                            setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("client_api2")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 以/eureka-client/api2/demo1 完成的url路径匹配
                    add(new ApiPathPredicateItem().setPattern("/eureka-client1/eureka-client/api2/demo1"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

}

增加路由controller

@RestController
public class GatewayDemoRest {

    //限流
    @RequestMapping("/eureka-client/api1/demo1")
    public String demo1() {
        return "/eureka-client/api1/demo1";
    }

    //限流
    @RequestMapping("/eureka-client/api1/demo2")
    public String demo2() {
        return "/eureka-client/api1/demo2";
    }

    //限流
    @RequestMapping("/eureka-client/api2/demo1")
    public String demo3() {
        return "/eureka-client/api2/demo1";
    }

    //未限流
    @RequestMapping("/eureka-client/api2/demo2")
    public String demo4() {
        return "/eureka-client/api2/demo2";
    }
}

测试

http://localhost:7001/eureka-client1/eureka-client/api2/demo2 未限流,其他三个限流

附录(码云地址):https://gitee.com/zengqingfa/springcloud-demo.git

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值