中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBoot如何實現簡單的分布式鎖

發布時間:2021-09-10 14:46:08 來源:億速云 閱讀:139 作者:chen 欄目:編程語言

本篇內容介紹了“SpringBoot如何實現簡單的分布式鎖”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

1.簡介

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

基于mysql數據庫  基于redis  基于ZooKeeper

網上的實現方式有很多,本文主要介紹的是如果使用mysql實現簡單的分布式鎖,加鎖流程如下圖:

其實大致思想如下:

1.根據一個值來獲取鎖(也就是我這里的tag),如果當前不存在鎖,那么在數據庫插入一條記錄,然后進行處理業務,當結束,釋放鎖(刪除鎖)。

2.如果存在鎖,判斷鎖是否過期,如果過期則更新鎖的有效期,然后繼續處理業務,當結束時,釋放鎖。如果沒有過期,那么獲取鎖失敗,退出。

2.數據庫設計

2.1 數據表介紹

數據庫表是由JPA自動生成的,稍后會對實體進行介紹,內容如下:

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:鎖的標示,以訂單為例,可以鎖訂單id  expiration_time:過期時間  status:鎖狀態,0,未鎖,1,已經上鎖

3.實現

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

3.1 pom

新建項目,在項目中加入jpa和mysql依賴,完整內容如下:

<?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 配置文件

配置文件配置了一下數據庫信息和jpa的基本配置,如下:

server.port=20001##數據庫配置##數據庫地址spring.datasource.url=jdbc:mysql://localhost:3306/lock?characterEncoding=utf8&useSSL=false##數據庫用戶名spring.datasource.username=root##數據庫密碼spring.datasource.password=12345678##數據庫驅動spring.datasource.driver-class-name=com.mysql.jdbc.Driver##validate 加載hibernate時,驗證創建數據庫表結構##create 每次加載hibernate,重新創建數據庫表結構,這就是導致數據庫表數據丟失的原因。##create-drop  加載hibernate時創建,退出是刪除表結構##update     加載hibernate自動更新數據庫結構##validate 啟動時驗證表的結構,不會創建表##none 啟動時不做任何操作spring.jpa.hibernate.ddl-auto=update##控制臺打印sqlspring.jpa.show-sql=true##設置innodbspring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect

3.3 實體類

實體類如下,這里給tag字段設置了唯一索引,防止重復插入相同的數據:

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; /**  * 鎖的標示,以訂單為例,可以鎖訂單id  */ @Column(nullable = false) private String tag; /**  * 過期時間  */ @Column(nullable = false) private Date expirationTime; /**  * 鎖狀態,0,未鎖,1,已經上鎖  */ @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層只添加了兩個簡單的方法,根據tag查找鎖和根據tag刪除鎖的操作,內容如下:

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接口定義了兩個方法,獲取鎖和釋放鎖,內容如下:

package com.dalaoyang.service;public interface LockService { /**  * 嘗試獲取鎖  * @param tag 鎖的鍵  * @param expiredSeconds 鎖的過期時間(單位:秒),默認10s  * @return  */ boolean tryLock(String tag, Integer expiredSeconds); /**  * 釋放鎖  * @param tag 鎖的鍵  */ void unlock(String tag);}

實現類對上面方法進行了實現,其內容與上述流程圖中一致,這里不在做介紹,完整內容如下:

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;@Servicepublic 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 測試類

創建了一個測試的controller進行測試,里面寫了一個test方法,方法在獲取鎖的時候會sleep 2秒,便于我們進行測試。完整內容如下:

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;@RestControllerpublic 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 "當前tag:" + tag + "已經存在鎖,請稍后重試!"; }}

3.測試

項目使用maven打包,分別使用兩個端口啟動,分別是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

分別訪問兩個端口的項目,如圖所示,只有一個請求可以獲取鎖。

4.總結

本案例實現的分布式鎖只是一個簡單的實現方案,還具備很多問題,不適合生產環境使用。

“SpringBoot如何實現簡單的分布式鎖”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

开化县| 保亭| 湄潭县| 阜新市| 巴里| 凌源市| 肃南| 神木县| 夏津县| 张北县| 柳州市| 冀州市| 莱阳市| 巫山县| 邢台市| 上蔡县| 峨眉山市| 西华县| 济阳县| 桃江县| 区。| 伊宁市| 紫云| 海淀区| 大化| 黄龙县| 黄平县| 定安县| 大英县| 响水县| 山东| 昌宁县| 南宁市| 平和县| 密云县| 五大连池市| 图们市| 苏尼特左旗| 涞源县| 香河县| 克东县|