Cloud Native 11 min read

Spring Cloud Gateway Guide: Discovery, Hystrix, Filters & Rate Limiting

This tutorial demonstrates how to configure Spring Cloud Gateway with service discovery, integrate Hystrix for circuit breaking, implement global and custom route filters, and apply Redis-based rate limiting, providing complete Maven dependencies, Java code examples, and YAML configurations for a robust microservice gateway.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Cloud Gateway Guide: Discovery, Hystrix, Filters & Rate Limiting

Environment: Springboot 2.3.10.RELEASE, Spring Cloud Hoxton.SR11, Spring Cloud Alibaba 2.2.5.RELEASE, Redis

Dependencies

<dependencies>
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
  </dependency>
</dependencies>

Project structure

service-consumer submodule interface

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

  @Resource
  private RestTemplate restTemplate;

  @Resource
  private HttpServletRequest request;

  @GetMapping("/get")
  public Object invoke(String serviceName) {
    try {
      TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return restTemplate.getForObject("http://service-producer/discovery/get?serviceName=" + serviceName, Object.class);
  }
}

service-producer submodule interface

@RestController
@RequestMapping("discovery")
public class DiscoveryController {

  @NacosInjected
  private NamingService namingService;

  @Resource
  private DiscoveryClient discoverClient;

  @GetMapping(value = "/get")
  public Object get(@RequestParam String serviceName) throws Exception {
    Map<String, Object> res = new HashMap<>();
    res.put("services", discoverClient.getServices());
    res.put("instances", discoverClient.getInstances(serviceName));
    res.put("port", 9000);
    return res;
  }
}

Service Discovery Configuration (Direct Service Name Access)

Method One (not recommended):

spring:
  cloud:
    gateway:
      discovery:
        locator:
          # e.g., http://localhost:9999/service-consumer/consumer/get?serviceName=service-producer
          enabled: true
          lower-case-service-id: true

With this configuration, services can be accessed directly by their Spring application name, but this is usually disabled in production.

Method Two:

spring:
  cloud:
    gateway:
      routes:
      - id: gw001
        uri: lb://service-consumer
        predicates:
        - Path=/api/**,/api-a/**,/api-b/**
        filters:
        - StripPrefix=1

Using this approach, the three prefixes /api, /api-a, and /api-b can be used to access the service.

Integrate Hystrix

Add the Hystrix dependency:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
spring:
  cloud:
    gateway:
      routes:
      - id: gw001
        uri: lb://service-consumer
        predicates:
        - Path=/api/**,/api-a/**,/api-b/**
        filters:
        - StripPrefix=1
        - name: Hystrix
          args:
            name: gatewayfallback
            fallbackUri: forward:/gateway/error

Note: The Hystrix filter name must be provided; the fallbackUri is called when the circuit opens.

@RestController
@RequestMapping("/gateway")
public class DefaultController {

  @RequestMapping("/error")
  public Object error() {
    Map<String, Object> result = new HashMap<>();
    result.put("code", -1);
    result.put("message", "Service unavailable, please try again later");
    result.put("data", null);
    return result;
  }
}

Hystrix’s default timeout is 1 second, so the consumer endpoint sleeps for 3 seconds to simulate a timeout.

Custom Filters

Global Filter

Implement GlobalFilter and register it as a bean to apply to all routes.

@Component
public class GlobalExecutionTimeFilter implements GlobalFilter {

  private static Logger logger = LoggerFactory.getLogger(GlobalExecutionTimeFilter.class);

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    long start = System.currentTimeMillis();
    Mono<Void> result = chain.filter(exchange);
    logger.info("Execution time: {} ms", System.currentTimeMillis() - start);
    return result;
  }
}

Custom Route Filter (Token Validation)

This filter must be attached to a specific route.

@Component
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.TokenConfig> {

  public static final String ENABLE_KEY = "enable";
  public static final String EXCLUDE_KEY = "exclude";

  public TokenGatewayFilterFactory() {
    super(TokenConfig.class);
  }

  @Override
  public List<String> shortcutFieldOrder() {
    return Arrays.asList(ENABLE_KEY, EXCLUDE_KEY);
  }

  @Override
  public GatewayFilter apply(TokenConfig config) {
    return (exchange, chain) -> {
      if (!config.isEnable()) {
        return chain.filter(exchange);
      }
      ServerHttpRequest request = exchange.getRequest();
      ServerHttpResponse response = exchange.getResponse();
      String token = request.getHeaders().getFirst("access-token");
      if (token == null || "".equals(token)) {
        token = request.getQueryParams().getFirst("access-token");
      }
      if (token == null || "".equals(token)) {
        DataBuffer data = setErrorInfo(response, "Please check if Token is provided");
        return response.writeWith(Mono.just(data));
      }
      return chain.filter(exchange);
    };
  }

  private DataBuffer setErrorInfo(ServerHttpResponse response, String msg) {
    HttpHeaders httpHeaders = response.getHeaders();
    httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
    httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
    String body = "{\"code\":-1, \"message\": \"" + msg + "\"}";
    return response.bufferFactory().wrap(body.getBytes());
  }

  public static class TokenConfig {
    private boolean enable;
    private String exclude;
    public boolean isEnable() { return enable; }
    public void setEnable(boolean enable) { this.enable = enable; }
    public String getExclude() { return exclude; }
    public void setExclude(String exclude) { this.exclude = exclude; }
  }
}

Configuration:

spring:
  cloud:
    gateway:
      routes:
      - id: gw001
        uri: lb://service-consumer
        predicates:
        - Path=/api/**,/api-a/**,/api-b/**
        filters:
        - StripPrefix=1
        - name: Hystrix
          args:
            name: gatewayfallback
            fallbackUri: forward:/gateway/error
        - name: Token
          args:
            enable: true

Rate Limiting

Add dependencies for Redis and Commons Pool2.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
</dependency>
spring:
  cloud:
    gateway:
      routes:
      - id: gw001
        uri: lb://service-consumer
        predicates:
        - Path=/api/**,/api-a/**,/api-b/**
        filters:
        - StripPrefix=1
        - name: RequestRateLimiter
          args:
            keyResolver: '#{@redisKeyResolver}'
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 2
@Component
public class RedisKeyResolver implements KeyResolver {

  // Rate limiting based on IP address
  @Override
  public Mono<String> resolve(ServerWebExchange exchange) {
    String address = exchange.getRequest().getRemoteAddress().getHostString();
    return Mono.just(address);
  }
}

If requests exceed the configured rate, a 429 status code is returned.

Finished!

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

service discoverygatewayrate limitingSpring CloudCustom FilterHystrix
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.