文章首发于:clawhub.club


最近有个需求,涉及到生成特定流水号:编码+14位时间YYYYMMDDHH24MMSS+6位流水号(定长),序号从000001开始,增量步长为1。
因为系统为集群部署,所以需要中间件来做后6位的增量计数。
redis的INCR命令正好合适:INCR 命令将 key 中储存的数字值增一,如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

因为系统使用的是jedis 2.9.0 所以封装了两个简单的方法:
接口:

1
2
3
4
5
6
7
8
9
10
11
/**
* 对某个键的值自增
* 内部键对应的值自增与键的过期时间设置非原子性,
* 假设多线程,同时执行自增操作,计数没有问题,
* 过期时间在此键没有调用时开始生校。
*
* @param key 键
* @param cacheSeconds 超时时间,0为不超时
* @return
*/
long getAndIncrement(String key, int cacheSeconds);

单机版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public long getAndIncrement(String key, int cacheSeconds) {
long result = 0;
Jedis jedis = null;
try {
jedis = pool.getResource();
result = jedis.incr(key);
if (cacheSeconds != 0) {
jedis.expire(key, cacheSeconds);
}
} catch (Exception e) {
LOGGER.error("set " + key + " = " + result);
} finally {
// 返还到连接池
IOUtils.close(jedis);
}
return result;
}

集群版:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public long getAndIncrement(String key, int cacheSeconds) {
long result = 0;
try {
result = jedisCluster.incr(key);
if (cacheSeconds != 0) {
jedisCluster.expire(key, cacheSeconds);
}
} catch (Exception e) {
LOGGER.error("set " + key + " = " + result);
}
return result;
}

代码中涉及到原子性问题,但是因为正好符合我的需求,所以就不成问题。
最终流水号生成方案:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 流水号生成规则
* <p>
*
* @return the transaction id
*/
private String getTransactionID() {
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String key = "xxx:xxxx:" + time;
//数字补0,redis自增,两秒超时
return REQUEST + time + String.format("%06d", getAndIncrement(key, 2) + 1);
}