总结之Temporal实现全局速率控制方案(一)

两种设计方案轮询等待模式、回调模式

方案一:轮询等待模式

java 复制代码
/**
 * 轮询等待模式实现
 * 
 * 流程:
 * 1. Activity开始执行
 * 2. 循环调用速率服务获取令牌
 * 3. 获取到令牌后执行实际逻辑
 * 4. 完成Activity
 */
public class PollingRateLimitedActivity {
    
    public String sendMessage(Message message) {
        long startTime = System.currentTimeMillis();
        boolean acquired = false;
        
        // 轮询获取令牌(带超时)
        while (!acquired && (System.currentTimeMillis() - startTime) < TIMEOUT_MS) {
            try {
                // 调用外部速率服务
                acquired = rateLimitService.tryAcquire("send_message", 1);
                
                if (!acquired) {
                    // 发送心跳,避免Activity超时
                    Activity.getExecutionContext().heartbeat("waiting_for_rate_limit");
                    
                    // 等待100ms后重试
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ActivityFailureException("Rate limit wait interrupted");
            }
        }
        
        if (!acquired) {
            throw new ActivityFailureException("Rate limit timeout");
        }
        
        // 执行实际逻辑
        return doSendMessage(message);
    }
}

方案二:回调模式

java 复制代码
/**
 * 回调模式实现
 * 
 * 流程:
 * 1. Activity调用速率服务注册回调
 * 2. Activity暂停(异步完成)
 * 3. 速率服务控制速率,令牌可用时回调
 * 4. Temporal Worker接收回调,恢复Activity执行
 */
public class CallbackRateLimitedActivity {
    
    public String sendMessage(Message message) {
        // 获取Activity上下文
        ActivityExecutionContext context = Activity.getExecutionContext();
        String activityId = context.getInfo().getActivityId();
        String taskToken = context.getTaskToken();
        
        // 1. 向速率服务注册回调
        rateLimitService.registerCallback(
            activityId,
            taskToken,
            "send_message",
            1,
            callbackUrl
        );
        
        // 2. 告诉Temporal这个Activity将异步完成
        context.doNotCompleteOnReturn();
        
        // 3. 返回中间结果(可选)
        return "WAITING_FOR_RATE_LIMIT";
    }
}

/**
 * 回调处理端点
 */
@RestController
public class RateLimitCallbackController {
    
    @PostMapping("/callback/rate-limit")
    public void handleRateLimitCallback(@RequestBody CallbackRequest request) {
        // 使用Temporal Client恢复Activity执行
        try {
            // 方式1:通过TaskToken直接完成Activity
            completionClient.complete(request.getTaskToken(), request.getResult());
            
            // 方式2:通过ActivityId发送信号
            // workflowClient.signalActivityById(
            //     request.getActivityId(),
            //     "rateLimitGranted",
            //     request.getResult()
            // );
        } catch (Exception e) {
            log.error("Failed to handle rate limit callback", e);
        }
    }
}

两种方案对比分析

架构复杂度对比

维度 轮询等待模式 回调模式
架构复杂度
依赖组件 少(只需速率服务) 多(需要回调端点、消息队列、Temporal Client)
状态管理 无状态 需要管理回调状态
容错处理 简单重试 复杂(需要保证回调可靠性)

性能对比

维度 轮询等待模式 回调模式
Worker线程占用 高(长时间阻塞) 低(立即释放)
网络开销 高(频繁轮询) 低(一次注册+一次回调)
内存消耗 较高(每个Activity占用线程栈) 较低(只保存回调信息)
可扩展性 差(受限于Worker线程数) 好(Worker可处理更多并发)

可靠性对比

维度 轮询等待模式 回调模式
超时处理 简单(Activity自带超时) 复杂(需要额外超时机制)
失败恢复 简单重试 需要补偿机制
消息丢失 无(同步请求) 可能(异步回调可能丢失)
幂等性 容易实现 需要额外设计

运维复杂度对比

维度 轮询等待模式 回调模式
监控 简单(Temporal原生支持) 复杂(需要跨服务追踪)
调试 简单(线性执行) 复杂(分布式追踪)
日志聚合 简单 复杂
部署 简单 复杂(多组件协调)

详细技术实现对比

轮询等待模式的实现细节

java 复制代码
/**
 * 轮询等待模式的优缺点分析
 */
public class PollingPatternAnalysis {
    
    // 优点:
    // 1. 实现简单直观
    // 2. 代码逻辑线性,易于理解和调试
    // 3. 无需额外的回调基础设施
    // 4. 利用Temporal原生的重试和超时机制
    // 5. 状态完全由Temporal管理
    
    // 缺点:
    // 1. Worker线程阻塞,影响吞吐量
    // 2. 网络开销大(频繁轮询)
    // 3. 不适合长时间等待的场景
    // 4. 可能触发Temporal的心跳超时
    // 5. 难以支持大规模并发
    
    /**
     * 优化后的轮询实现
     */
    public String optimizedPolling(Message message) {
        ActivityExecutionContext context = Activity.getExecutionContext();
        
        // 使用指数退避减少轮询频率
        int maxRetries = 10;
        long baseDelay = 100; // 100ms
        long maxDelay = 5000; // 5s
        
        for (int attempt = 0; attempt < maxRetries; attempt++) {
            // 发送心跳
            context.heartbeat("rate_limit_attempt_" + attempt);
            
            // 尝试获取令牌
            if (rateLimitService.tryAcquire("send_message", 1)) {
                return doSendMessage(message);
            }
            
            // 计算退避时间
            long delay = Math.min(
                baseDelay * (long) Math.pow(2, attempt),
                maxDelay
            );
            
            // 等待(可被心跳中断)
            waitWithInterruptibleSleep(delay, context);
        }
        
        throw new ActivityFailureException("Rate limit retries exhausted");
    }
}

轮训等待模式-使用外部速率限制服务的详细设计

架构概览
复制代码
┌─────────────────────────────────────────────────────────────┐
│                    Temporal 集群                             │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                      │
│  │ Worker1 │  │ Worker2 │  │ Worker3 │                      │
│  └────┬────┘  └────┬────┘  └────┬────┘                      │
│       │             │            │                           │
│       └─────────────┼────────────┘                           │
│                     │                                        │
│               ┌─────▼─────┐                                  │
│               │ Activity  │                                  │
│               │ 实现类    │                                  │
│               └─────┬─────┘                                  │
│                     │                                        │
└─────────────────────┼────────────────────────────────────────┘
                      │
              调用速率限制服务
                      │
          ┌───────────▼───────────┐
          │   外部速率限制服务     │
          │  (Redis/Consul/等)    │
          └───────────────────────┘
1. 选择合适的外部服务
Redis(推荐)
java 复制代码
// 优点:
// 1. 性能极高(10万+ QPS)
// 2. 支持原子操作
// 3. 丰富的限流算法实现
// 4. 集群和持久化支持
Consul / Etcd
java 复制代码
// 优点:
// 1. 强一致性
// 2. 服务发现集成
// 3. 分布式锁支持
专用限流服务(如:Sentinel, Envoy)
java 复制代码
// 优点:
// 1. 功能完善
// 2. 监控集成
// 3. 动态配置

2. 核心设计模式

模式一:令牌桶模式(Token Bucket)
java 复制代码
/**
 * 基于Redis的分布式令牌桶实现
 */
public class RedisTokenBucketRateLimiter {
    
    private final JedisPool jedisPool;
    private final String rateLimitKey;
    private final int maxTokens;
    private final int refillRate; // tokens per second
    
    public RedisTokenBucketRateLimiter(String serviceName, int maxTokens, int refillRate) {
        this.jedisPool = new JedisPool("localhost", 6379);
        this.rateLimitKey = "rate_limit:" + serviceName;
        this.maxTokens = maxTokens;
        this.refillRate = refillRate;
    }
    
