最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot基于數(shù)據(jù)庫(kù)如何實(shí)現(xiàn)簡(jiǎn)單的分布式鎖

 更新時(shí)間:2019年07月21日 08:36:58   作者:dalaoyang  
這篇文章主要給大家介紹了關(guān)于Spring Boot基于數(shù)據(jù)庫(kù)如何實(shí)現(xiàn)簡(jiǎn)單的分布式鎖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

1.簡(jiǎn)介

分布式鎖的方式有很多種,通常方案有:

  • 基于mysql數(shù)據(jù)庫(kù)
  • 基于redis
  • 基于ZooKeeper

網(wǎng)上的實(shí)現(xiàn)方式有很多,本文主要介紹的是如果使用mysql實(shí)現(xiàn)簡(jiǎn)單的分布式鎖,加鎖流程如下圖:

其實(shí)大致思想如下:

1.根據(jù)一個(gè)值來獲取鎖(也就是我這里的tag),如果當(dāng)前不存在鎖,那么在數(shù)據(jù)庫(kù)插入一條記錄,然后進(jìn)行處理業(yè)務(wù),當(dāng)結(jié)束,釋放鎖(刪除鎖)。

2.如果存在鎖,判斷鎖是否過期,如果過期則更新鎖的有效期,然后繼續(xù)處理業(yè)務(wù),當(dāng)結(jié)束時(shí),釋放鎖。如果沒有過期,那么獲取鎖失敗,退出。

2.數(shù)據(jù)庫(kù)設(shè)計(jì)

2.1 數(shù)據(jù)表介紹

數(shù)據(jù)庫(kù)表是由JPA自動(dòng)生成的,稍后會(huì)對(duì)實(shí)體進(jìn)行介紹,內(nèi)容如下:

