亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring?Boot?實(shí)現(xiàn)Redis分布式鎖原理

 更新時(shí)間:2022年08月04日 09:37:42   作者:劍圣無痕  
這篇文章主要介紹了Spring?Boot實(shí)現(xiàn)Redis分布式鎖原理,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下

分布式鎖實(shí)現(xiàn)

引入jar包

<dependency>
 <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <exclusions>
   <exclusion>
 <groupId>io.lettuce</groupId>
 <artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
 <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
 </dependency>

說明:本文采用jedis來實(shí)現(xiàn)分布式鎖。

封裝工具類

@Component
public class RedisLockUtil
{
    private static final Logger logger = LoggerFactory.getLogger(RedisLockUtil.class);
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String LOCK_SUCCESS = "OK";
    private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 加鎖方法僅針對(duì)單實(shí)例 Redis,哨兵、集群模式無法使用
     *
     * @param lockKey 加鎖鍵
     * @param clientId 加鎖客戶端唯一標(biāo)識(shí)(采用UUID)
     * @param seconds 鎖過期時(shí)間
     * @return true標(biāo)識(shí)加鎖成功、false代表加鎖失敗
     */
    public Boolean tryLock(String lockKey, String clientId, long seconds)
    {
        try
        {
            return redisTemplate
                    .execute((RedisCallback<Boolean>) redisConnection -> {
                        Jedis jedis = (Jedis) redisConnection.getNativeConnection();
                        SetParams params =new SetParams();
                        params.nx();
                        params.px(seconds);
                        String result = jedis.set(lockKey, clientId, params);
                        if (LOCK_SUCCESS.equals(result))
                        {
                            return Boolean.TRUE;
                        }
                        return Boolean.FALSE;
                    });
        }
        catch (Exception e)
        {
            logger.error("tryLock error",e);
        }

        return false;
    }
    /**
     *釋放鎖,保持原子性操作,采用了lua腳本
     *
     * @param lockKey
     * @param clientId
     * @return
     */
    public Boolean unLock(String lockKey, String clientId)
    {
        try
        {
            return  redisTemplate
                    .execute((RedisCallback<Boolean>) redisConnection -> {
                        Jedis jedis = (Jedis) redisConnection.getNativeConnection();
                        Object result = jedis.eval(RELEASE_LOCK_SCRIPT,
                                Collections.singletonList(lockKey),
                                Collections.singletonList(clientId));
                        if (RELEASE_SUCCESS.equals(result))
                        {
                            return Boolean.TRUE;
                        }
                        return Boolean.FALSE;
                    });
        }
        catch (Exception e)
        {
            logger.error("unlock error",e);
        }
        return Boolean.FALSE;
    }
}

說明:加鎖的原理是基于Redis的NX、PX命令,而解鎖采用的是lua腳本實(shí)現(xiàn)。

模擬秒殺扣減庫存

public int lockStock()
    {
        String lockKey="lock:stock";
        String clientId = UUID.randomUUID().toString();
        long seconds =1000l;

        try
        {
            //加鎖
            boolean flag=redisLockUtil.tryLock(lockKey, clientId, seconds);
            //加鎖成功
            if(flag)
            {
               logger.info("加鎖成功 clientId:{}",clientId);
               int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));
               if(stockNum>0)
               {
                  stockNum--;
                  redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));
                  logger.info("秒殺成功,剩余庫存:{}",stockNum);
               }
               else
               {
                  logger.error("秒殺失敗,剩余庫存:{}", stockNum);
               }
               //獲取庫存數(shù)量
               return stockNum;
            }
            else
            {
                logger.error("加鎖失敗:clientId:{}",clientId);
            }
        }
        catch (Exception e)
        {
           logger.error("decry stock eror",e);
        }
        finally
        {
           redisLockUtil.unLock(lockKey, clientId);
        }
        return 0;
    }

測試代碼

@RequestMapping("/redisLockTest")
    public void redisLockTest()
    {
        // 初始化秒殺庫存數(shù)量
        redisUtil.set("seckill:goods:stock", "10");

        List<Future> futureList = new ArrayList<>();

        //多線程異步執(zhí)行
        ExecutorService executors = Executors.newScheduledThreadPool(10);
        //
        for (int i = 0; i < 30; i++)
        {
            futureList.add(executors.submit(this::lockStock));

            try
            {
               Thread.sleep(100);
            }
            catch (InterruptedException e) 
            {
               logger.error("redisLockTest error",e);
            }
        }

        // 等待結(jié)果,防止主線程退出
        futureList.forEach(t -> {
            try 
            {
                int stockNum =(int) t.get();
                logger.info("庫存剩余數(shù)量:{}",stockNum);
            }
            catch (Exception e)
            {
               logger.error("get stock num error",e);
            }
        });
    }

執(zhí)行結(jié)果如下:

方案優(yōu)化

上述分布式鎖實(shí)現(xiàn)庫存扣減是否存在相關(guān)問題呢?

問題1:扣減庫存邏輯無法保證原子性,

具體的代碼如下:

int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));
if(stockNum>0)
 {
    stockNum--;
    redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));
 }

這是典型的RMW模型,前面章節(jié)已經(jīng)介紹了具體的實(shí)現(xiàn)方案,可以采用lua腳本和Redis的incry原子性命令實(shí)現(xiàn),這里采用lua腳本來實(shí)現(xiàn)原子性的庫存扣減。

