前言
在前面的七篇文章中,我们从零开始构建了一个功能完整的 HTTP 客户端框架。理论知识固然重要,但实际应用才是检验框架价值的试金石。本文将通过三个不同复杂度的实战案例,展示如何在真实项目中使用 Atlas HTTP Client 框架。
这三个案例分别是:
- 基础案例:用户管理系统 - 展示基本的 CRUD 操作
- 进阶案例:电商订单系统 - 展示复杂业务场景和异步处理
- 高级案例:微服务网关 - 展示高性能和高可用架构
每个案例都会包含完整的代码实现、配置说明和最佳实践建议。
案例一:用户管理系统(基础案例)
业务场景
这是一个典型的用户管理系统,需要与后端 API 进行交互,实现用户的增删改查功能。这个案例展示了框架的基本使用方法。
项目结构
bash
user-management-demo/
├── src/main/java/
│ ├── com/example/user/
│ │ ├── UserManagementApplication.java
│ │ ├── client/
│ │ │ └── UserApiClient.java
│ │ ├── controller/
│ │ │ └── UserController.java
│ │ ├── service/
│ │ │ └── UserService.java
│ │ ├── model/
│ │ │ ├── User.java
│ │ │ ├── CreateUserRequest.java
│ │ │ └── UpdateUserRequest.java
│ │ └── config/
│ │ └── HttpClientConfig.java
│ └── resources/
│ └── application.yml
└── pom.xml
1. 依赖配置
xml
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.github.nemoob</groupId>
<artifactId>atlas-httpclient-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
2. 配置文件
yaml
# application.yml
server:
port: 8080
atlas:
httpclient:
enabled: true
default-connect-timeout: 5000
default-read-timeout: 10000
logging-enabled: true
metrics-enabled: true
# 拦截器配置
interceptors:
logging:
log-headers: true
log-body: true
max-body-length: 1024
retry:
enabled: true
max-retries: 3
retry-delay: 1000
# 外部 API 配置
api:
user-service:
base-url: https://jsonplaceholder.typicode.com
auth-token: your-api-token-here
3. 数据模型
java
// User.java
package com.example.user.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
private Long id;
private String name;
private String username;
private String email;
private String phone;
private String website;
private Address address;
private Company company;
// 构造函数
public User() {}
public User(String name, String username, String email) {
this.name = name;
this.username = username;
this.email = email;
}
### 6. 配置类
```java
// HttpClientConfig.java
package com.example.order.config;
import io.github.nemoob.httpclient.spring.annotation.EnableAtlasHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
@EnableAtlasHttpClient(basePackages = "com.example.order.client")
@EnableAsync
public class HttpClientConfig {
@Bean("batchExecutor")
public ExecutorService batchExecutor() {
return Executors.newFixedThreadPool(20, r -> {
Thread thread = new Thread(r, "batch-");
thread.setDaemon(true);
return thread;
});
}
@Bean("notificationExecutor")
public ExecutorService notificationExecutor() {
return Executors.newCachedThreadPool(r -> {
Thread thread = new Thread(r, "notification-");
thread.setDaemon(true);
return thread;
});
}
}
案例二总结
这个进阶案例展示了:
- 微服务协调:多个服务间的复杂交互和编排
- 异步处理:大量使用异步调用提高性能
- 高级拦截器:熔断器、限流器、分布式追踪
- 错误处理:完善的异常处理和回滚机制
- 事务管理:分布式事务的处理
- 性能优化:批量处理、并行执行
最佳实践:
- 使用熔断器防止服务雪崩
- 实现限流保护服务稳定性
- 添加分布式追踪便于问题排查
- 设计完善的补偿机制
- 合理使用异步处理提高吞吐量
案例三:微服务网关(高级案例)
业务场景
这是一个高性能的微服务网关,需要处理大量的并发请求,提供路由、负载均衡、认证、限流、监控等功能。这个案例展示了框架在高并发、高可用场景下的应用。
系统架构
scss
客户端请求
↓
网关 (Gateway)
├── 路由服务 (Routing)
├── 负载均衡 (Load Balancer)
├── 认证服务 (Authentication)
├── 限流服务 (Rate Limiting)
├── 监控服务 (Monitoring)
└── 后端服务 (Backend Services)
├── 用户服务
├── 订单服务
├── 商品服务
└── 支付服务
项目结构
bash
gateway-demo/
├── src/main/java/
│ ├── com/example/gateway/
│ │ ├── GatewayApplication.java
│ │ ├── controller/
│ │ │ └── GatewayController.java
│ │ ├── service/
│ │ │ ├── GatewayService.java
│ │ │ ├── RouteService.java
│ │ │ ├── LoadBalancerService.java
│ │ │ └── AuthenticationService.java
│ │ ├── client/
│ │ │ └── BackendServiceClient.java
│ │ ├── model/
│ │ │ ├── GatewayRequest.java
│ │ │ ├── GatewayResponse.java
│ │ │ └── RouteConfig.java
│ │ ├── interceptor/
│ │ │ ├── PerformanceInterceptor.java
│ │ │ ├── SecurityInterceptor.java
│ │ │ └── MonitoringInterceptor.java
│ │ └── config/
│ │ ├── GatewayConfig.java
│ │ └── PerformanceConfig.java
│ └── resources/
│ └── application.yml
└── pom.xml
1. 配置文件
yaml
# application.yml
server:
port: 8080
tomcat:
threads:
max: 1000
min-spare: 100
max-connections: 10000
accept-count: 1000
atlas:
httpclient:
enabled: true
default-connect-timeout: 1000
default-read-timeout: 3000
logging-enabled: false # 网关场景下关闭详细日志
metrics-enabled: true
# 高性能配置
interceptors:
retry:
enabled: false # 网关层不重试,由客户端决定
# 网关配置
gateway:
routes:
- id: user-service
path: /api/users/**
url: http://user-service:8080
load-balancer:
type: round-robin
health-check: true
- id: order-service
path: /api/orders/**
url: http://order-service:8081
load-balancer:
type: weighted
weights: [70, 30] # 权重分配
- id: product-service
path: /api/products/**
url: http://product-service:8082
load-balancer:
type: least-connections
# 限流配置
rate-limit:
global:
requests-per-second: 10000
burst-capacity: 20000
per-client:
requests-per-second: 100
burst-capacity: 200
# 认证配置
auth:
enabled: true
jwt-secret: your-jwt-secret-key
token-expiry: 3600
excluded-paths:
- /api/auth/login
- /api/health
- /actuator/**
# 监控配置
monitoring:
metrics-enabled: true
tracing-enabled: true
slow-request-threshold: 1000 # 慢请求阈值(毫秒)
# 性能调优
spring:
task:
execution:
pool:
core-size: 50
max-size: 500
queue-capacity: 1000
2. 数据模型
java
// GatewayRequest.java
package com.example.gateway.model;
import java.util.Map;
public class GatewayRequest {
private String path;
private String method;
private Map<String, String> headers;
private Map<String, String> queryParams;
private String body;
private String clientIp;
private String userId;
private long timestamp;
// 构造函数和 Getter/Setter 方法省略...
}
// GatewayResponse.java
package com.example.gateway.model;
import java.util.Map;
public class GatewayResponse {
private int statusCode;
private Map<String, String> headers;
private String body;
private long processingTime;
private String errorMessage;
// 构造函数和 Getter/Setter 方法省略...
}
// RouteConfig.java
package com.example.gateway.model;
import java.util.List;
public class RouteConfig {
private String id;
private String path;
private String url;
private LoadBalancerConfig loadBalancer;
private boolean authRequired = true;
private RateLimitConfig rateLimit;
// 内部类
public static class LoadBalancerConfig {
private String type; // round-robin, weighted, least-connections
private boolean healthCheck = true;
private List<Integer> weights;
private int healthCheckInterval = 30; // 秒
// Getter/Setter 方法省略...
}
public static class RateLimitConfig {
private int requestsPerSecond;
private int burstCapacity;
// Getter/Setter 方法省略...
}
// Getter/Setter 方法省略...
}
3. 高性能后端服务客户端
java
// BackendServiceClient.java
package com.example.gateway.client;
import io.github.nemoob.httpclient.annotation.*;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* 通用后端服务客户端
* 支持动态路由到不同的后端服务
*/
@HttpClient // 不设置固定URL,运行时动态指定
@Interceptor({PerformanceInterceptor.class, MonitoringInterceptor.class})
public interface BackendServiceClient {
/**
* 通用GET请求
*/
@GET("{path}")
@Async(executor = "gatewayExecutor", timeout = 3000)
CompletableFuture<String> get(
@Path("path") String path,
@Header("Host") String host,
@Header Map<String, String> headers
);
/**
* 通用POST请求
*/
@POST("{path}")
@Async(executor = "gatewayExecutor", timeout = 5000)
CompletableFuture<String> post(
@Path("path") String path,
@Header("Host") String host,
@Header Map<String, String> headers,
@Body String body
);
/**
* 通用PUT请求
*/
@PUT("{path}")
@Async(executor = "gatewayExecutor", timeout = 5000)
CompletableFuture<String> put(
@Path("path") String path,
@Header("Host") String host,
@Header Map<String, String> headers,
@Body String body
);
/**
* 通用DELETE请求
*/
@DELETE("{path}")
@Async(executor = "gatewayExecutor", timeout = 3000)
CompletableFuture<String> delete(
@Path("path") String path,
@Header("Host") String host,
@Header Map<String, String> headers
);
/**
* 健康检查
*/
@GET("/health")
@Async(executor = "healthCheckExecutor", timeout = 1000)
CompletableFuture<String> healthCheck(@Header("Host") String host);
}
4. 网关核心服务
java
// GatewayService.java
package com.example.gateway.service;
import com.example.gateway.client.BackendServiceClient;
import com.example.gateway.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* 网关核心服务
* 处理请求路由和转发
*/
@Service
public class GatewayService {
@Autowired
private BackendServiceClient backendServiceClient;
@Autowired
private RouteService routeService;
@Autowired
private LoadBalancerService loadBalancerService;
@Autowired
private AuthenticationService authenticationService;
/**
* 处理网关请求
*/
public CompletableFuture<GatewayResponse> handleRequest(GatewayRequest request) {
long startTime = System.currentTimeMillis();
return CompletableFuture.supplyAsync(() -> {
try {
// 1. 路由匹配
RouteConfig route = routeService.findRoute(request.getPath());
if (route == null) {
return createErrorResponse(404, "Route not found", startTime);
}
// 2. 认证检查
if (route.isAuthRequired() && !authenticationService.authenticate(request)) {
return createErrorResponse(401, "Authentication failed", startTime);
}
// 3. 负载均衡
String targetUrl = loadBalancerService.selectServer(route);
if (targetUrl == null) {
return createErrorResponse(503, "No available servers", startTime);
}
// 4. 转发请求
return forwardRequest(request, targetUrl, startTime).join();
} catch (Exception e) {
return createErrorResponse(500, "Internal server error: " + e.getMessage(), startTime);
}
});
}
/**
* 转发请求到后端服务
*/
private CompletableFuture<GatewayResponse> forwardRequest(GatewayRequest request, String targetUrl, long startTime) {
// 构建请求头
Map<String, String> headers = request.getHeaders();
headers.put("X-Forwarded-For", request.getClientIp());
headers.put("X-Gateway-Request-Id", generateRequestId());
CompletableFuture<String> responseFuture;
// 根据HTTP方法转发请求
switch (request.getMethod().toUpperCase()) {
case "GET":
responseFuture = backendServiceClient.get(request.getPath(), targetUrl, headers);
break;
case "POST":
responseFuture = backendServiceClient.post(request.getPath(), targetUrl, headers, request.getBody());
break;
case "PUT":
responseFuture = backendServiceClient.put(request.getPath(), targetUrl, headers, request.getBody());
break;
case "DELETE":
responseFuture = backendServiceClient.delete(request.getPath(), targetUrl, headers);
break;
default:
return CompletableFuture.completedFuture(createErrorResponse(405, "Method not allowed", startTime));
}
return responseFuture
.thenApply(responseBody -> {
GatewayResponse response = new GatewayResponse();
response.setStatusCode(200);
response.setBody(responseBody);
response.setProcessingTime(System.currentTimeMillis() - startTime);
return response;
})
.exceptionally(throwable -> {
return createErrorResponse(502, "Backend service error: " + throwable.getMessage(), startTime);
});
}
/**
* 创建错误响应
*/
private GatewayResponse createErrorResponse(int statusCode, String errorMessage, long startTime) {
GatewayResponse response = new GatewayResponse();
response.setStatusCode(statusCode);
response.setErrorMessage(errorMessage);
response.setBody("{\"error\": \"" + errorMessage + "\"}");
response.setProcessingTime(System.currentTimeMillis() - startTime);
return response;
}
/**
* 生成请求ID
*/
private String generateRequestId() {
return "GW-" + System.currentTimeMillis() + "-" + Thread.currentThread().getId();
}
}
5. 高性能拦截器
java
// PerformanceInterceptor.java
package com.example.gateway.interceptor;
import io.github.nemoob.httpclient.AbstractRequestInterceptor;
import io.github.nemoob.httpclient.RequestContext;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* 高性能拦截器
* 专为网关场景优化的性能监控
*/
@Component
public class PerformanceInterceptor extends AbstractRequestInterceptor {
private final LongAdder totalRequests = new LongAdder();
private final AtomicLong totalResponseTime = new AtomicLong();
private final LongAdder errorCount = new LongAdder();
@Override
public void preHandle(RequestContext context) throws Exception {
totalRequests.increment();
context.setAttribute("startTime", System.nanoTime());
}
@Override
public void postHandle(RequestContext context) throws Exception {
long startTime = (Long) context.getAttribute("startTime");
long duration = System.nanoTime() - startTime;
totalResponseTime.addAndGet(duration);
}
@Override
public void afterThrowing(RequestContext context, Exception ex) throws Exception {
errorCount.increment();
}
/**
* 获取性能统计
*/
public PerformanceStats getStats() {
long requests = totalRequests.sum();
long totalTime = totalResponseTime.get();
long errors = errorCount.sum();
double avgResponseTime = requests > 0 ? (double) totalTime / requests / 1_000_000 : 0; // 转换为毫秒
double errorRate = requests > 0 ? (double) errors / requests : 0;
return new PerformanceStats(requests, avgResponseTime, errorRate);
}
public static class PerformanceStats {
public final long totalRequests;
public final double avgResponseTime;
public final double errorRate;
public PerformanceStats(long totalRequests, double avgResponseTime, double errorRate) {
this.totalRequests = totalRequests;
this.avgResponseTime = avgResponseTime;
this.errorRate = errorRate;
}
}
}
案例三总结
这个高级案例展示了:
- 高并发处理:优化的线程池和连接配置
- 动态路由:灵活的路由匹配和负载均衡
- 性能监控:轻量级的性能统计和监控
- 安全认证:JWT 认证和权限控制
- 容错机制:熔断器、限流器等保护机制
- 可观测性:分布式追踪和监控指标
最佳实践:
- 关闭不必要的日志以提高性能
- 使用异步处理提高吞吐量
- 实现健康检查和故障转移
- 合理配置线程池和连接池
- 添加性能监控和告警
总结
通过这三个实战案例,我们展示了 Atlas HTTP Client 框架在不同场景下的应用:
案例对比
特性 | 基础案例 | 进阶案例 | 高级案例 |
---|---|---|---|
复杂度 | 简单 CRUD | 微服务编排 | 高并发网关 |
并发量 | 中等 | 高 | 极高 |
异步使用 | 部分异步 | 大量异步 | 全异步 |
拦截器 | 基础拦截器 | 高级拦截器 | 性能优化拦截器 |
错误处理 | 简单异常处理 | 分布式事务 | 容错和降级 |
监控 | 基础监控 | 分布式追踪 | 实时性能监控 |
关键学习点
- 渐进式学习:从简单到复杂,逐步掌握框架特性
- 实际应用:每个案例都来自真实的业务场景
- 性能优化:展示了不同场景下的性能优化技巧
- 最佳实践:总结了每个场景的最佳实践
- 扩展性:展示了框架的强大扩展能力
选择建议
- 新手开发者:从案例一开始,掌握基本用法
- 有经验开发者:重点关注案例二的微服务编排
- 架构师:深入研究案例三的高性能设计
希望这些实战案例能够帮助你更好地理解和使用 Atlas HTTP Client 框架,在实际项目中发挥其最大价值!
本文是《手把手教你写 httpclient 框架》系列的第八篇(实战案例篇)。通过三个不同复杂度的案例,展示了框架在实际项目中的应用。如果你觉得这个系列对你有帮助,欢迎点赞、收藏和分享!完整代码可以在 GitHub 上找到。
typescript
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getWebsite() { return website; }
public void setWebsite(String website) { this.website = website; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public Company getCompany() { return company; }
public void setCompany(Company company) { this.company = company; }
// 内部类
public static class Address {
private String street;
private String suite;
private String city;
private String zipcode;
private Geo geo;
// Getter 和 Setter 方法省略...
public static class Geo {
private String lat;
private String lng;
// Getter 和 Setter 方法省略...
}
}
public static class Company {
private String name;
private String catchPhrase;
private String bs;
// Getter 和 Setter 方法省略...
}
}
// CreateUserRequest.java package com.example.user.model;
import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank;
public class CreateUserRequest { @NotBlank(message = "姓名不能为空") private String name;
less
@NotBlank(message = "用户名不能为空")
private String username;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
private String email;
private String phone;
private String website;
// 构造函数和 Getter/Setter 方法省略...
}
// UpdateUserRequest.java package com.example.user.model;
import javax.validation.constraints.Email;
public class UpdateUserRequest { private String name; private String username;
typescript
@Email(message = "邮箱格式不正确")
private String email;
private String phone;
private String website;
// 构造函数和 Getter/Setter 方法省略...
}
less
### 4. HTTP 客户端接口
```java
// UserApiClient.java
package com.example.user.client;
import com.example.user.model.User;
import com.example.user.model.CreateUserRequest;
import com.example.user.model.UpdateUserRequest;
import io.github.nemoob.httpclient.annotation.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* 用户 API 客户端
* 与外部用户服务进行交互
*/
@HttpClient("${api.user-service.base-url}")
@Interceptor({AuthInterceptor.class, LoggingInterceptor.class})
public interface UserApiClient {
/**
* 获取所有用户
*/
@GET("/users")
List<User> getAllUsers();
/**
* 根据 ID 获取用户
*/
@GET("/users/{id}")
User getUserById(@Path("id") Long id);
/**
* 根据用户名搜索用户
*/
@GET("/users")
List<User> searchUsersByUsername(@Query("username") String username);
/**
* 创建新用户
*/
@POST("/users")
@Header("Content-Type: application/json")
User createUser(@Body CreateUserRequest request);
/**
* 更新用户信息
*/
@PUT("/users/{id}")
@Header("Content-Type: application/json")
User updateUser(@Path("id") Long id, @Body UpdateUserRequest request);
/**
* 删除用户
*/
@DELETE("/users/{id}")
@MethodInterceptor(AuditInterceptor.class)
void deleteUser(@Path("id") Long id);
/**
* 异步获取用户列表
*/
@GET("/users")
@Async(executor = "userServiceExecutor")
CompletableFuture<List<User>> getAllUsersAsync();
/**
* 批量获取用户信息
*/
@GET("/users")
@Async(executor = "batchExecutor")
CompletableFuture<List<User>> getUsersByIds(@Query("ids") List<Long> ids);
}
5. 自定义拦截器
java
// AuthInterceptor.java
package com.example.user.interceptor;
import io.github.nemoob.httpclient.AbstractRequestInterceptor;
import io.github.nemoob.httpclient.RequestContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 认证拦截器
* 自动为请求添加认证令牌
*/
@Component
public class AuthInterceptor extends AbstractRequestInterceptor {
@Value("${api.user-service.auth-token}")
private String authToken;
@Override
public void preHandle(RequestContext context) throws Exception {
if (context.getRequest() != null && authToken != null && !authToken.isEmpty()) {
context.getRequest().getHeaders().put("Authorization", "Bearer " + authToken);
}
}
}
// AuditInterceptor.java
package com.example.user.interceptor;
import io.github.nemoob.httpclient.AbstractRequestInterceptor;
import io.github.nemoob.httpclient.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 审计拦截器
* 记录敏感操作的审计日志
*/
@Component
public class AuditInterceptor extends AbstractRequestInterceptor {
private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT");
@Override
public void preHandle(RequestContext context) throws Exception {
String method = context.getRequest().getMethod().name();
String url = context.getRequest().getUrl();
auditLogger.info("审计日志 - 操作开始: {} {}", method, url);
}
@Override
public void postHandle(RequestContext context) throws Exception {
String method = context.getRequest().getMethod().name();
String url = context.getRequest().getUrl();
long duration = context.getEndTime() - context.getStartTime();
auditLogger.info("审计日志 - 操作完成: {} {} (耗时: {}ms)", method, url, duration);
}
@Override
public void afterThrowing(RequestContext context, Exception ex) throws Exception {
String method = context.getRequest().getMethod().name();
String url = context.getRequest().getUrl();
auditLogger.error("审计日志 - 操作失败: {} {} - 错误: {}", method, url, ex.getMessage());
}
}
6. 业务服务层
java
// UserService.java
package com.example.user.service;
import com.example.user.client.UserApiClient;
import com.example.user.model.User;
import com.example.user.model.CreateUserRequest;
import com.example.user.model.UpdateUserRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* 用户业务服务
* 封装用户相关的业务逻辑
*/
@Service
public class UserService {
@Autowired
private UserApiClient userApiClient;
/**
* 获取所有用户(带缓存)
*/
@Cacheable(value = "users", key = "'all'")
public List<User> getAllUsers() {
return userApiClient.getAllUsers();
}
/**
* 根据 ID 获取用户(带缓存)
*/
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("用户 ID 不能为空或小于等于 0");
}
User user = userApiClient.getUserById(id);
if (user == null) {
throw new RuntimeException("用户不存在: " + id);
}
return user;
}
/**
* 搜索用户
*/
public List<User> searchUsers(String username) {
if (username == null || username.trim().isEmpty()) {
return getAllUsers();
}
return userApiClient.searchUsersByUsername(username.trim());
}
/**
* 创建用户
*/
@CacheEvict(value = "users", allEntries = true)
public User createUser(CreateUserRequest request) {
validateCreateRequest(request);
try {
return userApiClient.createUser(request);
} catch (Exception e) {
throw new RuntimeException("创建用户失败: " + e.getMessage(), e);
}
}
/**
* 更新用户
*/
@CacheEvict(value = "users", key = "#id")
public User updateUser(Long id, UpdateUserRequest request) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("用户 ID 不能为空或小于等于 0");
}
// 先检查用户是否存在
getUserById(id);
try {
return userApiClient.updateUser(id, request);
} catch (Exception e) {
throw new RuntimeException("更新用户失败: " + e.getMessage(), e);
}
}
/**
* 删除用户
*/
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("用户 ID 不能为空或小于等于 0");
}
// 先检查用户是否存在
getUserById(id);
try {
userApiClient.deleteUser(id);
} catch (Exception e) {
throw new RuntimeException("删除用户失败: " + e.getMessage(), e);
}
}
/**
* 异步获取用户列表
*/
public CompletableFuture<List<User>> getAllUsersAsync() {
return userApiClient.getAllUsersAsync()
.exceptionally(throwable -> {
// 异步异常处理
throw new RuntimeException("异步获取用户列表失败", throwable);
});
}
/**
* 批量获取用户
*/
public CompletableFuture<List<User>> getUsersByIds(List<Long> ids) {
if (ids == null || ids.isEmpty()) {
return CompletableFuture.completedFuture(List.of());
}
return userApiClient.getUsersByIds(ids);
}
/**
* 验证创建用户请求
*/
private void validateCreateRequest(CreateUserRequest request) {
if (request == null) {
throw new IllegalArgumentException("创建用户请求不能为空");
}
if (request.getName() == null || request.getName().trim().isEmpty()) {
throw new IllegalArgumentException("用户姓名不能为空");
}
if (request.getUsername() == null || request.getUsername().trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (request.getEmail() == null || request.getEmail().trim().isEmpty()) {
throw new IllegalArgumentException("邮箱不能为空");
}
}
}
7. 控制器层
java
// UserController.java
package com.example.user.controller;
import com.example.user.model.User;
import com.example.user.model.CreateUserRequest;
import com.example.user.model.UpdateUserRequest;
import com.example.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* 用户控制器
* 提供用户管理的 REST API
*/
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@Autowired
private UserService userService;
/**
* 获取所有用户
*/
@GetMapping
public ResponseEntity<List<User>> getAllUsers(
@RequestParam(required = false) String username) {
List<User> users;
if (username != null && !username.trim().isEmpty()) {
users = userService.searchUsers(username);
} else {
users = userService.getAllUsers();
}
return ResponseEntity.ok(users);
}
/**
* 根据 ID 获取用户
*/
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(
@PathVariable @Min(value = 1, message = "用户 ID 必须大于 0") Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
/**
* 创建新用户
*/
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
User user = userService.createUser(request);
return ResponseEntity.ok(user);
}
/**
* 更新用户信息
*/
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable @Min(value = 1, message = "用户 ID 必须大于 0") Long id,
@Valid @RequestBody UpdateUserRequest request) {
User user = userService.updateUser(id, request);
return ResponseEntity.ok(user);
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(
@PathVariable @Min(value = 1, message = "用户 ID 必须大于 0") Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
/**
* 异步获取用户列表
*/
@GetMapping("/async")
public CompletableFuture<ResponseEntity<List<User>>> getAllUsersAsync() {
return userService.getAllUsersAsync()
.thenApply(ResponseEntity::ok);
}
/**
* 批量获取用户
*/
@PostMapping("/batch")
public CompletableFuture<ResponseEntity<List<User>>> getUsersByIds(
@RequestBody List<Long> ids) {
return userService.getUsersByIds(ids)
.thenApply(ResponseEntity::ok);
}
}
8. 配置类
java
// HttpClientConfig.java
package com.example.user.config;
import io.github.nemoob.httpclient.spring.annotation.EnableAtlasHttpClient;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* HTTP 客户端配置
*/
@Configuration
@EnableAtlasHttpClient(basePackages = "com.example.user.client")
@EnableCaching
@EnableAsync
public class HttpClientConfig {
/**
* 用户服务专用执行器
*/
@Bean("userServiceExecutor")
public ExecutorService userServiceExecutor() {
return Executors.newFixedThreadPool(10, r -> {
Thread thread = new Thread(r, "user-service-");
thread.setDaemon(true);
return thread;
});
}
/**
* 批量处理执行器
*/
@Bean("batchExecutor")
public ExecutorService batchExecutor() {
return Executors.newCachedThreadPool(r -> {
Thread thread = new Thread(r, "batch-");
thread.setDaemon(true);
return thread;
});
}
}
9. 启动类
java
// UserManagementApplication.java
package com.example.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserManagementApplication {
public static void main(String[] args) {
SpringApplication.run(UserManagementApplication.class, args);
}
}
10. 测试示例
java
// UserServiceTest.java
package com.example.user.service;
import com.example.user.client.UserApiClient;
import com.example.user.model.User;
import com.example.user.model.CreateUserRequest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@SpringBootTest
@SpringJUnitConfig
class UserServiceTest {
@MockBean
private UserApiClient userApiClient;
@Autowired
private UserService userService;
@Test
void testGetAllUsers() {
// 准备测试数据
List<User> mockUsers = Arrays.asList(
new User("John Doe", "john", "john@example.com"),
new User("Jane Smith", "jane", "jane@example.com")
);
// 配置 Mock 行为
when(userApiClient.getAllUsers()).thenReturn(mockUsers);
// 执行测试
List<User> result = userService.getAllUsers();
// 验证结果
assertNotNull(result);
assertEquals(2, result.size());
assertEquals("John Doe", result.get(0).getName());
// 验证方法调用
verify(userApiClient, times(1)).getAllUsers();
}
@Test
void testCreateUser() {
// 准备测试数据
CreateUserRequest request = new CreateUserRequest();
request.setName("Test User");
request.setUsername("testuser");
request.setEmail("test@example.com");
User mockUser = new User("Test User", "testuser", "test@example.com");
mockUser.setId(1L);
// 配置 Mock 行为
when(userApiClient.createUser(request)).thenReturn(mockUser);
// 执行测试
User result = userService.createUser(request);
// 验证结果
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals("Test User", result.getName());
// 验证方法调用
verify(userApiClient, times(1)).createUser(request);
}
}
案例一总结
这个基础案例展示了:
- 基本配置:如何配置 Atlas HTTP Client 和 Spring Boot 集成
- 接口定义:使用注解定义 HTTP 客户端接口
- 拦截器使用:认证和审计拦截器的实现
- 业务封装:在服务层封装业务逻辑和异常处理
- 缓存集成:与 Spring Cache 的集成使用
- 异步处理:异步方法的使用
- 测试方法:如何进行单元测试
最佳实践:
- 使用配置文件管理 API 地址和认证信息
- 在服务层进行参数验证和异常处理
- 合理使用缓存提高性能
- 为敏感操作添加审计日志
- 编写完整的单元测试
案例二:电商订单系统(进阶案例)
业务场景
这是一个电商订单系统,需要与多个微服务进行交互:用户服务、商品服务、库存服务、支付服务等。这个案例展示了复杂业务场景下的异步处理、事务管理和错误处理。
系统架构
scss
订单服务 (Order Service)
├── 用户服务 (User Service)
├── 商品服务 (Product Service)
├── 库存服务 (Inventory Service)
├── 支付服务 (Payment Service)
└── 通知服务 (Notification Service)
项目结构
css
order-system-demo/
├── src/main/java/
│ ├── com/example/order/
│ │ ├── OrderSystemApplication.java
│ │ ├── client/
│ │ │ ├── UserServiceClient.java
│ │ │ ├── ProductServiceClient.java
│ │ │ ├── InventoryServiceClient.java
│ │ │ ├── PaymentServiceClient.java
│ │ │ └── NotificationServiceClient.java
│ │ ├── service/
│ │ │ ├── OrderService.java
│ │ │ └── OrderOrchestrationService.java
│ │ ├── model/
│ │ │ ├── Order.java
│ │ │ ├── OrderItem.java
│ │ │ ├── CreateOrderRequest.java
│ │ │ └── dto/
│ │ ├── interceptor/
│ │ │ ├── CircuitBreakerInterceptor.java
│ │ │ ├── RateLimitInterceptor.java
│ │ │ └── DistributedTracingInterceptor.java
│ │ └── config/
│ │ ├── HttpClientConfig.java
│ │ └── ResilienceConfig.java
│ └── resources/
│ └── application.yml
└── pom.xml
1. 配置文件
yaml
# application.yml
server:
port: 8081
atlas:
httpclient:
enabled: true
default-connect-timeout: 3000
default-read-timeout: 5000
logging-enabled: true
metrics-enabled: true
# 客户端特定配置
clients:
userService:
base-url: http://user-service:8080
connect-timeout: 2000
read-timeout: 3000
productService:
base-url: http://product-service:8080
connect-timeout: 2000
read-timeout: 4000
inventoryService:
base-url: http://inventory-service:8080
connect-timeout: 1000
read-timeout: 2000
paymentService:
base-url: http://payment-service:8080
connect-timeout: 5000
read-timeout: 10000
notificationService:
base-url: http://notification-service:8080
connect-timeout: 1000
read-timeout: 2000
async: true
# 拦截器配置
interceptors:
logging:
log-headers: false
log-body: true
max-body-length: 2048
retry:
enabled: true
max-retries: 2
retry-delay: 500
# 熔断器配置
resilience:
circuit-breaker:
failure-rate-threshold: 50
wait-duration-in-open-state: 10000
sliding-window-size: 10
rate-limiter:
limit-for-period: 100
limit-refresh-period: 1000
timeout-duration: 1000
# 分布式追踪
tracing:
enabled: true
service-name: order-service
2. 数据模型
java
// Order.java
package com.example.order.model;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public class Order {
private Long id;
private Long userId;
private String orderNumber;
private OrderStatus status;
private BigDecimal totalAmount;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private List<OrderItem> items;
private PaymentInfo paymentInfo;
private ShippingInfo shippingInfo;
// 构造函数和 Getter/Setter 方法省略...
public enum OrderStatus {
PENDING, // 待处理
CONFIRMED, // 已确认
PAID, // 已支付
SHIPPED, // 已发货
DELIVERED, // 已送达
CANCELLED, // 已取消
REFUNDED // 已退款
}
}
// OrderItem.java
package com.example.order.model;
import java.math.BigDecimal;
public class OrderItem {
private Long id;
private Long productId;
private String productName;
private BigDecimal price;
private Integer quantity;
private BigDecimal subtotal;
// 构造函数和 Getter/Setter 方法省略...
}
// CreateOrderRequest.java
package com.example.order.model;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
public class CreateOrderRequest {
@NotNull(message = "用户 ID 不能为空")
private Long userId;
@NotEmpty(message = "订单项不能为空")
@Valid
private List<OrderItemRequest> items;
@Valid
private ShippingInfoRequest shippingInfo;
// 构造函数和 Getter/Setter 方法省略...
public static class OrderItemRequest {
@NotNull(message = "商品 ID 不能为空")
private Long productId;
@NotNull(message = "数量不能为空")
@Min(value = 1, message = "数量必须大于 0")
private Integer quantity;
// Getter/Setter 方法省略...
}
}
3. 微服务客户端接口
java
// UserServiceClient.java
package com.example.order.client;
import com.example.order.model.dto.UserInfo;
import io.github.nemoob.httpclient.annotation.*;
import java.util.concurrent.CompletableFuture;
@HttpClient("${atlas.httpclient.clients.userService.base-url}")
@Interceptor({CircuitBreakerInterceptor.class, RateLimitInterceptor.class})
public interface UserServiceClient {
@GET("/api/users/{id}")
@Async(timeout = 3000)
CompletableFuture<UserInfo> getUserInfo(@Path("id") Long userId);
@GET("/api/users/{id}/addresses")
@Async(timeout = 2000)
CompletableFuture<List<Address>> getUserAddresses(@Path("id") Long userId);
}
// ProductServiceClient.java
package com.example.order.client;
import com.example.order.model.dto.ProductInfo;
import io.github.nemoob.httpclient.annotation.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@HttpClient("${atlas.httpclient.clients.productService.base-url}")
@Interceptor({CircuitBreakerInterceptor.class, DistributedTracingInterceptor.class})
public interface ProductServiceClient {
@GET("/api/products/{id}")
@Async(timeout = 4000)
CompletableFuture<ProductInfo> getProductInfo(@Path("id") Long productId);
@POST("/api/products/batch")
@Async(executor = "batchExecutor", timeout = 5000)
CompletableFuture<List<ProductInfo>> getProductInfoBatch(@Body List<Long> productIds);
}
// InventoryServiceClient.java
package com.example.order.client;
import com.example.order.model.dto.InventoryInfo;
import com.example.order.model.dto.ReservationRequest;
import com.example.order.model.dto.ReservationResult;
import io.github.nemoob.httpclient.annotation.*;
import java.util.concurrent.CompletableFuture;
@HttpClient("${atlas.httpclient.clients.inventoryService.base-url}")
@Interceptor({CircuitBreakerInterceptor.class, RateLimitInterceptor.class})
public interface InventoryServiceClient {
@GET("/api/inventory/{productId}")
@Async(timeout = 2000)
CompletableFuture<InventoryInfo> getInventory(@Path("productId") Long productId);
@POST("/api/inventory/reserve")
@Async(timeout = 3000, retryCount = 2)
CompletableFuture<ReservationResult> reserveInventory(@Body ReservationRequest request);
@POST("/api/inventory/release")
@Async(timeout = 2000)
CompletableFuture<Void> releaseInventory(@Body String reservationId);
}
// PaymentServiceClient.java
package com.example.order.client;
import com.example.order.model.dto.PaymentRequest;
import com.example.order.model.dto.PaymentResult;
import io.github.nemoob.httpclient.annotation.*;
import java.util.concurrent.CompletableFuture;
@HttpClient("${atlas.httpclient.clients.paymentService.base-url}")
@Interceptor({CircuitBreakerInterceptor.class, DistributedTracingInterceptor.class})
public interface PaymentServiceClient {
@POST("/api/payments/process")
@Async(timeout = 10000, retryCount = 1)
CompletableFuture<PaymentResult> processPayment(@Body PaymentRequest request);
@POST("/api/payments/{id}/refund")
@Async(timeout = 8000)
CompletableFuture<PaymentResult> refundPayment(@Path("id") String paymentId);
}
// NotificationServiceClient.java
package com.example.order.client;
import com.example.order.model.dto.NotificationRequest;
import io.github.nemoob.httpclient.annotation.*;
import java.util.concurrent.CompletableFuture;
@HttpClient("${atlas.httpclient.clients.notificationService.base-url}")
@Interceptor({RateLimitInterceptor.class})
public interface NotificationServiceClient {
@POST("/api/notifications/send")
@Async(executor = "notificationExecutor", timeout = 2000)
CompletableFuture<Void> sendNotification(@Body NotificationRequest request);
@POST("/api/notifications/sms")
@Async(executor = "notificationExecutor", timeout = 3000)
CompletableFuture<Void> sendSms(@Body SmsRequest request);
@POST("/api/notifications/email")
@Async(executor = "notificationExecutor", timeout = 5000)
CompletableFuture<Void> sendEmail(@Body EmailRequest request);
}
4. 高级拦截器实现
java
// CircuitBreakerInterceptor.java
package com.example.order.interceptor;
import io.github.nemoob.httpclient.AbstractRequestInterceptor;
import io.github.nemoob.httpclient.RequestContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* 熔断器拦截器
* 实现服务熔断保护机制
*/
@Component
public class CircuitBreakerInterceptor extends AbstractRequestInterceptor {
@Value("${resilience.circuit-breaker.failure-rate-threshold:50}")
private int failureRateThreshold;
@Value("${resilience.circuit-breaker.wait-duration-in-open-state:10000}")
private long waitDurationInOpenState;
@Value("${resilience.circuit-breaker.sliding-window-size:10}")
private int slidingWindowSize;
// 每个服务的熔断器状态
private final ConcurrentHashMap<String, CircuitBreakerState> circuitBreakers = new ConcurrentHashMap<>();
@Override
public void preHandle(RequestContext context) throws Exception {
String serviceKey = getServiceKey(context);
CircuitBreakerState state = circuitBreakers.computeIfAbsent(serviceKey, k -> new CircuitBreakerState());
if (state.isOpen()) {
if (state.shouldAttemptReset()) {
state.halfOpen();
} else {
throw new RuntimeException("Circuit breaker is OPEN for service: " + serviceKey);
}
}
}
@Override
public void postHandle(RequestContext context) throws Exception {
String serviceKey = getServiceKey(context);
CircuitBreakerState state = circuitBreakers.get(serviceKey);
if (state != null) {
state.recordSuccess();
}
}
@Override
public void afterThrowing(RequestContext context, Exception ex) throws Exception {
String serviceKey = getServiceKey(context);
CircuitBreakerState state = circuitBreakers.get(serviceKey);
if (state != null) {
state.recordFailure();
}
}
private String getServiceKey(RequestContext context) {
String url = context.getRequest().getUrl();
// 从 URL 中提取服务标识
return url.split("/")[2]; // 简化实现
}
/**
* 熔断器状态
*/
private class CircuitBreakerState {
private volatile State state = State.CLOSED;
private final AtomicInteger failureCount = new AtomicInteger(0);
private final AtomicInteger successCount = new AtomicInteger(0);
private final AtomicLong lastFailureTime = new AtomicLong(0);
enum State {
CLOSED, OPEN, HALF_OPEN
}
public boolean isOpen() {
return state == State.OPEN;
}
public boolean shouldAttemptReset() {
return System.currentTimeMillis() - lastFailureTime.get() > waitDurationInOpenState;
}
public void halfOpen() {
state = State.HALF_OPEN;
}
public void recordSuccess() {
successCount.incrementAndGet();
if (state == State.HALF_OPEN) {
state = State.CLOSED;
failureCount.set(0);
}
}
public void recordFailure() {
int failures = failureCount.incrementAndGet();
lastFailureTime.set(System.currentTimeMillis());
int total = failures + successCount.get();
if (total >= slidingWindowSize) {
double failureRate = (double) failures / total * 100;
if (failureRate >= failureRateThreshold) {
state = State.OPEN;
}
}
}
}
}
// RateLimitInterceptor.java
package com.example.order.interceptor;
import io.github.nemoob.httpclient.AbstractRequestInterceptor;
import io.github.nemoob.httpclient.RequestContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* 限流拦截器
* 实现基于令牌桶的限流机制
*/
@Component
public class RateLimitInterceptor extends AbstractRequestInterceptor {
@Value("${resilience.rate-limiter.limit-for-period:100}")
private int limitForPeriod;
@Value("${resilience.rate-limiter.limit-refresh-period:1000}")
private long limitRefreshPeriod;
@Value("${resilience.rate-limiter.timeout-duration:1000}")
private long timeoutDuration;
// 每个服务的限流器
private final ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();
@Override
public void preHandle(RequestContext context) throws Exception {
String serviceKey = getServiceKey(context);
RateLimiter rateLimiter = rateLimiters.computeIfAbsent(serviceKey, k -> new RateLimiter());
if (!rateLimiter.tryAcquire()) {
throw new RuntimeException("Rate limit exceeded for service: " + serviceKey);
}
}
private String getServiceKey(RequestContext context) {
String url = context.getRequest().getUrl();
return url.split("/")[2]; // 简化实现
}
/**
* 简单的令牌桶限流器
*/
private class RateLimiter {
private final AtomicInteger tokens = new AtomicInteger(limitForPeriod);
private final AtomicLong lastRefreshTime = new AtomicLong(System.currentTimeMillis());
public boolean tryAcquire() {
refreshTokens();
return tokens.getAndDecrement() > 0;
}
private void refreshTokens() {
long now = System.currentTimeMillis();
long lastRefresh = lastRefreshTime.get();
if (now - lastRefresh >= limitRefreshPeriod) {
if (lastRefreshTime.compareAndSet(lastRefresh, now)) {
tokens.set(limitForPeriod);
}
}
}
}
}
// DistributedTracingInterceptor.java
package com.example.order.interceptor;
import io.github.nemoob.httpclient.AbstractRequestInterceptor;
import io.github.nemoob.httpclient.RequestContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.UUID;
/**
* 分布式追踪拦截器
* 为请求添加追踪标识
*/
@Component
public class DistributedTracingInterceptor extends AbstractRequestInterceptor {
@Value("${tracing.service-name:order-service}")
private String serviceName;
private static final String TRACE_ID_HEADER = "X-Trace-Id";
private static final String SPAN_ID_HEADER = "X-Span-Id";
private static final String PARENT_SPAN_ID_HEADER = "X-Parent-Span-Id";
@Override
public void preHandle(RequestContext context) throws Exception {
String traceId = getOrCreateTraceId();
String spanId = UUID.randomUUID().toString();
String parentSpanId = getCurrentSpanId();
// 添加追踪头
context.getRequest().getHeaders().put(TRACE_ID_HEADER, traceId);
context.getRequest().getHeaders().put(SPAN_ID_HEADER, spanId);
if (parentSpanId != null) {
context.getRequest().getHeaders().put(PARENT_SPAN_ID_HEADER, parentSpanId);
}
// 设置到上下文
context.setAttribute("traceId", traceId);
context.setAttribute("spanId", spanId);
}
@Override
public void postHandle(RequestContext context) throws Exception {
String traceId = (String) context.getAttribute("traceId");
String spanId = (String) context.getAttribute("spanId");
long duration = context.getEndTime() - context.getStartTime();
// 记录追踪信息
System.out.printf("[TRACE] %s - %s: %s %s (duration: %dms)%n",
traceId, spanId,
context.getRequest().getMethod(),
context.getRequest().getUrl(),
duration
);
}
private String getOrCreateTraceId() {
// 简化实现,实际应该从 ThreadLocal 或 MDC 中获取
return UUID.randomUUID().toString();
}
private String getCurrentSpanId() {
// 简化实现,实际应该从当前上下文中获取
return null;
}
}
5. 订单编排服务
java
// OrderOrchestrationService.java
package com.example.order.service;
import com.example.order.client.*;
import com.example.order.model.*;
import com.example.order.model.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* 订单编排服务
* 协调多个微服务完成订单处理流程
*/
@Service
public class OrderOrchestrationService {
@Autowired
private UserServiceClient userServiceClient;
@Autowired
private ProductServiceClient productServiceClient;
@Autowired
private InventoryServiceClient inventoryServiceClient;
@Autowired
private PaymentServiceClient paymentServiceClient;
@Autowired
private NotificationServiceClient notificationServiceClient;
/**
* 创建订单的完整流程
*/
@Transactional
public CompletableFuture<Order> createOrder(CreateOrderRequest request) {
return validateUser(request.getUserId())
.thenCompose(userInfo -> validateAndReserveProducts(request.getItems()))
.thenCompose(reservationResults -> {
// 计算订单总金额
BigDecimal totalAmount = calculateTotalAmount(reservationResults);
// 创建订单对象
Order order = createOrderEntity(request, totalAmount);
// 处理支付
return processPayment(order)
.thenCompose(paymentResult -> {
if (paymentResult.isSuccess()) {
// 支付成功,确认订单
order.setStatus(Order.OrderStatus.PAID);
return confirmOrder(order)
.thenCompose(confirmedOrder -> sendNotifications(confirmedOrder))
.thenApply(v -> order);
} else {
// 支付失败,释放库存
return releaseInventory(reservationResults)
.thenCompose(v -> {
throw new RuntimeException("支付失败: " + paymentResult.getErrorMessage());
});
}
});
})
.exceptionally(throwable -> {
// 异常处理:回滚操作
handleOrderCreationFailure(throwable);
throw new RuntimeException("订单创建失败", throwable);
});
}
/**
* 验证用户信息
*/
private CompletableFuture<UserInfo> validateUser(Long userId) {
return userServiceClient.getUserInfo(userId)
.thenApply(userInfo -> {
if (userInfo == null) {
throw new RuntimeException("用户不存在: " + userId);
}
if (!userInfo.isActive()) {
throw new RuntimeException("用户账户已被禁用: " + userId);
}
return userInfo;
});
}
/**
* 验证商品并预留库存
*/
private CompletableFuture<List<ReservationResult>> validateAndReserveProducts(List<CreateOrderRequest.OrderItemRequest> items) {
// 批量获取商品信息
List<Long> productIds = items.stream()
.map(CreateOrderRequest.OrderItemRequest::getProductId)
.collect(Collectors.toList());
return productServiceClient.getProductInfoBatch(productIds)
.thenCompose(productInfos -> {
// 验证商品信息
validateProducts(productInfos, items);
// 并行预留库存
List<CompletableFuture<ReservationResult>> reservationFutures = items.stream()
.map(item -> {
ReservationRequest reservationRequest = new ReservationRequest(
item.getProductId(),
item.getQuantity(),
generateReservationId()
);
return inventoryServiceClient.reserveInventory(reservationRequest);
})
.collect(Collectors.toList());
return CompletableFuture.allOf(reservationFutures.toArray(new CompletableFuture[0]))
.thenApply(v -> reservationFutures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
});
}
/**
* 处理支付
*/
private CompletableFuture<PaymentResult> processPayment(Order order) {
PaymentRequest paymentRequest = new PaymentRequest(
order.getId(),
order.getUserId(),
order.getTotalAmount(),
"ORDER_PAYMENT"
);
return paymentServiceClient.processPayment(paymentRequest)
.thenApply(result -> {
// 更新订单支付信息
order.setPaymentInfo(new PaymentInfo(
result.getPaymentId(),
result.getPaymentMethod(),
result.getTransactionId()
));
return result;
});
}
/**
* 确认订单
*/
private CompletableFuture<Order> confirmOrder(Order order) {
// 这里应该保存订单到数据库
// 简化实现,直接返回
return CompletableFuture.completedFuture(order);
}
/**
* 发送通知
*/
private CompletableFuture<Void> sendNotifications(Order order) {
// 并行发送多种通知
CompletableFuture<Void> emailNotification = sendEmailNotification(order);
CompletableFuture<Void> smsNotification = sendSmsNotification(order);
CompletableFuture<Void> pushNotification = sendPushNotification(order);
return CompletableFuture.allOf(emailNotification, smsNotification, pushNotification)
.exceptionally(throwable -> {
// 通知失败不影响订单创建
System.err.println("发送通知失败: " + throwable.getMessage());
return null;
});
}
/**
* 释放库存
*/
private CompletableFuture<Void> releaseInventory(List<ReservationResult> reservationResults) {
List<CompletableFuture<Void>> releaseFutures = reservationResults.stream()
.filter(ReservationResult::isSuccess)
.map(result -> inventoryServiceClient.releaseInventory(result.getReservationId()))
.collect(Collectors.toList());
return CompletableFuture.allOf(releaseFutures.toArray(new CompletableFuture[0]));
}
/**
* 发送邮件通知
*/
private CompletableFuture<Void> sendEmailNotification(Order order) {
EmailRequest emailRequest = new EmailRequest(
order.getUserId(),
"订单确认",
"您的订单 " + order.getOrderNumber() + " 已确认,感谢您的购买!"
);
return notificationServiceClient.sendEmail(emailRequest);
}
/**
* 发送短信通知
*/
private CompletableFuture<Void> sendSmsNotification(Order order) {
SmsRequest smsRequest = new SmsRequest(
order.getUserId(),
"订单 " + order.getOrderNumber() + " 已确认"
);
return notificationServiceClient.sendSms(smsRequest);
}
/**
* 发送推送通知
*/
private CompletableFuture<Void> sendPushNotification(Order order) {
NotificationRequest notificationRequest = new NotificationRequest(
order.getUserId(),
"订单确认",
"您的订单已确认",
"ORDER_CONFIRMED"
);
return notificationServiceClient.sendNotification(notificationRequest);
}
/**
* 验证商品信息
*/
private void validateProducts(List<ProductInfo> productInfos, List<CreateOrderRequest.OrderItemRequest> items) {
if (productInfos.size() != items.size()) {
throw new RuntimeException("部分商品不存在");
}
for (ProductInfo productInfo : productInfos) {
if (!productInfo.isAvailable()) {
throw new RuntimeException("商品不可用: " + productInfo.getName());
}
}
}
/**
* 计算订单总金额
*/
private BigDecimal calculateTotalAmount(List<ReservationResult> reservationResults) {
return reservationResults.stream()
.filter(ReservationResult::isSuccess)
.map(result -> result.getPrice().multiply(BigDecimal.valueOf(result.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
/**
* 创建订单实体
*/
private Order createOrderEntity(CreateOrderRequest request, BigDecimal totalAmount) {
Order order = new Order();
order.setUserId(request.getUserId());
order.setOrderNumber(generateOrderNumber());
order.setStatus(Order.OrderStatus.PENDING);
order.setTotalAmount(totalAmount);
order.setCreatedAt(LocalDateTime.now());
// 设置订单项
List<OrderItem> orderItems = request.getItems().stream()
.map(item -> {
OrderItem orderItem = new OrderItem();
orderItem.setProductId(item.getProductId());
orderItem.setQuantity(item.getQuantity());
return orderItem;
})
.collect(Collectors.toList());
order.setItems(orderItems);
return order;
}
/**
* 生成订单号
*/
private String generateOrderNumber() {
return "ORD" + System.currentTimeMillis();
}
/**
* 生成预留ID
*/
private String generateReservationId() {
return "RSV" + System.currentTimeMillis() + "_" + Thread.currentThread().getId();
}
/**
* 处理订单创建失败
*/
private void handleOrderCreationFailure(Throwable throwable) {
System.err.println("订单创建失败,执行回滚操作: " + throwable.getMessage());
// 这里应该实现具体的回滚逻辑
}
}