乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      java開發(fā)中 防止重復(fù)提交的幾種方案

       hncdman 2022-12-21 發(fā)布于湖南

      暴力小熊

      已于 2022-07-01 16:13:00 修改

      3624

       收藏 25

      分類專欄: redis java 數(shù)據(jù)庫 文章標(biāo)簽: java 前端 面試

      版權(quán)

      redis

      同時被 3 個專欄收錄

      4 篇文章1 訂閱

      訂閱專欄

      java

      25 篇文章2 訂閱

      訂閱專欄

      數(shù)據(jù)庫

      8 篇文章0 訂閱

      訂閱專欄

      開場白:老鐵們對于文章有錯誤、不準(zhǔn)確,或需要補充的請留言討論 ,大家共同學(xué)習(xí)。如果覺得還不錯的請關(guān)注、留言、點贊 、收藏。 創(chuàng)作不易,且看且珍惜

      一、產(chǎn)生原因

      對于重復(fù)提交的問題,主要由于重復(fù)點擊或者網(wǎng)絡(luò)重發(fā)請求, 我要先了解產(chǎn)生原因幾種方式:

      點擊提交按鈕兩次;

      點擊刷新按鈕;

      使用瀏覽器后退按鈕重復(fù)之前的操作,導(dǎo)致重復(fù)提交表單;

      使用瀏覽器歷史記錄重復(fù)提交表單;

      瀏覽器重復(fù)的HTTP請;

      nginx重發(fā)等情況;

      分布式RPC的try重發(fā)等點擊提交按鈕兩次;

      等… …

      二、冪等

      對于重復(fù)提交的問題 主要涉及到時 冪等 問題,那么先說一下什么是冪等。

      冪等:F(F(X)) = F(X)多次運算結(jié)果一致;簡單點說就是對于完全相同的操作,操作一次與操作多次的結(jié)果是一樣的。

      在開發(fā)中,我們都會涉及到對數(shù)據(jù)庫操作。例如:

      select 查詢天然冪等

      delete 刪除也是冪等,刪除同一個多次效果一樣

      update 直接更新某個值(如:狀態(tài) 字段固定值),冪等

      update 更新累加操作(如:商品數(shù)量 字段),非冪等

      (可以采用簡單的樂觀鎖和悲觀鎖 個人更喜歡樂觀鎖。

      樂觀鎖:數(shù)據(jù)庫表加version字段的方式;

      悲觀鎖:用了 select…for update 的方式,* 要使用悲觀鎖,我們必須關(guān)閉mysql數(shù)據(jù)庫的自動提交屬性。

      這種在大數(shù)據(jù)量和高并發(fā)下效率依賴數(shù)據(jù)庫硬件能力,可針對并發(fā)量不高的非核心業(yè)務(wù);)

      insert 非冪等操作,每次新增一條 重點 (數(shù)據(jù)庫簡單方案:可采取數(shù)據(jù)庫唯一索引方式;這種在大數(shù)據(jù)量和高并發(fā)下效率依賴數(shù)據(jù)庫硬件能力,可針對并發(fā)量不高的非核心業(yè)務(wù);)

      三、解決方案

      1. 方案對比

      序號 前端/后端 方案 優(yōu)點 缺點 代碼實現(xiàn)

      1) 前端 前端js提交后禁止按鈕,返回結(jié)果后解禁等 簡單 方便 只能控制頁面,通過工具可繞過不安全

      2) 后端 提交后重定向到其他頁面,防止用戶F5和瀏覽器前進后退等重復(fù)提交問題 簡單 方便 體驗不好,適用部分場景,若是遇到網(wǎng)絡(luò)問題 還會出現(xiàn)

      3) 后端 在表單、session、token 放入唯一標(biāo)識符(如:UUID),每次操作時,保存標(biāo)識一定時間后移除,保存期間有相同的標(biāo)識就不處理或提示 相對簡單 表單:有時需要前后端協(xié)商配合; session、token:加大服務(wù)性能開銷

      4) 后端 ConcurrentHashMap 、LRUMap 、google Cache 都是采用唯一標(biāo)識(如:用戶ID+請求路徑+參數(shù)) 相對簡單 適用于單機部署的應(yīng)用 見下

      5) 后端 redis 是線程安全的,可以實現(xiàn)redis分布式鎖。設(shè)置唯一標(biāo)識(如:用戶ID+請求路徑+參數(shù))當(dāng)做key ,value值可以隨意(推薦設(shè)置成過期的時間點),在設(shè)置key的過期時間 單機、分布式、高并發(fā)都可以決絕 相對復(fù)雜需要部署維護redis 見下

      2. 代碼實現(xiàn)

      4). google cache 代碼實現(xiàn) 注解方式 Single lock

      pom.xml 引入

      <dependency>

         <groupId>com.google.guava</groupId>

          <artifactId>guava</artifactId>

          <version>28.2-jre</version>

      </dependency>

      1

      2

      3

      4

      5

      配置文件 .yml

      resubmit:

        local:

          timeOut: 30

      1

      2

      3

      實現(xiàn)代碼

      import java.lang.annotation.*;

      @Target({ElementType.METHOD})

      @Retention(RetentionPolicy.RUNTIME)

      @Documented

      @Inherited

      public @interface LocalLock {

      }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      import com.alibaba.fastjson.JSONObject;

      import com.example.mydemo.common.utils.IpUtils;

      import com.example.mydemo.common.utils.Result;

      import com.example.mydemo.common.utils.SecurityUtils;

      import com.example.mydemo.common.utils.sign.MyMD5Util;

      import com.google.common.cache.Cache;

      import com.google.common.cache.CacheBuilder;

      import lombok.Data;

      import org.apache.commons.lang3.StringUtils;

      import org.aspectj.lang.ProceedingJoinPoint;

      import org.aspectj.lang.annotation.Around;

      import org.aspectj.lang.annotation.Aspect;

      import org.aspectj.lang.reflect.MethodSignature;

      import org.springframework.beans.factory.annotation.Value;

      import org.springframework.context.annotation.Configuration;

      import org.springframework.web.context.request.RequestAttributes;

      import org.springframework.web.context.request.RequestContextHolder;

      import org.springframework.web.context.request.ServletRequestAttributes;

      import javax.servlet.http.HttpServletRequest;

      import java.lang.reflect.Method;

      import java.security.NoSuchAlgorithmException;

      import java.util.ArrayList;

      import java.util.List;

      import java.util.concurrent.TimeUnit;

      /**

       * @author: xx

       * @description: 單機放重復(fù)提交

       */

      @Data

      @Aspect

      @Configuration

      public class LocalLockMethodInterceptor {

          @Value("${spring.profiles.active}")

          private String springProfilesActive;

          @Value("${spring.application.name}")

          private String springApplicationName;

          private static int expireTimeSecond =5;

          @Value("${resubmit:local:timeOut}")

          public void setExpireTimeSecond(int expireTimeSecond) {

              LocalLockMethodInterceptor.expireTimeSecond = expireTimeSecond;

          }

          //定義緩存,設(shè)置最大緩存數(shù)及過期日期

          private static final Cache<String,Object> CACHE =

                  CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(expireTimeSecond, TimeUnit.SECONDS).build();

          @Around("execution(public * *(..))  && @annotation(com.example.mydemo.common.interceptor.annotation.LocalLock)")

          public Object interceptor(ProceedingJoinPoint joinPoint){

              MethodSignature signature = (MethodSignature) joinPoint.getSignature();

              Method method = signature.getMethod();

      //        LocalLock localLock = method.getAnnotation(LocalLock.class);

              try{

              String key = getLockUniqueKey(signature,joinPoint.getArgs());

              if(CACHE.getIfPresent(key) != null){

                  return Result.fail("不允許重復(fù)提交,請稍后再試");

              }

              CACHE.put(key,key);

                  return joinPoint.proceed();

              }catch (Throwable throwable){

                  throw new RuntimeException(throwable.getMessage());

              }finally {

              }

          }

          /**

           * 獲取唯一標(biāo)識key

           *

           * @param methodSignature

           * @param args

           * @return

           */

          private String getLockUniqueKey(MethodSignature methodSignature, Object[] args) throws NoSuchAlgorithmException {

              //請求uri, 獲取類名稱,方法名稱

              RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

              ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;

              HttpServletRequest request = servletRequestAttributes.getRequest();

      //        HttpServletResponse responese = servletRequestAttributes.getResponse();

              //獲取用戶信息

              String userMsg = SecurityUtils.getUsername(); //獲取登錄用戶名稱

              //1.判斷用戶是否登錄

              if (StringUtils.isEmpty(userMsg)) { //未登錄用戶獲取真實ip

                  userMsg = IpUtils.getIpAddr(request);

              }

              String hash = "";

              List list = new ArrayList();

              if (args.length > 0) {

                  String[] parameterNames = methodSignature.getParameterNames();

                  for (int i = 0; i < parameterNames.length; i++) {

                      Object obj = args[i];

                      list.add(obj);

                  }

                  hash = JSONObject.toJSONString(list);

              }

              //項目名稱 + 環(huán)境編碼 + 獲取類名稱 + 方法名稱 + 唯一key

              String key = "locallock:" + springApplicationName + ":" + springProfilesActive + ":" + userMsg + ":" + request.getRequestURI();

              if (StringUtils.isNotEmpty(key)) {

                  key = key + ":" + hash;

              }

              key = MyMD5Util.getMD5(key);

              return key;

          }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      81

      82

      83

      84

      85

      86

      87

      88

      89

      90

      91

      92

      93

      94

      95

      96

      97

      98

      99

      100

      101

      102

      103

      104

      105

      106

      107

      108

      109

      110

      111

      112

      113

      114

      115

      116

      117

      使用:

      @LocalLock

          public void save(@RequestBody User user) {

          }

      1

      2

      3

      4

      5)redis

      pom.xml 引入

      <dependency>

          <groupId>org.springframework.boot</groupId>

          <artifactId>spring-boot-starter-data-redis</artifactId>

      </dependency>

      1

      2

      3

      4

      .yml文件 redis 配置

      spring:

        redis:

          host: localhost

          port: :6379

          password: 123456

      1

      2

      3

      4

      5

      import java.lang.annotation.*;

      @Target({ElementType.METHOD})

      @Retention(RetentionPolicy.RUNTIME)

      @Documented

      @Inherited

      public @interface RedisLock {

          int expire() default 5;

      }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      import com.alibaba.fastjson.JSONObject;

      import com.google.common.collect.Lists;

      import com.heshu.sz.blockchain.utonhsbs.common.utils.MyMD5Util;

      import com.heshu.sz.blockchain.utonhsbs.common.utils.SecurityUtils;

      import com.heshu.sz.blockchain.utonhsbs.common.utils.ip.IpUtils;

      import com.heshu.sz.blockchain.utonhsbs.framework.interceptor.annotation.RedisLock;

      import com.heshu.sz.blockchain.utonhsbs.framework.system.domain.BaseResult;

      import lombok.extern.slf4j.Slf4j;

      import org.apache.commons.lang3.StringUtils;

      import org.aspectj.lang.ProceedingJoinPoint;

      import org.aspectj.lang.annotation.Around;

      import org.aspectj.lang.annotation.Aspect;

      import org.aspectj.lang.annotation.Pointcut;

      import org.aspectj.lang.reflect.MethodSignature;

      import org.springframework.beans.factory.annotation.Autowired;

      import org.springframework.beans.factory.annotation.Value;

      import org.springframework.context.annotation.Configuration;

      import org.springframework.data.redis.core.StringRedisTemplate;

      import org.springframework.data.redis.core.script.DefaultRedisScript;

      import org.springframework.data.redis.core.script.RedisScript;

      import org.springframework.web.context.request.RequestAttributes;

      import org.springframework.web.context.request.RequestContextHolder;

      import org.springframework.web.context.request.ServletRequestAttributes;

      import javax.servlet.http.HttpServletRequest;

      import java.lang.reflect.Method;

      import java.security.NoSuchAlgorithmException;

      import java.util.ArrayList;

      import java.util.List;

      /**

       * @author :xx

       * @description:

       * @date : 2022/7/1 9:41

       */

      @Slf4j

      @Aspect

      @Configuration

      public class RedisLockMethodInterceptor {

          @Value("${spring.profiles.active}")

          private String springProfilesActive;

          @Value("${spring.application.name}")

          private String springApplicationName;

          @Autowired

          private StringRedisTemplate stringRedisTemplate;

          @Pointcut("@annotation(com.heshu.sz.blockchain.utonhsbs.framework.interceptor.annotation.RedisLock)")

          public void point() {

          }

          @Around("point()")

          public Object doaround(ProceedingJoinPoint joinPoint) {

              MethodSignature signature = (MethodSignature) joinPoint.getSignature();

              Method method = signature.getMethod();

              RedisLock localLock = method.getAnnotation(RedisLock.class);

              try {

                  String lockUniqueKey = getLockUniqueKey(signature, joinPoint.getArgs());

                  Integer expire = localLock.expire();

                  if (expire < 0) {

                      expire = 5;

                  }

                  ArrayList<String> keys = Lists.newArrayList(lockUniqueKey);

                  String result = stringRedisTemplate.execute(setNxWithExpireTime, keys, expire.toString());

                  if (!"ok".equalsIgnoreCase(result)) {//不存在

                      return BaseResult.error("不允許重復(fù)提交,請稍后再試");

                  }

                  return joinPoint.proceed();

              } catch (Throwable throwable) {

                  throw new RuntimeException(throwable.getMessage());

              }

          }

          /**

           * lua腳本

           */

          private RedisScript<String> setNxWithExpireTime = new DefaultRedisScript<>(

                  "return redis.call('set', KEYS[1], 1, 'ex', ARGV[1], 'nx');",

                  String.class

          );

          /**

           * 獲取唯一標(biāo)識key

           *

           * @param methodSignature

           * @param args

           * @return

           */

          private String getLockUniqueKey(MethodSignature methodSignature, Object[] args) throws NoSuchAlgorithmException {

              //請求uri, 獲取類名稱,方法名稱

              RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

              ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;

              HttpServletRequest request = servletRequestAttributes.getRequest();

      //        HttpServletResponse responese = servletRequestAttributes.getResponse();

              //獲取用戶信息

              String userMsg = SecurityUtils.getUsername(); //獲取登錄用戶名稱

              //1.判斷用戶是否登錄

              if (StringUtils.isEmpty(userMsg)) { //未登錄用戶獲取真實ip

                  userMsg = IpUtils.getIpAddr(request);

              }

              String hash = "";

              List list = new ArrayList();

              if (args.length > 0) {

                  String[] parameterNames = methodSignature.getParameterNames();

                  for (int i = 0; i < parameterNames.length; i++) {

                      Object obj = args[i];

                      list.add(obj);

                  }

                  String param = JSONObject.toJSONString(list);

                  hash = MyMD5Util.getMD5(param);

              }

              //項目名稱 + 環(huán)境編碼 + 獲取類名稱 + 加密參數(shù)

              String key = "lock:" + springApplicationName + ":" + springProfilesActive + ":" + userMsg + ":" + request.getRequestURI();

              if (StringUtils.isNotEmpty(key)) {

                  key = key + ":" + hash;

              }

              return key;

          }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      76

      77

      78

      79

      80

      81

      82

      83

      84

      85

      86

      87

      88

      89

      90

      91

      92

      93

      94

      95

      96

      97

      98

      99

      100

      101

      102

      103

      104

      105

      106

      107

      108

      109

      110

      111

      112

      113

      114

      115

      116

      117

      118

      119

      120

      121

      122

      123

      124

      125

      使用

      @RedisLock

          public void save(@RequestBody User user) {

          }

      1

      2

      3

      4

      文章知識

      ————————————————

      版權(quán)聲明:本文為CSDN博主「暴力小熊」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多