spring层面处理网络抖动导致的重复写入数据,实现请求的幂等性

栏目: Java · 发布时间: 6年前

内容简介:在用户使用网站创建新闻时,由于网络波动,导致发送了多个创建新闻的请求,使得系统中存在了冗余的数据。由于我的应用是单例的,所以采用guava来做缓存。

问题描述

在用户使用网站创建新闻时,由于网络波动,导致发送了多个创建新闻的请求,使得系统中存在了冗余的数据。

问题分析

  1. 看过很多文章,大部分思路都是:如果同一用户在很短时间内发送了重复的post请求,那么后台只处理第一个请求,后面的请求则过滤掉。
  2. 具体实现方式则是添加一个springmvc的拦截器,当一个post请求过来时,首先去缓存中查询短时间内有没有相同请求到来,如果没有,则把该请求放入缓存,并将请求传递下去;如果有,则不再执行后续操作。
  3. 但是这种方式也会面临一个问题:如果两个请求同时到来,同时读缓存,这样两个请求都不会读到记录,导致重复执行了两个请求。
  4. 于是我们想到给缓存加锁,这样就可以避免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 {

    }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro CSS Techniques

Pro CSS Techniques

Jeff Croft、Ian Lloyd、Dan Rubin / Apress / 2009-5-4 / GBP 31.49

Web Standards Creativity: Innovations in Web Design with CSS, DOM Scripting, and XHTML一起来看看 《Pro CSS Techniques》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具