    public boolean tryAcquire(int tokens) {
        try (Jedis jedis = jedisPool.getResource()) {
            // Lua脚本保证原子性
            String luaScript = """
                local key = KEYS[1]
                local tokensRequested = tonumber(ARGV[1])
                local maxTokens = tonumber(ARGV[2])
                local refillRate = tonumber(ARGV[3])
                local now = tonumber(ARGV[4])
                
                -- 获取当前桶状态
                local bucket = redis.call('HMGET', key, 'tokens', 'lastRefill')
                local currentTokens = maxTokens
                local lastRefill = now
                
                if bucket[1] then
                    currentTokens = tonumber(bucket[1])
                    lastRefill = tonumber(bucket[2])
                else
                    -- 初始化桶
                    redis.call('HMSET', key, 'tokens', maxTokens, 'lastRefill', now)
                    redis.call('EXPIRE', key, 3600)
                end
                
                -- 计算应补充的令牌数
                local timePassed = now - lastRefill
                local tokensToAdd = math.floor(timePassed * refillRate)
                local newTokens = math.min(currentTokens + tokensToAdd, maxTokens)
                
                -- 检查是否有足够令牌
                if newTokens >= tokensRequested then
                    newTokens = newTokens - tokensRequested
                    redis.call('HMSET', key, 'tokens', newTokens, 'lastRefill', now)
                    return 1  -- 成功
                else
                    return 0  -- 失败
                end
            """;
            
            long now = System.currentTimeMillis() / 1000;
            Long result = (Long) jedis.eval(
                luaScript,
                Collections.singletonList(rateLimitKey),
                Arrays.asList(
                    String.valueOf(tokens),
                    String.valueOf(maxTokens),
                    String.valueOf(refillRate),
                    String.valueOf(now)
                )
            );
            
            return result == 1;
        }
    }
    
    public void waitUntilAcquired(int tokens) {
        while (!tryAcquire(tokens)) {
            try {
                Thread.sleep(100); // 等待100ms后重试
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Rate limit wait interrupted", e);
            }
        }
    }
}
模式二:滑动窗口计数器
java 复制代码
/**
 * 基于Redis的滑动窗口限流
 */
public class RedisSlidingWindowRateLimiter {
    
    private final JedisPool jedisPool;
    private final String windowKey;
    private final int maxRequests;
    private final long windowSizeInMillis;
    
    public RedisSlidingWindowRateLimiter(String serviceName, int maxRequests, Duration windowSize) {
        this.jedisPool = new JedisPool("localhost", 6379);
        this.windowKey = "sliding_window:" + serviceName;
        this.maxRequests = maxRequests;
        this.windowSizeInMillis = windowSize.toMillis();
    }
    
    public boolean tryAcquire() {
        try (Jedis jedis = jedisPool.getResource()) {
            long now = System.currentTimeMillis();
            long windowStart = now - windowSizeInMillis;
            
            // 清理过期请求并统计
            String luaScript = """
                local key = KEYS[1]
                local now = tonumber(ARGV[1])
                local windowStart = tonumber(ARGV[2])
                local maxRequests = tonumber(ARGV[3])
                local windowSize = tonumber(ARGV[4])
                
                -- 清理窗口之前的记录
                redis.call('ZREMRANGEBYSCORE', key, 0, windowStart)
                
                -- 获取当前窗口内的请求数
                local currentCount = redis.call('ZCOUNT', key, windowStart, now)
                
                if currentCount < maxRequests then
                    -- 添加新请求
                    redis.call('ZADD', key, now, now)
                    redis.call('EXPIRE', key, math.ceil(windowSize / 1000) * 2)
                    return 1
                else
                    return 0
                end
            """;
            
            Long result = (Long) jedis.eval(
                luaScript,
                Collections.singletonList(windowKey),
                Arrays.asList(
                    String.valueOf(now),
                    String.valueOf(windowStart),
                    String.valueOf(maxRequests),
                    String.valueOf(windowSizeInMillis)
                )
            );
            
            return result == 1;
        }
    }
}
3. Temporal Activity 集成方案
方案A:Activity拦截器模式
java 复制代码
/**
 * 全局速率限制拦截器
 */
public class DistributedRateLimitInterceptor implements ActivityInboundInterceptor {
    
    private final RateLimitService rateLimitService;
    private final Map<String, RateLimitConfig> activityConfigs;
    
    public DistributedRateLimitInterceptor() {
        this.rateLimitService = new RedisRateLimitService();
        this.activityConfigs = loadRateLimitConfigs();
    }
    
    @Override
    public ActivityOutput execute(ActivityInput input) {
        String activityType = input.getActivityType();
        RateLimitConfig config = activityConfigs.get(activityType);
        
        if (config != null && config.isEnabled()) {
            String limitKey = buildLimitKey(activityType, input);
            
            // 等待获取执行许可
            boolean acquired = rateLimitService.acquire(
                limitKey, 
                config.getMaxRequests(),
                config.getWindow()
            );
            
            if (!acquired && config.isBlocking()) {
                // 阻塞等待
                rateLimitService.waitUntilAcquired(
                    limitKey, 
                    config.getMaxRequests(),
                    config.getWindow(),
                    config.getTimeout()
                );
            } else if (!acquired) {
                // 快速失败
                throw new ActivityFailureException(
                    "Rate limit exceeded for activity: " + activityType,
                    "RATE_LIMIT_EXCEEDED"
                );
            }
        }
        
        return delegate.execute(input);
    }
    
    private String buildLimitKey(String activityType, ActivityInput input) {
        // 可根据业务需求构建不同的限流key
        // 例如:按用户限流、按商户限流、全局限流等
        return String.format("rate:%s:%s", 
            activityType, 
            extractTenantId(input)
        );
    }
}
方案B:Activity装饰器模式
java 复制代码
/**
 * 速率限制Activity装饰器
 */
public class RateLimitedSendMessageActivity implements SendMessageActivity {
    
    private final SendMessageActivity delegate;
    private final DistributedRateLimiter rateLimiter;
    private final ActivityExecutionContext context;
    
    public RateLimitedSendMessageActivity(
        SendMessageActivity delegate,
        DistributedRateLimiter rateLimiter
    ) {
        this.delegate = delegate;
        this.rateLimiter = rateLimiter;
        this.context = Activity.getExecutionContext();
    }
    
    @Override
    public String sendMessage(Message message) {
        String rateLimitKey = String.format("send_message:%s", message.getReceiver());
        
        // 带心跳的等待
        return withHeartbeat(() -> {
            rateLimiter.acquireWithWait(rateLimitKey, 1, Duration.ofSeconds(1));
            return delegate.sendMessage(message);
        });
    }
    
    private <T> T withHeartbeat(Callable<T> task) {
        ScheduledExecutorService heartbeatExecutor = 
            Executors.newSingleThreadScheduledExecutor();
        
        try {
            // 开始发送心跳
            ScheduledFuture<?> heartbeatFuture = heartbeatExecutor.scheduleAtFixedRate(
                () -> context.heartbeat("rate_limit_waiting"),
                0,  // 立即开始
                10, // 每10秒
                TimeUnit.SECONDS
            );
            
            try {
                return task.call();
            } finally {
                heartbeatFuture.cancel(true);
            }
        } catch (Exception e) {
            throw Activity.wrap(e);
        } finally {
            heartbeatExecutor.shutdown();
        }
    }
}
4. 配置管理
动态配置类
java 复制代码
@Configuration
public class RateLimitConfiguration {
    
    @Value("${rate-limit.redis.host:localhost}")
    private String redisHost;
    
    @Value("${rate-limit.redis.port:6379}")
    private int redisPort;
    
    @Bean
    public DistributedRateLimiter distributedRateLimiter() {
        Config redisConfig = new Config();
        redisConfig.useSingleServer()
            .setAddress(String.format("redis://%s:%d", redisHost, redisPort))
            .setConnectionPoolSize(10)
            .setConnectTimeout(3000);
        
        RedissonClient redisson = Redisson.create(redisConfig);
        return new RedissonDistributedRateLimiter(redisson);
    }
    
