redis 计数器用途
社交产品业务里有很多统计计数的功能,比如:
- 用户: 总点赞数,关注数,粉丝数
- 帖子: 点赞数,评论数,热度
- 消息: 已读,未读,红点消息数
- 话题: 阅读数,帖子数,收藏数
统计计数的特点
- 实时性要求高
- 写的频率很高
- 写的性能对MySQL是一个挑战
- 可以采用redis来优化高频率写入的性能要求。
实现防止表单重复提交
NoRepeatSubmit
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeatSubmit {
}
SubmitAspect
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import javax.servlet.http.HttpServletRequest;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.concurrent.TimeUnit;
@Aspect
@Configuration
@Slf4j
public class SubmitAspect {
@Autowired
private HttpServletRequest httpServletRequest;
@Autowired
private RedisTemplate redisTemplate;
@Around("execution(public * *(..)) && @annotation(com.runwu.newpay.member.client.util.NoRepeatSubmit)")
public Object interceptor(ProceedingJoinPoint point) throws Throwable {
Object paramsObj = point.getArgs()[0];
String redisKey = this.getCacheKey(paramsObj);
long count = redisTemplate.opsForValue().increment(redisKey, 1);
if (count == 1) {
redisTemplate.expire(redisKey, 2, TimeUnit.SECONDS);
}
if (count > 1) {
return Response.fail("请勿重复提交");
}
return point.proceed();
}
private String getCacheKey(Object paramsObj) {
String result = "";
try {
JWTUser user = (JWTUser) SecurityUtils.getSubject().getPrincipal();
Object unique = user.getUser().getId();
if (unique == null) {
LoginMember member = (LoginMember) user.getUser();
unique = member.getMemberNo();
}
String ip = IpHelper.getIpAddr(httpServletRequest);
unique = unique == null ? "" : unique.toString();
String key = unique + ip + JsonUtil.toJsonNotNull(paramsObj);
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(key.getBytes());
result = new BigInteger(1, md.digest()).toString(16);
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException(Response.fail("请求参数获取唯一标识异常"));
}
return result;
}
}
IpHelper
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.logging.Logger;
public class IpHelper {
private static final Logger logger = Logger.getLogger("IpHelper");
private static String LOCAL_IP_STAR_STR = "192.168.";
static {
String ip = null;
String hostName = null;
try {
hostName = InetAddress.getLocalHost().getHostName();
InetAddress ipAddr[] = InetAddress.getAllByName(hostName);
for (int i = 0; i < ipAddr.length; i++) {
ip = ipAddr[i].getHostAddress();
if (ip.startsWith(LOCAL_IP_STAR_STR)) {
break;
}
}
if (ip == null) {
ip = ipAddr[0].getHostAddress();
}
} catch (UnknownHostException e) {
logger.severe("IpHelper error.");
e.printStackTrace();
}
LOCAL_IP = ip;
HOST_NAME = hostName;
}
public static final String LOCAL_IP;
public static final String HOST_NAME;
public static String getIpAddr(HttpServletRequest request) {
String fromSource = "X-Real-IP";
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
fromSource = "X-Forwarded-For";
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
fromSource = "Proxy-Client-IP";
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
fromSource = "WL-Proxy-Client-IP";
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
fromSource = "request.getRemoteAddr";
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if (ip.equals("127.0.0.1")) {
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
ip = inet.getHostAddress();
} catch (UnknownHostException e) {
logger.severe("IpHelper error." + e.toString());
}
}
}
if (ip != null && ip.length() > 15) {
if (ip.indexOf(",") > 0) {
ip = ip.substring(0, ip.indexOf(","));
}
}
return ip;
}
}
test
@Path("/testSubmit")
@POST
@Api(value = "测试防止重复提交", desc = "测试防止重复提交")
@Consumes(MediaType.APPLICATION_JSON)
@NoRepeatSubmit
public Response testSubmit(SncodeAddRequest request) {
log.info("request:{}", JSONUtil.toJsonStr(request));
return Response.ok("请求成功");
}