UUID雪花算法,自动生成ID方法

UUID雪花算法,自动生成ID方法
//创建包名uuid,包下3个实体类
//IdWorker

/******************************************************************************
* Copyright (C) 2018-2022 Yantai HaiYi Software Co., Ltd
* All Rights Reserved.
* Developed by Yantai Haiyi Software . Without the prior written consent of the company hereto,
* No party may use, copy, modify or release this software.
*****************************************************************************/
package com.ruoyi.system.uuid;

import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.servlet.http.HttpServletRequest;


import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;





/**
* twitter的SnowFlake算法获取18位的数字id
*
* @author liulei
*
*<p>Modification History:</p>
*<p>Date Author Description</p>
*<p>------------------------------------------------------------------</p>
*<p>2018年8月23日 liulei 新建</p>
*/
public class IdWorker {

private static Long p1;
private static Long p2;
private static SnowflakeIdWorker snowflakeIdWorker;
private IdWorker(){}

/**
* 获取一个id
* @return
* @author liulei - 2018年8月23日 :
*/
public static String getId() {
if(snowflakeIdWorker == null) {
if( p1 == null || p2 == null ){
getP1andP2();
}
snowflakeIdWorker = new SnowflakeIdWorker(p1, p2);
}
return snowflakeIdWorker.nextId();
}

private static void getP1andP2(){
try{
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = null;
String serverPort = ""; // 取得本地端口
String serverIp = "";// 获取IP
if (servletRequestAttributes != null) {
// 获取端口号
request = servletRequestAttributes.getRequest();
serverPort = String.valueOf(request.getLocalPort()); // 取得本地端口
}
try {
// 获取IP
serverIp = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
IdWorkerBean bean = new IdWorkerBean();
if( !CollectionUtils.isEmpty(bean.getIdWorker()) && StringUtils.isNotBlank(serverPort) && StringUtils.isNotBlank(serverIp) ){
for (String ip : bean.getIdWorker()) {
String[] split = ip.split(",");
if( split[0].equals(serverIp) && split[1].equals(serverPort) ){
p1 = Long.valueOf(split[2]);
p2 = Long.valueOf(split[3]);
System.out.println("P1:"+p1+";P2:"+p2);
}
}
}
// 供现场调试使用
System.out.println("serverIp:" + serverIp + "---------serverPort:" + serverPort);
}
catch(Exception e){
e.printStackTrace();
}
finally{
if( p1 == null || p2 == null ){
p1 = 0L;
p2 = 0L;
}
}
// 供现场调试使用
System.out.println("p1:" + p1 + "-------------p2:" + p2);
}

/**
* 获取一定数量的id
* @param num
* @return
* @author liulei - 2018年8月23日 :
*/
public static String[] getIds(int num) {
if(snowflakeIdWorker == null) {
if( p1 == null || p2 == null ){
getP1andP2();
}
snowflakeIdWorker = new SnowflakeIdWorker(p1, p2);
}
String[] ids = new String[num];
for (int i = 0; i < num; i++) {
ids[i] = snowflakeIdWorker.nextId();
}
return ids;
}

/**
* 返回String类型的id
* @return
* @author gongd - 2020年1月7日 :
*/
public static String getIdStr() {
return getId() + "";
}

/**
* 返回String[]类型的id集合
* @param num
* @return
* @author gongd - 2020年1月7日 :
*/
public static String[] getIdStrs(int num) {
String[] idLongs = getIds(num);
String[] ids = new String[num];
for (int i = 0; i < num; i++) {
ids[i] = idLongs[i] + "";
}
return ids;
}

}

//IdWorkerBean

package com.ruoyi.system.uuid;

import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


/**
* 实现了....
*
* @author Administrator
*
*<p>Modification History:</p>
*<p>Date Author Description</p>
*<p>------------------------------------------------------------------</p>
*<p>2020年5月12日 Administrator 新建</p>
*/
public class IdWorkerBean {

private List<String> idWorker;

public List<String> getIdWorker() {
return idWorker;
}

public void setIdWorker(List<String> idWorker) {
this.idWorker = idWorker;
}

/**
* Get the node name.
* @param ip
* @param port
* @return
*/
public String getNodeName(String ip, String port) {
try {
if (CollectionUtils.isEmpty(idWorker)) {
return "";
}

if (StringUtils.isEmpty(ip) || StringUtils.isEmpty(port)) {
return "";
}

for (int i = 0; i < idWorker.size(); i++) {
String[] arr = idWorker.get(i).split(",");
if (arr.length != 5) {
continue;
}
String ipa = arr[0];
String porta = arr[1];

if (ip.equals(ipa) && port.equals(porta)) {
return arr[4];
}
}
} catch (Exception e) {
e.printStackTrace();
}

return "";

}

}

//SnowflakeIdWorker
package com.ruoyi.system.uuid;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
public class SnowflakeIdWorker {

