分布式锁(一)——实例基本环境搭建

前言

其实针对分布式锁这个问题,我们并不陌生,这里简单说明一下什么是分布式锁。

如果问一个问题:多线程环境下如何保证线程同步,其实我们可以有很多可说的——synchronized,cas锁,可重入锁等。但是这个只是针对单点系统下能正常保证数据和操作同步,但是现在更多的应用都是微服务的架构方式,单点应用在业务较为复杂的场景下其实有着很高的成本,为了降低成本,许多公司开始采用分布式应用,这就带来许多问题,分布式环境下,不同JVM进程中如何保证数据和代码的同步?这个问题也有很多人进行了解答,而且都解答的非常形象——什么是分布式锁——漫画。这里就不再介绍。只是分布式锁的实现有三种常见的方式:1、数据库分布式锁,2、zookeeper实现分布式锁,3、Redis实现分布式锁。

这一系列博客主要通过实例实现这三种方式,这一篇博客主要介绍基础环境的搭建,后续依次介绍三种实现方式。

构建多模块应用

1、创建一个Maven的普通工程

这里只是创建一个Maven的普通工程,往往有时候,我们构建springboot应用的时候,我们会通过spring.io这个组件去构建,但是会在pom文件中出现parent这个元素,如果通过spring.io这个组件构建的springboot应用,需要删除pom文件中parent这个元素构建完成之后删除src文件

在这里插入图片描述

pom文件如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.learn</groupId>
    <artifactId>springboot_distributelock</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <description>distribute_lock</description>

    <modules>
        <module>lock-model</module>
        <module>lock-servce</module>
    </modules>

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

    <!-- 阿里云maven仓库 -->
    <repositories>
        <repository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

主要是元素,告知该父应用的几个模块

2、建立两个子模块

lock-model

所有数据库访问的内容都在这个模块中,所有对数据库的操作均放在这个模块。

pom文件中指定parent为第一步中建立的应用,pom文件如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>springboot_distributelock</artifactId>
        <groupId>com.learn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>lock-model</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <mybatis-spring-boot.version>1.1.1</mybatis-spring-boot.version>
        <mybatis-pagehelper.version>4.1.2</mybatis-pagehelper.version>
        <lombok.version>1.16.10</lombok.version>
    </properties>

    <dependencies>
        <!--spring-mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot.version}</version>
        </dependency>

        <!--for page-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>${mybatis-pagehelper.version}</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>
</project>

整体结构如下:

在这里插入图片描述

主要是一些实体类和数据访问的Mapper。

lock-service

