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.
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: trueWith 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=1Using 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/errorNote: 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: trueRate 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!
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