CREATE TABLE `lock_info` (
 `id` bigint(20) NOT NULL,
 `expiration_time` datetime NOT NULL,
 `status` int(11) NOT NULL,
 `tag` varchar(255) NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `uk_tag` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

其中:

  • id:主鍵
  • tag:鎖的標(biāo)示,以訂單為例,可以鎖訂單id
  • expiration_time:過期時(shí)間
  • status:鎖狀態(tài),0,未鎖,1,已經(jīng)上鎖

3.實(shí)現(xiàn)

本文使用SpringBoot 2.0.3.RELEASE,MySQL 8.0.16,ORM層使用的JPA。

3.1 pom

新建項(xiàng)目,在項(xiàng)目中加入jpa和mysql依賴,完整內(nèi)容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.3.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>com.dalaoyang</groupId>
 <artifactId>springboot2_distributed_lock_mysql</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>springboot2_distributed_lock_mysql</name>
 <description>springboot2_distributed_lock_mysql</description>

 <properties>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.16.22</version>
   <scope>provided</scope>
  </dependency>
 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>

</project>

3.2 配置文件

配置文件配置了一下數(shù)據(jù)庫(kù)信息和jpa的基本配置,如下:

server.port=20001


##數(shù)據(jù)庫(kù)配置
##數(shù)據(jù)庫(kù)地址
spring.datasource.url=jdbc:mysql://localhost:3306/lock?characterEncoding=utf8&useSSL=false
##數(shù)據(jù)庫(kù)用戶名
spring.datasource.username=root
##數(shù)據(jù)庫(kù)密碼
spring.datasource.password=12345678
##數(shù)據(jù)庫(kù)驅(qū)動(dòng)
spring.datasource.driver-class-name=com.mysql.jdbc.Driver


##validate 加載hibernate時(shí),驗(yàn)證創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu)
##create 每次加載hibernate,重新創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu),這就是導(dǎo)致數(shù)據(jù)庫(kù)表數(shù)據(jù)丟失的原因。
##create-drop  加載hibernate時(shí)創(chuàng)建,退出是刪除表結(jié)構(gòu)
##update     加載hibernate自動(dòng)更新數(shù)據(jù)庫(kù)結(jié)構(gòu)
##validate 啟動(dòng)時(shí)驗(yàn)證表的結(jié)構(gòu),不會(huì)創(chuàng)建表
##none 啟動(dòng)時(shí)不做任何操作
spring.jpa.hibernate.ddl-auto=update

##控制臺(tái)打印sql
spring.jpa.show-sql=true
##設(shè)置innodb
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

3.3 實(shí)體類

實(shí)體類如下,這里給tag字段設(shè)置了唯一索引,防止重復(fù)插入相同的數(shù)據(jù):

package com.dalaoyang.entity;


import lombok.Data;
import javax.persistence.*;
import java.util.Date;

@Data
@Entity
@Table(name = "LockInfo",
  uniqueConstraints={@UniqueConstraint(columnNames={"tag"},name = "uk_tag")})
public class Lock {

 public final static Integer LOCKED_STATUS = 1;
 public final static Integer UNLOCKED_STATUS = 0;

 /**
  * 主鍵id
  */
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;

 /**
  * 鎖的標(biāo)示,以訂單為例,可以鎖訂單id
  */
 @Column(nullable = false)
 private String tag;

 /**
  * 過期時(shí)間
  */
 @Column(nullable = false)
 private Date expirationTime;

 /**
  * 鎖狀態(tài),0,未鎖,1,已經(jīng)上鎖
  */
 @Column(nullable = false)
 private Integer status;

 public Lock(String tag, Date expirationTime, Integer status) {
  this.tag = tag;
  this.expirationTime = expirationTime;
  this.status = status;
 }

 public Lock() {
 }
}

3.4 repository

repository層只添加了兩個(gè)簡(jiǎn)單的方法,根據(jù)tag查找鎖和根據(jù)tag刪除鎖的操作,內(nèi)容如下:

package com.dalaoyang.repository;

import com.dalaoyang.entity.Lock;
import org.springframework.data.jpa.repository.JpaRepository;


public interface LockRepository extends JpaRepository<Lock, Long> {

 Lock findByTag(String tag);

 void deleteByTag(String tag);
}

3.5 service

service接口定義了兩個(gè)方法,獲取鎖和釋放鎖,內(nèi)容如下:

package com.dalaoyang.service;


public interface LockService {

 /**
  * 嘗試獲取鎖
  * @param tag 鎖的鍵
  * @param expiredSeconds 鎖的過期時(shí)間(單位:秒),默認(rèn)10s
  * @return
  */
 boolean tryLock(String tag, Integer expiredSeconds);

 /**
  * 釋放鎖
  * @param tag 鎖的鍵
  */
 void unlock(String tag);
}

實(shí)現(xiàn)類對(duì)上面方法進(jìn)行了實(shí)現(xiàn),其內(nèi)容與上述流程圖中一致,這里不在做介紹,完整內(nèi)容如下:

package com.dalaoyang.service.impl;

import com.dalaoyang.entity.Lock;
import com.dalaoyang.repository.LockRepository;
import com.dalaoyang.service.LockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.Objects;


@Service
public class LockServiceImpl implements LockService {

 private final Integer DEFAULT_EXPIRED_SECONDS = 10;

 @Autowired
 private LockRepository lockRepository;

 @Override
 @Transactional(rollbackFor = Throwable.class)
 public boolean tryLock(String tag, Integer expiredSeconds) {
  if (StringUtils.isEmpty(tag)) {
   throw new NullPointerException();
  }
  Lock lock = lockRepository.findByTag(tag);
  if (Objects.isNull(lock)) {
   lockRepository.save(new Lock(tag, this.addSeconds(new Date(), expiredSeconds), Lock.LOCKED_STATUS));
   return true;
  } else {
   Date expiredTime = lock.getExpirationTime();
   Date now = new Date();
   if (expiredTime.before(now)) {
    lock.setExpirationTime(this.addSeconds(now, expiredSeconds));
    lockRepository.save(lock);
    return true;
   }
  }
  return false;
 }

 @Override
 @Transactional(rollbackFor = Throwable.class)
 public void unlock(String tag) {
  if (StringUtils.isEmpty(tag)) {
   throw new NullPointerException();
  }
  lockRepository.deleteByTag(tag);
 }

 private Date addSeconds(Date date, Integer seconds) {
  if (Objects.isNull(seconds)){
   seconds = DEFAULT_EXPIRED_SECONDS;
  }
  Calendar calendar = Calendar.getInstance();
  calendar.setTime(date);
  calendar.add(Calendar.SECOND, seconds);
  return calendar.getTime();
 }
}

3.6 測(cè)試類

創(chuàng)建了一個(gè)測(cè)試的controller進(jìn)行測(cè)試,里面寫了一個(gè)test方法,方法在獲取鎖的時(shí)候會(huì)sleep 2秒,便于我們進(jìn)行測(cè)試。完整內(nèi)容如下:

package com.dalaoyang.controller;

import com.dalaoyang.service.LockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

 @Autowired
 private LockService lockService;

 @GetMapping("/tryLock")
 public Boolean tryLock(String tag, Integer expiredSeconds) {
  return lockService.tryLock(tag, expiredSeconds);
 }

 @GetMapping("/unlock")
 public Boolean unlock(String tag) {
  lockService.unlock(tag);
  return true;
 }

 @GetMapping("/test")
 public String test(String tag, Integer expiredSeconds) {
  if (lockService.tryLock(tag, expiredSeconds)) {
   try {
    //do something
    //這里使用睡眠兩秒,方便觀察獲取不到鎖的情況
    Thread.sleep(2000);
   } catch (Exception e) {

   } finally {
    lockService.unlock(tag);
   }
   return "獲取鎖成功,tag是:" + tag;
  }
  return "當(dāng)前tag:" + tag + "已經(jīng)存在鎖,請(qǐng)稍后重試!";
 }
}

