内容简介:在用户使用网站创建新闻时,由于网络波动,导致发送了多个创建新闻的请求,使得系统中存在了冗余的数据。由于我的应用是单例的,所以采用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视频数据统计之延时、抖动与丢包
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java语言程序设计
(美) Y. Daniel Liang / 李娜 / 机械工业出版社 / 2011-6 / 75.00元
本书是Java语言的经典教材,多年来畅销不衰。本书全面整合了Java 6的特性,采用“基础优先,问题驱动”的教学方式,循序渐进地介绍了程序设计基础、解决问题的方法、面向对象程序设计、图形用户界面设计、异常处理、I/O和递归等内容。此外,本书还全面且深入地覆盖了一些高级主题,包括算法和数据结构、多线程、网络、国际化、高级GUI等内容。 本书中文版由《Java语言程序设计:基础篇》和《Java语......一起来看看 《Java语言程序设计》 这本书的介绍吧!