主要的核心模块,之后我们所有分布式锁的实例都会在这个模块中完成。pom文件中的parent属性需要指定为第一步中建立的应用,整个pom文件如下所示:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>springboot_distributelock</artifactId>
        <groupId>com.learn</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>lock-service</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>1.3.3.RELEASE</spring-boot.version>

        <slf4j.version>1.7.13</slf4j.version>
        <log4j.version>1.2.17</log4j.version>
        <mysql.version>5.1.37</mysql.version>
        <druid.version>1.0.16</druid.version>
        <guava.version>19.0</guava.version>
        <joda-time.version>2.9.2</joda-time.version>
        <poi.version>3.15</poi.version>
        <weixin-java-cp.version>2.5.1</weixin-java-cp.version>
        <elastic-job.version>2.1.4</elastic-job.version>
        <retrofit.version>2.3.0</retrofit.version>

        <cglib.version>3.1</cglib.version>
        <dubbo.version>2.8.4</dubbo.version>
        <resteasy.version>3.0.14.Final</resteasy.version>
        <disconf.version>2.6.36</disconf.version>

        <commons-fileupload.version>1.3.1</commons-fileupload.version>
        <curator.version>2.10.0</curator.version>
        <zookeeper.version>3.4.6</zookeeper.version>
        <redisson.version>3.8.2</redisson.version>
    </properties>

    <!-- 依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
        </dependency>

        <!--依赖平级模块-->
        <dependency>
            <groupId>com.learn</groupId>
            <artifactId>lock-model</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--guava-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!--log-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>


        <!-- zookeeper start -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>${zookeeper.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>${curator.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>${curator.version}</version>
        </dependency>
        <!-- zookeeper end -->

        <!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>${redisson.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>

基本上常用的组件都在pom文件中有所体现。

配置文件中配置相关数据库访问,日志路径等基本信息,暂时没有用上redis和zookeeper,后续配置加上的时候会进一步补充。

server.port=8999
logging.level.com.learn.lockservce=debug

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/distribute_lock?useUnicode=true&amp;characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
spring.datasource.password=root
spring.datasource.username=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.initial-size=5
spring.datasource.min-idle=5
spring.datasource.max-active=20
spring.datasource.max-wait=6000

# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
spring.datasource.logSlowSql=true

#Mapper.xml文件包
mybatis.mapper-locations=classpath:mappers/*.xml
#实体文件包
mybatis.type-aliases-package=com.learn.lockmodel.entity

之后在启动类中完成MyBatis,Mapper接口配置的指定

package com.learn.lockservce;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@MapperScan(basePackages = "com.learn.lockmodel.mapper")
public class LockServceApplication {

    public static void main(String[] args) {
        SpringApplication.run(LockServceApplication.class, args);
    }

}

之后在MySQL客户端中执行以下SQL

USE distribute_lock;

DROP TABLE IF EXISTS crm_order;

CREATE TABLE crm_order (
  id INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  user_id INT(11) DEFAULT NULL COMMENT '用户id',
  mobile VARCHAR(255) DEFAULT NULL COMMENT '手机号',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_time TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  is_active INT(255) DEFAULT '1' COMMENT '是否有效(1=有效;0=无效)',
  PRIMARY KEY (id),
  UNIQUE KEY idx_mobile (mobile,is_active) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=139 DEFAULT CHARSET=utf8 COMMENT='crm抢单记录表';

INSERT  INTO crm_order(id,user_id,mobile,create_time,update_time,is_active) VALUES (60,233,'15627284603','2018-10-31 22:05:17',NULL,1),(61,233,'15627284604','2018-10-31 22:11:23',NULL,1),(72,234,'15627284605','2018-10-31 22:15:07',NULL,1),(73,236,'15627284606','2018-10-31 22:22:28',NULL,1),(138,235,'15627284607','2018-10-31 22:25:30',NULL,1);

DROP TABLE IF EXISTS product_lock;

CREATE TABLE product_lock (
  id INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  product_no VARCHAR(255) DEFAULT NULL COMMENT '产品编号',
  stock INT(11) DEFAULT NULL COMMENT '库存量',
  version INT(11) DEFAULT NULL COMMENT '版本号',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_time TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  UNIQUE KEY idx_unique (id) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='产品信息-数据库级别lock测试';

INSERT  INTO product_lock(id,product_no,stock,version,create_time,update_time) VALUES (1,'10010',265,735,'2018-10-17 21:16:33','2020-01-19 21:24:37');

DROP TABLE IF EXISTS user;

CREATE TABLE user (
  id INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  user_name VARCHAR(255) DEFAULT NULL COMMENT '用户名',
  email VARCHAR(255) DEFAULT NULL COMMENT '邮箱',
  create_time DATETIME DEFAULT NULL COMMENT '创建时间',
  update_time TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id)
) ENGINE=INNODB AUTO_INCREMENT=237 DEFAULT CHARSET=utf8 COMMENT='用户信息表';

INSERT  INTO user(id,user_name,email,create_time,update_time) VALUES (233,'learn','[email protected]','2018-10-27 11:23:24',NULL),(234,'SteadyJack','[email protected]','2018-10-27 11:23:24',NULL),(235,'linsen','[email protected]','2018-10-27 11:23:24',NULL),(236,'jack','[email protected]','2018-10-27 11:23:24',NULL);

所有工作完成之后,启动程序,如果正常启动说明基本环境搭建完成,后续我们会在这个基础上完成分布式锁的三种具体实现。这里也写个简单的controller自己测试一下。

总结

搭建基本环境,没啥可总结的。

发布了129 篇原创文章 · 获赞 37 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/liman65727/article/details/104084762