一、分布式锁
java测试
@RestController
public class SecondKillController {
private static Map
<String,Integer> itemStock
= new HashMap<>();
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
;
private static Map
<String,Integer> itemStock
= new HashMap<>();
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/>
</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() {
restTemplate
.getForObject("http://localhost:8085/item", String
.class);
System
.out
.println("减库存");
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() {
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