    @Bean
    public Map<String, RateLimitConfig> activityRateLimits() {
        Map<String, RateLimitConfig> configs = new HashMap<>();
        
        // SendMessageActivity: 每秒1次,按接收者限流
        configs.put("SendMessageActivity", RateLimitConfig.builder()
            .maxRequests(1)
            .window(Duration.ofSeconds(1))
            .strategy(RateLimitStrategy.PER_USER)
            .blocking(true)
            .timeout(Duration.ofMinutes(5))
            .build());
        
        // ProcessPaymentActivity: 每分钟100次,全局限流
        configs.put("ProcessPaymentActivity", RateLimitConfig.builder()
            .maxRequests(100)
            .window(Duration.ofMinutes(1))
            .strategy(RateLimitStrategy.GLOBAL)
            .blocking(false)
            .build());
            
        return configs;
    }
}
配置热更新
java 复制代码
@Component
public class DynamicRateLimitManager {
    
    private final ConfigRepository configRepository;
    private final Map<String, RateLimitConfig> currentConfigs;
    private final ScheduledExecutorService updater;
    
    public DynamicRateLimitManager(ConfigRepository configRepository) {
        this.configRepository = configRepository;
        this.currentConfigs = new ConcurrentHashMap<>();
        this.updater = Executors.newSingleThreadScheduledExecutor();
        
        // 每30秒检查配置更新
        updater.scheduleAtFixedRate(this::refreshConfigs, 0, 30, TimeUnit.SECONDS);
    }
    
    private void refreshConfigs() {
        Map<String, RateLimitConfig> newConfigs = 
            configRepository.loadRateLimitConfigs();
        
        // 比较并应用变更
        newConfigs.forEach((activityType, newConfig) -> {
            RateLimitConfig oldConfig = currentConfigs.get(activityType);
            if (!newConfig.equals(oldConfig)) {
                applyConfigChange(activityType, newConfig);
                currentConfigs.put(activityType, newConfig);
            }
        });
    }
}
5. 监控和告警
java 复制代码
@Component
public class RateLimitMetricsCollector {
    
    private final MeterRegistry meterRegistry;
    private final Map<String, DistributionSummary> metrics;
    
    public void recordRateLimitEvent(String activityType, String key, 
                                     boolean success, long waitTime) {
        
        // 记录指标
        meterRegistry.counter("rate_limit_requests_total",
            "activity", activityType,
            "key", key,
            "success", String.valueOf(success)
        ).increment();
        
        if (!success) {
            meterRegistry.counter("rate_limit_rejections_total",
                "activity", activityType,
                "key", key
            ).increment();
        }
        
        meterRegistry.timer("rate_limit_wait_time",
            "activity", activityType
        ).record(waitTime, TimeUnit.MILLISECONDS);
        
        // 记录到日志用于调试
        log.debug("Rate limit event: activity={}, key={}, success={}, wait={}ms",
            activityType, key, success, waitTime);
    }
    
    @EventListener
    public void onRateLimitViolation(RateLimitViolationEvent event) {
        // 触发告警
        alertService.sendAlert(Alert.builder()
            .level(AlertLevel.WARNING)
            .title("Rate Limit Violation")
            .message(String.format(
                "Activity %s exceeded rate limit for key %s",
                event.getActivityType(),
                event.getLimitKey()
            ))
            .build());
    }
}
6. 故障处理和降级
java 复制代码
public class FallbackRateLimiter implements DistributedRateLimiter {
    
    private final DistributedRateLimiter primary;
    private final LocalRateLimiter fallback;
    private final CircuitBreaker circuitBreaker;
    private volatile boolean externalServiceAvailable = true;
    
    @Override
    public boolean tryAcquire(String key, int tokens) {
        if (externalServiceAvailable) {
            try {
                boolean result = circuitBreaker.executeSupplier(() -> 
                    primary.tryAcquire(key, tokens));
                return result;
            } catch (Exception e) {
                // 外部服务故障,切换到本地限流
                log.warn("External rate limit service failed, falling back to local", e);
                externalServiceAvailable = false;
                
                // 启动健康检查
                scheduleHealthCheck();
                
                return fallback.tryAcquire(key, tokens);
            }
        } else {
            return fallback.tryAcquire(key, tokens);
        }
    }
    
    private void scheduleHealthCheck() {
        ScheduledExecutorService scheduler = 
            Executors.newSingleThreadScheduledExecutor();
        
        scheduler.scheduleAtFixedRate(() -> {
            try {
                if (primary.healthCheck()) {
                    externalServiceAvailable = true;
                    scheduler.shutdown();
                    log.info("External rate limit service recovered");
                }
            } catch (Exception e) {
                // 继续使用本地限流
            }
        }, 10, 10, TimeUnit.SECONDS);
    }
}
7. 最佳实践建议
  1. 分级限流策略

    java 复制代码
    // 多级限流:全局 → 服务 → 用户 → 操作
    String[] limitKeys = {
        "global:send_message",
        "service:notification_service",
        "user:" + userId,
        "operation:send_email"
    };
  2. 预热机制

    java 复制代码
    // 系统启动时缓慢增加限流阈值
    RateLimiter rateLimiter = RateLimiter.create(10.0, 5, TimeUnit.MINUTES);
  3. 动态配额调整

    java 复制代码
    // 根据系统负载自动调整限流阈值
    if (systemLoad > 0.8) {
        // 降低限流阈值
        adjustRateLimit("send_message", -20);
    }
  4. 限流响应头

    java 复制代码
    // 返回剩余请求数和重置时间
    public class RateLimitResponse {
        private boolean allowed;
        private int remaining;
        private long resetAfter; // seconds
        private int limit;
    }
部署架构
复制代码
                      ┌─────────────────┐
                      │   Temporal      │
                      │   Activities    │
                      └────────┬────────┘
                               │
                               ▼
            ┌─────────────────────────────────┐
            │      Rate Limit Proxy           │
            │  (可选:Envoy/Sidecar模式)       │
            └─────────────────────────────────┘
                     │                │
            ┌───────▼────┐   ┌───────▼─────┐
            │  Redis     │   │  Sentinel   │
            │  Cluster   │   │  Dashboard  │
            └────────────┘   └─────────────┘

这种设计提供了高可用、可观测、可动态调整的分布式速率控制方案,能够满足生产环境的需求。

回调模式的实现细节

java 复制代码
/**
 * 回调模式的优缺点分析
 */
public class CallbackPatternAnalysis {
    
    // 优点:
    // 1. Worker线程立即释放,提高吞吐量
    // 2. 适合长时间等待的场景
    // 3. 支持大规模并发
    // 4. 减少网络调用
    // 5. 可扩展性强
    
    // 缺点:
    // 1. 架构复杂,引入多个组件
    // 2. 需要保证回调的可靠性
    // 3. 需要处理异步状态管理
    // 4. 调试和追踪困难
    // 5. 可能引入分布式事务问题
    
    /**
     * 完整的回调模式实现架构
     */
    @Component
    public class CallbackRateLimitSystem {
        
        // 1. 回调注册表(Redis或数据库)
        private final CallbackRegistry callbackRegistry;
        
        // 2. 消息队列(用于可靠回调)
        private final MessageQueue messageQueue;
        
        // 3. Temporal客户端
        private final TemporalClient temporalClient;
        
        // 4. 超时处理器
        private final TimeoutHandler timeoutHandler;
        
        /**
         * Activity注册回调
         */
        public void registerCallback(String activityId, String taskToken, 
                                     String rateKey, int tokens) {
            
            // 生成回调ID
            String callbackId = UUID.randomUUID().toString();
            
            // 保存回调信息
            CallbackInfo info = new CallbackInfo(
                callbackId,
                activityId,
                taskToken,
                rateKey,
                tokens,
                System.currentTimeMillis()
            );
            
            callbackRegistry.save(info);
            
            // 将请求放入速率队列
            messageQueue.enqueue(rateKey, callbackId);
            
            // 启动超时检查
            timeoutHandler.scheduleTimeoutCheck(
                callbackId, 
                Duration.ofMinutes(5)
            );
        }
        
