소스 검색

zookeeper实现分布式锁

Long 2 년 전
커밋
7f6558f23e

+ 97 - 0
pom.xml

@@ -0,0 +1,97 @@
+<?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>
+
+    <groupId>org.example</groupId>
+    <artifactId>zk_product</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>war</packaging>
+
+    <properties>
+        <spring.version>5.2.7.RELEASE</spring.version>
+    </properties>
+    <dependencies>
+        <!-- Spring -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+            <version>${spring.version}</version>
+        </dependency>
+        <!-- Mybatis -->
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis</artifactId>
+            <version>3.5.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-spring</artifactId>
+            <version>2.0.5</version>
+        </dependency>
+        <!-- 连接池 -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.1.10</version>
+        </dependency>
+        <!-- 数据库 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.20</version>
+        </dependency>
+        <!-- junit -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-recipes</artifactId>
+            <version>4.2.0</version> <!-- 网友投票最牛逼版本 -->
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <!-- maven内嵌的tomcat插件 -->
+            <plugin>
+                <groupId>org.apache.tomcat.maven</groupId>
+                <!-- 目前apache只提供了tomcat6和tomcat7两个插件 -->
+                <artifactId>tomcat7-maven-plugin</artifactId>
+                <version>2.2</version>
+                <configuration>
+                    <port>8002</port>
+                    <path>/</path>
+                </configuration>
+                <executions>
+                    <execution>
+                        <!-- 打包完成后,运行服务 -->
+                        <phase>package</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 43 - 0
src/main/java/controller/ProductAction.java

@@ -0,0 +1,43 @@
+package controller;
+
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.recipes.locks.InterProcessMutex;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import service.ProductService;
+
+@Controller
+public class ProductAction {
+
+    @Autowired
+    private ProductService productService;
+
+    private static String connectString = "192.168.48.128:2181,192.168.48.129:2181,192.168.48.130:2181";
+
+    @GetMapping("/product/reduce")
+    @ResponseBody
+    public Object reduce(int id) throws Exception {
+        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
+
+        CuratorFramework client = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
+        client.start();
+
+        InterProcessMutex lock = new InterProcessMutex(client, "/product_"+id);
+        try {
+            lock.acquire();
+            productService.reduceStock(id);
+        } catch (Exception e) {
+            if (e instanceof RuntimeException) {
+                throw e;
+            }
+        } finally {
+            lock.release();
+        }
+        return "ok";
+    }
+}

+ 15 - 0
src/main/java/mapper/OrderMapper.java

@@ -0,0 +1,15 @@
+package mapper;
+
+import models.Order;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Controller;
+
+@Mapper
+@Controller
+public interface OrderMapper {
+
+    @Insert("insert into orders(id, pid, userId) values(#{id}, #{pid}, #{userId})")
+    int insert(Order order);
+
+}

+ 19 - 0
src/main/java/mapper/ProductMapper.java

@@ -0,0 +1,19 @@
+package mapper;
+
+import models.Product;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+import org.springframework.stereotype.Controller;
+
+@Mapper
+@Controller
+public interface ProductMapper {
+
+    @Select("select * from product where id = #{id}")
+    Product getProduct(@Param("id") int id);
+
+    @Update("update product set stock = stock-1 where id = #{id}")
+    int reduceStock(@Param("id") int id);
+}

+ 43 - 0
src/main/java/models/Order.java

