RedisTemplate的使用
@Autowired
private RedisTemplate<String,String> redisTemplate;
//简单的键值对存储到redis中
//判断是否存在这个key
redisTemplate.hasKey("")
//获取redis中的这个key
redisTemplate.opsForValue().get("");
//设置redis的key和值
redisTemplate.opsForValue().set("key",token,1, TimeUnit.DAYS);
Redis的使用场景
1、在实现短信登录的时候需要限制每个用户发送短信的数量,例如每天只能发送三次短信,这个限制的实现可以通过redis来实现,把手机号码或者用户id作为key,当天发送短信的次数作为值,并且设置过期时间,短信验证码也是可以用redis实现的,把手机号码或者用户id作为key,验证码作为值,同样需要设置过期时间
2、在调用第三方接口,比如某个消息提醒的接口的时候需要发请求获取一个token,然后再调用接口的时候带上这个token才可以有权限访问,这个token一般是有一天有效期的,所以这个token可以放在Redis里面,设置一天过期,这样可以避免每个接口调用的时候都去获取一个新的token,从而消耗了资源【需要注意一个地方,那就是在测试环境和生成环境之间切换发送请求调用第三方接口获取token的时候,会导致原来的token直接失效,例如我测试环境开vpn,然后调用了这个接口生成了一个token存储在测试环境的redis里面,然后由于代码更新到了生成环境上去,所以我去生成环境测试的时候也调用这个接口,那么就会重新生成一个新的token存在生产环境的redis里面,这个时候测试环境中redis存储的token就是失效的token,但是由于有效期设置的一天,所以当又切换会测试环境调用这个接口的时候就会返回一个旧的失效的token,那么拿着这个失效的token去调用其他接口就会导致调用失败,但是这个旧的失效的token会一直在redis中保留一天才会过期,那么这个期间就不会重新获取新的token,因为我代码的逻辑是判断这个key是否有值,有值的话就直接返回redis中这个key对应的值,没有才会去重新获取token,所以就会存在这个问题,那么解决办法就是在拿着失效的token去发请求调用第三方接口的时候,会响应token失效的标识,那么判断这个标识进行处理,处理逻辑就是删掉redis中旧的token,重新获取新的token】
package com.misoft.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.redxun.common.base.entity.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/**
* 第三方服务消息推送工具类
*/
@Component
public class ThirdPartyMsgUtils {
@Value("${third.party.clientId}")
private String clientId;
@Value("${third.party.clientSecret}")
private String clientSecret;
@Value("${third.party.baseUrl}")
private String baseUrl;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 发送消息通知
* @param param 消息参数
* @return 处理结果
*/
public JsonResult sendMsg(JSONObject param) {
String accessToken = null;
// 获取接口调用凭证【优先从缓存获取】
if (!redisTemplate.hasKey("THIRD_PARTY_ACCESS_TOKEN")) {
HashMap<String, Object> tokenParams = new HashMap<>();
tokenParams.put("grant_type", "client_credentials");
tokenParams.put("client_id", clientId);
tokenParams.put("client_secret", clientSecret);
HttpResponse tokenResp = HttpRequest
.post(baseUrl + "/oauth/token")
.form(tokenParams)
.timeout(20000)
.execute();
if (tokenResp.getStatus() == 200) {
String tokenJson = tokenResp.body();
JSONObject tokenObj = JSON.parseObject(tokenJson);
accessToken = tokenObj.getString("access_token");
if (org.springframework.util.StringUtils.isEmpty(accessToken)) {
return JsonResult.Fail("认证失败");
}
// 缓存凭证,有效期5小时
redisTemplate.opsForValue().set("THIRD_PARTY_ACCESS_TOKEN", accessToken, 5, TimeUnit.HOURS);
}
} else {
accessToken = redisTemplate.opsForValue().get("THIRD_PARTY_ACCESS_TOKEN");
}
// 发送消息请求
HttpResponse msgResp = HttpRequest
.post(baseUrl + "/api/message/send")
.header("token", "true")
.header("Authorization", "Bearer " + accessToken)
.body(param.toJSONString(), "application/json;charset=UTF-8")
.timeout(20000)
.execute();
if (msgResp.getStatus() == 200) {
return JsonResult.Success("消息发送成功");
} else {
// 处理凭证无效的情况
if (msgResp.body().contains("invalid_token")) {
redisTemplate.delete("THIRD_PARTY_ACCESS_TOKEN");
String retryKey = "THIRD_PARTY_RETRY_COUNT";
String retryCount = redisTemplate.opsForValue().get(retryKey);
if (org.springframework.util.StringUtils.isEmpty(retryCount)) {
redisTemplate.opsForValue().set(retryKey, "1", 5, TimeUnit.MINUTES);
return sendMsg(param); // 重试一次
} else {
return JsonResult.Fail("消息发送失败:凭证无效");
}
}
}
return JsonResult.Fail("消息发送失败");
}
}
3、存储移动端签到码的有效期,因为移动端的缓存都是本地缓存,只有在自己的手机上才有用,所以这里需要在后端去调用redis接口来存储签到码的有效期