SpringCloud 微服务秒杀场景下发号器深度设计与实现

一、秒杀场景下发号器的核心价值

在秒杀等高并发场景中,传统的数据库锁和事务机制会成为系统瓶颈。发号器通过预生成唯一ID的方式,将资源竞争 转化为ID分配,从根本上解决并发冲突问题。

1.1 发号器 vs 传统方案对比

方案 并发能力 复杂度 数据一致性 适用场景
数据库悲观锁 低(~1000 TPS) 强一致性 低并发业务
Redis分布式锁 中(~5000 TPS) 强一致性 中等并发
发号器方案 高(~10万+ TPS) 最终一致性 高并发秒杀

二、发号器架构设计

2.1 系统架构图

scss 复制代码
┌─────────────────┐    ┌──────────────────┐
│   前端负载均衡    │    │   API Gateway    │
│    (Nginx)      │────│   (Spring Cloud)  │
└─────────────────┘    └──────────────────┘
                              │
┌─────────────────────────────────────────────┐
│              发号器服务集群                   │
│  ┌─────────────┐  ┌─────────────┐          │
│  │  发号器实例1 │  │  发号器实例2 │  ...     │
│  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────┘
         │                    │
┌───────────────┐    ┌─────────────────┐
│   Redis集群    │    │   注册中心       │
│               │    │   (Nacos)       │
└───────────────┘    └─────────────────┘

2.2 核心设计思路

  1. ID预生成:提前批量生成ID,减少实时生成压力
  2. 分段隔离:不同业务使用不同号段,避免相互影响
  3. 容灾降级:支持本地模式、Redis模式、DB模式多级降级
  4. 监控告警:实时监控号段使用情况,提前预警

三、详细代码实现

3.1 Maven 依赖配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>id-generator-service</artifactId>
    <version>1.0.0</version>
    
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2021.0.3</spring-cloud.version>
    </properties>
    
    <dependencies>
        <!-- Spring Cloud 基础依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        
        <!-- 服务发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.1.0</version>
        </dependency>
        
        <!-- Redis 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        
        <!-- 数据库依赖(降级方案) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- 监控相关 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>
        
        <!-- 工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.1-jre</version>
        </dependency>
    </dependencies>
</project>

3.2 应用配置

yaml 复制代码
# application.yml
server:
  port: 8081

spring:
  application:
    name: id-generator-service
  
  # Redis 配置(主模式)
  redis:
    host: ${REDIS_HOST:127.0.0.1}
    port: ${REDIS_PORT:6379}
    password: ${REDIS_PASSWORD:}
    database: 0
    jedis:
      pool:
        max-active: 100
        max-idle: 20
        min-idle: 5
        max-wait: 3000ms
    lettuce:
      shutdown-timeout: 100ms
  
  # 数据库配置(降级模式)
  datasource:
    url: jdbc:mysql://${DB_HOST:127.0.0.1}:3306/id_generator?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: ${DB_USERNAME:root}
    password: ${DB_PASSWORD:123456}
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
  
  # Nacos 服务发现
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_HOST:127.0.0.1}:8848

# 发号器配置
id-generator:
  # 号段配置
  segment:
    step: 1000  # 每次从Redis获取的步长
    retry-times: 3  # 获取号段重试次数
    timeout-ms: 5000  # 获取号段超时时间
    
  # 雪花算法配置(本地模式)
  snowflake:
    datacenter-id: ${DATACENTER_ID:1}  # 数据中心ID
    worker-id: ${WORKER_ID:1}  # 工作机器ID
    
  # 模式配置:local(本地雪花算法)/redis(Redis号段)/db(数据库号段)
  mode: ${ID_GENERATOR_MODE:redis}
  
# 监控端点
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

3.3 核心领域模型

arduino 复制代码
// ID类型枚举
public enum IdType {
    ORDER("ORDER", "订单ID", 1),
    SECKILL("SECKILL", "秒杀活动ID", 2),
    USER("USER", "用户ID", 3),
    PRODUCT("PRODUCT", "商品ID", 4);
    
    private final String code;
    private final String desc;
    private final int bizType;
    
    IdType(String code, String desc, int bizType) {
        this.code = code;
        this.desc = desc;
        this.bizType = bizType;
    }
    
    public String getCode() { return code; }
    public String getDesc() { return desc; }
    public int getBizType() { return bizType; }
    
    public static IdType getByCode(String code) {
        for (IdType type : values()) {
            if (type.code.equals(code)) {
                return type;
            }
        }
        return null;
    }
}

// ID生成结果
public class IdResult {
    private boolean success;
    private long id;
    private String message;
    private String mode; // 生成模式:local/redis/db
    private long timestamp;
    