        /**
         * 速率服务处理逻辑
         */
        @Scheduled(fixedRate = 1000) // 每秒处理一次
        public void processRateLimitQueue() {
            for (String rateKey : getAllRateKeys()) {
                // 检查速率限制
                if (canProcess(rateKey)) {
                    // 从队列中取出下一个回调ID
                    String callbackId = messageQueue.dequeue(rateKey);
                    
                    if (callbackId != null) {
                        // 获取回调信息
                        CallbackInfo info = callbackRegistry.get(callbackId);
                        
                        // 回调Temporal
                        callbackToTemporal(info);
                        
                        // 清理回调记录
                        callbackRegistry.delete(callbackId);
                    }
                }
            }
        }
        
        /**
         * 回调Temporal Worker
         */
        private void callbackToTemporal(CallbackInfo info) {
            try {
                // 方式1:直接完成Activity
                temporalClient.completeActivity(
                    info.getTaskToken(),
                    new ActivityResult("RATE_LIMIT_GRANTED")
                );
                
                // 方式2:发送信号
                // temporalClient.signalActivity(
                //     info.getActivityId(),
                //     "rateLimitGranted",
                //     new RateLimitGrantedEvent()
                // );
                
            } catch (ActivityNotFoundException e) {
                // Activity可能已超时或取消
                log.warn("Activity not found, cleaning up callback", e);
                callbackRegistry.delete(info.getCallbackId());
            }
        }
        
        /**
         * 超时处理
         */
        public void handleTimeout(String callbackId) {
            CallbackInfo info = callbackRegistry.get(callbackId);
            
            if (info != null) {
                // 尝试失败Activity
                try {
                    temporalClient.failActivity(
                        info.getTaskToken(),
                        new ActivityFailureException("Rate limit timeout")
                    );
                } catch (Exception e) {
                    log.error("Failed to timeout activity", e);
                }
                
                // 清理
                callbackRegistry.delete(callbackId);
            }
        }
    }
}

建议与推荐方案

基于场景的推荐

场景 推荐方案 理由
低并发,短等待(<5秒) 轮询等待模式 实现简单,无额外复杂度
高并发,短等待 轮询+优化 使用退避算法,减少轮询
长等待(>30秒) 回调模式 避免线程长期阻塞
大规模分布式 回调模式 支持水平扩展
关键业务,需高可靠 轮询等待模式 同步处理,可靠性高
资源受限环境 轮询等待模式 无需额外基础设施

混合方案(推荐)

java 复制代码
/**
 * 智能混合方案:根据等待时间动态选择模式
 */
public class SmartRateLimitStrategy {
    
    private static final long POLLING_THRESHOLD = 5000; // 5秒
    private static final long HYBRID_THRESHOLD = 30000; // 30秒
    
    public String executeWithSmartRateLimit(Message message) {
        // 1. 先尝试快速获取(轮询模式)
        RateLimitResult quickResult = tryQuickAcquisition();
        
        if (quickResult.isSuccess()) {
            return doSendMessage(message);
        }
        
        // 2. 根据预估等待时间选择策略
        long estimatedWait = estimateWaitTime();
        
        if (estimatedWait <= POLLING_THRESHOLD) {
            // 短等待:使用优化轮询
            return executeWithOptimizedPolling(message);
            
        } else if (estimatedWait <= HYBRID_THRESHOLD) {
            // 中等等待:使用带心跳的轮询
            return executeWithHeartbeatPolling(message, estimatedWait);
            
        } else {
            // 长等待:切换到回调模式
            return executeWithCallback(message);
        }
    }
    
    /**
     * 预估等待时间算法
     */
    private long estimateWaitTime() {
        // 基于:
        // 1. 当前队列长度
        // 2. 历史等待时间统计
        // 3. 系统负载
        // 4. 时间因素(高峰/低谷)
        
        int queueSize = rateLimitService.getQueueSize();
        long avgWaitTime = getHistoricalAverageWait();
        double loadFactor = getSystemLoad();
        
        return (long) (queueSize * avgWaitTime * loadFactor);
    }
}

决策矩阵

技术决策表

决策因子 权重 轮询等待模式得分 回调模式得分
实现复杂度 30% 9/10 4/10
性能影响 25% 5/10 9/10
可靠性 20% 8/10 6/10
可维护性 15% 8/10 5/10
扩展性 10% 4/10 9/10
加权总分 100% 7.25/10 6.35/10

业务决策表

业务需求 轮询等待模式 回调模式
快速上线 ✅ 优 ❌ 差
团队熟悉度 ✅ 优 ❌ 差
运维能力 ✅ 优 ❌ 差
未来扩展 ❌ 差 ✅ 优
SLA要求 ✅ 优 ❌ 中
成本控制 ✅ 优 ❌ 差

最终建议

我的推荐:采用优化后的轮询等待模式

理由:

  1. 实现简单,风险低:对于每秒1次的速率控制,轮询模式完全足够
  2. 维护成本低:不需要额外的回调基础设施
  3. 可靠性高:同步处理,无消息丢失风险
  4. 符合Temporal设计哲学:Temporal本身就是为处理长时间运行任务设计的,Activity可以适当阻塞

优化建议:

java 复制代码
/**
 * 最终推荐的优化实现
 */
@Slf4j
public class RecommendedRateLimitedActivity {
    
    private static final long MAX_WAIT_TIME = 30000; // 最大等待30秒
    private static final long HEARTBEAT_INTERVAL = 10000; // 心跳间隔10秒
    
    @Override
    public String sendMessage(Message message) {
        ActivityExecutionContext context = Activity.getExecutionContext();
        long startTime = System.currentTimeMillis();
        
        try {
            // 尝试立即获取
            if (rateLimitService.tryAcquireImmediate("send_message", 1)) {
                return doSendMessage(message);
            }
            
            // 启动心跳线程
            ScheduledExecutorService heartbeatExecutor = 
                Executors.newSingleThreadScheduledExecutor();
            
            ScheduledFuture<?> heartbeatFuture = heartbeatExecutor.scheduleAtFixedRate(
                () -> {
                    try {
                        context.heartbeat("waiting_for_rate_limit");
                    } catch (Exception e) {
                        log.warn("Heartbeat failed", e);
                    }
                },
                0, // 立即开始
                HEARTBEAT_INTERVAL,
                TimeUnit.MILLISECONDS
            );
            
            try {
                // 使用退避算法等待
                int attempt = 0;
                long baseDelay = 100; // 100ms
                
                while (System.currentTimeMillis() - startTime < MAX_WAIT_TIME) {
                    long delay = Math.min(
                        baseDelay * (long) Math.pow(2, attempt),
                        5000 // 最大5秒
                    );
                    
                    // 等待
                    Thread.sleep(delay);
                    
                    // 尝试获取
                    if (rateLimitService.tryAcquire("send_message", 1)) {
                        return doSendMessage(message);
                    }
                    
                    attempt++;
                }
                
                // 超时
                throw new ActivityFailureException(
                    "Rate limit timeout after " + MAX_WAIT_TIME + "ms",
                    "RATE_LIMIT_TIMEOUT"
                );
                
            } finally {
                // 清理心跳
                heartbeatFuture.cancel(true);
                heartbeatExecutor.shutdown();
            }
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new ActivityFailureException("Rate limit wait interrupted", e);
        }
    }
}

何时考虑切换到回调模式:

