Redis序列号生成器.docx
- 文档编号:30692968
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:16
- 大小:41.50KB
Redis序列号生成器.docx
《Redis序列号生成器.docx》由会员分享,可在线阅读,更多相关《Redis序列号生成器.docx(16页珍藏版)》请在冰豆网上搜索。
Redis序列号生成器
先看整体效果
把简单的东西“傻瓜化”是软件开发追求的目标之一。
请看下图:
左边是在application.yml里配置了3个生成器,右边可以直接注入到代码中使用,注意,不用写任何代码。
这酸爽。
下面请看效果:
上面是3个生成器生成的第一个序号。
哎吆,还不错哦。
慢慢学会分析
序列号大家都非常熟悉,无非就是一个初始值、步长,有时还有最大值。
这只是最基本的信息,还可以按需添加其他的。
很容易抽象出一个接口,如下代码:
/**
*序列号生成器
*@authorlixinjie
*@since2019-04-04
*/
publicinterfaceSnGenerator{
/**名称,根据实际情况使用*/
StringgetName();
/**注册到容器中的bean名称*/
StringgetBeanName();
/**初始值*/
longgetInitNum();
/**步长*/
longgetStep();
/**获取下一个序列号*/
longnextNum();
/**最大值*/
longgetMaxNum();
}
剩下的就是下面这三个问题了:
接口有了之后,自然就是基于Redis的实现了,这是遇到第一个问题。
还需要根据配置动态向容器中注册bean定义,这是第二个问题。
自然需要从配置文件中读出这些配置信息,供上一步使用,这是第三个问题。
再来学会实现
接口实现时主要用到Redis的INCRBY命令,这是个原子命令。
即使你有多个节点,每个节点有多个线程同时来调用,也是OK的。
而且key不存在时,首次调用时还会将key设置为0。
这样带来的好处就是,程序不用考虑key是否存在,直接调用自增就可以了。
唯一不爽的就是key只会被设置为0,如果初始值不是从0开始的,就真有些不爽,最简单的办法就是在程序中把初始值作为偏移量叠加上去。
当然还有另一个办法,就是主动设置key的初始值。
因为存在并发,自然要使用SETNX命令。
这样已经完全OK,但还是会有人在心理上觉得不安全。
那就在应用启动阶
段执行该命令,此时肯定不会有调用的。
请看下面源码:
/**
*基于Redis的实现
*@authorlixinjie
*@since2019-04-04
*/
publicclassRedisSnGeneratorimplementsSnGenerator{
@Autowired
privateStringRedisTemplatestringRedisTemplate;
privateStringname;
privateStringbeanName;
privatelonginitNum;
privatelongstep;
privatelongmaxNum;
publicRedisSnGenerator(Stringname,StringbeanName,longinitNum,longstep,longmaxNum){
this.name=name;
this.beanName=beanName;
this.initNum=initNum;
this.step=step;
this.maxNum=maxNum;
}
@PostConstruct
publicvoidinit(){
if(!
stringRedisTemplate.hasKey(getName())){
stringRedisTemplate.opsForValue().setIfAbsent(getName(),String.valueOf(getInitNum()));
}
@Override
publicStringgetName(){
returnname;
}
publicStringgetBeanName(){
returnbeanName;
}
@Override
publiclonggetInitNum(){
returninitNum;
}
@Override
publiclonggetStep(){
returnstep;
}
@Override
publiclongnextNum(){
returnstringRedisTemplate.opsForValue().increment(getName(),getStep());
}
@Override
publiclonggetMaxNum(){
returnmaxNum;
}
}
要动态注册bean定义,Spring框架提供了专用接口,BeanDefinitionRegistryPostProcessor:
publicinterfaceBeanDefinitionRegistryPostProcessorextendsBeanFactoryPostProcessor{
/**
*@paramregistrythebeandefinitionregistryusedbytheapplicationcontext
*@throwsorg.springframework.beans.BeansExceptionincaseoferrors
*/
voidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry)throwsBeansException;
}
它是一个bean工厂后处理器,应用启动时框架会自动检测并应用该类型的实现类。
要使用配置文件中的内容,Spring框架提供了专门的接口,EnvironmentAware:
publicinterfaceEnvironmentAwareextendsAware{
/**
*Setthe{@codeEnvironment}thatthiscomponentrunsin.
*/
voidsetEnvironment(Environmentenvironment);
}
它是一个Aware接口,应用启动时框架会自动检测该类型的接口并把你需要的给你set进去。
这块源码有点多,我们就只看两部分吧。
下面是从配置文件中读出配置信息的:
privateSnGeneInfo[]parseSnGeneInfos(){
Stringprefix="sngenerator";
String[]generators=environment.getProperty(prefix+".generators",String[].class);
SnGeneInfo[]infos=newSnGeneInfo[generators.length];
for(inti=0;i infos[i]=buildSnGeneInfo(prefix,generators[i]); } returninfos; } privateSnGeneInfobuildSnGeneInfo(Stringprefix,Stringgenerator){ returnnewSnGeneInfo( prefix+": "+generator, environment.getProperty(prefix+"."+generator+".bean-name"), environment.getProperty(prefix+"."+generator+".init-num",long.class), environment.getProperty(prefix+"."+generator+".step",long.class), environment.getProperty(prefix+"."+generator+".max-num",long.class) ); } 下面是注册bean定义的: @Override publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry)throwsBeansException{ SnGeneInfo[]geneInfos=parseSnGeneInfos(); System.out.println(logInfo(geneInfos)); for(SnGeneInfogeneInfo: geneInfos){ BeanDefinitionBuilderbdb=BeanDefinitionBuilder.genericBeanDefinition(RedisSnGenerator.class); bdb.addConstructorArgValue(geneInfo.getKeyName()); bdb.addConstructorArgValue(geneInfo.getBeanName()); bdb.addConstructorArgValue(geneInfo.getInitNum()); bdb.addConstructorArgValue(geneInfo.getStep()); bdb.addConstructorArgValue(geneInfo.getMaxNum()); registry.registerBeanDefinition(geneInfo.getBeanName(),bdb.getBeanDefinition()); } } 需要很多序列的话? 如果需要非常多的序列生成器的话,上面的方法不可取。 可以采用“分组”序列生成器,每一组内可以有足够多的序列,且组与组之间互不影响。 下面请看使用方法,还是一样的简单: 下面请看效果: 每一组内部的key,就是上面的f1/f2/f3等,不用配置,程序按需直接传入即可。 此时获取下一个序列号的方法需要带一个参数,就是用来传这个key的。 它是基于Redis的哈希(Hash)实现的。 /** *分组序列号生成器 *@authorlixinjie *@since2019-04-04 */ publicinterfaceGroupSnGenerator{ /**名称,根据实际情况使用*/ StringgetName(); /**注册到容器中的bean名称*/ StringgetBeanName(); /**初始值*/ longgetInitNum(); /**步长*/ longgetStep(); /**获取下一个序列号*/ longnextNum(Stringidentifier); /**最大值*/ longgetMaxNum(); } 此时也会遇到给hashkey设置初始值的问题,肯定和上面不一样,请阅读源码吧。 /** *基于Redis的实现 *@authorlixinjie *@since2019-04-04 */ publicclassRedisGroupSnGeneratorimplementsGroupSnGenerator{ privateMap @Autowired privateStringRedisTemplatestringRedisTemplate; privateStringname; privateStringbeanName; privatelonginitNum; privatelongstep; privatelongmaxNum; publicRedisGroupSnGenerator(Stringname,StringbeanName,longinitNum,longstep,longmaxNum){ this.name=name; this.beanName=beanName; this.initNum=initNum; this.step=step; this.maxNum=maxNum; } @PostConstruct publicvoidinit(){ if(! stringRedisTemplate.hasKey(getName())){ stringRedisTemplate.opsForHash().putIfAbsent(getName(),"flag",String.valueOf(getInitNum())); } } @Override publicStringgetName(){ returnname; } publicStringgetBeanName(){ returnbeanName; } @Override publiclonggetInitNum(){ returninitNum; } @Override publiclonggetStep(){ returnstep; } @Override publiclongnextNum(Stringidentifier){ if(! existHashKeys.containsKey(identifier)){ stringRedisTemplate.opsForHash().putIfAbsent(getName(),identifier,String.valueOf(getInitNum())); existHashKeys.putIfAbsent(identifier,Boolean.TRUE); } returnstringRedisTemplate.opsForHash().increment(getName(),identifier,getStep()); } @Override publiclonggetMaxNum(){ returnmaxNum; } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Redis 序列号 生成器