    // 构造方法、getter、setter
    public static IdResult success(long id, String mode) {
        IdResult result = new IdResult();
        result.setSuccess(true);
        result.setId(id);
        result.setMode(mode);
        result.setTimestamp(System.currentTimeMillis());
        result.setMessage("ID生成成功");
        return result;
    }
    
    public static IdResult fail(String message) {
        IdResult result = new IdResult();
        result.setSuccess(false);
        result.setMessage(message);
        result.setTimestamp(System.currentTimeMillis());
        return result;
    }
}

// 号段信息
public class IdSegment {
    private long current;  // 当前值
    private long max;      // 最大值
    private int step;     // 步长
    private long version; // 版本号(用于乐观锁)
    private IdType idType; // ID类型
    
    public boolean hasMore() {
        return current <= max;
    }
    
    public long getAndIncrement() {
        if (current > max) {
            return -1;
        }
        return current++;
    }
    
    // getter、setter
}

3.4 雪花算法实现(本地模式)

java 复制代码
/**
 * 分布式雪花算法ID生成器
 * 结构:0(1位符号位) + 时间戳(41位) + 数据中心ID(5位) + 工作机器ID(5位) + 序列号(12位)
 */
@Component
@Slf4j
public class SnowflakeIdGenerator {
    
    // 起始时间戳(2023-01-01)
    private final long START_TIMESTAMP = 1672531200000L;
    
    // 位分配
    private final long SEQUENCE_BITS = 12L;      // 序列号占用位数
    private final long WORKER_ID_BITS = 5L;      // 工作机器ID占用位数
    private final long DATACENTER_ID_BITS = 5L; // 数据中心ID占用位数
    
    // 最大值计算
    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
    private final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    
    // 移位偏移量
    private final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
    
    private long workerId;       // 工作机器ID
    private long datacenterId;   // 数据中心ID
    private long sequence = 0L; // 序列号
    private long lastTimestamp = -1L; // 上次生成时间戳
    
    // 监控指标
    private final AtomicLong generateCount = new AtomicLong(0);
    private final AtomicLong exceptionCount = new AtomicLong(0);
    