  1. 当并发数超过1000:Worker线程可能成为瓶颈
  2. 当平均等待时间超过10秒:长时间阻塞不合理
  3. 当需要支持优先级队列:回调模式支持更复杂的调度
  4. 当有跨地域部署需求:回调模式更适合分布式场景

分阶段实施建议:

阶段1(现在) :实现优化后的轮询等待模式
阶段2(3个月后) :监控系统性能,评估是否需要优化
阶段3(6个月后) :根据业务增长,考虑实现混合方案
阶段4(1年后):如果需要,部分迁移到回调模式

结论:如果你的需求(每秒1次速率控制),轮询等待模式是更合适的选择。它简单、可靠、易于维护,完全能满足你的需求。只有当业务量大幅增长或需求变更时,才需要考虑更复杂的回调模式。

轮询等待模式并发限制的深度分析与优化方案

一、为什么轮询等待模式并发低?

1.1 线程资源瓶颈

java 复制代码
/**
 * Temporal Worker 线程模型
 * 
 * 问题根源:每个Activity执行都需要占用一个Worker线程
 * 轮询等待期间线程被阻塞,无法处理其他任务
 */
public class WorkerThreadModel {
    // 典型的Temporal Worker配置
    Worker worker = factory.newWorker("task-queue",
        WorkerOptions.newBuilder()
            .setMaxConcurrentActivityExecutionSize(50)  // 最大并发Activity数
            .build());
    
    /**
     * 问题演示:
     * 假设有100个Activity需要执行
     * 每个Activity需要等待速率控制服务(平均等待2秒)
     * 
     * 计算:
     * - Worker线程数:50
     * - Activity总数:100
     * - 每个Activity执行时间:0.1秒(实际业务)+ 2秒(等待)
     * 
     * 理论执行时间:
     * 第一波:50个Activity同时开始,等待2.1秒完成
     * 第二波:剩余50个Activity开始,再等2.1秒完成
     * 总时间:~4.2秒
     * 
     * 如果是理想的非阻塞模式:
     * - 实际业务时间:100 * 0.1 = 10秒
     * - 等待时间可以重叠执行
     * - 总时间:~2.1秒
     */
}

1.2 系统资源浪费

java 复制代码
/**
 * 资源浪费分析
 */
public class ResourceWasteAnalysis {
    
    // 资源类型 | 轮询等待模式 | 理想模式
    // ---------|-------------|-----------
    // CPU      | 高(频繁轮询) | 低(事件驱动)
    // 内存      | 高(线程栈占用) | 低
    // 网络      | 高(频繁请求) | 低
    // 线程数    | 高(1:1占用) | 低(线程复用)
    
    /**
     * 具体分析:
     * 1. 线程栈内存:每个Java线程需要约1MB栈内存
     *    50个线程 = 50MB(仅栈内存)
     * 
     * 2. CPU上下文切换:
     *    - 线程从运行态切换到等待态
     *    - 调度器需要选择下一个线程
     *    - 频繁切换导致CPU缓存失效
     * 
     * 3. 网络连接消耗:
     *    - 每个轮询请求都建立TCP连接
     *    - SSL握手开销(如果有)
     *    - 序列化/反序列化成本
     */
    
    private void calculateResourceUsage() {
        int concurrentActivities = 1000;
        long waitTimePerActivity = 2000; // 2秒
        long pollInterval = 100; // 100ms轮询一次
        
        // 网络请求次数
        long pollRequests = (waitTimePerActivity / pollInterval) * concurrentActivities;
        // 2000/100 * 1000 = 20 * 1000 = 20,000次请求!
        
        // 线程等待时间总和
        long totalWaitTime = waitTimePerActivity * concurrentActivities;
        // 2000 * 1000 = 2,000,000毫秒 = 2000秒的线程等待时间
    }
}

1.3 可扩展性差的根本原因

java 复制代码
/**
 * 扩展性瓶颈分析
 */
public class ScalabilityBottleneck {
    
    /**
     * 水平扩展限制:
     * 
     * 1. Worker扩展有限:
     *    - 每个Worker有最大线程数限制(通常50-100)
     *    - 增加Worker会增加资源消耗
     *    - 业务逻辑可能限制跨Worker状态共享
     * 
     * 2. 速率服务压力:
     *    - 所有Worker都轮询同一个速率服务
     *    - 高并发时速率服务成为瓶颈
     *    - 需要复杂的分片和负载均衡
     * 
     * 3. 协调复杂度:
     *    - 跨多个Worker的速率控制需要协调
     *    - 全局速率限制实现困难
     */
    
    /**
     * 并发增长时的性能衰减曲线
     * 
     * 并发数   | 吞吐量   | 延迟     | 资源使用率
     * --------|---------|---------|-----------
     * 10      | 高       | 低       | 20%
     * 50      | 中       | 中       | 60%
     * 100     | 低       | 高       | 90%
     * 200     | 很低     | 很高     | 100% (队列积压)
     */
    
    /**
     * 排队理论分析(Little's Law)
     * L = λW
     * L: 系统中平均请求数
     * λ: 到达率
     * W: 平均等待时间
     * 
     * 问题:轮询模式下W增长,导致L增长
     * 最终系统进入恶性循环
     */
}

二、提高并发性的架构设计

2.1 架构演进路径

复制代码
阶段1:简单轮询(当前)
    缺点:低并发,高资源消耗
    
阶段2:智能轮询 + 本地缓存
    改进:减少轮询频率,使用本地令牌缓存
    
阶段3:异步事件驱动
    改进:Activity不阻塞,事件通知恢复
    
阶段4:流式处理 + 背压控制
    改进:基于流的速率控制,自动背压

2.2 方案一:智能轮询 + 本地缓存

java 复制代码
/**
 * 智能轮询模式:减少等待,提高并发
 */
@Slf4j
public class SmartPollingRateLimiter {
    
    private final DistributedRateLimitService remoteService;
    private final LocalTokenCache localCache;
    private final RateLimitPredictor predictor;
    private final ScheduledExecutorService scheduler;
    
    // 每个Activity实例的令牌缓存
    private final Map<String, TokenReservation> reservations = new ConcurrentHashMap<>();
    
    /**
     * 核心改进点:
     * 1. 批量预取令牌
     * 2. 本地令牌缓存
     * 3. 智能调度
     * 4. 异步刷新
     */
    
    /**
     * 改进后的Activity执行逻辑
     */
    public String sendMessageWithSmartPolling(Message message) {
        String activityId = Activity.getExecutionContext().getInfo().getActivityId();
        
        // 1. 首先检查本地缓存
        TokenReservation reservation = reservations.get(activityId);
        if (reservation != null && reservation.isValid()) {
            // 使用缓存的令牌
            return doSendMessage(message);
        }
        
        // 2. 智能预取:根据预测需求预取多个令牌
        int predictedNeed = predictor.predictTokenNeed(activityId);
        List<Token> tokens = remoteService.reserveTokens(
            "send_message", 
            predictedNeed,  // 批量预取
            Duration.ofSeconds(30)  // 预订30秒内使用
        );
        
        // 3. 存入本地缓存
        reservation = new TokenReservation(tokens);
        reservations.put(activityId, reservation);
        
        // 4. 异步刷新令牌(不阻塞当前线程)
        scheduleTokenRefresh(activityId, reservation);
        
        // 5. 使用令牌执行
        reservation.useToken();
        return doSendMessage(message);
    }
    
    /**
     * 令牌缓存管理
     */
    private static class TokenReservation {
        private final Queue<Token> tokens;
        private final long expiryTime;
        private volatile boolean refreshing = false;
        
        public TokenReservation(List<Token> tokens) {
            this.tokens = new ConcurrentLinkedQueue<>(tokens);
            this.expiryTime = System.currentTimeMillis() + 30000; // 30秒有效期
        }
        
        public boolean isValid() {
            return !tokens.isEmpty() && System.currentTimeMillis() < expiryTime;
        }
        
        public void useToken() {
            tokens.poll(); // 使用一个令牌
        }
        
        public int remainingTokens() {
            return tokens.size();
        }
    }
    
    /**
     * 预测器:智能预测令牌需求
     */
    public class RateLimitPredictor {
        private final Map<String, UsagePattern> patterns = new ConcurrentHashMap<>();
        
        public int predictTokenNeed(String activityId) {
            UsagePattern pattern = patterns.get(activityId);
            
            if (pattern == null) {
                // 初始预测:基于历史平均值
                return 5; // 预取5个令牌
            }
            
            // 基于滑动窗口预测
            return pattern.predictNextWindow();
        }
        
        private static class UsagePattern {
            private final Deque<Integer> history = new ArrayDeque<>();
            private final int windowSize = 10;
            
