复制代码

为懒人提供无限可能,生命不息,code不止

人类感性的情绪,让我们知难行难
我思故我在
日拱一卒,功不唐捐
  • 首页
  • 前端
  • 后台
  • 数据库
  • 运维
  • 资源下载
  • 实用工具
  • 接口文档工具
  • 企业管理平台Demo
  • 登录
  • 注册

ERP

【原创】mybatis-flex或mybatis-plus雪花算法datacenterId和workid获取思考

作者: whooyun发表于: 2025-10-29 18:14

核心思想:

在数据库中新增一个表,用来存储dataCenterId和workId,dataCenterId和workId需要在web应用启动时就进行生成,并插入到数据库表中,并通过定时任务自动包活

表设计:

CREATE TABLE worker_node (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    host_name VARCHAR(100) NOT NULL,      -- 主机名(可选)
    port INT NOT NULL,                    -- 端口(可选)
    ip VARCHAR(50) NOT NULL,              -- IP 地址(可选)
    status TINYINT DEFAULT 1,             -- 1=活跃, 0=过期
    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    modified_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    -- 关键:唯一索引确保 (datacenter_id, worker_id) 全局唯一
    UNIQUE KEY uk_datacenter_worker (datacenter_id, worker_id),
    
    datacenter_id INT NOT NULL DEFAULT 1,
    worker_id INT NOT NULL                -- 实际分配的 workerId (0~31)
);
mybatis-flex的实现

@Component
public class SnowflakeWorkerIdInitializer {

    @Autowired
    private WorkerNodeMapper workerNodeMapper; // MyBatis-Flex Mapper

    @PostConstruct
    public void initSnowflakeGenerator() {
        // 1. 生成当前实例唯一标识(如 UUID + 时间戳)
        String instanceId = UUID.randomUUID().toString();
        String ip = getLocalIp();
        String hostname = getHostname();
        int port = 8080; // 或从配置读取

        // 2. 尝试插入新记录,由数据库分配 workerId
        int maxRetries = 32; // workerId 范围 0~31
        for (int i = 0; i < maxRetries; i++) {
            try {
                // 插入时尝试分配 workerId = i
                WorkerNode node = new WorkerNode();
                node.setHostName(hostname);
                node.setPort(port);
                node.setIp(ip);
                node.setDatacenterId(1);
                node.setWorkerId(i);

                workerNodeMapper.insert(node); // 成功插入 = 分配成功
                
                // 3. 设置全局雪花生成器
                SnowflakeKeyConfig config = new SnowflakeKeyConfig();
                config.setDatacenterId(1L);
                config.setWorkerId((long) i);
                FlexGlobalConfiguration.getDefaultConfig()
                    .setSnowflakeKeyGenerator(new FlexSnowflakeKeyGenerator(config));
                
                log.info("成功分配 workerId: {}, instance: {}", i, instanceId);
                return;
                
            } catch (Exception e) {
                if (e.getMessage().contains("Duplicate entry") || 
                    e instanceof DuplicateKeyException) {
                    // workerId=i 已被占用,继续尝试 i+1
                    continue;
                } else {
                    throw new RuntimeException("注册 workerId 失败", e);
                }
            }
        }
        
        throw new IllegalStateException("无法分配可用的 workerId(0~31 已耗尽)");
    }

    private String getLocalIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            return "127.0.0.1";
        }
    }

    private String getHostname() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (Exception e) {
            return "unknown";
        }
    }
}