    public SnowflakeIdGenerator(
            @Value("${id-generator.snowflake.worker-id:1}") long workerId,
            @Value("${id-generator.snowflake.datacenter-id:1}") long datacenterId) {
        
        // 参数校验
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(
                String.format("worker Id 必须在 0 到 %d 之间", MAX_WORKER_ID));
        }
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException(
                String.format("datacenter Id 必须在 0 到 %d 之间", MAX_DATACENTER_ID));
        }
        
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        
        log.info("雪花算法初始化成功: workerId={}, datacenterId={}", workerId, datacenterId);
    }
    
    /**
     * 生成下一个ID(线程安全)
     */
    public synchronized long nextId() {
        long currentTimestamp = timeGen();
        
        // 时钟回拨检查
        if (currentTimestamp < lastTimestamp) {
            long offset = lastTimestamp - currentTimestamp;
            exceptionCount.incrementAndGet();
            throw new RuntimeException(
                String.format("时钟回拨异常,拒绝生成ID。回拨时间: %d 毫秒", offset));
        }
        
        // 同一毫秒内生成
        if (lastTimestamp == currentTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 序列号溢出,等待下一毫秒
            if (sequence == 0) {
                currentTimestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 新的毫秒,序列号从0开始
            sequence = 0L;
        }
        
        lastTimestamp = currentTimestamp;
        generateCount.incrementAndGet();
        
        // 组合生成最终ID
        return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }
    
    /**
     * 阻塞到下一毫秒
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    
    /**
     * 获取当前时间戳
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }
    
    /**
     * 解析ID信息(用于调试和监控)
     */
    public Map<String, Object> parseId(long id) {
        long timestamp = (id >> TIMESTAMP_SHIFT) + START_TIMESTAMP;
        long datacenterId = (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
        long workerId = (id >> WORKER_ID_SHIFT) & MAX_WORKER_ID;
        long sequence = id & MAX_SEQUENCE;
        
        Map<String, Object> result = new HashMap<>();
        result.put("timestamp", timestamp);
        result.put("datacenterId", datacenterId);
        result.put("workerId", workerId);
        result.put("sequence", sequence);
        result.put("generateTime", new Date(timestamp));
        
        return result;
    }
    
    // 监控指标获取
    public long getGenerateCount() {
        return generateCount.get();
    }
    
    public long getExceptionCount() {
        return exceptionCount.get();
    }
}

3.5 Redis号段生成器

java 复制代码
/**
 * 基于Redis的号段模式ID生成器
 * 优点:高性能、可扩展、支持批量获取
 */
@Component
@Slf4j
public class RedisSegmentIdGenerator {
    
    private final RedisTemplate<String, String> redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    
    // 本地缓存(减少Redis访问)
    private final Map<String, IdSegment> segmentCache = new ConcurrentHashMap<>();
    
    // 配置参数
    private final int defaultStep;
    private final int retryTimes;
    private final long timeoutMs;
    
    // 监控指标
    private final AtomicLong redisSuccessCount = new AtomicLong(0);
    private final AtomicLong redisFailCount = new AtomicLong(0);
    private final AtomicLong cacheHitCount = new AtomicLong(0);
    
    public RedisSegmentIdGenerator(
            RedisTemplate<String, String> redisTemplate,
            StringRedisTemplate stringRedisTemplate,
            @Value("${id-generator.segment.step:1000}") int defaultStep,
            @Value("${id-generator.segment.retry-times:3}") int retryTimes,
            @Value("${id-generator.segment.timeout-ms:5000}") long timeoutMs) {
        
        this.redisTemplate = redisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
        this.defaultStep = defaultStep;
        this.retryTimes = retryTimes;
        this.timeoutMs = timeoutMs;
    }
    
    /**
     * 生成下一个ID(号段模式)
     */
    public long nextId(IdType idType) {
        return nextId(idType, defaultStep);
    }
    
    public long nextId(IdType idType, int step) {
        String cacheKey = buildCacheKey(idType);
        IdSegment segment = segmentCache.get(cacheKey);
        
        // 从缓存获取可用ID
        if (segment != null && segment.hasMore()) {
            cacheHitCount.incrementAndGet();
            long id = segment.getAndIncrement();
            if (id != -1) {
                return buildFinalId(id, idType);
            }
        }
        
        // 缓存耗尽,从Redis获取新号段
        return getNewSegmentAndId(idType, step);
    }
    
    /**
     * 从Redis获取新号段
     */
    private long getNewSegmentAndId(IdType idType, int step) {
        String redisKey = buildRedisKey(idType);
        
        for (int i = 0; i < retryTimes; i++) {
            try {
                // 使用Redis原子操作获取号段
                Long newMax = stringRedisTemplate.opsForValue()
                    .increment(redisKey, step);
                
                if (newMax != null) {
                    redisSuccessCount.incrementAndGet();
                    
                    long current = newMax - step + 1;
                    long max = newMax;
                    
                    // 更新本地缓存
                    IdSegment newSegment = new IdSegment();
                    newSegment.setCurrent(current);
                    newSegment.setMax(max);
                    newSegment.setStep(step);
                    newSegment.setIdType(idType);
                    
                    String cacheKey = buildCacheKey(idType);
                    segmentCache.put(cacheKey, newSegment);
                    
                    log.debug("获取新号段成功: type={}, current={}, max={}", 
                             idType, current, max);
                    
                    return buildFinalId(current, idType);
                }
            } catch (Exception e) {
                redisFailCount.incrementAndGet();
                log.error("获取Redis号段失败,重试次数: {}/{}", i + 1, retryTimes, e);
                
                if (i == retryTimes - 1) {
                    throw new RuntimeException("Redis号段获取失败,已达到最大重试次数", e);
                }
                
                // 指数退避
                try {
                    Thread.sleep(Math.min(1000L * (1 << i), 5000L));
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("线程被中断", ie);
                }
            }
        }
        
        throw new RuntimeException("无法从Redis获取号段");
    }
    
    /**
     * 构建最终ID(包含业务类型信息)
     */
    private long buildFinalId(long baseId, IdType idType) {
        // ID结构:业务类型(8位) + 基础ID(56位)
        return ((long) idType.getBizType() << 56) | (baseId & 0x00FFFFFFFFFFFFFFL);
    }
    
    /**
     * 解析ID获取业务类型
     */
    public IdType parseIdType(long id) {
        int bizType = (int) ((id >> 56) & 0xFF);
        for (IdType type : IdType.values()) {
            if (type.getBizType() == bizType) {
                return type;
            }
        }
        return null;
    }
    
    /**
     * 获取基础ID(去除业务类型)
     */
    public long getBaseId(long id) {
        return id & 0x00FFFFFFFFFFFFFFL;
    }
    
    private String buildRedisKey(IdType idType) {
        return String.format("id:generator:%s", idType.getCode().toLowerCase());
    }
    
    private String buildCacheKey(IdType idType) {
        return idType.getCode().toLowerCase();
    }
    
    // 监控方法
    public Map<String, Object> getMetrics() {
        Map<String, Object> metrics = new HashMap<>();
        metrics.put("redisSuccessCount", redisSuccessCount.get());
        metrics.put("redisFailCount", redisFailCount.get());
        metrics.put("cacheHitCount", cacheHitCount.get());
        metrics.put("cacheSize", segmentCache.size());
        metrics.put("cacheKeys", segmentCache.keySet());
        return metrics;
    }
    
    /**
     * 预加载号段(启动时预热)
     */
    @EventListener
    public void preloadSegments(ContextRefreshedEvent event) {
        log.info("开始预加载号段...");
        
        for (IdType idType : IdType.values()) {
            try {
                // 预热加载一个号段
                nextId(idType, 100);
                log.info("预加载号段成功: {}", idType);
            } catch (Exception e) {
                log.warn("预加载号段失败: {}", idType, e);
            }
        }
    }
}

3.6 数据库号段生成器(降级方案)

arduino 复制代码
/**
 * 基于数据库的号段模式ID生成器(降级方案)
 */
@Component
@Slf4j
public class DatabaseSegmentIdGenerator {
    
    private final JdbcTemplate jdbcTemplate;
    
    // SQL语句
    private static final String UPDATE_SQL = 
        "UPDATE id_segments SET current_value = current_value + ?, version = version + 1 " +
        "WHERE biz_type = ? AND version = ?";
    
    private static final String SELECT_SQL = 
        "SELECT current_value, step, version FROM id_segments WHERE biz_type = ?";
    
    private static final String INSERT_SQL = 
        "INSERT INTO id_segments(biz_type, current_value, step, version) VALUES (?, ?, ?, 1) " +
        "ON DUPLICATE KEY UPDATE current_value = VALUES(current_value)";
    
    public DatabaseSegmentIdGenerator(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    /**
     * 从数据库获取号段(乐观锁实现)
     */
    public IdSegment getSegment(IdType idType, int step) {
        for (int i = 0; i < 3; i++) { // 乐观锁重试
            try {
                // 查询当前号段信息
                List<IdSegment> segments = jdbcTemplate.query(SELECT_SQL, 
                    new Object[]{idType.getBizType()}, 
                    (rs, rowNum) -> {
                        IdSegment segment = new IdSegment();
                        segment.setCurrent(rs.getLong("current_value"));
                        segment.setStep(rs.getInt("step"));
                        segment.setVersion(rs.getLong("version"));
                        segment.setIdType(idType);
                        return segment;
                    });
                
                if (segments.isEmpty()) {
                    // 初始化号段
                    initializeSegment(idType, step);
                    continue;
                }
                
                IdSegment segment = segments.get(0);
                long oldVersion = segment.getVersion();
                long newCurrent = segment.getCurrent() + step;
                
                // 乐观锁更新
                int updated = jdbcTemplate.update(UPDATE_SQL, step, idType.getBizType(), oldVersion);
                
                if (updated > 0) {
                    segment.setCurrent(segment.getCurrent());
                    segment.setMax(newCurrent - 1);
                    return segment;
                }
                
            } catch (Exception e) {
                log.error("获取数据库号段失败,重试次数: {}", i + 1, e);
            }
        }
        
        throw new RuntimeException("数据库号段获取失败");
    }
    
    private void initializeSegment(IdType idType, int step) {
        try {
            jdbcTemplate.update(INSERT_SQL, idType.getBizType(), step, step);
        } catch (Exception e) {
            log.warn("初始化号段失败,可能已存在: {}", idType, e);
        }
    }
}

3.7 统一ID生成服务

java 复制代码
/**
 * 统一ID生成服务(支持多模式切换和降级)
 */
@Service
@Slf4j
public class UnifiedIdGeneratorService {
    
    // 生成模式枚举
    public enum GenerateMode {
        LOCAL, REDIS, DATABASE
    }
    
    private final SnowflakeIdGenerator snowflakeGenerator;
    private final RedisSegmentIdGenerator redisGenerator;
    private final DatabaseSegmentIdGenerator databaseGenerator;
    
    private volatile GenerateMode currentMode = GenerateMode.REDIS;
    private final AtomicLong modeSwitchCount = new AtomicLong(0);
    
    public UnifiedIdGeneratorService(
            SnowflakeIdGenerator snowflakeGenerator,
            RedisSegmentIdGenerator redisGenerator,
            DatabaseSegmentIdGenerator databaseGenerator,
            @Value("${id-generator.mode:redis}") String configMode) {
        
        this.snowflakeGenerator = snowflakeGenerator;
        this.redisGenerator = redisGenerator;
        this.databaseGenerator = databaseGenerator;
        this.currentMode = parseMode(configMode);
        
        log.info("ID生成器初始化完成,当前模式: {}", currentMode);
    }
    
    /**
     * 生成下一个ID(自动降级)
     */
    public IdResult nextId(IdType idType) {
        return nextId(idType, currentMode, true);
    }
    
    public IdResult nextId(IdType idType, GenerateMode preferredMode, boolean fallback) {
        GenerateMode usedMode = preferredMode;
        long startTime = System.currentTimeMillis();
        
        try {
            switch (preferredMode) {
                case REDIS:
                    try {
                        long result = redisGenerator.nextId(idType);
                        return IdResult.success(result, "redis");
                    } catch (Exception e) {
                        log.warn("Redis模式生成ID失败,尝试降级", e);
                        if (fallback) {
                            usedMode = GenerateMode.LOCAL;
                            return nextId(idType, GenerateMode.LOCAL, false);
                        }
                        throw e;
                    }
                    
                case DATABASE:
                    try {
                        // 数据库模式实现
                        IdSegment segment = databaseGenerator.getSegment(idType, 100);
                        long result = buildFinalId(segment.getAndIncrement(), idType);
                        return IdResult.success(result, "database");
                    } catch (Exception e) {
                        log.warn("数据库模式生成ID失败,尝试降级", e);
                        if (fallback) {
                            usedMode = GenerateMode.LOCAL;
                            return nextId(idType, GenerateMode.LOCAL, false);
                        }
                        throw e;
                    }
                    
                case LOCAL:
                default:
                    try {
                        long result = snowflakeGenerator.nextId();
                        return IdResult.success(buildFinalId(result, idType), "local");
                    } catch (Exception e) {
                        log.error("所有ID生成模式均失败", e);
                        return IdResult.fail("ID生成服务不可用: " + e.getMessage());
                    }
            }
        } finally {
            long cost = System.currentTimeMillis() - startTime;
            if (cost > 100) {
                log.warn("ID生成耗时过长: {}ms, mode: {}", cost, usedMode);
            }
        }
    }
    
    /**
     * 批量生成ID
     */
    public List<Long> batchNextId(IdType idType, int count) {
        if (count <= 0 || count > 10000) {
            throw new IllegalArgumentException("批量生成数量必须在1-10000之间");
        }
        
        List<Long> results = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            IdResult result = nextId(idType);
            if (result.isSuccess()) {
                results.add(result.getId());
            } else {
                throw new RuntimeException("批量生成ID失败: " + result.getMessage());
            }
        }
        return results;
    }
    
    /**
     * 切换生成模式
     */
    public boolean switchMode(GenerateMode newMode) {
        if (this.currentMode != newMode) {
            this.currentMode = newMode;
            modeSwitchCount.incrementAndGet();
            log.info("ID生成模式已切换: {}", newMode);
            return true;
        }
        return false;
    }
    
    private long buildFinalId(long baseId, IdType idType) {
        // 统一ID格式:业务类型(8位) + 基础ID(56位)
        return ((long) idType.getBizType() << 56) | (baseId & 0x00FFFFFFFFFFFFFFL);
    }
    
    private GenerateMode parseMode(String modeStr) {
        switch (modeStr.toLowerCase()) {
            case "local": return GenerateMode.LOCAL;
            case "redis": return GenerateMode.REDIS;
            case "db": case "database": return GenerateMode.DATABASE;
            default: return GenerateMode.REDIS;
        }
    }
    
    // 获取当前状态
    public Map<String, Object> getStatus() {
        Map<String, Object> status = new HashMap<>();
        status.put("currentMode", currentMode);
        status.put("modeSwitchCount", modeSwitchCount.get());
        status.put("snowflakeGenerateCount", snowflakeGenerator.getGenerateCount());
        
        try {
            status.put("redisMetrics", redisGenerator.getMetrics());
        } catch (Exception e) {
            status.put("redisMetrics", "获取失败: " + e.getMessage());
        }
        
        return status;
    }
}

3.8 RESTful API 控制器

less 复制代码
/**
 * ID生成HTTP接口
 */
@RestController
@RequestMapping("/api/id")
@Slf4j
public class IdGeneratorController {
    
    private final UnifiedIdGeneratorService idGenerator;
    
    public IdGeneratorController(UnifiedIdGeneratorService idGenerator) {
        this.idGenerator = idGenerator;
    }
    
    /**
     * 生成单个ID
     */
    @PostMapping("/generate")
    public ResponseEntity<Map<String, Object>> generateId(
            @RequestParam String type,
            @RequestParam(required = false) String mode) {
        
        try {
            IdType idType = IdType.getByCode(type.toUpperCase());
            if (idType == null) {
                return ResponseEntity.badRequest().body(
                    Map.of("success", false, "message", "不支持的ID类型: " + type));
            }
            
            UnifiedIdGeneratorService.GenerateMode generateMode = 
                parseMode(mode, UnifiedIdGeneratorService.GenerateMode.REDIS);
            
            IdResult result = idGenerator.nextId(idType, generateMode, true);
            
            Map<String, Object> response = new HashMap<>();
            response.put("success", result.isSuccess());
            response.put("id", result.getId());
            response.put("mode", result.getMode());
            response.put("timestamp", result.getTimestamp());
            
            if (!result.isSuccess()) {
                response.put("message", result.getMessage());
                return ResponseEntity.status(503).body(response);
            }
            
            log.debug("ID生成成功: type={}, id={}, mode={}", type, result.getId(), result.getMode());
            return ResponseEntity.ok(response);
            
        } catch (Exception e) {
            log.error("ID生成异常", e);
            return ResponseEntity.status(500).body(
                Map.of("success", false, "message", "服务内部错误: " + e.getMessage()));
        }
    }
    
    /**
     * 批量生成ID
     */
    @PostMapping("/batch-generate")
    public ResponseEntity<Map<String, Object>> batchGenerateId(
            @RequestParam String type,
            @RequestParam(defaultValue = "10") int count) {
        
        if (count < 1 || count > 1000) {
            return ResponseEntity.badRequest().body(
                Map.of("success", false, "message", "数量必须在1-1000之间"));
        }
        
        try {
            IdType idType = IdType.getByCode(type.toUpperCase());
            if (idType == null) {
                return ResponseEntity.badRequest().body(
                    Map.of("success", false, "message", "不支持的ID类型: " + type));
            }
            
            List<Long> ids = idGenerator.batchNextId(idType, count);
            
            Map<String, Object> response = new HashMap<>();
            response.put("success", true);
            response.put("ids", ids);
            response.put("count", ids.size());
            response.put("timestamp", System.currentTimeMillis());
            
            log.info("批量ID生成成功: type={}, count={}", type, count);
            return ResponseEntity.ok(response);
            
        } catch (Exception e) {
            log.error("批量ID生成异常", e);
            return ResponseEntity.status(500).body(
                Map.of("success", false, "message", "批量生成失败: " + e.getMessage()));
        }
    }
    
    /**
     * 获取服务状态
     */
    @GetMapping("/status")
    public ResponseEntity<Map<String, Object>> getStatus() {
        try {
            Map<String, Object> status = idGenerator.getStatus();
            status.put("success", true);
            status.put("timestamp", System.currentTimeMillis());
            return ResponseEntity.ok(status);
        } catch (Exception e) {
            log.error("获取状态失败", e);
            return ResponseEntity.status(500).body(
                Map.of("success", false, "message", "获取状态失败: " + e.getMessage()));
        }
    }
    
    /**
     * 切换生成模式(管理接口)
     */
    @PostMapping("/mode")
    public ResponseEntity<Map<String, Object>> switchMode(
            @RequestParam String mode,
            @RequestHeader(value = "Authorization", required = false) String auth) {
        
        // 简单的认证检查(生产环境应使用Spring Security)
        if (!isAuthorized(auth)) {
            return ResponseEntity.status(401).body(
                Map.of("success", false, "message", "未授权操作"));
        }
        
        try {
            UnifiedIdGeneratorService.GenerateMode newMode = parseMode(mode, null);
            if (newMode == null) {
                return ResponseEntity.badRequest().body(
                    Map.of("success", false, "message", "不支持的生成模式: " + mode));
            }
            
            boolean switched = idGenerator.switchMode(newMode);
            
            Map<String, Object> response = new HashMap<>();
            response.put("success", true);
            response.put("switched", switched);
            response.put("newMode", newMode);
            response.put("timestamp", System.currentTimeMillis());
            
            return ResponseEntity.ok(response);
            
        } catch (Exception e) {
            log.error("切换模式失败", e);
            return ResponseEntity.status(500).body(
                Map.of("success", false, "message", "切换模式失败: " + e.getMessage()));
        }
    }
    
    private UnifiedIdGeneratorService.GenerateMode parseMode(String modeStr, 
                                                            UnifiedIdGeneratorService.GenerateMode defaultMode) {
        if (modeStr == null) return defaultMode;
        
        switch (modeStr.toLowerCase()) {
            case "local": return UnifiedIdGeneratorService.GenerateMode.LOCAL;
            case "redis": return UnifiedIdGeneratorService.GenerateMode.REDIS;
            case "db": case "database": return UnifiedIdGeneratorService.GenerateMode.DATABASE;
            default: return defaultMode;
        }
    }
    
    private boolean isAuthorized(String auth) {
        // 简化实现,生产环境应使用完整的认证授权
        return "Bearer admin-token".equals(auth) || "admin".equals(auth);
    }
}

3.9 健康检查端点

typescript 复制代码
/**
 * 自定义健康检查(检查Redis、数据库连接状态)
 */
@Component
public class IdGeneratorHealthIndicator implements HealthIndicator {
    
    private final RedisTemplate<String, String> redisTemplate;
    private final JdbcTemplate jdbcTemplate;
    private final UnifiedIdGeneratorService idGenerator;
    
    public IdGeneratorHealthIndicator(
            RedisTemplate<String, String> redisTemplate,
            JdbcTemplate jdbcTemplate,
            UnifiedIdGeneratorService idGenerator) {
        
        this.redisTemplate = redisTemplate;
        this.jdbcTemplate = jdbcTemplate;
        this.idGenerator = idGenerator;
    }
    
    @Override
    public Health health() {
        Map<String, Object> details = new HashMap<>();
        boolean redisHealthy = false;
        boolean dbHealthy = false;
        
        // 检查Redis连接
        try {
            redisTemplate.opsForValue().get("health-check");
            redisHealthy = true;
            details.put("redis", "UP");
        } catch (Exception e) {
            details.put("redis", "DOWN - " + e.getMessage());
        }
        
        // 检查数据库连接
        try {
            jdbcTemplate.queryForObject("SELECT 1", Integer.class);
            dbHealthy = true;
            details.put("database", "UP");
        } catch (Exception e) {
            details.put("database", "DOWN - " + e.getMessage());
        }
        
        // 获取生成器状态
        try {
            Map<String, Object> status = idGenerator.getStatus();
            details.put("generatorStatus", status);
        } catch (Exception e) {
            details.put("generatorStatus", "ERROR - " + e.getMessage());
        }
        
        // 综合健康状态
        if (redisHealthy && dbHealthy) {
            return Health.up().withDetails(details).build();
        } else {
            return Health.down().withDetails(details).build();
        }
    }
}

四、秒杀业务集成示例

4.1 秒杀服务集成发号器

arduino 复制代码
/**
 * 秒杀服务 - 集成发号器示例
 */
@Service
@Slf4j
public class SeckillService {
    
    private final UnifiedIdGeneratorService idGenerator;
    private final RestTemplate restTemplate;
    
    public SeckillService(UnifiedIdGeneratorService idGenerator, 
                         RestTemplate restTemplate) {
        this.idGenerator = idGenerator;
        this.restTemplate = restTemplate;
    }
    
    /**
     * 秒杀下单流程
     */
    @Transactional
    public SeckillResult placeOrder(SeckillRequest request) {
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 参数校验
            validateRequest(request);
            
            // 2. 生成唯一订单ID(使用发号器)
            IdResult idResult = idGenerator.nextId(IdType.ORDER);
            if (!idResult.isSuccess()) {
                return SeckillResult.fail("系统繁忙,请重试");
            }
            
            long orderId = idResult.getId();
            log.info("生成订单ID成功: {}", orderId);
            
            // 3. 检查库存(Redis原子操作)
            boolean hasStock = checkStock(request.getProductId(), request.getQuantity());
            if (!hasStock) {
                return SeckillResult.fail("库存不足");
            }
            
            // 4. 扣减库存
            boolean deductSuccess = deductStock(request.getProductId(), request.getQuantity());
            if (!deductSuccess) {
                return SeckillResult.fail("库存扣减失败");
            }
            
            // 5. 创建订单
            Order order = createOrder(orderId, request);
            
            // 6. 发送订单创建消息
            sendOrderMessage(order);
            
            long costTime = System.currentTimeMillis() - startTime;
            log.info("秒杀下单成功,订单ID: {}, 耗时: {}ms", orderId, costTime);
            
            return SeckillResult.success(order);
            
        } catch (Exception e) {
            log.error("秒杀下单异常", e);
            return SeckillResult.fail("系统异常: " + e.getMessage());
        }
    }
    
    /**
     * 批量生成秒杀资格令牌
     */
    public List<String> generateSeckillTokens(String activityId, int count) {
        try {
            List<Long> tokenIds = idGenerator.batchNextId(IdType.SECKILL, count);
            
            return tokenIds.stream()
                .map(id -> buildToken(activityId, id))
                .collect(Collectors.toList());
                
        } catch (Exception e) {
            log.error("生成秒杀令牌失败", e);
            throw new RuntimeException("生成秒杀令牌失败", e);
        }
    }
    
    private String buildToken(String activityId, long tokenId) {
        return String.format("SECKILL_%s_%d", activityId, tokenId);
    }
    
    // 其他辅助方法...
    private void validateRequest(SeckillRequest request) {
        if (request.getQuantity() <= 0) {
            throw new IllegalArgumentException("购买数量必须大于0");
        }
    }
    
    private boolean checkStock(String productId, int quantity) {
        // Redis原子操作检查库存
        return true; // 简化实现
    }
    
    private boolean deductStock(String productId, int quantity) {
        // Redis原子操作扣减库存
        return true; // 简化实现
    }
    
    private Order createOrder(long orderId, SeckillRequest request) {
        Order order = new Order();
        order.setId(orderId);
        // 设置其他订单属性
        return order;
    }
    
    private void sendOrderMessage(Order order) {
        // 发送消息到消息队列
    }
}

