前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MyChat 采用雪花算法生成 ID

MyChat 采用雪花算法生成 ID

作者头像
星尘的一个朋友
发布2021-03-02 14:37:32
7340
发布2021-03-02 14:37:32
举报

MyChat 群组 ID 的生成

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

在这里插入图片描述
在这里插入图片描述

算法实现

代码语言:javascript
复制
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;
    }
}

测试

代码语言:javascript
复制
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);
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-02-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MyChat 群组 ID 的生成
    • 算法实现
      • 测试
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档