手把手教你写 httpclient 框架(八)- 实战案例集锦

前言

在前面的七篇文章中,我们从零开始构建了一个功能完整的 HTTP 客户端框架。理论知识固然重要,但实际应用才是检验框架价值的试金石。本文将通过三个不同复杂度的实战案例,展示如何在真实项目中使用 Atlas HTTP Client 框架。

这三个案例分别是:

  1. 基础案例:用户管理系统 - 展示基本的 CRUD 操作
  2. 进阶案例:电商订单系统 - 展示复杂业务场景和异步处理
  3. 高级案例:微服务网关 - 展示高性能和高可用架构

每个案例都会包含完整的代码实现、配置说明和最佳实践建议。

案例一:用户管理系统(基础案例)

业务场景

这是一个典型的用户管理系统,需要与后端 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;
        });
    }
}

案例二总结

这个进阶案例展示了:

  1. 微服务协调:多个服务间的复杂交互和编排
  2. 异步处理:大量使用异步调用提高性能
  3. 高级拦截器:熔断器、限流器、分布式追踪
  4. 错误处理:完善的异常处理和回滚机制
  5. 事务管理:分布式事务的处理
  6. 性能优化:批量处理、并行执行

最佳实践

  • 使用熔断器防止服务雪崩
  • 实现限流保护服务稳定性
  • 添加分布式追踪便于问题排查
  • 设计完善的补偿机制
  • 合理使用异步处理提高吞吐量

案例三:微服务网关(高级案例)

业务场景

这是一个高性能的微服务网关,需要处理大量的并发请求,提供路由、负载均衡、认证、限流、监控等功能。这个案例展示了框架在高并发、高可用场景下的应用。

系统架构

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;
        }
    }
}

案例三总结

这个高级案例展示了:

  1. 高并发处理:优化的线程池和连接配置
  2. 动态路由:灵活的路由匹配和负载均衡
  3. 性能监控:轻量级的性能统计和监控
  4. 安全认证:JWT 认证和权限控制
  5. 容错机制:熔断器、限流器等保护机制
  6. 可观测性:分布式追踪和监控指标

最佳实践

  • 关闭不必要的日志以提高性能
  • 使用异步处理提高吞吐量
  • 实现健康检查和故障转移
  • 合理配置线程池和连接池
  • 添加性能监控和告警

总结

通过这三个实战案例,我们展示了 Atlas HTTP Client 框架在不同场景下的应用:

案例对比

特性 基础案例 进阶案例 高级案例
复杂度 简单 CRUD 微服务编排 高并发网关
并发量 中等 极高
异步使用 部分异步 大量异步 全异步
拦截器 基础拦截器 高级拦截器 性能优化拦截器
错误处理 简单异常处理 分布式事务 容错和降级
监控 基础监控 分布式追踪 实时性能监控

关键学习点

  1. 渐进式学习:从简单到复杂,逐步掌握框架特性
  2. 实际应用:每个案例都来自真实的业务场景
  3. 性能优化:展示了不同场景下的性能优化技巧
  4. 最佳实践:总结了每个场景的最佳实践
  5. 扩展性:展示了框架的强大扩展能力

选择建议

  • 新手开发者:从案例一开始,掌握基本用法
  • 有经验开发者:重点关注案例二的微服务编排
  • 架构师:深入研究案例三的高性能设计

希望这些实战案例能够帮助你更好地理解和使用 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);
    }
}

案例一总结

这个基础案例展示了:

  1. 基本配置:如何配置 Atlas HTTP Client 和 Spring Boot 集成
  2. 接口定义:使用注解定义 HTTP 客户端接口
  3. 拦截器使用:认证和审计拦截器的实现
  4. 业务封装:在服务层封装业务逻辑和异常处理
  5. 缓存集成:与 Spring Cache 的集成使用
  6. 异步处理:异步方法的使用
  7. 测试方法:如何进行单元测试

最佳实践

  • 使用配置文件管理 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());
        // 这里应该实现具体的回滚逻辑
    }
}
相关推荐
肖老师xy2 小时前
uniapp 苹果端bug合集
java·服务器·uni-app
zlpzlpzyd2 小时前
idea 2025.2 在Windows 11中执行maven命令中文乱码处理
java·maven·intellij-idea
刘一说3 小时前
IntelliJ IDEA 查找和替换使用指南
java·ide·intellij-idea
白露与泡影4 小时前
2025年高质量Java面试真题汇总
java·python·面试
꒰ঌ 安卓开发໒꒱4 小时前
Java 面试 -Java基础
java·开发语言·面试
玛卡巴卡015 小时前
HTTPS工作过程
网络协议·http·https
菠菠萝宝5 小时前
【Java八股文】13-中间件面试篇
java·docker·kafka·rabbitmq·canal·rocketmq·es
瓯雅爱分享6 小时前
基于Java后端与Vue前端的MES生产管理系统,涵盖生产调度、资源管控及数据分析,提供全流程可视化支持,包含完整可运行源码,助力企业提升生产效率与管理水平
java·mysql·vue·软件工程·源代码管理
hello_2509 小时前
k8s安全机制解析:RBAC、Service Account与安全上下文
java·安全·kubernetes