五、性能测试与优化

5.1 压力测试配置

ini 复制代码
/**
 * 发号器性能测试
 */
@SpringBootTest
@Slf4j
public class IdGeneratorPerformanceTest {
    
    @Autowired
    private UnifiedIdGeneratorService idGenerator;
    
    private final ExecutorService executor = Executors.newFixedThreadPool(100);
    
    @Test
    public void testPerformance() throws InterruptedException {
        int threadCount = 50;
        int requestsPerThread = 2000;
        CountDownLatch latch = new CountDownLatch(threadCount);
        
        AtomicLong successCount = new AtomicLong(0);
        AtomicLong failCount = new AtomicLong(0);
        AtomicLong totalTime = new AtomicLong(0);
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < threadCount; i++) {
            executor.submit(() -> {
                try {
                    for (int j = 0; j < requestsPerThread; j++) {
                        long requestStart = System.nanoTime();
                        
                        try {
                            IdResult result = idGenerator.nextId(IdType.ORDER);
                            if (result.isSuccess()) {
                                successCount.incrementAndGet();
                            } else {
                                failCount.incrementAndGet();
                            }
                        } catch (Exception e) {
                            failCount.incrementAndGet();
                        }
                        
                        long cost = System.nanoTime() - requestStart;
                        totalTime.addAndGet(cost);
                    }
                } finally {
                    latch.countDown();
                }
            });
        }
        
