解决分布式环境中多线程高并发重复请求服务器。造成的数据冗余问题

来源:互联网 发布:linux拼接回车符 编辑:程序博客网 时间:2024/06/16 12:59

解决分布式环境中高并发重复请求服务器。造成的数据冗余问题

前言

 最近遇到了一个很坑爹的问题,也是由于自己的经验不足和疏忽,第一次负责用户比较活跃并发比较大的 项目,前期设计阶段 也没有进行严格的把控,导致有些接口没有考虑到高并发多线程状态下 数据一致性。。导致造成了 种种怪相 如:库存扣除不精准,商品数量计算不精准,唯一数据验证失效 等等一些列问题,我这个负责人应担负首要责任。 检讨的话不多说了 下面说一下我的应急的解决办法

方案

 经分析造成数据冗余的原因:由于网络和前端设计不合理的原因 前端经常向服务器抛出大量内容完全相同的 http请求, 那么考虑到项目正在运行,依次调整接口不现实 我们采用了如下方案:filter + redis + 请求数据摘要 对重复请求进行过率。 加锁的机制参考了连接池的源码, ContextLJ.java的思路 也可以用于 抢购项目中的并发控制

代码:

package com.nursling.web.filter.context;import com.nursling.nosql.redis.RedisUtil;import com.nursling.sign.SignType;import com.nursling.sign.SignUtil;import redis.clients.jedis.Jedis;import javax.servlet.ServletRequest;import javax.servlet.http.HttpServletRequest;import java.util.HashMap;import java.util.Map;/** * 并发拦截 * 高并发下 过滤掉 相同请求的工具 * @author 杨. * */public class ContextLJ {    private static final Integer JD = 0;    /**     * 上锁 使用redis 为分布式项目 加锁     * @param sign     * @param tiD     * @return     * @throws Exception     */    public static boolean lock(String sign, String tiD) {        synchronized (JD) { // 加锁            Jedis jedis = RedisUtil.getJedis();            String uTid = jedis.get(sign);            if (uTid == null) {                jedis.set(sign, tiD);                jedis.expire(sign, 36);                return true;            }            return false;        }    }    /**     * 锁验证     * @param sign     * @param tiD     * @return     */    public static boolean checklock(String sign, String tiD){        Jedis jedis = RedisUtil.getJedis();        String uTid = jedis.get(sign);        return tiD.equals(uTid);    }    /**     * 去掉锁     * @param sign     * @param tiD     */    public static void clent (String sign, String tiD){        if (checklock(sign, tiD)) {            Jedis jedis = RedisUtil.getJedis();            jedis.del(sign);        }    }    /**     * 获取摘要     * @param request     * @return     */    public static String getSign(ServletRequest request){        // 此工具是将 request中的请求内容 拼装成 key=value&key=value2 的形式 源码在线面        Map<String, String> map =             SignUtil.getRequstMap((HttpServletRequest) request);        String sign = null;        try {            // 这里使用md5方法生成摘要 方法源码就不贴了            sign = SignUtil.buildRequest(map, SignType.MD5);        } catch (Exception e) {            e.printStackTrace();        }        return sign;    }}
public static Map<String, String> getRequstMap(HttpServletRequest req){        Map<String,String> params = new HashMap<String,String>();        Map<String, String[]> requestParams = req.getParameterMap();        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {            String name = (String) iter.next();            String[] values = (String[]) requestParams.get(name);            String valueStr = "";            for (int i = 0; i < values.length; i++) {                valueStr = (i == values.length - 1) ? valueStr + values[i]                        : valueStr + values[i] + ",";            }            params.put(name, valueStr);        }        return params;    }

下面是过滤器代码

package com.nursling.web.filter.transaction;import com.google.gson.Gson;import com.nursling.common.RandomUtil;import com.nursling.dao.util.TransactionUtils;import com.nursling.model.ApiResult;import com.nursling.model.ApiRtnCode;import com.nursling.web.filter.context.ContextLJ;import org.apache.log4j.Logger;import javax.servlet.*;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * 对事物进行控制 并且 避免接口 直接报漏异常信息 * 并且过滤频繁请求 * Created by yangchao on 2016/11/4. */public class TransactionFilter implements Filter {    Logger log = Logger.getLogger(this.getClass());    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest request, ServletResponse myResp, FilterChain chain) throws IOException, ServletException {        String sign = "sign_" + ContextLJ.getSign(request); // 生成摘要        String tiD = RandomUtil.getRandomString(3) + "_" + Thread.currentThread().getId();        try { // 直接放行 具体的事物创建 类            if (!ContextLJ.lock(sign, tiD)) {                log.warn("放弃相同 并发请求" + sign);                frequentlyError(myResp);                return;            }            if (!ContextLJ.checklock(sign, tiD)) {                log.warn("加锁验证失败  " + sign + " " + tiD);                frequentlyError(myResp);                return;            }            chain.doFilter(request, myResp);        } catch (Exception e) { // 捕获到异常 进行异常过滤            log.error("", e);            retrunErrorInfo(myResp);        } finally {            ContextLJ.clent(sign, tiD);        }    }    /**     * 频繁请求     * @param myResp     */    private void frequentlyError(ServletResponse myResp) throws IOException {        ApiResult<Object> re = new ApiResult<>();        ((HttpServletResponse) myResp).setHeader("Content-type", "text/html;charset=UTF-8");        re.setMsg("稍安勿躁,不要频繁请求");        re.setCode(ApiRtnCode.API_VERIFY_FAIL);        myResp.getWriter().write(new Gson().toJson(re));    }    /**     * 返回异常信息      * @param myResp     */    private void retrunErrorInfo(ServletResponse myResp) throws IOException {        ApiResult<Object> re = new ApiResult<>();        re.setMsg("server error");        // 这里不必理会        re.setCode(ApiRtnCode.SERVICE_ERROR);        myResp.getWriter().write(new Gson().toJson(re));    }    @Override    public void destroy() {    }}
阅读全文
1 0
原创粉丝点击