1.Redisson组件,缓存映射MapCache 可以很好地解决DB负载过高的问题,每次进行校验,只需从缓存中查询,不需再查数据库。
2.可以解决定时任务处理不及时的问题,通过实现ApplicationRunner, Ordered两个接口,可以在应用启动和运行期间,不间断监听,并执行我们所需的业务逻辑代码。
3.解决了批次查询的数据量可能过大占用过多的缓存的问题
我们使用纯sql加定时器进行短信验证码失效验证和使用redis进行短信验证码失效验证
与使用Redisson的mapCache有什么区别呢?
redisson的RMapCache,号称是redis的最佳实践,就是由于他通过层层封装,很好地将时间属性赋予了MapCache。记得本汪前段时间还写过一个基于WeakHashMap的具有时间属性的缓存,这个发布在本汪另一篇文章中,大家可以看看。
下面,请大家和本汪挚友Lucky一起,一边吹风,一边进入今天的讲解吧!、
本汪来了:现在由本汪带大家一起看看他的思维导图,其实结合思维导图和源码,就可以解决60%的问题了
依照惯例,先上思维导图和源码链接
源码链接:https://github.com/HuskyCorps/distributeMiddleware
强烈建议下载源码,搭建环境进行学习。
1.首先配置下Rediss集群
这是集群的配置redisson.url.cluster=redis://127.0.0.1:7100,redis://127.0.0.1:7200,redis://127.0.0.1:7300,redis://127.0.0.1:7101,redis://127.0.0.1:7201,redis://127.0.0.1:7301
/**
* 自定义注入Redisson操作bean组件
*
* @author Yuezejian
* @date 2020年 09月01日 20:10:14
*/
@Configuration
public class RedissionConfig {
@Autowired
private Environment env;
@Bean
public RedissonClient client() {
Config config = new Config();
config.useClusterServers().setScanInterval(2000)
.addNodeAddress(StringUtils.split(env.getProperty("redisson.url.cluster"),","))
.setMasterConnectionMinimumIdleSize(10)
.setMasterConnectionPoolSize(64)
.setSlaveConnectionMinimumIdleSize(10)
.setSlaveConnectionPoolSize(64)
.setConnectTimeout(15000);
RedissonClient client = Redisson.create(config);
return client;
}
}
2.编写service
/**
* 短信验证码失效验证
*
* @author Yuezejian
* @date 2020年 09月02日 20:18:09
*/
@Service
public class MsgCodeService {
@Autowired
private RedissonClient redissonClient;
@Autowired
private SendRecordMapper recordMapper;
public String getRandomCode(final String phone) throws Exception {
RMapCache<String, String> rMapCache = redissonClient.getMapCache(Constant.RedissonMsgCodeKey);
String code = rMapCache.get(phone);
if (StringUtils.isNotBlank(code)) {
return code;
}
//TODO:在网络不稳定的情况下,可能手机没能接收到短信验证码,此时需要重新申请,即
//TODO: 同个手机号多次申请验证码,如果该手机号存在着
//30min内有效的验证码,则直接取出发给他即可,而无需重新生成,
//TODO: 以免造成空间和资源的浪费
String msgCode = RandomUtil.randomMsgCode(4);
SendRecord entity = new SendRecord(phone,msgCode);
entity.setSendTime(DateTime.now().toDate());
int res = recordMapper.insertSelective(entity);
if (res > 0) {
rMapCache.put(phone,msgCode,1, TimeUnit.MINUTES);
}
//调用短信供应商提供的发送短信的api - 阿里云sms、网建短信通...
//这个的实现在SendMsg_webchineseUtil中,
//这里不加缀余,大家可以下载源码自行研究
return msgCode;
}
public boolean validateCode(final String phone,final String code) throws Exception{
RMapCache<String,String> rMapCache = redissonClient.getMapCache(Constant.RedissonMsgCodeKey);
String cacheCode = rMapCache.get(phone);
return StringUtils.isNotBlank(cacheCode) && cacheCode.equals(code);
}
}
3.Controller进行调用即可(了解统一响应模型,自行查看本汪相关博客)
/**
* 短信验证码失效验证
*
* @author Yuezejian
* @date 2020年 08月31日 19:14:59
*/
@RestController
@RequestMapping("msg/code")
public class IdentifyingCodeController extends AbstractController{
@Autowired
private IdentifyingCodeService identifyingCodeService;
@Autowired
private MsgCodeService codeService;
//获取验证码
@RequestMapping("send")
public BaseResponse sendCode(@RequestParam String phone) {
if (StringUtils.isBlank(phone)) {
return new BaseResponse(StatusCode.InvalidParams);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try {
response.setData(codeService.getRandomCode(phone));
} catch (Exception e) {
response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
//验证验证码
@RequestMapping("validate")
public BaseResponse validateCode(@RequestParam String phone, @RequestParam String code ) {
if (StringUtils.isBlank(phone) || StringUtils.isBlank(code)) {
return new BaseResponse(StatusCode.InvalidParams);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try {
response.setData(codeService.validateCode(phone,code));
} catch (Exception e) {
response = new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
}
4.设置程序监听器
/**
* 短信验证码失效验证-key(其中的元素 - field)失效监听
*
* @author Yuezejian
* @date 2020年 09月06日 16:36:33
*/
//实现了ApplicationRunner,Ordered后,在应用启动后可以不间断地执行监听,
// Order用来对多个自动监听进行排序,0优先级最高,bean为2147483647,优先级最低
@Service
public class RedissonMapCacheMsgCode implements ApplicationRunner, Ordered {
private static final Logger log = LoggerFactory.getLogger(RedissonMapCacheMsgCode.class);
@Autowired
private RedissonClient redisson;
@Autowired
private SendRecordMapper recordMapper;
@Autowired
private CommonService commonService;
//TODO:应用启动及运行期间,可以不间断地执行我们自定义的服务逻辑
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("——————应用启动期间,不间断执行短信验证失效的业务逻辑-order 0 --");
this.listenExpireCode();
}
@Override
public int getOrder() {
return 0;
}
//监听器--监听mapCache里失效的验证码
private void listenExpireCode() {
RMapCache<String, String> rMapCache = redisson.getMapCache(Constant.RedissonMsgCodeKey);
rMapCache.addListener(new EntryExpiredListener<String,String>() {
@Override
public void onExpired(EntryEvent<String, String> entryEvent) {
try {
String phone = entryEvent.getKey();
String msgCode = entryEvent.getValue();
log.info("————当前手机号:{},对应的验证码:{}", phone, msgCode);
if (StringUtils.isNotBlank(phone) && StringUtils.isNotBlank(msgCode)) {
int res = recordMapper.updateExpirePhoneCode(phone, msgCode);
if (res > 0) {
commonService.recordLog(phone + "--" + msgCode, "监听mapCache里失效的验证码,对数据库进行更新", "listenExpireCode");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
大多需要说明的,本汪都在注释中说明了,下面的作为拓展了解:
rMapCache.addListener(),共可以用四种监听,分别是添加监听,失效监听,更新监听和移除监听
EntryCreatedListener(org.redisson.api.map.event)
EntryExpiredListener(org.redisson.api.map.event)
EntryRemovedListener(org.redisson.api.map.event)
EntryUpdatedListener(org.redisson.api.map.event)
如果想了解更多Redisson的信息,可以取Redisson的Github上进行学习和了解,地址为:
https://github.com/redisson/redisson/wiki/