            public void recordUsage(int tokensUsed) {
                history.addLast(tokensUsed);
                if (history.size() > windowSize) {
                    history.removeFirst();
                }
            }
            
            public int predictNextWindow() {
                if (history.isEmpty()) return 1;
                
                // 简单移动平均
                double avg = history.stream()
                    .mapToInt(Integer::intValue)
                    .average()
                    .orElse(1.0);
                
                // 加上20%的缓冲
                return (int) Math.ceil(avg * 1.2);
            }
        }
    }
    
    /**
     * 异步刷新机制:不阻塞Activity执行
     */
    private void scheduleTokenRefresh(String activityId, TokenReservation reservation) {
        // 当令牌剩余量低于阈值时触发刷新
        scheduler.schedule(() -> {
            if (reservation.remainingTokens() < 2 && !reservation.refreshing) {
                reservation.refreshing = true;
                
                try {
                    // 异步预取更多令牌
                    int needMore = predictor.predictTokenNeed(activityId);
                    List<Token> newTokens = remoteService.reserveTokens(
                        "send_message", 
                        needMore,
                        Duration.ofSeconds(30)
                    );
                    
                    // 合并到现有缓存
                    reservation.tokens.addAll(newTokens);
                    
                } finally {
                    reservation.refreshing = false;
                }
            }
        }, 100, TimeUnit.MILLISECONDS); // 100ms后检查
    }
}

2.3 方案二:异步事件驱动架构

java 复制代码
/**
 * 异步事件驱动架构:彻底解决并发瓶颈
 */
public class AsyncEventDrivenArchitecture {
    
    /**
     * 架构组件:
     * 1. RateLimitEventBus: 事件总线
     * 2. ActivitySuspensionService: Activity挂起/恢复服务
     * 3. TokenStreamProcessor: 令牌流处理器
     * 4. PriorityQueueManager: 优先级队列管理器
     */
    
    /**
     * 执行流程:
     * 1. Activity注册到事件总线,然后挂起
     * 2. 速率服务处理令牌,发出事件
     * 3. 事件总线分发令牌事件
     * 4. 对应的Activity被唤醒执行
     */
}

/**
 * 事件总线实现
 */
@Component
public class RateLimitEventBus {
    
    private final Map<String, List<EventListener>> listeners = new ConcurrentHashMap<>();
    private final ExecutorService eventExecutor;
    private final EventQueue eventQueue;
    
    public RateLimitEventBus() {
        // 使用Disruptor或高性能队列
        this.eventQueue = new DisruptorEventQueue(1024);
        this.eventExecutor = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2
        );
        
        startEventProcessing();
    }
    
    /**
     * Activity注册监听器
     */
    public void register(String activityId, EventListener listener) {
        listeners.computeIfAbsent(activityId, k -> new CopyOnWriteArrayList<>())
                .add(listener);
    }
    
    /**
     * 发布令牌可用事件
     */
    public void publishTokenAvailable(String rateKey, String consumerId) {
        TokenEvent event = new TokenEvent(rateKey, consumerId, System.currentTimeMillis());
        eventQueue.publish(event);
    }
    
    private void startEventProcessing() {
        // 事件处理线程
        new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    TokenEvent event = eventQueue.take();
                    
                    // 异步处理事件
                    eventExecutor.submit(() -> {
                        List<EventListener> activityListeners = listeners.get(event.getConsumerId());
                        if (activityListeners != null) {
                            activityListeners.forEach(listener -> 
                                listener.onTokenAvailable(event)
                            );
                        }
                    });
                    
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "event-bus-processor").start();
    }
}

/**
 * Activity挂起服务
 */
@Component
public class ActivitySuspensionService {
    
    private final TemporalClient temporalClient;
    private final CompletableFutureStore futureStore;
    
    /**
     * 挂起Activity,返回Future
     */
    public CompletableFuture<String> suspendActivity(String activityId, String taskToken) {
        CompletableFuture<String> future = new CompletableFuture<>();
        
        // 存储Future,等待事件触发
        futureStore.store(activityId, future);
        
        // 异步完成Activity当前执行
        temporalClient.completeActivity(taskToken, "SUSPENDED");
        
        return future;
    }
    
    /**
     * 恢复Activity执行
     */
    public void resumeActivity(String activityId, String result) {
        CompletableFuture<String> future = futureStore.get(activityId);
        if (future != null) {
            future.complete(result);
            futureStore.remove(activityId);
            
            // 触发新的Activity执行
            triggerResumedActivity(activityId, result);
        }
    }
    
    private void triggerResumedActivity(String activityId, String result) {
        // 通过信号或新的Activity执行来继续工作流
        // 这取决于工作流设计
    }
}

/**
 * 流式令牌处理器
 */
@Component
public class TokenStreamProcessor {
    
    private final Map<String, TokenBucket> buckets = new ConcurrentHashMap<>();
    private final PriorityQueue<RateLimitRequest> priorityQueue;
    private final ScheduledExecutorService tokenGenerator;
    
    public TokenStreamProcessor() {
        this.priorityQueue = new PriorityQueue<>(
            Comparator.comparingInt(RateLimitRequest::getPriority)
                     .thenComparing(RateLimitRequest::getRequestTime)
        );
        
        this.tokenGenerator = Executors.newScheduledThreadPool(1);
        
        startTokenGeneration();
    }
    
    /**
     * 流式处理请求
     */
    public void processStream(RateLimitRequest request) {
        // 将请求加入优先级队列
        priorityQueue.offer(request);
        
        // 立即尝试处理
        tryProcessQueue();
    }
    
    private void tryProcessQueue() {
        while (!priorityQueue.isEmpty()) {
            RateLimitRequest request = priorityQueue.peek();
            TokenBucket bucket = buckets.get(request.getRateKey());
            
            if (bucket != null && bucket.tryConsume(1)) {
                // 令牌可用,从队列移除并处理
                priorityQueue.poll();
                
                // 触发事件通知Activity
                eventBus.publishTokenAvailable(
                    request.getRateKey(), 
                    request.getActivityId()
                );
            } else {
                // 令牌不足,等待下次生成
                break;
            }
        }
    }
    
    private void startTokenGeneration() {
        // 按固定速率生成令牌
        tokenGenerator.scheduleAtFixedRate(() -> {
            buckets.values().forEach(TokenBucket::refill);
            tryProcessQueue(); // 令牌生成后尝试处理队列
        }, 0, 100, TimeUnit.MILLISECONDS); // 每100ms生成一次
    }
}

2.4 方案三:响应式流 + 背压控制

java 复制代码
/**
 * 响应式流架构:最高效的并发控制
 * 
 * 使用Reactor或RxJava实现响应式流
 * 自动背压控制,防止过载
 */
@Slf4j
public class ReactiveRateLimitSystem {
    
    private final Flux<RateLimitRequest> requestStream;
    private final Sinks.Many<RateLimitRequest> requestSink;
    private final Map<String, TokenBucket> buckets;
    private final Scheduler scheduler;
    
    public ReactiveRateLimitSystem() {
        // 创建响应式流
        this.requestSink = Sinks.many().multicast().directBestEffort();
        this.requestStream = requestSink.asFlux();
        this.buckets = new ConcurrentHashMap<>();
        this.scheduler = Schedulers.newBoundedElastic(
            50,  // 最大线程数
            1000, // 最大任务队列
            "rate-limit-processor"
        );
        
        initializeProcessingPipeline();
    }
    
    /**
     * 初始化处理流水线
     */
    private void initializeProcessingPipeline() {
        requestStream
            // 1. 分组:按速率键分组
            .groupBy(RateLimitRequest::getRateKey)
            
            // 2. 背压控制:每组独立控制
            .flatMap(group -> group
                .onBackpressureBuffer(1000, // 缓冲大小
                    BufferOverflowStrategy.DROP_OLDEST)
                
                // 3. 窗口化:按时间窗口处理
                .window(Duration.ofMillis(100))
                
                // 4. 并发处理:每个窗口内并发处理
                .flatMap(window -> window
                    .parallel()
                    .runOn(Schedulers.parallel())
                    .map(this::tryAcquireToken)
                    .sequential()
                )
                
                // 5. 限流:控制处理速率
                .limitRate(1000) // 每秒最多1000个
            )
            
            // 6. 处理结果
            .subscribe(
                this::handleProcessedRequest,
                this::handleError,
                () -> log.info("Stream completed")
            );
    }
    
