内容简介:在用户使用网站创建新闻时,由于网络波动,导致发送了多个创建新闻的请求,使得系统中存在了冗余的数据。由于我的应用是单例的,所以采用guava来做缓存。
问题描述
在用户使用网站创建新闻时,由于网络波动,导致发送了多个创建新闻的请求,使得系统中存在了冗余的数据。
问题分析
- 看过很多文章,大部分思路都是:如果同一用户在很短时间内发送了重复的post请求,那么后台只处理第一个请求,后面的请求则过滤掉。
- 具体实现方式则是添加一个springmvc的拦截器,当一个post请求过来时,首先去缓存中查询短时间内有没有相同请求到来,如果没有,则把该请求放入缓存,并将请求传递下去;如果有,则不再执行后续操作。
- 但是这种方式也会面临一个问题:如果两个请求同时到来,同时读缓存,这样两个请求都不会读到记录,导致重复执行了两个请求。
- 于是我们想到给缓存加锁,这样就可以避免3中出现的情况。但是众所周知,普通的加锁是很影响效率的,我们也不能舍弃效率来保证幂等性。为了提升加锁后的效率,我采用了DCL(双重检查锁,没有学过的小伙伴可以去查一查)来进行缓存的加锁读写。
代码
由于我的应用是单例的,所以采用guava来做缓存。
import com.csdc.cett.exception.RequestException;
import com.csdc.cett.util.MD5;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.concurrent.TimeUnit;
/**
* 处理重复请求,保持请求的幂等性
* 使用双重检查锁定(DCL)
*
* @author ksyzz
* @since <pre>2019/06/20</pre>
*/
@Component
public class RequestInterceptor implements HandlerInterceptor {
private static volatile Cache<String, String> loadingCache = CacheBuilder.newBuilder()
.maximumSize(500)
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 只需要保持用户登录后的POST请求的幂等性,此处要排除文件分片上传
if ("POST".equals(request.getMethod()) && request.getHeader("Auth-Token") != null && !request.getRequestURI().contains("/upload/files")) {
// 要求:用户5秒内不能重复提交相同url,相同参数的请求
// 存储方式为 md5(URI+Auth-Token+RequestParams+InputStream)
StringBuilder md5 = new StringBuilder();
// Auth-Token为用户身份标识
String token = request.getHeader("Auth-Token");
md5.append(request.getRequestURI());
md5.append(token);
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String key = parameterNames.nextElement();
String parameter = request.getParameter(key);
md5.append("&").append(key).append("=").append(parameter);
}
String cacheKey = MD5.getHashString(md5.toString());
// 校验是否已经提交过请求
if (loadingCache.getIfPresent(cacheKey) == null) {
synchronized (loadingCache) {
if (loadingCache.getIfPresent(cacheKey) == null) {
// 此处value可不设置
loadingCache.put(cacheKey, "");
return true;
}
}
}
throw new RequestException("请勿重复提交");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- KeyEvents与输入抖动问题
- 从源码分析TabBar的文字抖动问题
- 应付网络抖动等临时故障的重试策略
- 服务重启导致的Java服务抖动CPU占用高
- 【拒绝一问就懵】之没听说过内存抖动吧
- WebRTC视频数据统计之延时、抖动与丢包
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Algorithmic Beauty of Plants
Przemyslaw Prusinkiewicz、Aristid Lindenmayer / Springer / 1996-4-18 / USD 99.00
Now available in an affordable softcover edition, this classic in Springer's acclaimed Virtual Laboratory series is the first comprehensive account of the computer simulation of plant development. 150......一起来看看 《The Algorithmic Beauty of Plants》 这本书的介绍吧!