1. 背景
在分布式系统中,不可避免的需要协调多个服务器节点生成分布式的单号问题,如果采取单节点的方式,可能会出现单号重复的问题。从具体的实现方式而言,可以采取数据库的方案,但这个可能存在多节点同时争抢同一条数据造成死锁的问题。我们这里采取分布式中间件Redis的自增函数,实现分布式单号的生成。
2. 业务要求
分布式的单号生成组件,需要根据不同的业务要求生成不同规则的业务单号,并且在高并发的场景下保证单号唯一。
3. 分布式单号组件的实现
3.1 单号的生成业务规则接口
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13/** * 分布式单号的格式化前缀接口 */ public interface DistributedSeqNoFormat<T>{ /** * 根据特定的业务对象类型,进行定制化的单号前缀格式化输出 * @param t 单号的前缀格式化操作对象 * @return 单号的前缀 * @author chenyz18 */ String format(T t); }
3.2 单号生成器
复制代码
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/** * 分布式单号生成器 * */ public class DistributedSeqNoGenerator <T>{ private static final Logger logger = LoggerFactory.getLogger(DistributedSeqNoGenerator.class); /** * 根据业务定制化的前缀格式化生成器,生成分布式的业务单号 * @param format 格式化生成器的实现实例 * @param expiredTime 单号的前缀超时时间, 如果设置为-1则不会超时 * @param t 格式化生成器的格式化业务操作类型实例对象 //* @param <T> 格式化生成器的格式化业务操作类型 * @return 分布式的单号 */ public String getBizSerialNo(DistributedSeqNoFormat<T> format, T t, Long expiredTime) { StringBuilder serialNo = new StringBuilder(); String prefix = format.format(t); serialNo.append(prefix); serialNo.append(getCommonSerialNo(prefix, expiredTime)); return serialNo.toString(); } /** * 根据业务定制化的前缀格式化生成器,生成定长的分布式业务单号 * @param format 格式化生成器的实现实例 * @param t 格式化生成器的格式化业务操作类型实例对象 * @param maxSerialNo 序列化的对打长度,如果生成的序列号不足该长度,则补零 * @param expiredTime 单号的前缀超时时间, 如果设置为-1则不会超时 //* @param <T> 格式化生成器的格式化业务操作类型 * @return 分布式的单号 */ public String getFixedLengthBizSerialNo(DistributedSeqNoFormat<T> format, T t, Integer maxSerialNo, Long expiredTime) { String prefix = format.format(t); Long seqNo = getCommonSerialNo(prefix, expiredTime); int startNoLen = Long.toString(seqNo).length(); if (startNoLen <= maxSerialNo) { String tmpStr = ""; //空缺的为止用0补全 for (int i = 0; i < (maxSerialNo - startNoLen); i++) { tmpStr = tmpStr + "0"; } String serialNo = tmpStr + seqNo; serialNo = prefix + serialNo; return serialNo; } else { return getFixedLengthBizSerialNo(format, t, maxSerialNo, expiredTime); } } /** * 根据redis生成分布式的序列号 * * @param prefix 序列号前缀 * @param expiredTime 序列号的过期时间 * @return 根据序列号前缀生成的序列号 */ public static Long getCommonSerialNo(String prefix, Long expiredTime) { if (StringUtils.isBlank(prefix)) { throw new IllegalArgumentException("单号前缀不能为空!"); } long resultSeq; //get redis client implements instantce CodisImpl cache = (CodisImpl) SpringUtil.getBean("redisBean"); resultSeq = cache.incr(prefix); if (expiredTime > 0) { cache.expire(prefix, expiredTime); } return resultSeq; } }
3.3测试类
复制代码
1
2
3
4
5
6
7
8
9
10
11
12public static String getBillNO( String prefix){ DistributedSeqNoGenerator<String> generator = new DistributedSeqNoGenerator<>(); return generator.getFixedLengthBizSerialNo(new DistributedSeqNoFormat<String>() { @Override public String format(String s) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); String str = dateFormat.format(new Date()); return s+str; } },prefix, maxSeriaNo,86400L); }
实际测试,单秒的并发量维持在8500+的水平,不同的业务场景,可以根据DistributedSeqNoFormat并使用泛型对象来组装不同的业务单号前缀,指定单号的长度,从而满足不同的业务需求。
最后
以上就是懦弱饼干最近收集整理的关于基于Redis自增函数生成分布式单号的全部内容,更多相关基于Redis自增函数生成分布式单号内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复