具體實(shí)現(xiàn)如下:

  public long surplusStock(String key ,int num)
   {
       StringBuilder lua_surplusStock = new StringBuilder();
       lua_surplusStock.append("   local key = KEYS[1];");
       lua_surplusStock.append("   local subNum = tonumber(ARGV[1]);");
       lua_surplusStock.append("   local surplusStock=tonumber(redis.call('get',key));");
       lua_surplusStock.append("    if (surplusStock- subNum>= -1) then");
       lua_surplusStock.append("        return redis.call('incrby', KEYS[1], 0-subNum);");
       lua_surplusStock.append("    else ");
       lua_surplusStock.append("    return -1;");
       lua_surplusStock.append("    end");
       
       List<String> keys = new ArrayList<>();
       keys.add(key);
       // 腳本里的ARGV參數(shù)
       List<String> args = new ArrayList<>();
       args.add(Integer.toString(num));

       long result = redisTemplate.execute(new RedisCallback<Long>() {
           @Override
           public Long doInRedis(RedisConnection connection) throws DataAccessException {
               Object nativeConnection = connection.getNativeConnection();
               // 單機(jī)模式
               if (nativeConnection instanceof Jedis) 
               {
                   return (Long) ((Jedis) nativeConnection).eval(lua_surplusStock.toString(), keys, args);
               }
               return -1l;
           }
       });
       return result;
   }

問題2:如果加鎖失敗,則會(huì)直接訪問,無法重入鎖

因?yàn)閱螜C(jī)版本的鎖是無法重入鎖,所以加鎖失敗就直接返回,此問題的解決方案,可以采用Redisson來實(shí)現(xiàn),關(guān)于Redisson實(shí)現(xiàn)分布式鎖,將在后續(xù)的文章中進(jìn)行詳細(xì)的講解。

總結(jié)

本文主要講解了Spring Boot集成Redis實(shí)現(xiàn)單機(jī)版本分布式鎖,雖然單機(jī)版分布式鎖存在鎖的續(xù)期、鎖的重入問題,但是我們還是需要掌握其原理和實(shí)現(xiàn)方法

到此這篇關(guān)于Spring Boot 實(shí)現(xiàn)Redis分布式鎖原理的文章就介紹到這了,更多相關(guān)Spring Boot Redis分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 三行Java代碼實(shí)現(xiàn)計(jì)算多邊形的幾何中心點(diǎn)

    三行Java代碼實(shí)現(xiàn)計(jì)算多邊形的幾何中心點(diǎn)

    因?yàn)楣ぷ餍枰?jì)算采煤機(jī)工作面的中心點(diǎn),如果套用數(shù)學(xué)的計(jì)算公式,用java去實(shí)現(xiàn),太麻煩了。本文將利用java幾何計(jì)算的工具包,幾行代碼就能求出多變形的中心,簡直yyds!還不快跟隨小編一起學(xué)起來
    2022-10-10
  • java 如何復(fù)制非空對(duì)象屬性值

    java 如何復(fù)制非空對(duì)象屬性值

    這篇文章主要介紹了java 如何復(fù)制非空對(duì)象屬性值的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Spring中的注解@Value("#{}")與@Value("${}")的區(qū)別介紹

    Spring中的注解@Value("#{}")與@Value("${}")的區(qū)別

    這篇文章主要介紹了Spring中的注解@Value(“#{}“)與@Value(“${}“)的區(qū)別到底是什么,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • 關(guān)于Maven如何構(gòu)建生命周期

    關(guān)于Maven如何構(gòu)建生命周期

    這篇文章主要介紹了關(guān)于Maven如何構(gòu)建生命周期,Maven構(gòu)建生命周期描述的是一次構(gòu)建過程經(jīng)歷經(jīng)歷了多少個(gè)事件,需要的朋友可以參考下
    2023-04-04
  • idea在用Mybatis時(shí)xml文件sql不提示解決辦法(提示后背景顏色去除)

    idea在用Mybatis時(shí)xml文件sql不提示解決辦法(提示后背景顏色去除)

    mybatis的xml文件配置的時(shí)候,有時(shí)候會(huì)沒有提示,這讓我們很頭疼,下面這篇文章主要給大家介紹了關(guān)于idea在用Mybatis時(shí)xml文件sql不提示的解決辦法,提示后背景顏色去除的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • Java實(shí)例講解動(dòng)態(tài)代理

    Java實(shí)例講解動(dòng)態(tài)代理

    動(dòng)態(tài)代理指的是,代理類和目標(biāo)類的關(guān)系在程序運(yùn)行的時(shí)候確定的,客戶通過代理類來調(diào)用目標(biāo)對(duì)象的方法,是在程序運(yùn)行時(shí)根據(jù)需要?jiǎng)討B(tài)的創(chuàng)建目標(biāo)類的代理對(duì)象。本文將通過案例詳細(xì)講解一下動(dòng)態(tài)代理,需要的可以參考一下
    2022-06-06
  • 如何基于Jenkins構(gòu)建Docker鏡像

    如何基于Jenkins構(gòu)建Docker鏡像

    這篇文章主要介紹了基于Jenkins構(gòu)建Docker鏡像,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Spring?Boot?集成Elasticsearch模塊實(shí)現(xiàn)簡單查詢功能

    Spring?Boot?集成Elasticsearch模塊實(shí)現(xiàn)簡單查詢功能

    本文講解了Spring?Boot集成Elasticsearch采用的是ES模板的方式實(shí)現(xiàn)基礎(chǔ)查詢,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2022-06-06
  • 解析SpringSecurity+JWT認(rèn)證流程實(shí)現(xiàn)

    解析SpringSecurity+JWT認(rèn)證流程實(shí)現(xiàn)

    這篇文章主要介紹了解析SpringSecurity+JWT認(rèn)證流程實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 解決Spring?AOP攔截抽象類(父類)中方法失效問題

    解決Spring?AOP攔截抽象類(父類)中方法失效問題

    這篇文章主要介紹了解決Spring?AOP攔截抽象類(父類)中方法失效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評(píng)論