Tigs:
- 相同的application文件优先使用公共的配置
一些没什么用的小概念
- 并发量:同一时间内,系统中同时处理的用户请求数
- 响应时间:系统处理一个请求所需的时间
- 吞吐量:系统在给定时间内处理处理的业务请求数量
- QPS(Queries Per Second) 表示系统每秒钟处理的请求数量
- TPS(Transactions Per Second ) 表示系统每秒钟完成的事务数量
Gateway 网关负责分发前端请求
使用分布式锁来解决缓存击穿
- 以下是要自己完成的:
- 使用Spring Security和GateWay完成路由转发和认证
- 使用kafka或rocketmq
Redis作为缓存
- 缓存问题:
- 每天的用户很多但是用户每天使用的次数很少,同时会员的信息涉及的表很多
- 解决:使用本地缓存,因为每个会员使用次数很少,一分钟有效,
- 问题:fullgc频繁,导致短时间内大量请求失败,因为缓存时间很短,所以大量的新生代出现,引起频繁的gc,然后大量放入老年代引起fullgc , 解决:不用本地缓存,而是用线程本地变量,放在内存中。
- 每天的用户很多但是用户每天使用的次数很少,同时会员的信息涉及的表很多
Senta
- 原理:生成反向sql
- 模式:
- AT模式:默认,添加undo_log,反向生成sql,回滚之后原来没数据的依然没有数据
- 使用方法:
- 建立undo_log表
- 使用方法:
- TCC模式: try confirm/cancel 三个阶段的代码自己实现,Seata负责调度
- SAGA模式:长事务解决方案,需要编写两个阶段的代码,需要一个JSON文件,可以异步执行
- XA模式:适用于银行和金融,需要数据库支持XA协议
try 之前的代码出现异常会直接结束,不会走finally
- AT模式:默认,添加undo_log,反向生成sql,回滚之后原来没数据的依然没有数据
如何处理多并发的买票
- 使用synchronized ,缺点:会导致卡住,只适合单机
- 使用Redis分布式锁,使用日期+车次来作为锁key,然后放入Redis中,如果拿到锁则继续执行,使用的使setIfAbsent(key,value,timeout),如果这个锁不存在则设置并且返回true,买到票之后删除key 缺点:如果线程执行时间超过了超时时间,也会导致超卖 对应Redis的命令是setnx
- 使用Redisson看门狗,使用一个守护线程来关注超时间是,如果事务未完成但是锁即将过期则重置时间,如果事务结束则守护线程结束,lock.isHeldByCurrentThread来判断是否是当前线程的锁,缺点:Redis集群中Redis宕机,会导致获得得不到锁,然后新的线程向新的Redis主节点中获得锁,仍然可以获得锁 , 最常用 ,Redisson中的锁在释放了之后Redis就查不到了!!!
- 使用红锁:只有拿到半数以上的同等地位的Redis的锁才算拿到锁,Redisson中也有自带的红锁,不常用, 缺点:性能问题,并且如果都得不到锁就都会等待了,尽量去尝试获得更多的锁来解决单机宕机问题
@RestController
@Slf4j
public class RedisController {
@Resource
private RedissonClient redissonClient;
@PostMapping("/buy")
public String set(String ticket) throws InterruptedException {
//当前日期
String key = ticket ;
log.info("key:{}", key);
RLock lock = null;
//获取锁
lock = redissonClient.getLock(key);
//尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean tryLock = lock.tryLock( 0, TimeUnit.SECONDS); //等待100秒,上锁以后10秒自动解锁
log.info("tryLock:{}", tryLock);
//模拟业务处理
if(tryLock){
Thread.sleep(10000); //单位是毫秒
log.info("购买成功");
if(null != lock && lock.isHeldByCurrentThread()){
lock.unlock();
}
return "购买成功";
} else {
log.info("没拿到锁");
return "购买失败";
// throw new RuntimeException("没拿到锁");
}
}
}
使用Sentinal进行限流和降级
- 常见的限流算法:
- 静态窗口限流:每秒限制多少个请求,例如:第2.5会统计第2秒到现在的流量
- 动态窗口限流:滑动窗口,往前取1秒,例如:第2.5秒会统计第1.5秒到现在的请求数
- 漏桶限流:队列,请求全在队列中,出队是匀速的
- 令牌桶限流:放的是令牌,令牌就是计数,会有一个计数器匀速产生令牌,出队不是匀速的可以适应短时间内大量请求
- 令牌大闸:当令牌到达一定数量就不再产生新的令牌
如何应对刷票
使用令牌大闸:令牌按照匀速生成,即使有再多的机器人刷票也会被领票的数量限制,判断令牌肯定比更新库存更快
- 使用令牌锁,持有令牌锁的人才能对令牌进行操作
- 检测令牌数量,如果有就执行,没有就不能执行
- 不要立刻释放令牌锁,使用固定的时间来释放令牌锁,这样即使有机器人也得等待令牌锁的释放
- 同时也可以加入验证码来防止机器人刷票
优化:使用缓存加速令牌锁
将数据库查出来的令牌存在Redis中,每次对Redis中的令牌数量-1,只有当数量等于我们设定的阈值时再去更改数据库的令牌数量,所以需要在Redis中长期保持这个key
使用RocketMQ
使用RocketMQ来完成
购票之后,将请求发送给RocketMQ,然后另一边去消费这个消息,并且进行数据库的增删改查,可以将请求直接转为String 发送,之后另一端pull然后转为需要的类,之后执行具体具体的逻辑
纯手写
- 登录:
- 使用gateway + SpringSecurity + JWT + Redis实现微服务登录
- Mybatis+Mybatis-plus