im即时通讯消息id的设计

im即时通讯消息id的设计

消息id的特性

在im通讯中,消息的id在项目的功能实现中具有非常重要的作用。例如:消息接受顺序的保证,客户端消息重传时的去重,离线消息拉取的定位,以及消息幂等操作的判断等。因此消息id设计中,需要具备 递增,唯一

消息id生成策略

对于递增、唯一有许多的设计策略,例如有:

  • 主键自增:数据插入后不能获取消息id,需要再次查询获得插入的消息id

  • 时间戳+UUID:长度太长,索引性能差。分布式下对时钟一致要求

  • 雪花算法:使得高并发。但长度过长,可能出现丢失精度

  • 使用redis自增

良好的消息id可使项目功能实现可为简洁,对此我参考的IM融云的消息id设计并对此进行的实现。

融云消息id的设计

融云消息id为16字符的字符串,字串中每个字符由5bit的数据编码而来,即消息id是由80bit的数据转换而来:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X4VVp4qh-1660895848977)(C:\Users\86139\AppData\Roaming\Typora\typora-user-images\image-20220819121831978.png)]

  • 时间戳:最高位的时间戳保证了id的唯一性。相比于雪花算法41位时间戳,42位的时间戳延长使用到2019年保证了使用。
  • 自旋ID:在高并发场景下,同一时间戳可能有多个消息id生成。自旋id对这一时刻的消息进行自增编号,避免id的重复。12bit最多可表示4096条消息
  • 会话类型:即表示会话类型:单聊、群聊、客户、聊天室等
  • 会话id:如群聊id,聊天室id等
消息id的实现

这里使用Java进行实现,由于Java中64bit的long并不能存储80bit的数据,所以要对80bit的数据进行分段:

  1. 64bit : 时间戳 42bit | 自旋ID 12bit | 会话类型4bit | 会话ID高位6bit
  2. 16bit:会话ID低位
前64bit获取
    /**
     * @param type 会话类型
     * @param highCid 会话id高位6bit
     * @return 高位64bit数据
     */
    private static long createHighBits(int type, int highCid) {
        long highBits = System.currentTimeMillis(); // 获取时间戳
        int spinId = createSpinId(); 				// 获取自旋id
        // 依次移位
        highBits = highBits<<12 | spinId; 			
        highBits = highBits<<4 | (type&0xF); 		
        highBits = highBits<<6 | (highCid&0x3F);

        return highBits;
    }
后16bit获取
long lowCid = (int) (cid&0xFFFF);
自旋ID获取

并发条件下,自旋ID获取要具有唯一性。这里采用AtomicInteger的cas机制获取

    /**
     * 获取自旋id : 0 ~ 0xFFF(4095)
     */
    private final static int SPIN_ID_LIMIT = 0xFFF;	// 最高值限制
    private final static AtomicInteger INCR = new AtomicInteger(0); // 递增值
    private static int createSpinId() {
        int ret = INCR.getAndIncrement();
        if(INCR.get() >= SPIN_ID_LIMIT) {
            INCR.set(0);
        }
        return ret;
    }
编码

对两段数据进行32位编码(5bit > 1char

    //  编码转为字符串:5 bit > 1 char
    private final static char[] CHS = {'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};

    private static String createStrId(long highBit, long lowCid) {
        char[] arr = new char[16];
        int pos = 15;
        while(pos >= 0) {
            int tmp = 0;
            if(pos>=13) {
                tmp = (int) (lowCid&0x1F);
                lowCid>>=5;
            } else if(pos == 12) {
                tmp = (int) ((highBit&0xF)<<1 | lowCid);
                highBit >>= 5;
            } else {
                tmp = (int) (highBit&0x1F);
                highBit >>= 5;
            }
            arr[pos--] = CHS[tmp];
        }
        return new String(arr);
    }
测试

多个线程去获取消息id。判断是否具有递增,唯一,以及同一时刻获取的id是否唯一?

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int i=0; i<10; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for(int i=0; i<10; i++)
                        log.debug("生成id : {}", createId(0xF,0x3FFFFF));
                }
            });
        }
    }

