基于eureka和snowflake生成唯一id

基于在eureka上注册的信息,instanceId在eureka上是唯一的,是有ip+port组成的。

    @Value("${spring.cloud.client.ipAddress}")
    private String clientAddress;

    @Value("${server.port}")
    private String serverPort;

我们创建了一个表来记录这些信息

CREATE TABLE `t_medical_instance` (
  `id` int(4) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `instance_id` varchar(100) NOT NULL COMMENT '服务标示',
  `del_flag` tinyint(1) DEFAULT NULL COMMENT '删除标示',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `service_id_UNIQUE` (`instance_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='节点信息保存';

每次读取表信息

MedicalInstance medicalInstance = medicalInstanceService.selectMedicalInstanceByInstance(instanceId);
            if (medicalInstance == null) {
                medicalInstance = new MedicalInstance();
                medicalInstance.setInstanceId(instanceId);
                medicalInstanceService.addData(medicalInstance);
            }
            if (medicalInstance.getId() > maxWorkerId) {
                logger.info("workId不能大于最大机器码");
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            this.workerId = medicalInstance.getId();
            medicalSnowFlake = this;
        } catch (BizException e) {
            logger.error("获取id失败,展示不能生成id",e);
            throw new IllegalArgumentException("id生成初始化失败");
        }

完整代码如下

package com.pingan.medical.util;

import com.pingan.jkx.bj.base.exception.BizException;
import com.pingan.medical.entity.MedicalInstance;
import com.pingan.medical.service.IMedicalInstanceBiz;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 *@Author GUOSHAOHUA093
 *@Description snowFlakeid生成类
 *@Date 14:15 2019/1/11
 */
@Component
public class MedicalSnowFlake {

    static Logger logger = LoggerFactory.getLogger(MedicalSnowFlake.class);

    //其实时间戳   2017-01-01 00:00:00
    private final static long twepoch = 1288834974657L;

    //10bit(位)的工作机器id  中IP标识所占的位数 5bit(位)
    private final static long workerIdBits = 5L;

    //wordId标识最大值 31  即2的5次方减一。
    private final static long maxWorkerId = ~(-1L << workerIdBits);

    //10bit(位)的工作机器id  中数字标识id所占的位数 5bit(位)
    private final static long dataCenterIdBits = 5L;

    //数字标识id最大值 31  即2的5次方减一。
    private final static long maxDatacenterId = ~(-1L << dataCenterIdBits);

    //序列在id中占的位数 12bit
    private final static long sequenceBits = 12L;

    //序列最大值 4095 即2的12次方减一。
    private final static long sequenceMax = ~(-1L << sequenceBits);

    // 64位的数字:首位0  随后41位表示时间戳 随后10位工作机器id(5位IP标识 + 5位数字标识) 最后12位序列号
    private final static long workerIdShift = sequenceBits;
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    private final static long timeLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;


    //机器码
    private long workerId;

    // 数据中心ID(0~3)
    private long datacenterId = 0;

    // 毫秒内序列(0~4095)
    private long sequence = 0L;

    // 上次生成ID的时间截
    private long lastTime = -1L;

    private static MedicalSnowFlake medicalSnowFlake;

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${spring.cloud.client.ipAddress}")
    private String clientAddress;

    @Value("${server.port}")
    private String serverPort;

    @Autowired
    @Qualifier("medicalInstanceService")
    private IMedicalInstanceBiz medicalInstanceService;

    @PostConstruct
    public void init() {
        try {
            //用instanceId来指定workerId
            String instanceId = clientAddress + ":" + serverPort;
            MedicalInstance medicalInstance = medicalInstanceService.selectMedicalInstanceByInstance(instanceId);
            if (medicalInstance == null) {
                medicalInstance = new MedicalInstance();
                medicalInstance.setInstanceId(instanceId);
                medicalInstanceService.addData(medicalInstance);
            }
            if (medicalInstance.getId() > maxWorkerId) {
                logger.info("workId不能大于最大机器码");
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            this.workerId = medicalInstance.getId();
            medicalSnowFlake = this;
        } catch (BizException e) {
            logger.error("获取id失败,展示不能生成id",e);
            throw new IllegalArgumentException("id生成初始化失败");
        }
    }

    public static MedicalSnowFlake getInstance() {
        return medicalSnowFlake;
    }

    public synchronized long nextId() {
        //获取当前时间
        long nowTime = currentTimeMillis();
        //如果当前时间小于最后一次时间
        if (nowTime < lastTime) {
            logger.info("当前时间前于上次操作时间,当前时间有误: " + nowTime);
            throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTime - nowTime));
        }
        if (nowTime == lastTime) {
            sequence = (sequence + 1) & sequenceMax;
            if (sequence == 0) {
                nowTime = getNextTimeStamp();
            }
        } else {
            sequence = 0L;
        }

        lastTime = nowTime;

        return ((nowTime - twepoch) << timeLeftShift)
                | (workerId << workerIdShift)
                | (datacenterId << datacenterIdShift)
                | sequence;
    }

    private long getNextTimeStamp() {
        long nowTime;
        do {
            nowTime = System.currentTimeMillis();
        } while (nowTime <= lastTime);
        return nowTime;
    }

    protected long currentTimeMillis() {
        return System.currentTimeMillis();
    }
}

 

你可能感兴趣的:(实用案例)