Java生成id方式

雪花算法生成id

启动类
(关于后端id为Long类型造成的精度丢失问题,前端用bigNumber的类型,可以处理长整数,也可避免此问题)

/**
	 * 雪花算法.
	 *
	 * @param dataSource
	 * @param environment
	 * @return
	 */
	@Bean
	public TwitterSnowflakeIdGenerator twitterSnowflakeIdGenerator(DataSource dataSource, Environment environment) {
		return TwitterSnowflakeIdGenerator.createInstance(dataSource, environment.getProperty("spring.application.name"));
	}

	/**
	 * 转化long类型为String,处理前后端精度问题.
	 * @return
	 */
	@Bean("jackson2ObjectMapperBuilderCustomizer")
	public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
		return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
				.serializerByType(Long.TYPE, ToStringSerializer.instance);
	}

配置类

package com.mn.common.utils;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 基于Twitter/snowflake 算法的分布式全局id生成器 64位ID (42(毫秒)+10位(统一分配的业务号workerIdBits)+12(重复累加))
 * 
 * 整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
 * 
 * @author xuwei
 * @date 2021年9月27日
 * @version 1.0
 */
public final class TwitterSnowflakeIdGenerator {

    private final static long twepoch = 1288834974657L;
    // 机器标识位数
    private final static long workerIdBits = 10L;
    // 机器ID最大值
    private final static long maxWorkerId = ~(-1L << workerIdBits);

    // 毫秒内自增位
    private final static long sequenceBits = 12L;
    // 机器ID偏左移12位
    private final static long workerIdShift = sequenceBits;

    // 时间毫秒左移22位
    private final static long timestampLeftShift = sequenceBits + workerIdBits;

    private final static long sequenceMask = ~(-1L << sequenceBits);

    private long lastTimestamp = -1L;

    private long sequence = 0L;
    private final long workerId; // 此workId通过集中产生

    private static TwitterSnowflakeIdGenerator generator = null;

    /**
     * @param ds 包含sys_set的数据源
     * @param webDisplayName,web的上下文路径,即web.xml里面的displayName
     * @return
     */
    public static TwitterSnowflakeIdGenerator createInstance(DataSource ds, String webDisplayName) {
        if (generator == null) {
            synchronized (TwitterSnowflakeIdGenerator.class) {
                if (generator == null) {
                    long wId = getWorkerId(ds, webDisplayName);
                    if (wId == -1L)
                        return null;
                    generator = new TwitterSnowflakeIdGenerator(wId);
                }
            }
        }
        return generator;
    }

    private static long getWorkerId(DataSource ds, String webDisplayName) {
        long wId = -1L;
        try {
            // 获取主机名

            String hostname = NetworkUtil.getFirstIp();
            /*
             * InitialContext initialContext = new InitialContext(); DataSource ds =
             * (DataSource)initialContext.lookup(dbJndiName);
             */
            JdbcTemplate t = new JdbcTemplate(ds);
            // 主机名+web.xml里面的displayName,这个目前是唯一不变的,由它来决定唯一号,保存在SYS_SET表中
            // 命名规则为 主机名_displayName_id
            String key = hostname + "_" + webDisplayName + "_id";
            List<Map<String, Object>> list = t.queryForList("select svalue from sys_set where skey = ?", key);
            if (list.isEmpty()) {// 数据库没有这个记录
                // 这两个地方需要事务,暂不加
                List<Map<String, Object>> list2 = t.queryForList(
                        "select svalue from sys_set where status = 1 and class = ? order by sort", "ID_GENERATOR");
                boolean found = false;
                for (int i = 0; i < list2.size(); i++) {
                    Map<String, Object> map = list2.get(i);
                    if (map != null) {
                        int value = Integer.parseInt(map.get("svalue").toString());
                        if (value == i + 1) {
                            continue;
                        }
                        if (i < value) {// 找到一个小值,占用这个值
                            wId = i + 1;
                            t.update(
                                    "insert into sys_set(skey,sname,svalue,sort,status,class,remark,time_update) "
                                            + "values(?,'id_generator',?,?,1,'ID_GENERATOR','',current_timestamp)",
                                    key, wId, wId);// 插入数据库中
                            found = true;
                            break;
                        }
                    }
                }
                if (!found) {
                    wId = list2.size() + 1;
                    t.update(
                            "insert into sys_set(skey,sname,svalue,sort,status,class,remark,time_update) "
                                    + "values(?,'id_generator',?,?,1,'ID_GENERATOR','',current_timestamp)",
                            key, wId, wId);// 插入数据库中
                }
            } else {
                wId = Long.parseLong(list.get(0).get("svalue").toString());
            }
        } catch (Exception e) {
            // Auto-generated catch block
            e.printStackTrace();
        }

        return wId;
    }

    /**
     * @param workerId - 机器ID,范围[0,1024]
     */
    private TwitterSnowflakeIdGenerator(long workerId) {
        Assert.isTrue(workerId <= maxWorkerId && workerId >= 0, "worker Id can't be greater than %d or less than 0");
        this.workerId = workerId;
    }

    public synchronized Long nextId() {
        return getNextId();
    }

    public List<Long> nextIds(int batchSize) {
        synchronized (TwitterSnowflakeIdGenerator.class) {
            Assert.isTrue(batchSize > 1, "worker Id can't be less than 1");
            List<Long> list = new ArrayList<>((int) (batchSize * 1.2));
            for (int i = 0; i < batchSize; i++) {
                list.add(getNextId());
            }
            return list;
        }
    }

    private long getNextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            try {
                throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp)
                        + " milliseconds");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (lastTimestamp == timestamp) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        return ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

}
package com.mn.common.utils;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

public class NetworkUtil {
    
    private NetworkUtil() {
        
    }
    
    public static boolean internalIp(byte[] addr) {
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                if(b1 == SECTION_6) {
                    return true;
                }
//                switch (b1) {
//                    case SECTION_6:
//                        return true;
//                    default:
//                        break;
//                }
            default:
                return false;
        }
    }

    // 获取第一个内网地址
    public static String getFirstIp() {
        String ipAddress = "";
        try {
            Enumeration<NetworkInterface> interfaces;
            interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface ni = interfaces.nextElement();
                Enumeration<InetAddress> addresss = ni.getInetAddresses();
                while (addresss.hasMoreElements()) {
                    InetAddress nextElement = addresss.nextElement();
                    nextElement.isLoopbackAddress();
                    String hostAddress = nextElement.getHostAddress();
                    if (nextElement instanceof Inet4Address) { // 只关心 IPv4 地址
                        boolean isIn = internalIp(nextElement.getAddress());
                        if (isIn) {// 是内网地址
                            return hostAddress;
                        }
                        System.out.println("网卡接口地址:" + nextElement.getHostAddress());
                        System.out.println("网卡接口地址:" + isIn);
                        // 判断能否到达数据库,如果能到达,选这个
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        return ipAddress;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值