zoukankan      html  css  js  c++  java
  • 分步锁的测试开发

    domain层开发及测试数据准备

    1. 修改pom.xml配置文件 屏幕快照 2019-04-11 01.25.41

    2. 创建四层结构 屏幕快照 2019-04-11 01.14.53

    3. 在domain层,创建Orders.class和Iterms.class两个类

      Orders.class

      import lombok.Data;
      
      import javax.persistence.Entity;
      import javax.persistence.Id;
      import javax.persistence.Table;
      
      @Entity
      @Table
      @Data
      public class Orders {
      
          @Id
          private String id;
      
          private String itemId;
      }
      

      Iterms.class

      import lombok.Data;
      
      import javax.persistence.Entity;
      import javax.persistence.Id;
      import javax.persistence.Table;
      
      @Entity
      @Table
      @Data
      public class Items {
      
          @Id
          private String id;
      
          private String name;
      
          private Integer counts;
      
      }
      
    4. 进行测试.

      打开终端,连接mysql数据库, $ mysql -uroot -proot mysql> use interview; mysql> show tables; 启动PayApplication.class 在数据库中查看,表是否被创建 屏幕快照 2019-04-11 01.36.29 插入测试数据 屏幕快照 2019-04-11 01.46.25

    5. Domain层开发完毕



    dao层开发

    OrdersDAO.class

    import com.next.interview.pay.domain.Items;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    /**
     * 
     *Orders: <T> the domain type the repository manages
     *String: <ID> the type of the id of the entity the repository manages
     */
    public interface ItemsDAO extends JpaRepository<Items, String> {
    }
    
    

    ItemsDAO.class

    import com.next.interview.pay.domain.Items;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    /**
     *
     *Orders: <T> the domain type the repository manages
     *String: <ID> the type of the id of the entity the repository manages
     */
    public interface ItemsDAO extends JpaRepository<Items, String> {
    }
    
    

    开发完毕~



    service层开发

    OrdersService.class

    import com.next.interview.pay.dao.OrdersDAO;
    import com.next.interview.pay.domain.Orders;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.UUID;
    
    @Service
    @Slf4j    // 加注释
    public class OrdersService {
    
        @Autowired
        private OrdersDAO ordersDAO;
    
        public boolean save(String itemId) {
    
            try {
                Orders orders = new Orders();
                orders.setId(UUID.randomUUID().toString());
                orders.setItemId(itemId);
    
                ordersDAO.save(orders);
                log.info("订单创建成功.....");
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    }
    

    ItemsService.class

    package com.next.interview.pay.service;
    
    import com.next.interview.pay.dao.ItemsDAO;
    
    import com.next.interview.pay.domain.Items;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.UUID;
    
    @Service
    public class ItemsService {
    
        @Autowired  //注进来  service层注入DAO层,通过 @Autowired 注进来
        private ItemsDAO itemsDAO;
    
        // 获取订单
        public Items getItem(String itemId) {
            return itemsDAO.findById(itemId).orElse(null);
        }
    
        // 存储订单
        public void save(Items items) {
            items.setId(UUID.randomUUID().toString());
            itemsDAO.save(items);
        }
    
        // 根据商品id获取它的库存数量
        public int getItemCounts(String itemId) {
            return itemsDAO.findById(itemId).orElse(null).getCounts();
        }
    
        // 订单创建后,需要减少库存数量
        public void reduceCount(String itemId, int count) {
            Items items = getItem(itemId);
            items.setCounts(items.getCounts() - count);
            itemsDAO.save(items);
        }
    }
    

    PayService.class

    package com.next.interview.pay.service;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    @Slf4j
    public class PayService {
    
        @Autowired
        private OrdersService ordersService;
    
        @Autowired
        private ItemsService itemsService;
    
    
        public boolean buy(String itemId) {
    
            // 假设每次购买9个
            int buyCount = 9;
    
            // step1  判断库存
            int count = itemsService.getItemCounts(itemId);
            if (count < buyCount) {
                log.info("库存不足,下单失败。 购买数{}件,库存只有{}件", buyCount, count);
                return false;
            }
    
            // step2 创建订单
            boolean flag = ordersService.save(itemId);
    
            // TODO... 模拟高并发场景
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
    
            // step3 扣库存
            if (flag) {
                itemsService.reduceCount(itemId,buyCount);
            } else {
                log.info("订单创建失败.....");
                return false;
            }
            return true;
        }
    }
    

    测试代码开发: (需要在测试目录中,创建新的测试类)

    文件路径: pay-java/src/test/java/com/next/interview/pay/PayServiceTest.java PayServiceTest.class

    package com.next.interview.pay;
    
    
    import com.next.interview.pay.service.PayService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class PayServiceTest {
    
        @Autowired
        private PayService payService;
    
        @Test
        public void testBuy() {
            payService.buy("1");
        }
    }
    

    Controller层开发(模拟高并发)

    BuyController.class

    import com.next.interview.pay.service.PayService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class BuyController {
    
        @Autowired
        private PayService payService;
    
        @GetMapping("/buy1")
        @ResponseBody
        public String buy1(String itemId) {
    
            if (!StringUtils.isEmpty(itemId)) {
                if (payService.buy(itemId)) {
                    return "订单创建成功.... ";
                } else {
                    return "订单创建失败.... ";
                }
            }else {
                return "条目ID不能为空";
            }
        }
    
        @GetMapping("/buy2")
        @ResponseBody
        public String buy2(String itemId) {
    
            if (!StringUtils.isEmpty(itemId)) {
                if (payService.buy(itemId)) {
                    return "订单创建成功.... ";
                } else {
                    return "订单创建失败.... ";
                }
            }else {
                return "条目ID不能为空";
            }
        }
    }
    

    重新启动 PayApplication.class程序,整个项目就跑起来了

    分布式锁总体架构实现(Zookeeper)

    分布式锁的基本架构实现

    1. 在添加pom.xml中添加zookeeper依赖 屏幕快照 2019-04-11 03.59.42

    2. 创建utils目录,创建DistributedLock.class文件

    DistributedLock.class

    ```
    import lombok.extern.slf4j.Slf4j;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.springframework.context.annotation.Configuration;
    
    /**
     *
     * 分布式锁,基本架构
     */
    @Configuration
    @Slf4j
    public class DistributedLock {
    
        private CuratorFramework client = null;
    
        private static final String ZK_LOCK = "pk-zk-locks";
    
        private static final String DISTRIBUTED_LOCK = "pk-distributed-lock";
    
        // 构造方法
        public DistributedLock() {
    
            client = CuratorFrameworkFactory.builder()
                    .connectString("192.168.1.100:2181")
                    .sessionTimeoutMs(10000)
                    .retryPolicy(new ExponentialBackoffRetry(100, 5))
                    .namespace("zk-namespace")
                    .build();
            client.start();;
        }
    
        // 获得分布式锁
        public void getLock() {
            log.info("分布式锁创建成功.... ");
    
        }
    
        // 释放分布式锁: 订单创建成功或者异常的时候释放锁
        public boolean releaseLock() {
    
            log.info("分布式锁释放成功.... ");
            return true;
        }
    }
    ```
    
    1. 修改PayService.class,在里面添加

      1 @Autowired private DistributedLock distributedLock; 2 distributedLock.getLock(); 3 distributedLock.releaseLock();

    PayService.class

    ```
    import com.next.interview.pay.utils.DistributedLock;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    @Slf4j
    public class PayService {
    
        @Autowired
        private OrdersService ordersService;
    
        @Autowired
        private ItemsService itemsService;   
    
        @Autowired
        private DistributedLock distributedLock;    // 分布式锁的添加
    
    
        public boolean buy(String itemId) {
    
            distributedLock.getLock();    // 获取分布式锁
    
            // 假设每次购买9个
            int buyCount = 9;
    
            // step1  判断库存
            int count = itemsService.getItemCounts(itemId);
            if (count < buyCount) {
                log.info("库存不足,下单失败。 购买数{}件,库存只有{}件", buyCount, count);
                distributedLock.releaseLock();  // 释放分布式锁
                return false;
            }
    
            // step2 创建订单
            boolean flag = ordersService.save(itemId);
    
            // TODO... 模拟高并发场景
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                distributedLock.releaseLock();   // 释放分布式锁
                e.printStackTrace();
            }
    
    
            // step3 扣库存
            if (flag) {
                itemsService.reduceCount(itemId,buyCount);
                distributedLock.releaseLock();  // 释放分布式锁
            } else {
                log.info("订单创建失败.....");
                distributedLock.releaseLock();   // 释放分布式锁
                return false;
            }
            return true;
        }
    }
    ```
    
    1. 分布式锁实现

      DistributedLock.class的完整版(基于Zookeeper)

      import lombok.extern.slf4j.Slf4j;
      import org.apache.curator.framework.CuratorFramework;
      import org.apache.curator.framework.CuratorFrameworkFactory;
      import org.apache.curator.framework.recipes.cache.PathChildrenCache;
      import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
      import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
      import org.apache.curator.retry.ExponentialBackoffRetry;
      import org.apache.zookeeper.CreateMode;
      import org.apache.zookeeper.ZooDefs;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      import java.util.concurrent.CountDownLatch;
      
      /**
       *
       * 分布式锁,基本架构
       */
      @Configuration
      @Slf4j
      public class DistributedLock {
      
          private CuratorFramework client = null;
      
          private static final String ZK_LOCK = "pk-zk-locks";
      
          private static final String DISTRIBUTED_LOCK = "pk-distributed-lock";
      
          private static CountDownLatch countDownLatch = new CountDownLatch(1);
      
          // 构造方法
          public DistributedLock() {
      
              client = CuratorFrameworkFactory.builder()
                      .connectString("192.168.1.100:2181")
                      .sessionTimeoutMs(10000)
                      .retryPolicy(new ExponentialBackoffRetry(100, 5))
                      .namespace("zk-namespace")
                      .build();
              client.start();;
          }
      
          @Bean
          public CuratorFramework getClient() {
      
              // ZK中最基本的东西
              client = client.usingNamespace("zk-namespace");
      
              try {
                  if (client.checkExists().forPath("/" + ZK_LOCK) == null) {
                      client.create()
                              .creatingParentsIfNeeded()
                              .withMode(CreateMode.PERSISTENT)
                              .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE);
                  }
      
                  addWatch("/" + ZK_LOCK);
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
              return null;
          }
      
          private void addWatch(String path) throws Exception {
      
              PathChildrenCache cache = new PathChildrenCache(client,path,true);
              cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
              cache.getListenable().addListener(new PathChildrenCacheListener() {
                  @Override
                  public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                      if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
      
                          String path = event.getData().getPath();
      
                          if (path.contains(DISTRIBUTED_LOCK)) {
                              countDownLatch.countDown();
                          }
                      }
                  }
              });
          }
      
          // 获得分布式锁
          public void getLock() {
              log.info("分布式锁创建成功.... ");
      
              while (true) {
                  try {
                      client.create()
                              .creatingParentsIfNeeded()
                              .withMode(CreateMode.EPHEMERAL)
                              .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
                              .forPath("/" + ZK_LOCK + "/" + DISTRIBUTED_LOCK);
      
                      log.info("分布式锁获得成功... ");
                  } catch (Exception e) {
                      e.printStackTrace();
      
                      if (countDownLatch.getCount() <= 0) {
                          countDownLatch = new CountDownLatch(1);
                      }
      
                      try {
                          countDownLatch.await();
                      } catch (InterruptedException e1) {
                          e1.printStackTrace();
                      }
                  }
      
                  log.info("分布式锁获得成功... ");
              }
      
          }
      
          // 释放分布式锁: 订单创建成功或者异常的时候释放锁
          public boolean releaseLock() {
      
              try {
                  if (client.checkExists().forPath("/" + ZK_LOCK + "/" + DISTRIBUTED_LOCK) != null) {
                      client.delete().forPath("/" + ZK_LOCK + "/" + DISTRIBUTED_LOCK);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
                  return false;
              }
      
              log.info("分布式锁释放成功.... ");
              return true;
          }
      }
      
      
    2. 分布式锁测试 运行程序时,需要事先打开
      zkServer.sh start zkCli.sh

      在zk客户端中: ls / ls /zk-namespace get /zk-namespace

      启动程序,进行测试,去WEB UI 查看结果,在IDEA查看结果,在数据库查看结果

  • 相关阅读:
    SDOI2008]仪仗队
    洛谷P1414 又是毕业季II
    P3865 【模板】ST表
    [HAOI2007]理想的正方形
    noip 2011 选择客栈
    [AHOI2009]中国象棋
    洛谷P3387 【模板】缩点
    [SCOI2005]最大子矩阵
    [CQOI2009]叶子的染色
    LibreOJ #116. 有源汇有上下界最大流
  • 原文地址:https://www.cnblogs.com/suixingc/p/fen-bu-suo-de-ce-shi-kai-fa.html
Copyright © 2011-2022 走看看