测试结果

16:14:46.557 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4i22zzzzz
16:14:46.558 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4n29zzzzz
16:14:46.562 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6a2czzzzz
16:14:46.562 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6a2dzzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2ezzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2fzzzzz
16:14:46.558 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4n28zzzzz
16:14:46.558 [pool-1-thread-2] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4i23zzzzz
16:14:46.563 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2gzzzzz
16:14:46.558 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4n26zzzzz
16:14:46.557 [pool-1-thread-8] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz522bzzzzz
16:14:46.558 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4n2azzzzz
16:14:46.557 [pool-1-thread-6] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4n27zzzzz
16:14:46.557 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4i24zzzzz
16:14:46.557 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz4i25zzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2izzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2hzzzzz
16:14:46.563 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2szzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2tzzzzz
16:14:46.563 [pool-1-thread-2] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2jzzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2uzzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2wzzzzz
16:14:46.563 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2vzzzzz
16:14:46.563 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2kzzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2yzzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2zzzzzz
16:14:46.563 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e32zzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e35zzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e34zzzzz
16:14:46.563 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e36zzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e37zzzzz
16:14:46.563 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2mzzzzz
16:14:46.563 [pool-1-thread-8] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2nzzzzz
16:14:46.563 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e39zzzzz
16:14:46.563 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2pzzzzz
16:14:46.563 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3bzzzzz
16:14:46.563 [pool-1-thread-8] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3czzzzz
16:14:46.563 [pool-1-thread-6] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2qzzzzz
16:14:46.563 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3ezzzzz
16:14:46.563 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3fzzzzz
16:14:46.563 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2rzzzzz
16:14:46.563 [pool-1-thread-6] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3hzzzzz
16:14:46.563 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3izzzzz
16:14:46.563 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3jzzzzz
16:14:46.563 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3kzzzzz
16:14:46.563 [pool-1-thread-2] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e2xzzzzz
16:14:46.563 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3nzzzzz
16:14:46.563 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e33zzzzz
16:14:46.563 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3qzzzzz
16:14:46.563 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3szzzzz
16:14:46.563 [pool-1-thread-9] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e38zzzzz
16:14:46.563 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3tzzzzz
16:14:46.563 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3uzzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3azzzzz
16:14:46.563 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3wzzzzz
16:14:46.563 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3xzzzzz
16:14:46.563 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3yzzzzz
16:14:46.563 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3zzzzzz
16:14:46.563 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e42zzzzz
16:14:46.563 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3dzzzzz
16:14:46.563 [pool-1-thread-8] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3gzzzzz
16:14:46.564 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i44zzzzz
16:14:46.563 [pool-1-thread-6] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3mzzzzz
16:14:46.563 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3pzzzzz
16:14:46.563 [pool-1-thread-2] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3rzzzzz
16:14:46.563 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e3vzzzzz
16:14:46.564 [pool-1-thread-5] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i4azzzzz
16:14:46.564 [pool-1-thread-7] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6e43zzzzz
16:14:46.564 [pool-1-thread-10] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i4czzzzz
16:14:46.564 [pool-1-thread-1] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i45zzzzz
16:14:46.564 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i46zzzzz
16:14:46.564 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i48zzzzz
16:14:46.564 [pool-1-thread-8] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i47zzzzz
16:14:46.564 [pool-1-thread-4] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i4gzzzzz
16:14:46.564 [pool-1-thread-6] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i49zzzzz
16:14:46.564 [pool-1-thread-3] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i4hzzzzz
16:14:46.564 [pool-1-thread-6] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i4kzzzzz
16:14:46.564 [pool-1-thread-2] INFO com.wcw.lowchat.util.MyIdGenerator - 生成id : 83dcctz6i4bzzzzz
...
参考文章

http://www.52im.net/thread-1998-1-1.html

http://www.52im.net/thread-2747-1-1.html

https://www.cnblogs.com/jiangxinlingdu/p/8440413.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值