    /**
     * 提交请求到流
     */
    public Mono<String> submitRequest(RateLimitRequest request) {
        return Mono.create(sink -> {
            // 创建可取消的请求
            CancellableRequest cancellableRequest = 
                new CancellableRequest(request, sink);
            
            // 将请求推送到流
            try {
                requestSink.tryEmitNext(cancellableRequest);
                
                // 设置超时
                cancellableRequest.setTimeout(Duration.ofSeconds(30));
                
            } catch (Exception e) {
                sink.error(e);
            }
        });
    }
    
    /**
     * 尝试获取令牌(非阻塞)
     */
    private ProcessedRequest tryAcquireToken(RateLimitRequest request) {
        TokenBucket bucket = buckets.computeIfAbsent(
            request.getRateKey(),
            k -> new TokenBucket(1.0) // 每秒1个令牌
        );
        
        boolean acquired = bucket.tryConsume(1);
        
        return new ProcessedRequest(request, acquired, System.currentTimeMillis());
    }
    
    /**
     * 处理已处理的请求
     */
    private void handleProcessedRequest(ProcessedRequest processed) {
        if (processed.isAcquired()) {
            // 令牌获取成功,通知Activity
            eventBus.publishTokenAvailable(
                processed.getRequest().getRateKey(),
                processed.getRequest().getActivityId()
            );
        } else {
            // 重新排队(带延迟)
            scheduleRetry(processed.getRequest());
        }
    }
    
    /**
     * 智能重试调度
     */
    private void scheduleRetry(RateLimitRequest request) {
        // 计算重试延迟(指数退避)
        long delay = calculateRetryDelay(request.getRetryCount());
        
        Mono.delay(Duration.ofMillis(delay))
            .subscribeOn(scheduler)
            .subscribe(tick -> {
                request.incrementRetryCount();
                requestSink.tryEmitNext(request);
            });
    }
    
    /**
     * 背压状态监控
     */
    @Component
    public class BackpressureMonitor {
        
        private final MeterRegistry meterRegistry;
        private final Map<String, BackpressureStats> stats = new ConcurrentHashMap<>();
        
        @Scheduled(fixedRate = 5000)
        public void monitorBackpressure() {
            requestSink.asFlux()
                .onBackpressureBuffer()
                .doOnNext(request -> {
                    // 监控每个速率键的队列深度
                    String rateKey = request.getRateKey();
                    BackpressureStats stat = stats.computeIfAbsent(
                        rateKey, k -> new BackpressureStats()
                    );
                    
                    stat.recordRequest();
                    
                    // 发布指标
                    meterRegistry.gauge("rate.limit.queue.depth", 
                        Tags.of("rateKey", rateKey),
                        stat.getQueueDepth()
                    );
                    
                    // 动态调整:如果队列过深,增加处理速率
                    if (stat.getQueueDepth() > 1000) {
                        adjustProcessingRate(rateKey, stat.getQueueDepth());
                    }
                });
        }
        
        private void adjustProcessingRate(String rateKey, int queueDepth) {
            // 动态调整令牌生成速率
            TokenBucket bucket = buckets.get(rateKey);
            if (bucket != null) {
                // 临时增加令牌生成速率
                double currentRate = bucket.getRate();
                double newRate = Math.min(currentRate * 1.5, 1000.0); // 不超过1000/秒
                
                bucket.setRate(newRate);
                
                // 30秒后恢复
                scheduler.schedule(() -> 
                    bucket.setRate(currentRate),
                    30, TimeUnit.SECONDS
                );
            }
        }
    }
}

三、扩展性设计模式

3.1 分片模式(Sharding)

java 复制代码
/**
 * 分片模式:水平扩展的核心
 */
public class RateLimitSharding {
    
    /**
     * 分片策略:
     * 1. 基于Key哈希分片
     * 2. 基于范围分片
     * 3. 基于地理位置分片
     * 4. 动态分片
     */
    
    /**
     * 一致性哈希分片实现
     */
    public class ConsistentHashSharding {
        
        private final SortedMap<Integer, ShardNode> circle = new TreeMap<>();
        private final int numberOfReplicas = 100; // 虚拟节点数
        
        public void addNode(ShardNode node) {
            for (int i = 0; i < numberOfReplicas; i++) {
                int hash = hash(node.getAddress() + ":" + i);
                circle.put(hash, node);
            }
        }
        
        public ShardNode getNode(String key) {
            if (circle.isEmpty()) return null;
            
            int hash = hash(key);
            
            // 找到第一个大于等于hash的节点
            SortedMap<Integer, ShardNode> tailMap = circle.tailMap(hash);
            Integer nodeHash = tailMap.isEmpty() ? 
                circle.firstKey() : tailMap.firstKey();
            
            return circle.get(nodeHash);
        }
        
        public String getShardKey(String originalKey) {
            ShardNode node = getNode(originalKey);
            return node != null ? node.getShardKey(originalKey) : originalKey;
        }
    }
    
    /**
     * 分片感知的速率限制器
     */
    public class ShardedRateLimiter {
        
        private final ConsistentHashSharding sharding;
        private final Map<String, DistributedRateLimiter> shardLimiters;
        
        public boolean tryAcquire(String originalKey, int tokens) {
            // 1. 计算分片键
            String shardKey = sharding.getShardKey(originalKey);
            
            // 2. 获取分片对应的限流器
            DistributedRateLimiter limiter = shardLimiters.get(shardKey);
            
            // 3. 在分片内执行限流
            return limiter.tryAcquire(originalKey, tokens);
        }
        
        /**
         * 动态重分片(无停机扩容)
         */
        public void rebalance(Set<ShardNode> newNodes) {
            // 1. 暂停新请求
            pauseRequests();
            
            // 2. 迁移数据
            migrateData(newNodes);
            
            // 3. 更新路由
            updateRouting(newNodes);
            
            // 4. 恢复请求
            resumeRequests();
        }
    }
}

3.2 多层缓存架构

java 复制代码
/**
 * 多层缓存:减少远程调用
 * 
 * L1: 本地内存缓存(毫秒级)
 * L2: 本地Redis缓存(微秒级)
 * L3: 远程分布式缓存(毫秒级)
 * L4: 持久化存储(秒级)
 */
public class MultiLevelCacheRateLimiter {
    
    private final Cache<String, TokenBucket> l1Cache; // Caffeine
    private final RedisTemplate<String, byte[]> l2Cache; // Redis
    private final HazelcastInstance l3Cache; // 分布式缓存
    private final DatabaseRepository l4Storage; // 数据库
    
    /**
     * 缓存读取策略
     */
    public TokenBucket getTokenBucket(String key) {
        // 1. 检查L1缓存
        TokenBucket bucket = l1Cache.getIfPresent(key);
        if (bucket != null) {
            return bucket;
        }
        
        // 2. 检查L2缓存
        byte[] l2Data = l2Cache.opsForValue().get(key);
        if (l2Data != null) {
            bucket = deserialize(l2Data);
            l1Cache.put(key, bucket);
            return bucket;
        }
        
        // 3. 检查L3缓存
        bucket = l3Cache.getMap("rate_buckets").get(key);
        if (bucket != null) {
            // 填充L2和L1缓存
            l2Cache.opsForValue().set(key, serialize(bucket), 1, TimeUnit.MINUTES);
            l1Cache.put(key, bucket);
            return bucket;
        }
        
        // 4. 从数据库加载
        bucket = l4Storage.loadTokenBucket(key);
        if (bucket != null) {
            // 填充所有缓存层
            l3Cache.getMap("rate_buckets").put(key, bucket);
            l2Cache.opsForValue().set(key, serialize(bucket), 5, TimeUnit.MINUTES);
            l1Cache.put(key, bucket);
        }
        
        return bucket;
    }
    
