分步锁的测试开发
domain层开发及测试数据准备
修改pom.xml配置文件

创建四层结构

在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; }
进行测试.
打开终端,连接mysql数据库, $ mysql -uroot -proot mysql> use interview; mysql> show tables; 启动PayApplication.class 在数据库中查看,表是否被创建
 插入测试数据

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)
分布式锁的基本架构实现
在添加pom.xml中添加zookeeper依赖

创建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;
}
}
```
修改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;
}
}
```
分布式锁实现
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; } }
分布式锁测试 运行程序时,需要事先打开
zkServer.sh start zkCli.sh在zk客户端中: ls / ls /zk-namespace get /zk-namespace
启动程序,进行测试,去WEB UI 查看结果,在IDEA查看结果,在数据库查看结果