Java-用于预订的分布式JPA锁

use*_*736 10 java concurrency spring jpa

我正在使用允许预定计划的旧系统。该应用程序是无状态REST,并且设计为可水平扩展。但是,数据库在所有实例之间共享。在进行有关设计和规模的讲座之前,这不是我的问题-必须充分利用糟糕的情况(或代码库)。最近,我们看到了一个重复预订的问题。我相信这是因为请求响应线程的性质。当前的过程是,接收请求,检查数据库是否有冲突的时间保留,如果没有,则插入。根据读取和插入之间的时间,有可能两者都插入。场景看起来像这样:

|------|-------|-------|
R1     C1      I1     RSP

-|--------|-------|---------|
R2       C2     I2   RSP
Run Code Online (Sandbox Code Playgroud)

其中R =请求,C = DB检查,I =插入。

因此,我相信我可以使用@Synchronized批注来强制所有线程进行排序。问题在于,有多个实例正在运行,因此无法正常运行整个实例。悲观或乐观的读写似乎并不适用,因为除非我完全误解,否则我们将尝试进行读写组合。有什么想法可以解决这个问题吗?宁愿通过表锁或类似方法在Java中处理它,而不是添加其他服务(kafka,redis等)。

编辑:数据库看起来像这样,在开发中使用h2,在生产中使用mysql。

 id |  start_time  | locationid | postingid | userid | durration
 --------------------------------------------------------------- 
Run Code Online (Sandbox Code Playgroud)

dpr*_*dpr 6

从头开始实现这样的东西并不是什么火箭科学,但也许你可能想看看这个 GitHub 项目: https: //github.com/alturkovic/distributed-lock

我既没有参与这个项目,也没有使用它,但它看起来很有前途。您只需使用以下命令创建 Spring 配置EnableJdbcDistributedLock

@Configuration
@EnableJdbcDistributedLock
public class LockConfiguration {
}
Run Code Online (Sandbox Code Playgroud)

并创建所需的数据库表:

create table lock (
  id int not null auto_increment primary key,
  lock_key varchar(255) unique,
  token varchar(255),
  expireAt timestamp,
);
Run Code Online (Sandbox Code Playgroud)

一旦到位,您就可以通过简单的注释(取自项目的示例)同步分布式环境中的方法调用:

@JdbcLocked(expression = "#name")
public String sayHello(final String name) {
  return "Hello " + name + "!";
}
Run Code Online (Sandbox Code Playgroud)