    /**
     * 缓存更新策略
     */
    public void updateTokenBucket(String key, TokenBucket bucket) {
        // 1. 更新数据库(最终一致性)
        l4Storage.saveTokenBucketAsync(key, bucket);
        
        // 2. 更新L3缓存
        l3Cache.getMap("rate_buckets").putAsync(key, bucket);
        
        // 3. 更新L2缓存
        l2Cache.opsForValue().set(key, serialize(bucket), 5, TimeUnit.MINUTES);
        
        // 4. 使L1缓存失效
        l1Cache.invalidate(key);
    }
}

3.3 自适应限流算法

java 复制代码
/**
 * 自适应限流:根据系统负载动态调整
 */
public class AdaptiveRateLimiting {
    
    /**
     * 基于PID控制器的自适应限流
     */
    public class PIDRateController {
        
        private double Kp = 1.0; // 比例系数
        private double Ki = 0.1; // 积分系数
        private double Kd = 0.01; // 微分系数
        
        private double lastError = 0;
        private double integral = 0;
        
        public double calculateRate(double targetLoad, double currentLoad) {
            // 计算误差
            double error = targetLoad - currentLoad;
            
            // PID计算
            double proportional = Kp * error;
            integral += Ki * error;
            double derivative = Kd * (error - lastError);
            
            // 更新状态
            lastError = error;
            
            // 计算新速率
            double adjustment = proportional + integral + derivative;
            
            // 应用限制
            return Math.max(0.1, Math.min(1000.0, adjustment));
        }
    }
    
    /**
     * 基于机器学习的智能限流
     */
    public class MLRateLimiter {
        
        private final Model model;
        private final FeatureExtractor featureExtractor;
        
        public double predictOptimalRate(String context) {
            // 提取特征
            Features features = featureExtractor.extract(context);
            
            // 模型预测
            return model.predict(features);
        }
        
        public void trainModel(List<TrainingExample> examples) {
            // 在线学习:根据系统表现调整模型
            model.update(examples);
        }
    }
    
    /**
     * 系统负载监控
     */
    public class SystemLoadMonitor {
        
        @Scheduled(fixedRate = 1000)
        public void monitorAndAdjust() {
            // 监控关键指标
            double cpuUsage = getCpuUsage();
            double memoryUsage = getMemoryUsage();
            double networkLatency = getNetworkLatency();
            double queueDepth = getQueueDepth();
            
            // 计算综合负载分数
            double loadScore = calculateLoadScore(
                cpuUsage, memoryUsage, networkLatency, queueDepth
            );
            
            // 根据负载调整限流策略
            if (loadScore > 0.8) {
                // 高负载:降低限流阈值
                adjustRateLimits(-0.2); // 降低20%
            } else if (loadScore < 0.3) {
                // 低负载:提高限流阈值
                adjustRateLimits(+0.1); // 提高10%
            }
        }
    }
}

四、未来扩展路线图

4.1 技术演进路径

yaml 复制代码
版本1.0(当前):
  架构: 轮询等待模式
  并发: 100-1000 TPS
  扩展性: 垂直扩展(增加Worker)
  可靠性: 中等
  复杂度: 低

版本2.0(6个月):
  架构: 智能轮询 + 本地缓存
  并发: 1000-10000 TPS
  扩展性: 有限水平扩展
  可靠性: 高
  复杂度: 中

版本3.0(12个月):
  架构: 异步事件驱动
  并发: 10000-100000 TPS
  扩展性: 良好水平扩展
  可靠性: 很高
  复杂度: 高

版本4.0(18个月):
  架构: 响应式流 + 智能分片
  并发: 100000+ TPS
  扩展性: 弹性自动扩展
  可靠性: 极高
  复杂度: 很高

4.2 容量规划建议

java 复制代码
/**
 * 容量规划工具
 */
public class CapacityPlanner {
    
    /**
     * 基于业务的容量规划
     */
    public CapacityPlan planForBusiness(double growthRate, int months) {
        // 当前指标
        double currentTPS = 1000;
        double currentLatency = 100; // ms
        
        // 预测未来需求
        double predictedTPS = currentTPS * Math.pow(1 + growthRate, months);
        
        // 计算所需资源
        return calculateRequiredResources(predictedTPS, currentLatency);
    }
    
    /**
     * 资源需求计算
     */
    private CapacityPlan calculateRequiredResources(double tps, double targetLatency) {
        // 根据排队理论计算
        CapacityPlan plan = new CapacityPlan();
        
        // Worker数量计算
        plan.setWorkersNeeded((int) Math.ceil(tps / 1000)); // 每个Worker处理1000 TPS
        
        // 内存计算
        plan.setMemoryGB(plan.getWorkersNeeded() * 4); // 每个Worker 4GB
        
        // CPU计算
        plan.setCpuCores(plan.getWorkersNeeded() * 2); // 每个Worker 2核
        
        // 存储计算
        plan.setStorageGB((int) (tps * 0.1 * 24 * 30)); // 每月存储
        
        return plan;
    }
}

五、总结与建议

5.1 当前优化建议

  1. 短期(立即实施)

    • 使用指数退避减少轮询频率
    • 实现本地令牌缓存
    • 添加智能预取机制
  2. 中期(3个月内)

    • 引入异步事件处理
    • 实现基本的分片策略
    • 添加多层缓存支持
  3. 长期(6个月后)

    • 迁移到响应式架构
    • 实现智能自适应限流
    • 构建弹性扩展能力

5.2 关键指标监控

java 复制代码
// 必须监控的指标
public enum CriticalMetrics {
    WORKER_THREAD_UTILIZATION,  // Worker线程使用率
    ACTIVITY_WAIT_TIME,         // Activity平均等待时间
    RATE_LIMIT_QUEUE_DEPTH,     // 限流队列深度
    TOKEN_ACQUISITION_LATENCY,  // 令牌获取延迟
    ERROR_RATE,                 // 错误率
    SYSTEM_LOAD_SCORE,          // 系统负载分数
    CACHE_HIT_RATIO,            // 缓存命中率
    NETWORK_IO_OVERHEAD         // 网络IO开销
}

5.3 决策框架

当面临扩展性问题时,使用以下决策树:

复制代码
是否并发超过1000 TPS?
├── 否 → 保持轮询模式,简单优化
└── 是 → 评估等待时间
    ├── 平均等待 < 1秒 → 使用智能轮询 + 缓存
    ├── 平均等待 1-10秒 → 使用异步事件驱动
    └── 平均等待 > 10秒 → 使用响应式流架构

最终建议:从简单的轮询模式开始,但设计时考虑到未来演进。实现模块化的架构,方便后续逐步升级到更高级的模式。监控关键指标,当系统达到瓶颈时,按照规划好的演进路径进行升级。

结论:如果是等待时间短,并发低使用轮训模式,如果等待时间长,并发高使用回调等待模式

相关推荐
code_li2 小时前
Android 16KB页面大小适配
java·架构·android-studio
Mr_Xuhhh2 小时前
MySQL表的内连接与外连接详解
java·前端·数据库
索荣荣2 小时前
Java正向代理与反向代理实战指南
java·开发语言
郑州光合科技余经理2 小时前
可独立部署的Java同城O2O系统架构:技术落地
java·开发语言·前端·后端·小程序·系统架构·uni-app
笨蛋不要掉眼泪2 小时前
Spring Boot + RedisTemplate 数据结构的基础操作
java·数据结构·spring boot·redis·wpf
打工的小王2 小时前
java并发编程(六)CountDownLatch和回环屏障CyclicBarrier
java·开发语言
nbsaas-boot2 小时前
如何进行 Vibe Coding:从“灵感驱动”到“可交付工程”的方法论
java·ai编程
Remember_9932 小时前
Spring 事务深度解析:实现方式、隔离级别与传播机制全攻略
java·开发语言·数据库·后端·spring·leetcode·oracle