再来看服务端的
-
我们通过查看client的源码,已经很清楚的发现, client获取号段的方式是 : 访问的url格式为
"http://{0}/tinyid/id/nextSegmentIdSimple?token={1}&bizType=";
-
那我们直接来server中Controller中的这个方法
-
@RequestMapping("nextSegmentIdSimple") public String nextSegmentIdSimple(String bizType, String 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; }
-
首先判断当前bizType和token是否可以visit
-
这是用token判断client是否合法
-
然后就是
segmentIdService.getNextSegmentId(bizType);
方法了 这个方法就是server的核心了 -
@Override @Transactional(isolation = Isolation.READ_COMMITTED) public SegmentId getNextSegmentId(String bizType) { // 获取nextTinyId的时候,有可能存在version冲突,需要重试 for (int i = 0; i < Constants.RETRY; i++) { TinyIdInfo tinyIdInfo = tinyIdInfoDAO.queryByBizType(bizType);//1 if (tinyIdInfo == null) { throw new TinyIdSysException("can not find biztype:" + bizType); } Long newMaxId = tinyIdInfo.getMaxId() + tinyIdInfo.getStep(); Long oldMaxId = tinyIdInfo.getMaxId(); 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"); }
-
1处根据bizType直接查询数据库,获取当前短号信息
-
String sql = "select id, biz_type, begin_id, max_id," + " step, delta, remainder, create_time, update_time, version" + " from tiny_id_info where biz_type = ?";
-
将查询结果封装到TinyIdInfo这个pojo中,然后返回
-
如果没有查询到,说明没有这个业务类型,则直接抛出异常
-
然后就是获取段号的逻辑了
-
新的maxId是旧的maxId+step
-
然后这里用的version参数来控制的乐观锁
-
String sql = "update tiny_id_info set max_id= ?," + " update_time=now(), version=version+1" + " where id=? and max_id=? and version=? and biz_type=?";
-
sql语句如上
-
注意到这里会一直尝试
Constant.RETRY
次,来进行’CAS’ -
在Constant这个类中定义了如下参数
-
public class Constants { /** * 预加载下个号段的百分比 */ public static final int LOADING_PERCENT = 20; /** * 重试次数 */ public static final int RETRY = 3; private Constants() { } }
-
重试的参数是3次
-
以此来解决并发的问题
-
同时这里有事务,保证读和写是同一个事务
-
大体的获取的逻辑是这样
-
其他的方法也大致是这个逻辑,没有什么不同
-
其他的也就一个
nextId
方法 -
逻辑和客户端的其实一样,都是有一个current,一个next号段
-
然后来获取id,如果current为空,或者怎样,去查询
-
区别在于这里的获取,不是http去向服务端请求,因为自己就是服务端
-
而是直接通过数据库查询
-
而我也发现了一个问题,如果通过
nextId
方法,server端,是将号段放在自己的内存中,这个可以直接client以nextId
方法请求 -
而如果
nextSegmentId
的话,是不涉及自己的内存中的号段的,而是直接去数据库中查询,
-
这个图很清晰
-
nextSegment方法会绕过server的内存
-
然后这里是有一个filter
-
@WebFilter(urlPatterns = "/*", filterName = "requestFilter")
-
进行了个处理
-
哦哦,就是打印了日志,也没干什么
-
这里有动态的数据源的机制
-
之后我会去了解一下,这里先mark一下
-
https://blog.csdn.net/jiachunchun/article/details/94569124