目录
- 1、客户端
- 1.1.实例化client对象
- 1.1.1.TinyId
- 1.1.2.IdGeneratorFactoryClient
- 1.2.初始化ID信息
- 1.2.1.TinyId
- 1.2.2、AbstractIdGeneratorFactory
- 1.2.3、IdGeneratorFactoryClient
- 1.2.4、CachedIdGenerator
- 1.2.5、HttpSegmentIdServiceImpl
- 1.3.获取ID
- 1.3.1.TinyId
- 1.3.2、CachedIdGenerator
- 1.3.3、SegmentId
- 1.4.总结:
- 2、###服务端
- 2.1.IdContronller
- 2.2、DbSegmentIdServiceImpl
- 2.3、定时加载token信息
1、客户端
1.1.实例化client对象
核心类及代码如下:
1.1.1.TinyId
复制代码
1
2private static IdGeneratorFactoryClient client = IdGeneratorFactoryClient.getInstance(null);
1.1.2.IdGeneratorFactoryClient
复制代码
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
32public class IdGeneratorFactoryClient extends AbstractIdGeneratorFactory { //引用客户端项目中配置文件 private static final String DEFAULT_PROP = "tinyid_client.properties"; //请求服务端的url private static String serverUrl = "http://{0}/tinyid/id/nextSegmentIdSimple?token={1}&bizType="; //初始化信息 public static IdGeneratorFactoryClient getInstance(String location) { if (idGeneratorFactoryClient == null) { synchronized (IdGeneratorFactoryClient.class) { if (idGeneratorFactoryClient == null) { if (location == null || "".equals(location)) { init(DEFAULT_PROP); } else { init(location); } } } } return idGeneratorFactoryClient; } //封装数据 private static void init(String location) { idGeneratorFactoryClient = new IdGeneratorFactoryClient(); Properties properties = PropertiesLoader.loadProperties(location); String tinyIdToken = properties.getProperty("tinyid.token"); String tinyIdServer = properties.getProperty("tinyid.server"); String readTimeout = properties.getProperty("tinyid.readTimeout"); String connectTimeout = properties.getProperty("tinyid.connectTimeout"); } }
1.2.初始化ID信息
核心类及代码如下:
1.2.1.TinyId
复制代码
1
2
3
4
5
6
7
8
9
10
11
12public class TinyId { public static Long nextId(String bizType) { if (bizType == null) { throw new IllegalArgumentException("type is null"); } //最终目的就是初始化SegmentId对象,获取id相关信息 IdGenerator idGenerator = client.getIdGenerator(bizType); .... .... } }
1.2.2、AbstractIdGeneratorFactory
复制代码
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
30public abstract class AbstractIdGeneratorFactory implements IdGeneratorFactory { private static ConcurrentHashMap<String, IdGenerator> generators = new ConcurrentHashMap<>(); //优先查询缓存中是否存在Id信息 @Override public IdGenerator getIdGenerator(String bizType) { if (generators.containsKey(bizType)) { return generators.get(bizType); } synchronized (this) { if (generators.containsKey(bizType)) { return generators.get(bizType); } //不存在则发起Http请求获取 IdGenerator idGenerator = createIdGenerator(bizType); generators.put(bizType, idGenerator); return idGenerator; } } /** * 根据bizType创建id生成器 * * @param bizType * @return */ protected abstract IdGenerator createIdGenerator(String bizType); }
1.2.3、IdGeneratorFactoryClient
复制代码
1
2
3
4
5
6
7public class IdGeneratorFactoryClient extends AbstractIdGeneratorFactory { @Override protected IdGenerator createIdGenerator(String bizType) { return new CachedIdGenerator(bizType, new HttpSegmentIdServiceImpl()); } }
1.2.4、CachedIdGenerator
复制代码
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
36public class CachedIdGenerator implements IdGenerator { public CachedIdGenerator(String bizType, SegmentIdService segmentIdService) { this.bizType = bizType; this.segmentIdService = segmentIdService; loadCurrent(); } //初始化数据,在每次启动项目时都会进行 public synchronized void loadCurrent() { if (current == null || !current.useful()) { if (next == null) { //从服务端获取id信息 SegmentId segmentId = querySegmentId(); this.current = segmentId; } else { current = next; next = null; } } } private SegmentId querySegmentId() { String message = null; try { //调用HttpSegmentIdServiceImpl的getNextSegmentId方法,从服务端获取id信息 SegmentId segmentId = segmentIdService.getNextSegmentId(bizType); if (segmentId != null) { return segmentId; } } catch (Exception e) { message = e.getMessage(); } throw new TinyIdSysException("error query segmentId: " + message); } }
1.2.5、HttpSegmentIdServiceImpl
复制代码
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
33public class HttpSegmentIdServiceImpl implements SegmentIdService { private static final Logger logger = Logger.getLogger(HttpSegmentIdServiceImpl.class.getName()); @Override public SegmentId getNextSegmentId(String bizType) { String url = chooseService(bizType); /* post请求服务端,获取数据,服务端返回各个如下: "currentId,loadingid,maxId,delta,remainder",比如:"1,20,100,1,1" currentId:当前ID loadingid:当该值小于当前ID时会请求一次服务请求,更新Id信息,当前Id+步长*0.2 maxId:当前能够获取的最大Id delta:每次id增量 remainder:余数量 */ String response = TinyIdHttpUtils.post(url, TinyIdClientConfig.getInstance().getReadTimeout(), TinyIdClientConfig.getInstance().getConnectTimeout()); logger.info("tinyId client getNextSegmentId end, response:" + response); if (response == null || "".equals(response.trim())) { return null; } SegmentId segmentId = new SegmentId(); String[] arr = response.split(","); segmentId.setCurrentId(new AtomicLong(Long.parseLong(arr[0]))); segmentId.setLoadingId(Long.parseLong(arr[1])); segmentId.setMaxId(Long.parseLong(arr[2])); segmentId.setDelta(Integer.parseInt(arr[3])); segmentId.setRemainder(Integer.parseInt(arr[4])); return segmentId; } }
1.3.获取ID
1.3.1.TinyId
复制代码
1
2
3
4
5
6
7
8
9
10
11
12public class TinyId { public static Long nextId(String bizType) { if (bizType == null) { throw new IllegalArgumentException("type is null"); } ... ... //获取id return idGenerator.nextId(); } }
1.3.2、CachedIdGenerator
复制代码
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
47public class CachedIdGenerator implements IdGenerator { @Override public Long nextId() { while (true) { if (current == null) { loadCurrent(); continue; } //获取下一次Id Result result = current.nextId(); //大于最大Id,重新进行初始化 if (result.getCode() == ResultCode.OVER) { loadCurrent(); } else { if (result.getCode() == ResultCode.LOADING) { //更新下一次数据Id信息,loadingId,只是让数据库提前更新Id信息,保证系统的健壮性 loadNext(); } //无论是否大于loadingId都会返回当前Id return result.getId(); } } } } //更新数据Id信息,loadingId,只是让数据库提前更新Id信息,保证系统的健壮性 public void loadNext() { if (next == null && !isLoadingNext) { synchronized (lock) { if (next == null && !isLoadingNext) { isLoadingNext = true; executorService.submit(new Runnable() { @Override public void run() { try { // 无论获取下个segmentId成功与否,都要将isLoadingNext赋值为false next = querySegmentId(); } finally { isLoadingNext = false; } } }); } } } }
1.3.3、SegmentId
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class SegmentId { public Result nextId() { init(); //本地获取Id值,返回的Id为Id的增量 long id = currentId.addAndGet(delta); //大于最大Id if (id > maxId) { return new Result(ResultCode.OVER, id); } //大于loadingId:当前ID+步长*0.2 if (id >= loadingId) { return new Result(ResultCode.LOADING, id); } //正常 return new Result(ResultCode.NORMAL, id); } }
1.4.总结:
具体流程如下:
-
1、实例化client对象—>读取配置文件,请求服务端的封装URL信息。
-
2、初始化ID信息—>判断缓存是否存在,存在则直接返回,—>不存在则请求服务端获取ID信息,存入本地。
-
3、获取Id—>判断是否进行了初始化,没有则进行 ,—>获取Id时,判断是否大于最大id,判断是否大于loadingId,如果大于最大Id则重新初始化,—>如果loadingId则请求服务端更新Id信息,但是还是会返回当前Id,只是让数据库提前更新Id信息,保证系统的健壮性。
客户端加载Id时,先判断是否存在本地缓存,如何没有则从服务端获取,并且存入本地。 客户端进行一次http会返回5个属性:currentId,loadingid,maxId,delta,remainder 当客户端存在缓存时,通过currentId.addAndGet(delta); 如何获取最新的Id值,大于loadingId(http返回的当前+步长*0.2)时,客户端会重新发起http请求,目的时让数据库更新信息,保证系统的健壮性。
2、###服务端
2.1.IdContronller
客户端封装的URL,就是该请求:nextSegmentIdSimple
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@RequestMapping("nextSegmentIdSimple") public String nextSegmentIdSimple(String bizType, String token) { logger.info("nextSegmentIdSimple ==== " + bizType); //对token进行验证 if (!tinyIdTokenService.canVisit(bizType, token)) { return ""; } String response = ""; try { SegmentId segmentId = segmentIdService.getNextSegmentId(bizType); response = segmentId.getCurrentId() + "," + segmentId.getLoadingId() + "," + segmentId.getMaxId() + "," + segmentId.getDelta() + "," + segmentId.getRemainder(); } catch (Exception e) { logger.error("nextSegmentIdSimple error", e); } return response; }
2.2、DbSegmentIdServiceImpl
请求数据库,获取当前的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
40public class DbSegmentIdServiceImpl implements SegmentIdService { public SegmentId getNextSegmentId(String bizType) { // 获取nextTinyId的时候,有可能存在version冲突,需要重试 for (int i = 0; i < Constants.RETRY; i++) { //获取ID信息 TinyIdInfo tinyIdInfo = tinyIdInfoDAO.queryByBizType(bizType); if (tinyIdInfo == null) { throw new TinyIdSysException("can not find biztype:" + bizType); } Long newMaxId = tinyIdInfo.getMaxId() + tinyIdInfo.getStep(); Long oldMaxId = tinyIdInfo.getMaxId(); //更新ID最大值 int row = tinyIdInfoDAO.updateMaxId(tinyIdInfo.getId(), newMaxId, oldMaxId, tinyIdInfo.getVersion(), tinyIdInfo.getBizType()); if (row == 1) { tinyIdInfo.setMaxId(newMaxId); //封装数据返回到客户端 SegmentId segmentId = convert(tinyIdInfo); logger.info("getNextSegmentId success tinyIdInfo:{} current:{}", tinyIdInfo, segmentId); return segmentId; } else { logger.info("getNextSegmentId conflict tinyIdInfo:{}", tinyIdInfo); } } throw new TinyIdSysException("get next segmentId conflict"); } //封装数据 public SegmentId convert(TinyIdInfo idInfo) { SegmentId segmentId = new SegmentId(); segmentId.setCurrentId(new AtomicLong(idInfo.getMaxId() - idInfo.getStep())); segmentId.setMaxId(idInfo.getMaxId()); segmentId.setRemainder(idInfo.getRemainder() == null ? 0 : idInfo.getRemainder()); segmentId.setDelta(idInfo.getDelta() == null ? 1 : idInfo.getDelta()); //设置LoadingId值,默认为达到步长的20%,客户端获取的当前Id>LoadingId时就会再次发起服务端请求 segmentId.setLoadingId(segmentId.getCurrentId().get() + idInfo.getStep() * Constants.LOADING_PERCENT / 100); return segmentId; } }
2.3、定时加载token信息
具体时间可以自定义,并且也可以取消Token信息验证
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class TinyIdTokenServiceImpl implements TinyIdTokenService { /** * 1分钟刷新一次token */ @Scheduled(cron = "0 0/1 * * * ?") public void refresh() { logger.info("refresh token begin"); init(); } @PostConstruct private synchronized void init() { logger.info("tinyId token init begin"); List<TinyIdToken> list = queryAll(); Map<String, Set<String>> map = converToMap(list); token2bizTypes = map; logger.info("tinyId token init success, token size:{}", list == null ? 0 : list.size()); } }
最后
以上就是忧郁芒果最近收集整理的关于分布式ID之滴滴Tinyid源码分析1、客户端2、###服务端的全部内容,更多相关分布式ID之滴滴Tinyid源码分析1、客户端2、###服务端内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复