3.測(cè)試

項(xiàng)目使用maven打包,分別使用兩個(gè)端口啟動(dòng),分別是20000和20001。

java -jar springboot2_distributed_lock_mysql-0.0.1-SNAPSHOT.jar --server.port=20001
java -jar springboot2_distributed_lock_mysql-0.0.1-SNAPSHOT.jar --server.port=20000

分別訪問兩個(gè)端口的項(xiàng)目,如圖所示,只有一個(gè)請(qǐng)求可以獲取鎖。

4.總結(jié)

本案例實(shí)現(xiàn)的分布式鎖只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn)方案,還具備很多問題,不適合生產(chǎn)環(huán)境使用。

5.源碼地址

源碼地址:https://gitee.com/dalaoyang/springboot_learn/tree/master/springboot2_distributed_lock_mysql (本地下載

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • 在Spring-Boot中如何使用@Value注解注入集合類

    在Spring-Boot中如何使用@Value注解注入集合類

    這篇文章主要介紹了在Spring-Boot中如何使用@Value注解注入集合類的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 詳解Java利用深度優(yōu)先遍歷解決迷宮問題

    詳解Java利用深度優(yōu)先遍歷解決迷宮問題

    深度優(yōu)先遍歷:深度優(yōu)先遍歷是圖論中的經(jīng)典算法。其利用了深度優(yōu)先搜索算法可以產(chǎn)生目標(biāo)圖的相應(yīng)拓?fù)渑判虮?,采用拓?fù)渑判虮砜梢越鉀Q很多相關(guān)的圖論問題,如最大路徑問題等等。本文將利用深度優(yōu)先遍歷解決迷宮問題,感興趣的可以了解一下
    2022-02-02
  • 詳解Java 類的加載、連接和初始化

    詳解Java 類的加載、連接和初始化

    這篇文章主要介紹了Java 類的加載、連接和初始化的的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Java貪心算法之Prime算法原理與實(shí)現(xiàn)方法詳解

    Java貪心算法之Prime算法原理與實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了Java貪心算法之Prime算法原理與實(shí)現(xiàn)方法,簡(jiǎn)單描述了Prime算法的概念、原理、實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下
    2017-09-09
  • mybatis 如何利用resultMap復(fù)雜類型list映射

    mybatis 如何利用resultMap復(fù)雜類型list映射

    這篇文章主要介紹了mybatis 如何利用resultMap復(fù)雜類型list映射的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java實(shí)現(xiàn)的按照順時(shí)針或逆時(shí)針方向輸出一個(gè)數(shù)字矩陣功能示例

    Java實(shí)現(xiàn)的按照順時(shí)針或逆時(shí)針方向輸出一個(gè)數(shù)字矩陣功能示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的按照順時(shí)針或逆時(shí)針方向輸出一個(gè)數(shù)字矩陣功能,涉及java基于數(shù)組遍歷、運(yùn)算的矩陣操作技巧,需要的朋友可以參考下
    2018-01-01
  • spring boot 2整合swagger-ui過程解析

    spring boot 2整合swagger-ui過程解析

    這篇文章主要介紹了spring boot 2整合swagger-ui過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML

    JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML

    這篇文章主要介紹了JAVA基于SnakeYAML實(shí)現(xiàn)解析與序列化YAML,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Java并發(fā)編程中構(gòu)建自定義同步工具

    Java并發(fā)編程中構(gòu)建自定義同步工具

    這篇文章主要介紹了Java并發(fā)編程中構(gòu)建自定義同步工具,本文講解了可阻塞狀態(tài)依賴操作的結(jié)構(gòu)、有界緩存實(shí)現(xiàn)基類示例、阻塞實(shí)現(xiàn)方式一:拋異常給調(diào)用者、阻塞實(shí)現(xiàn)方式二:通過輪詢和休眠、阻塞實(shí)現(xiàn)方式三:條件隊(duì)列等內(nèi)容,需要的朋友可以參考下
    2015-04-04
  • Java使用HttpClient實(shí)現(xiàn)Post請(qǐng)求實(shí)例

    Java使用HttpClient實(shí)現(xiàn)Post請(qǐng)求實(shí)例

    本篇文章主要介紹了Java使用HttpClient實(shí)現(xiàn)Post請(qǐng)求實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02

最新評(píng)論

峨边| 博野县| 谷城县| 邹平县| 泗水县| 昆山市| 南皮县| 固原市| 桃源县| 上杭县| 平凉市| 津市市| 托里县| 洛阳市| 墨竹工卡县| 渭南市| 铅山县| 色达县| 乳山市| 泌阳县| 丹阳市| 西畴县| 达日县| 塘沽区| 陇西县| 辰溪县| 黑河市| 界首市| 三门峡市| 上栗县| 吉安市| 垫江县| 舟曲县| 故城县| 逊克县| 正安县| 合川市| 喜德县| 安阳县| 页游| 修文县|