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生成需求,是构建高性能分布式系统的关键基础设施。

相关推荐
a crazy day5 小时前
Spring相关知识点【详细版】
java·spring·rpc
foundbug9995 小时前
配置Spring框架以连接SQL Server数据库
java·数据库·spring
-大头.5 小时前
JVM框架实战指南:Spring到微服务
jvm·spring·微服务
饕餮争锋6 小时前
Spring事件_发布&监听(2)_笔记
java·笔记·spring
wa的一声哭了7 小时前
并行计算 PCAM方法学
linux·运维·服务器·arm开发·python·spring·django
想不明白的过度思考者7 小时前
Spring Boot/Spring MVC核心注解深度解析
spring boot·spring·mvc
后端小张10 小时前
【JAVA 进阶】深入探索Spring AOP:从原理到实战
java·spring boot·后端·spring·spring cloud·aop·切面
q***380116 小时前
maven导入spring框架
数据库·spring·maven
RemainderTime18 小时前
基于Ollama和Spring AI:本地大模型对话与 RAG 功能实现
人工智能·spring·ai·postgresql·语言模型