Spring Cloud Microservices Tutorial – Sentinel for Fault Tolerance and Rate Limiting
This article walks through adding Alibaba Sentinel to a Spring Cloud microservice suite to protect against service outages, traffic spikes, and slow calls by configuring rate limiting, circuit breaking, and fallback mechanisms across user, order, and gateway services, with full Docker‑compose setup and testing steps.
Goal
Build a chain of services (client → Gateway → order‑service → user‑service) protected by Sentinel, providing:
Rate limiting – block requests when QPS exceeds a threshold.
Circuit breaking – open the circuit when error rate or slow‑call ratio is high.
Fallback – return default data when a downstream service is unavailable.
Why Service Fault Tolerance Is Needed
Service avalanche effect
User request → Gateway → order‑service → user‑service → DB
↓
user‑service timeout
↓
order‑service threads blocked
↓
Gateway threads blocked
↓
System crashSentinel solution
Traffic spike → Rate limiting (QPS / thread count)
Slow / timeout → Circuit breaking (slow‑call ratio)
Service error → Circuit breaking (exception ratio / count)
Upstream unavailable → Fallback
Project Structure (Sentinel added)
spring-cloud-teaching-ep06/
├── pom.xml # Sentinel dependency management
├── docker-compose.yml # Sentinel dashboard container
├── user-service/ # Sentinel config for user service
├── order-service/ # ★ Feign + Sentinel circuit break
├── gateway/ # Sentinel rate‑limit for gateway
└── README.mdEnvironment Preparation
1. Start Sentinel dashboard
# docker-compose.yml addition
sentinel:
image: bladex/sentinel-dashboard:1.8.6
container_name: sentinel-teaching
ports:
- "8858:8858"
environment:
- AUTH_USERNAME=sentinel
- AUTH_PASSWORD=sentinel2. Access the dashboard
Open http://localhost:8858 (username/password: sentinel / sentinel).
user‑service Integration
1. Dependency
<!-- user-service/pom.xml -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>2. Configuration
# user-service/src/main/resources/application.yml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8858
port: 8719
eager: true # register immediately3. Rate‑limit example
package com.teaching.user.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/sentinel")
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "test-resource", blockHandler = "handleBlock")
public Map<String, Object> test() {
Map<String, Object> result = new HashMap<>();
result.put("message", "正常响应");
result.put("time", System.currentTimeMillis());
return result;
}
// block handler
public Map<String, Object> handleBlock(BlockException ex) {
Map<String, Object> result = new HashMap<>();
result.put("code", 429);
result.put("message", "请求过于频繁,请稍后重试");
return result;
}
}order‑service Feign + Sentinel Circuit Break (Key Part)
1. Dependency
<!-- order-service/pom.xml -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>2. Configuration
# order-service/src/main/resources/application.yml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8858
port: 8719
eager: true
feign:
sentinel:
enabled: true # ★ Enable Feign‑Sentinel support3. Feign client with fallback
package com.teaching.order.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
@FeignClient(name = "user-service", fallback = UserFeignClientFallback.class) // ★ configure fallback
public interface UserFeignClient {
@GetMapping("/api/user/{id}")
Map<String, Object> getUser(@PathVariable("id") Long id);
} package com.teaching.order.client;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class UserFeignClientFallback implements UserFeignClient {
@Override
public Map<String, Object> getUser(Long id) {
Map<String, Object> fallback = new HashMap<>();
fallback.put("id", id);
fallback.put("name", "用户服务不可用(降级数据)");
fallback.put("error", true);
fallback.put("message", "服务熔断,返回默认数据");
return fallback;
}
}4. Circuit‑breaker test endpoint
package com.teaching.order.controller;
import com.teaching.order.client.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/circuit")
public class CircuitBreakerController {
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/test")
public Map<String, Object> testCircuitBreaker() {
long start = System.currentTimeMillis();
Map<String, Object> user = userFeignClient.getUser(1L); // may trigger fallback
long cost = System.currentTimeMillis() - start;
Map<String, Object> result = new HashMap<>();
result.put("userInfo", user);
result.put("costMs", cost);
result.put("message", user.containsKey("error") ? "降级生效" : "正常调用");
return result;
}
}Gateway Rate Limiting
1. Dependency
<!-- gateway/pom.xml -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>2. Configuration
# gateway/src/main/resources/application.yml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8858
port: 8719
eager: true
filter:
enabled: true3. Gateway rate‑limit rule (QPS = 5)
package com.teaching.gateway.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
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 org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import jakarta.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;
@Configuration
public class SentinelGatewayConfig {
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void initGatewayRules() {
// 1. Define API group
Set<ApiDefinition> apiDefinitions = new HashSet<>();
ApiDefinition orderApi = new ApiDefinition("order-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem()
.setPattern("/api/order/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
apiDefinitions.add(orderApi);
GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);
// 2. Configure rate‑limit rule
Set<GatewayFlowRule> gatewayRules = new HashSet<>();
GatewayFlowRule orderRule = new GatewayFlowRule("order-api")
.setCount(5) // QPS = 5
.setIntervalSec(1);
gatewayRules.add(orderRule);
GatewayRuleManager.loadRules(gatewayRules);
System.out.println("✅ Sentinel gateway rate‑limit rule loaded");
}
}Verification Tests
1. Start all services
# Start Nacos + Sentinel
docker-compose up -d
# Start user‑service
cd user-service && mvn spring-boot:run
# Start order‑service
cd order-service && mvn spring-boot:run
# Start gateway
cd gateway && mvn spring-boot:run2. Verify rate limiting (QPS > 5 should be blocked)
for i in {1..20}; do
curl -s http://localhost:8080/api/order/1 | grep -o '"message":"[^"]*"'
sleep 0.1
done3. Verify circuit breaking & fallback
# Normal call (user‑service up)
curl http://localhost:8080/api/order/1
# Stop user‑service, then call order‑service to trigger fallback
curl http://localhost:8082/api/circuit/test4. Observe metrics in Sentinel dashboard
Open http://localhost:8858 to view service list, real‑time metrics, and the configured rate‑limit and circuit‑break rules.
Common Issues & Troubleshooting
Issue 1: Service not shown in Sentinel console
Cause: Lazy registration – the service registers only after the first request.
Solution: Send an initial request or enable eager registration with eager: true in application.yml.
Issue 2: Feign fallback not triggered
Confirm feign.sentinel.enabled=true is set.
Ensure the fallback class is annotated with @Component.
Verify the @FeignClient declaration includes fallback = ….
Issue 3: Gateway rate limiting ineffective
Check that the SentinelGatewayFilter bean is present.
Confirm the rule is loaded (log should contain “✅ Sentinel gateway rate‑limit rule loaded”).
Verify the URL pattern in the rule matches the actual request path.
Code Structure Overview
spring-cloud-teaching-ep06/
├── pom.xml
├── docker-compose.yml
├── user-service/
│ ├── pom.xml
│ ├── src/main/resources/application.yml
│ └── src/main/java/.../controller/SentinelController.java
├── order-service/
│ ├── pom.xml
│ ├── src/main/resources/application.yml
│ └── src/main/java/.../client/UserFeignClientFallback.java
├── gateway/
│ ├── pom.xml
│ ├── src/main/resources/application.yml
│ └── src/main/java/.../config/SentinelGatewayConfig.java
└── README.mdSigned-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.
Coder Trainee
Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.
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.
