一、分布式锁
java测试
@RestController
public class SecondKillController {
//1准备商品库存
private static Map<String,Integer> itemStock = new HashMap<>();
//2.准备订单
private static Map<String,Integer> itemOrder = new HashMap<>();
static {
itemStock.put("牙刷", 10000);
itemOrder.put("牙刷", 0);
}
@GetMapping("kill")
public String kill(String item) throws InterruptedException {
//减库存
Integer stock = itemStock.get(item);
if(stock <= 0){
return "没有商品了";
}else{
Thread.sleep(100);
itemStock.put(item, stock-1);
}
Thread.sleep(100);
itemOrder.put(item, itemOrder.get(item)+1);
return "抢够成功了,"+item+"剩余:"+itemStock.get(item)+",订单数--"+itemOrder.get(item);
}
下载ab压力测试
1000请求 500并发
D:\environment\ab\Apache24\bin>ab -n 1000 -c 500 http://localhost:8082/kill?ite
=%E7%89%99%E5%88%B7
刷新发现超卖现象
zoekeeper实现分布式锁
@Configuration
public class ZkConfig {
@Bean
public CuratorFramework cf(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2 );
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString("192.168.232.128:2181")
.retryPolicy(retryPolicy)
.build();
curatorFramework.start();
return curatorFramework;
}
}
@Autowired
private CuratorFramework cf;
//1准备商品库存
private static Map<String,Integer> itemStock = new HashMap<>();
//2.准备订单
private static Map<String,Integer> itemOrder = new HashMap<>();
static {
itemStock.put("牙刷", 10000);
itemOrder.put("牙刷", 0);
}
@GetMapping("kill")
public String kill(String item) throws Exception {
//开始访问共享资源,这里是访问商品信息
InterProcessMutex lock = new InterProcessMutex(cf,"/lock" );
//。。。分布式 加锁
lock.acquire();
//减库存
Integer stock = itemStock.get(item);
if(stock <= 0){
return "没有商品了";
}
Thread.sleep(100);
itemStock.put(item, stock-1);
Thread.sleep(100);
itemOrder.put(item, itemOrder.get(item)+1);
//释放锁
lock.release();
//将锁归还
return "抢够成功了,"+item+"剩余:"+itemStock.get(item)+",订单数--"+itemOrder.get(item);
}
redis实现分布式锁
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
@Component
public class RedisLockUtil {
@Autowired
private RedisTemplate redisTemplate;
//加锁
public boolean lock(String key, String value,int second){
return redisTemplate.opsForValue().setIfAbsent(key, value, second, TimeUnit.SECONDS);
}
//释放锁
public void unlock(String key){
redisTemplate.delete(key);
}
}
@Autowired
private RedisLockUtil lockUtil;
@GetMapping("redis/kill")
public String redisKill(String item) throws Exception {
//。。。分布式 加锁
if(lockUtil.lock(item, System.currentTimeMillis()+"",1 )){
//减库存
Integer stock = itemStock.get(item);
if(stock <= 0){
return "没有商品了";
}
Thread.sleep(100);
itemStock.put(item, stock-1);
Thread.sleep(100);
itemOrder.put(item, itemOrder.get(item)+1);
//释放锁
lockUtil.unlock(item);
//将锁归还
return "抢够成功了,"+item+"剩余:"+itemStock.get(item)+",订单数--"+itemOrder.get(item);
}else{
return "你没有抢到商品";
}
分布式事务
MQ实现分布式事务
LCN实现分布式事务
准备3个项目 txlcn-manager txlcn-order txlcn-item
txlcn-manager:
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
server:
port: 8083
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/lcn?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password:
redis:
host: 192.168.232.128
port: 6379
tx-lcn:
manager:
port: 8070
@SpringBootApplication
@EnableTransactionManagerServer
public class application {
public static void main(String[] args) {
SpringApplication.run(application.class,args);
}
}
lcn对yml不友好,可再建一个空application.properties
访问http://localhost:8083/admin/index.html#/login
类似注册中心那种,可以看到你有哪些服务需要做事务处理的。登录的默认密码为codingapi
txlcn-order:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
server:
port: 8084
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/lcn?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password:
tx-lcn:
client:
manager-address: localhost:8070
@SpringBootApplication
@EnableDistributedTransaction
@MapperScan(basePackages = "com.rayc.mapper")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@GetMapping("/order")
public String create(){
orderService.createOrder();
return "创建订单成功";
}
public interface OrderService {
void createOrder();
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
RestTemplate restTemplate;
@Override
@Transactional
@LcnTransaction
public void createOrder() {
//1、减库存
restTemplate.getForObject("http://localhost:8085/item", String.class);
System.out.println("减库存");
// int i = 1/0;
//2、创建订单
orderMapper.save();
}
}
public interface OrderMapper {
@Insert("insert into order_item (id,name,money) values (1,'张三',123)")
void save();
}
txlcn-item
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
server:
port: 8085
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/lcn?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
username: root
password:
tx-lcn:
client:
manager-address: localhost:8070
@SpringBootApplication
@EnableDistributedTransaction
@MapperScan(basePackages = "com.rayc.mapper")
public class ItemApplication {
public static void main(String[] args) {
SpringApplication.run(ItemApplication.class,args);
}
}
@RestController
public class ItemController {
@Autowired
private ItemService itemService;
@GetMapping("/item")
public String item(){
System.out.println("开始减库存");
itemService.update();
return null;
}
}
public interface ItemService {
void update();
}
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemMapper itemMapper;
@Override
@Transactional
@LcnTransaction
public void update() {
//1、减库存
itemMapper.update();
}
}
public interface ItemMapper {
@Update("update item set stock = stock - 1 where id = 1")
void update();
}
另需创建lcn数据库及表
CREATE TABLE `t_tx_exception` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_state` tinyint(4) NULL DEFAULT NULL,
`registrar` tinyint(4) NULL DEFAULT NULL,
`remark` varchar(4096) NULL DEFAULT NULL,
`ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
加上自己的数据库 order_item和item表
int i = 1/0;
失败库存未减订单未生成
去掉则成功
学习视频: https://www.bilibili.com/video/BV17D4y1S7GQ?p=17