@@ -0,0 +1,43 @@
+package models;
+
+import java.io.Serializable;
+
+public class Order implements Serializable {
+
+    private String id;
+    private int pid;
+    private int userId;
+
+    public Order() {
+    }
+
+    public Order(String id, int pid, int userId) {
+        this.id = id;
+        this.pid = pid;
+        this.userId = userId;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getPid() {
+        return pid;
+    }
+
+    public void setPid(int pid) {
+        this.pid = pid;
+    }
+
+    public int getUserId() {
+        return userId;
+    }
+
+    public void setUserId(int userId) {
+        this.userId = userId;
+    }
+}

+ 53 - 0
src/main/java/models/Product.java

@@ -0,0 +1,53 @@
+package models;
+
+import java.io.Serializable;
+
+public class Product implements Serializable {
+
+    private int id;
+    private String product_name;
+    private int stock;
+    private int version;
+
+    public Product() {
+    }
+
+    public Product(int id, String product_name, int stock, int version) {
+        this.id = id;
+        this.product_name = product_name;
+        this.stock = stock;
+        this.version = version;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getProduct_name() {
+        return product_name;
+    }
+
+    public void setProduct_name(String product_name) {
+        this.product_name = product_name;
+    }
+
+    public int getStock() {
+        return stock;
+    }
+
+    public void setStock(int stock) {
+        this.stock = stock;
+    }
+
+    public int getVersion() {
+        return version;
+    }
+
+    public void setVersion(int version) {
+        this.version = version;
+    }
+}

+ 6 - 0
src/main/java/service/ProductService.java

@@ -0,0 +1,6 @@
+package service;
+
+public interface ProductService {
+
+    void reduceStock(int id) throws Exception;
+}

+ 41 - 0
src/main/java/service/impl/ProductServiceImpl.java

@@ -0,0 +1,41 @@
+package service.impl;
+
+import mapper.OrderMapper;
+import mapper.ProductMapper;
+import models.Order;
+import models.Product;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import service.ProductService;
+
+import java.util.UUID;
+
+@Service
+public class ProductServiceImpl implements ProductService {
+
+    @Autowired
+    private ProductMapper productMapper;
+
+    @Autowired
+    private OrderMapper orderMapper;
+
+    public void reduceStock(int id) throws Exception {
+
+        Product product = productMapper.getProduct(id);
+
+        if (product.getStock() <= 0) {
+            throw new RuntimeException("已抢光!");
+        }
+
+        int i = productMapper.reduceStock(id);
+        if (i == 1){
+            Order order = new Order();
+            order.setId(UUID.randomUUID().toString());
+            order.setPid(id);
+            order.setUserId(101);
+            orderMapper.insert(order);
+        } else {
+            throw new RuntimeException("减库存失败!");
+        }
+    }
+}

+ 10 - 0
src/main/resources/mybatis/mybatis-config.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE configuration
+        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-config.dtd">
+<configuration>
+    <!-- 后台的日志输出:针对开发者-->
+    <settings>
+        <setting name="logImpl" value="STDOUT_LOGGING"/>
+    </settings>
+</configuration>

+ 43 - 0
src/main/resources/spring/spring.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:mvc="http://www.springframework.org/schema/mvc"
+       xmlns:tx="http://www.springframework.org/schema/tx"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans
+       http://www.springframework.org/schema/beans/spring-beans.xsd
+       http://www.springframework.org/schema/context
+       http://www.springframework.org/schema/context/spring-context.xsd
+       http://www.springframework.org/schema/mvc
+       http://www.springframework.org/schema/mvc/spring-mvc.xsd
+       http://www.springframework.org/schema/tx
+       http://www.springframework.org/schema/tx/spring-tx.xsd
+">
+
+    <context:component-scan base-package="controller,service,mapper"/>
+
+    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
+        <property name="url" value="jdbc:mysql://127.0.0.1:3306/zkproduct?serverTimezone=GMT"></property>
+        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
+        <property name="username" value="root"/>
+        <property name="password" value="123456"/>
+        <property name="maxActive" value="10"/>
+        <property name="minIdle" value="5"/>
+    </bean>
+
+    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
+        <property name="dataSource" ref="dataSource"/>
+        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
+    </bean>
+
+    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
+        <property name="basePackage" value="mapper"/>
+    </bean>
+
+    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
+        <property name="dataSource" ref="dataSource"/>
+    </bean>
+
+    <tx:annotation-driven/>
+</beans>

+ 19 - 0
src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+    <servlet>
+        <servlet-name>springMVC</servlet-name>
+        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+        <init-param>
+            <param-name>contextConfigLocation</param-name>
+            <param-value>classpath:spring/spring.xml</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+        <async-supported>true</async-supported>
+    </servlet>
+    <servlet-mapping>
+        <servlet-name>springMVC</servlet-name>
+        <url-pattern>/</url-pattern>
+    </servlet-mapping>
+</web-app>