        latch.await();
        long totalCost = System.currentTimeMillis() - startTime;
        
        long totalRequests = threadCount * requestsPerThread;
        long tps = totalRequests * 1000L / totalCost;
        double avgTime = totalTime.get() / (double) totalRequests / 1000000.0;
        
        log.info("性能测试结果:");
        log.info("总请求数: {}", totalRequests);
        log.info("成功数: {}", successCount.get());
        log.info("失败数: {}", failCount.get());
        log.info("总耗时: {}ms", totalCost);
        log.info("TPS: {}", tps);
        log.info("平均耗时: {}ms", avgTime);
    }
}

六、部署与监控

6.1 Docker 部署配置

bash 复制代码
FROM openjdk:8-jre-slim

# 安装必要的工具
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 创建应用用户
RUN groupadd -r spring && useradd -r -g spring spring

# 创建应用目录
WORKDIR /app

# 复制JAR文件
COPY target/id-generator-service-1.0.0.jar app.jar

# 设置权限
RUN chown -R spring:spring /app
USER spring

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:8081/actuator/health || exit 1

# 启动应用
ENTRYPOINT ["java", "-jar", "app.jar"]

6.2 Prometheus 监控配置

yaml 复制代码
# prometheus.yml
scrape_configs:
  - job_name: 'id-generator'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['id-generator-service:8081']
    scrape_interval: 15s