   // ==============================Fields===========================================
   /** 开始时间截 (2018-01-01) */
   private final long twepoch = 1514736000000L;

   /** 机器id所占的位数 */
   private final long workerIdBits = 3L;

   /** 数据标识id所占的位数 */
   private final long datacenterIdBits = 3L;

   /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
   private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

   /** 支持的最大数据标识id,结果是31 */
   private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

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

   /** 机器ID向左移12位 */
   private final long workerIdShift = sequenceBits;

   /** 数据标识id向左移17位(12+5) */
   private final long datacenterIdShift = sequenceBits + workerIdBits;

   /** 时间截向左移22位(5+5+12) */
   private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

   /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
   private final long sequenceMask = -1L ^ (-1L << sequenceBits);

   /** 工作机器ID(0~31) */
   private long workerId;

   /** 数据中心ID(0~31) */
   private long datacenterId;

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

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

   //==============================Constructors=====================================
   /**
    * 构造函数
    * @param workerId 工作ID (0~31)
    * @param datacenterId 数据中心ID (0~31)
    */
   public SnowflakeIdWorker(long workerId, long datacenterId) {
       if (workerId > maxWorkerId || workerId < 0) {
           throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
      }
       if (datacenterId > maxDatacenterId || datacenterId < 0) {
           throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
      }
       this.workerId = workerId;
       this.datacenterId = datacenterId;
  }

   // ==============================Methods==========================================
   /**
    * 获得下一个ID (该方法是线程安全的)
    * @return SnowflakeId
    */
   public synchronized String nextId() {
       long timestamp = timeGen();

       //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
       if (timestamp < lastTimestamp) {
           throw new RuntimeException(
                   String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
      }

       //如果是同一时间生成的,则进行毫秒内序列
       if (lastTimestamp == timestamp) {
           sequence = (sequence + 1) & sequenceMask;
           //毫秒内序列溢出
           if (sequence == 0) {
               //阻塞到下一个毫秒,获得新的时间戳
               timestamp = tilNextMillis(lastTimestamp);
          }
      }
       //时间戳改变,毫秒内序列重置
       else {
           sequence = 0L;
      }

       //上次生成ID的时间截
       lastTimestamp = timestamp;

       //移位并通过或运算拼到一起组成64位的ID
       long id = ((timestamp - twepoch) << timestampLeftShift) //
               | (datacenterId << datacenterIdShift) //
               | (workerId << workerIdShift) //
               | sequence;
       return "6"+String.format("%18s", String.valueOf(id)).replace(" ", "0");
  }

   /**
    * 阻塞到下一个毫秒,直到获得新的时间戳
    * @param lastTimestamp 上次生成ID的时间截
    * @return 当前时间戳
    */
   protected long tilNextMillis(long lastTimestamp) {
       long timestamp = timeGen();
       while (timestamp <= lastTimestamp) {
           timestamp = timeGen();
      }
       return timestamp;
  }

   /**
    * 返回以毫秒为单位的当前时间
    * @return 当前时间(毫秒)
    */
   protected long timeGen() {
       return System.currentTimeMillis();
  }

   //==============================Test=============================================
   /** 测试 */
   public static void main(String[] args) {
       SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);
       Long startTime = System.currentTimeMillis();
       for (int i = 0; i < 1000; i++) {
           String id = idWorker.nextId();
           System.out.println(id);
      }
       Long endTime = System.currentTimeMillis();
       startTime = System.currentTimeMillis();
       String id0 = idWorker.nextId();
       endTime = System.currentTimeMillis();
  }
   
}
测试用例,写入id
@Override
   public int insertMdItemPanel(MdItemPanel mdItemPanel)
  {   //测试使用
       mdItemPanel.setPanelId(IdWorker.getIdStr());//为id传入雪花uuid
       
       return mdItemPanelMapper.insertMdItemPanel(mdItemPanel);
  }
 
posted @ 2022-11-29 11:05  爱豆技术部  阅读(348)  评论(0)    收藏  举报