概述 上面我们介绍了分布式自增ID算法snowflake ,其中需要 工作ID
和数据中心ID
,在微服务中可能有很多系统,需要对每个系统都配置这两个id,导致我们需要管理工作ID
和数据中心ID
,使用很不方便。
假如系统可以自动去获取分配,那么可以减少配置的工作量,避免配置错误的情况。
简介 本文基于分布式自增ID算法snowflake
和ZooKeeper
注册中心实现自增id获取。 需要安装ZooKeeper
,不会安装的同学可以参考ZooKeeper 安装
java实现
依赖ZooKeeper
注册中心,需要引入ZooKeeper
客户端工具,我使用的是zkclient
maven引入 1 2 3 4 5 <dependency > <groupId > com.101tec</groupId > <artifactId > zkclient</artifactId > <version > 0.10</version > </dependency >
创建Snowflake算法生成唯一id实现类
id自动生成实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 package com.fallsea.id.snowflake;public class SnowflakeIdWorker { private final long twepoch = 1420041600000L ; private final long workerIdBits = 5L ; private final long datacenterIdBits = 5L ; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final long sequenceBits = 12L ; private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); private long workerId; private long datacenterId; private long sequence = 0L ; private long lastTimestamp = -1L ; 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; } public synchronized long nextId () { long timestamp = timeGen(); 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 ; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } protected long tilNextMillis (long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen () { return System.currentTimeMillis(); } }
创建id生成工具类
通过zkclient
连接ZooKeeper
注册中心,注册中心维护snowflake
的工作id(0-1023),达到0配置的分布式id自动生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 package com.fallsea.id.snowflake;import java.util.HashSet;import java.util.List;import java.util.Random;import java.util.Set;import org.I0Itec.zkclient.ZkClient;public class IdUtil { private static volatile ZkClient zkClient; public static final String ROOT_PATH = "/fallsea" ; public static final String SNOWFLAKE_ID_PATH = ROOT_PATH + "/SnowflakeId" ; private static volatile Set<Long> WORKER_ID_SET; private static volatile SnowflakeIdWorker idWorker = null ; private static volatile long WORKER_ID ; static { zkClient = new ZkClient ("127.0.0.1:2181" ,3000 ,5000 ); WORKER_ID_SET = new HashSet <Long>(); for (long i = 0 ; i < 1024 ; i++) { WORKER_ID_SET.add(i); } if (!zkClient.exists(SNOWFLAKE_ID_PATH)) { zkClient.createPersistent(SNOWFLAKE_ID_PATH); } WORKER_ID = getWorkerId(); long workerId = WORKER_ID/32 ; long datacenterId = WORKER_ID%32 ; idWorker = new SnowflakeIdWorker (workerId, datacenterId); } public static long getId () { return idWorker.nextId(); } private static Long getWorkerId () { List<String> list = zkClient.getChildren(SNOWFLAKE_ID_PATH); if (null != list && !list.isEmpty()) { if (list.size()==1024 ) { return -1L ; } for (String str : list) { WORKER_ID_SET.remove(Long.valueOf(str)); } } long workerId=-1L ; if (!WORKER_ID_SET.isEmpty()) { Long[] workerIds=WORKER_ID_SET.toArray(new Long []{}); workerId=workerIds[new Random ().nextInt(WORKER_ID_SET.size())]; if (!zkClient.exists(SNOWFLAKE_ID_PATH+"/" +workerId)) { zkClient.createEphemeral(SNOWFLAKE_ID_PATH+"/" +workerId); } else { return getWorkerId(); } } return workerId; } }
测试类
运行测试类,查看id生成结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.fallsea.id.snowflake;import junit.framework.Test;import junit.framework.TestCase;import junit.framework.TestSuite;public class AppTest extends TestCase { public AppTest ( String testName ) { super ( testName ); } public static Test suite () { return new TestSuite ( AppTest.class ); } public void testApp () { long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000 ; i++) { long id = IdUtil.getId(); System.out.println(endTime - startTime ); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime ); assertTrue( true ); } }
源码下载 github下载 码云下载