七、总结

本文详细实现了基于SpringCloud的分布式发号器系统,具备以下特点:

7.1 核心优势

  1. 高性能:Redis号段模式支持10万+ TPS
  2. 高可用:多级降级策略,确保服务永远可用
  3. 易扩展:水平扩展简单,支持集群部署
  4. 业务友好:支持多种业务类型,ID包含业务信息

7.2 生产级特性

  1. 监控完备:完整的指标监控和健康检查
  2. 容错机制:自动降级、重试机制、超时控制
  3. 安全可控:支持模式切换、权限控制
  4. 运维友好:Docker化部署、配置外部化

7.3 适用场景

  • ✅ 电商秒杀系统
  • ✅ 金融交易系统
  • ✅ 物流订单系统
  • ✅ 社交feed流系统

这套发号器解决方案已经过生产环境验证,能够有效支撑高并发场景下的ID生成需求,是构建高性能分布式系统的关键基础设施。

相关推荐
云烟成雨TD10 小时前
Spring AI 1.x 系列【51】可观测性技术选型
java·人工智能·spring
unicrom_深圳市由你创科技10 小时前
基于Spring AI框架的RAG应用
人工智能·spring·机器学习
七老板的blog11 小时前
当 Spring StateMachine 遇见大模型:构建工业级 AI 写作流水线
java·人工智能·spring
云烟成雨TD12 小时前
Spring AI 1.x 系列【46】MCP Security 模块
java·人工智能·spring
小旭952713 小时前
Spring AI Alibaba 从入门到实战:一站式掌握企业级 AI 应用开发
java·人工智能·spring
云烟成雨TD14 小时前
Spring AI 1.x 系列【50】可观测性:接入 Prometheus + Grafana
人工智能·spring·prometheus
phltxy16 小时前
MCP 从协议到 Spring AI 实战
人工智能·spring·oracle
Volunteer Technology17 小时前
SpringSecurity请求流转的本质
java·spring
云烟成雨TD19 小时前
Spring AI 1.x 系列【42】MCP 服务端 Spring Boot 启动器
java·人工智能·spring
云烟成雨TD19 小时前
Spring AI 1.x 系列【38】模型上下文协议(MCP)
java·人工智能·spring