Some time ago, it was found that there were often two identical user data in the database, resulting in abnormal data query. After checking the reason, it was found that the front-end wechat small program sometimes sent two identical requests at the same time (also known as concurrent) when authorized login. Although the back-end code is judged to be anti-duplication, it cannot avoid the repetition of concurrent operations. So you start thinking about concurrency solutions, and there are many solutions, from intercepting requests to the database level.
We adopt the scheme of generating summary information and Redis distributed lock for the request message. Run for a period of time, the function is very reliable, the code is very concise. So I came up and made a note for subsequent reference.
Solution description: System architecture with Spring boot, define a Filter Filter to Filter the request, and then generate summary information on the request message and set Redis distributed lock. Determine whether the request is the same by digest and lock.
Distributed lock utility class
public class ContextLJ { private static final Integer JD = 0; * @param sign * @param tiD * @return * @throws Exception */ public static Boolean lock(String Signs and String dar) {synchronized (JD) {/ / lock Cache < String > Cache = CacheManager. GetCommonCache (sign); if(cache == null || StringUtils.isBlank(cache.getValue())) { CacheManager.putCommonCacheInfo(sign, tiD, 10000); return true; } return false; }} public static Boolean checklock(String sign, String sign); String tiD){ Cache<String> cache = CacheManager.getCommonCache(sign); String uTid = StringUtils.replace(cache.getValue(), "\"", ""); return tiD.equals(uTid); } @param sign @param tiD public static void clent (String sign, String tiD){if (checklock(sign, tiD)) { CacheManager.clearOnly(sign); }} @param Request */ public static String getSign(ServletRequest Request){// Public static String getSign(ServletRequest Request) Key =value&key=value2 try { Map<String, String> map = getRequstMap((HttpServletRequest) request); Sign = buildRequest(map); } catch (Exception e) { e.printStackTrace(); } return sign; } public static Map<String, String> getRequstMap(HttpServletRequest req) throws Exception{ Map<String,String> params = new HashMap<String,String>(); params.put("uri", req.getRequestURI()); 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; } private static String buildRequest(Map<String, String> map) { List<String> signList = new ArrayList<>(); for(Entry<String, String> entry : map.entrySet()) { signList.add(entry.getKey() + "=" + entry.getValue()); } String sign = StringUtils.join(signList, "&"); return DigestUtils.md5Hex(sign); }}Copy the code
Implement request interception in the filter
@slf4j @component public class MyFilter implements Filter{@override public void init(FilterConfig) filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse myResp, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; Boolean isDict = StringUtils.contains(req.getRequestURI(), "/dict/getDatas"); Boolean isFile = StringUtils.contains(req.getRequestURI(), "/files/file"); if(isDict || isFile) { chain.doFilter(request, myResp); // Query data dictionary or file, directly allow return; } String sign = "sign_" + ContextLJ.getSign(request); String tiD = randomUtils.randomCode (3) + "_" + thread.currentThread ().getid (); // The identity of the current thread try {if (! ContextLJ.lock(sign, tiD)) { Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request); Log.warn (" discard same concurrent request ["+ sign+ "] ["+ tiD+"] "] "+ json.tojsonString (map)); frequentlyError(myResp); return; } if (! ContextLJ.checklock(sign, tiD)) { Map<String,String> map = ContextLJ.getRequstMap((HttpServletRequest)request); Log.warn (" lock validation failed ["+ sign+ "] ["+ tiD+"] "] "+ json.tojsonString (map)); frequentlyError(myResp); return; } chain.doFilter(request, myResp); } catch (Exception e) {log.error("", e); Myresp.getwriter ().write(json.tojsonString (apirs.asError (" Server busy, please try again "))); } finally { ContextLJ.clent(sign, tiD); }} @override public void destroy() {} /** * frequent request */ private void frequentlyError(ServletResponse myResp) throws IOException { ((HttpServletResponse) myResp).setHeader("Content-type", "text/html; charset=UTF-8"); Myresp.getwriter ().write(json.tojsonString (apirs.asError (" Don't be impatient, don't request too often "))); }}Copy the code