MyChat 采用雪花算法生成 ID

MyChat 群组 ID 的生成

每个群组在创建时由后端分配 ID, 这个 ID 考虑到后面的集群情况, 所以需要做到全局唯一, 所以这里使用了雪花算法来实现

在这里插入图片描述

算法实现

package org.lvgo.mychat.server.util;

import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Random;

/**
 * MyChatID
 * <p>
 * MyChat 通用 ID 生成工具
 *
 * <p>
 * 欢迎跟我一起学习,微信(lvgocc)公众号:星尘的一个朋友
 *
 * @author lvgorice@gmail.com
 * @version 1.0
 * @blog @see http://lvgo.org
 * @CSDN @see https://blog.csdn.net/sinat_34344123
 * @date 2021/2/7
 */
public class MyChatID {


    /*↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=雪花算法分段各部分长度信息↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=*/
    /**
     * 时间部分所占长度
     */
    private static final int TIMESTAMP_LEN = 41;
    /**
     * 机器网络标识长度
     */
    private static final int MAC_NET_LEN = 5;
    private static final int MAC_NET_MAX = ~(-1 << MAC_NET_LEN); // 31
    /**
     * 机器名称标识长度
     */
    private static final int MAC_NAME_LEN = 5;
    private static final int MAC_NAME_MAX = ~(-1 << MAC_NAME_LEN); // 31
    /**
     * 毫秒内序列所占长度
     */
    private static final int SEQ_LEN = 12;
    private static final int SEQ_MAX = ~(-1 << SEQ_LEN);

    /*↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=雪花算法分段各部分长度信息↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=*/

    /*↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=分段各部分位置调整↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=↓=*/

    private static final byte TIMESTAMP_OFFSET = Long.SIZE - 1 - TIMESTAMP_LEN;
    private static final byte MAC_NET_OFFSET = TIMESTAMP_OFFSET - MAC_NET_LEN;
    private static final byte MAC_NAME_OFFSET = MAC_NET_OFFSET - MAC_NAME_LEN;

    /*↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=分段各部分位置调整↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=↑=*/

    /**
     * 机器ip标识
     */
    private static final long MAC_NET_ID = getMachineNetId();
    /**
     * 机器名称标识
     */
    private static final long MAC_NAME_ID = getMachineNameId();
    /**
     * 一毫秒内的序列号
     */
    private static long sequence = 0;

    /**
     * 上次生成序列号时间(毫秒)
     */
    private static long lastGenTime = -1;

    private static long getMachineNetId() {
        int macNetValue = 0;
        try {
            byte[] macNet = Inet4Address.getLocalHost().getAddress();
            for (byte b : macNet) {
                macNetValue += b;
            }
            macNetValue = macNetValue & MAC_NET_MAX;
        } catch (UnknownHostException e) {
            macNetValue = new Random().nextInt(32);
        }
        return macNetValue;
    }

    private static long getMachineNameId() {
        int macNameValue = 0;
        try {
            byte[] macName = Inet4Address.getLocalHost().getHostName().getBytes();
            for (byte b : macName) {
                macNameValue += b;
            }
            macNameValue = macNameValue & MAC_NAME_MAX;
        } catch (UnknownHostException e) {
            macNameValue = new Random().nextInt(32);
        }
        return macNameValue;
    }

    public synchronized static long nextId() {
        long now = System.currentTimeMillis();
        // 如果小于上次生成时间, 可以优化为重新生成, 不抛出异常
        if (now < lastGenTime) {
            throw new IllegalStateException("ERROR TIME");
        }

        if (now == lastGenTime) {
            sequence = ++sequence & SEQ_MAX;
            if (sequence == 0) {
                now = System.currentTimeMillis();
                while (now <= lastGenTime) {
                    now = System.currentTimeMillis();
                }
            }
        } else {
            sequence = 0;
        }

        lastGenTime = now;

        return (now << TIMESTAMP_OFFSET) | (MAC_NET_ID << MAC_NET_OFFSET) | (MAC_NAME_ID << MAC_NAME_OFFSET) | sequence;
    }
}

测试

package org.lvgo.mychat.server.util;

import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

/**
 * MyChatIDTest
 * <p>
 * 欢迎跟我一起学习,微信(lvgocc)公众号:星尘的一个朋友
 *
 * @author lvgorice@gmail.com
 * @version 1.0
 * @blog @see http://lvgo.org
 * @CSDN @see https://blog.csdn.net/sinat_34344123
 * @date 2021/2/7
 */
class MyChatIDTest {

    @Test
    void nextId() throws InterruptedException {
        long start = System.currentTimeMillis();
        Set<Long> ids = Collections.synchronizedSet(new HashSet<>());
        CountDownLatch countDownLatch = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    long genId = MyChatID.nextId();
//                    System.out.printf("%64s%n",Long.toBinaryString(genId));
                    System.out.println(genId);
                    ids.add(genId);

                }
                countDownLatch.countDown();
            }).start();
        }
        countDownLatch.await();
        System.out.printf("生成id %d个, 耗时: %d秒%n", ids.size(), (System.currentTimeMillis() - start) / 1000);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值