## 1、功能模块
- 用户中心
- 商品-库存中心
- 订单中心
- 活动中心(秒杀)
## 2、架构设计
### 2.1 RabbitMQ
#### MQ的作用
**1)解耦**:在项目启动之初是很难预测未来会遇到什么困难的,消息中间件在处理过程中插入了一个隐含的,基于数据的接口层,两边都实现这个接口,这样就允许独立的修改或者扩展两边的处理过程,只要两边遵守相同的接口约束即可。
**2)冗余(存储)**:在某些情况下处理数据的过程中会失败,消息中间件允许把数据持久化直到他们完全被处理
**3)扩展性**:消息中间件解耦了应用的过程,所以提供消息入队和处理的效率是很容易的,只需要增加处理流程就可以了。
**4)削峰**:在访问量剧增的情况下,但是应用仍然需要发挥作用,但是这样的突发流量并不常见。而使用消息中间件采用队列的形式可以减少突发访问压力,不会因为突发的超时负荷要求而崩溃
**5)可恢复性**:当系统一部分组件失效时,不会影响到整个系统。消息中间件降低了进程间的耦合性,当一个处理消息的进程挂掉后,加入消息中间件的消息仍然可以在系统恢复后重新处理
**6)顺序保证**:在大多数场景下,处理数据的顺序也很重要,大部分消息中间件支持一定的顺序性
**7)缓冲**:消息中间件通过一个缓冲层来帮助任务最高效率的执行
**8)异步通信**:通过把消息发送给消息中间件,消息中间件异步消费。
#### 削峰限流
设计一:
<img src="C:\Users\acer\Desktop\秒杀系统\images\image-20200706172144164.png" alt="image-20200706172144164" style="zoom:80%;" />
设计二:
<img src="C:\Users\acer\Desktop\秒杀系统\images\image-20200706172510601.png" alt="image-20200706172510601" style="zoom:80%;" />
设置消息队列容量大小,当队列满载时,此时再发送的消息就会被丢弃。
应用于限制秒杀请求,当秒杀请求达到一定数量时,其余请求将会被抛弃不进行处理,一定程度上减少了服务器处理秒杀请求的压力,从而达到削峰限流的效果。(从根源上限制了高并发流量)
MQ有界消息队列:
```java
/**
* 秒杀请求队列
*/
@Bean
public Queue orderSeckillQueue() {
return QueueBuilder
.durable(QUEUE_ORDER_SECKILL)
.maxLength(ACCESS_LIMIT) // 削峰限流 100->限制100个请求
.overflow(QueueBuilder.Overflow.rejectPublish) // 拒绝策略:超过队列容量大小将丢弃消息
.build();
}
```
#### 异步处理
采用消息的异步消费,减少服务器对客户端的响应时间,从而提高用户的体验,一定程度上解决了高并发秒杀带来的QPS低下的问题。
```java
/**
* 消费秒杀请求
*/
@RabbitListener(queues = RabbitConfig.QUEUE_ORDER_SECKILL)
public void receiveSeckillMsg(SeckillMsg seckillMsg, Message msg, Channel channel) {
log.info("TAG:{}",String.valueOf(msg.getMessageProperties().getDeliveryTag()));
// 消费者消费 执行秒杀请求
seckillService.seckill(seckillMsg.getSeckillId(),seckillMsg.getMemberId());
}
```
#### 自动取消超时订单
秒杀成功时,后台创建秒杀订单返回给客户端。订单有效时间为30分钟,超过30分钟未支付则自动取消订单。
解决方案:**RabbitMQ**延迟消息实现超时订单的取消处理
> 流程步骤:
- 根据秒杀用户生成订单
- 将订单id消息发送至**死信队列**,(设置30分钟不支付取消订单)
- 按订单超时时间发送一个延迟消息给RabbitMQ,让它在订单超时后触发取消订单的操作;
- 如果用户没有支付,进行取消订单操作(设置订单状态为交易取消,释放锁定商品库存)。
**延迟消息队列(死信队列)**
```java
public Queue orderTtlQueue() {
return QueueBuilder
.durable(QUEUE_TTL_ORDER)
.deadLetterExchange(EXCHANGE_ORDER_DIRECT)//到期后转发的交换机
.deadLetterRoutingKey(QUEUE_ORDER_CANCEL)//到期后转发的路由键
.build();
}
```
**发送延迟消息**
```java
public void ttlOrderCancelMsg(Long orderId ,final long delayTimes) {
// 给延迟队列发送消息
rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_TTL_ORDER_DIRECT,
RabbitConfig.QUEUE_TTL_ORDER,orderId,message -> {
// 给消息设置延迟时间,延迟时间一到立马被消费
message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
return message;
});
}
```
**订单超时后触发取消订单的操作**
```java
@RabbitListener(queues = RabbitConfig.QUEUE_ORDER_CANCEL)
public void receiveOrderCancelMsg(Long orderId, Message msg,Channel channel) {
log.info("超时订单取消:{}",orderId);
// 调用超时订单取消业务
omsOrderService.orderCancel(orderId);
}
```
### 2.2 Redis
#### 库存预热
传统的秒杀请求直接打到数据库,进行库存查询和锁定库存操作,但在巨大流量的面前,数据库将承受无比巨大的压力,最终可能导致数据库宕机的后果。
单机的数据库能承受的并发访问量是有限的,大概在200~300 QPS。
库存预热,就是在秒杀活动正式开始前,将秒杀商品的库存缓存到内存中。数据在内存中的读取是十分快速的,那么利用缓存中间件**Redis**就能提高数据的访问效率以及承受更大的并发访问量。使得用户秒杀请求不用去数据库中查询库存和减库存操作,而是直接从提前缓存好的内存中的数据进行读写,将并发压力转到了性能更高的Redis来承担,从而保障了数据库的服务质量。
单体的**Redis**最高能承受的最大并发访问量大约在10W/s
#### 内存标记
为了防止用户进行二次秒杀,将对用户秒杀成功记录进行内存标记,秒杀过的用户再次进行秒杀将会直接失败。
秒杀订单成功生成之后,将在内存中进行标记,前端访问根据用户和商品查询到订单则表示秒杀成功。
#### 缓存热点数据
将频繁访问数据库的数据缓存到内存中,以后每次访问数据就从内存中读取,而不必访问数据库,大大提高了系统的效率。
热点数据,例如登录用户信息,首页内容,用户角色权限等
### 2.3 定时任务
设置定时处理的任务
例如每天凌晨十二点进行日志分析、秒杀活动开始前定时进行库存预热
### 2.4认证+授权
> `JWT+SpringSecurity`
#### JWT
**JSON WEB TOKEN**用户令牌、是验证访问用户的凭证,实现单点登录的实现方式之一。
**JWT的组成**
- JWT token的格式:header.payload.signature
- 头部:header中用于存放签名的生成算法
```json
{"alg": "HS512"}
```
- 负载:payload中用于存放用户名、token的生成时间和过期时间
```json
{"sub":"admin","created":1489079981393,"exp":1489684781}
```
- 签名:signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败
```java
//secret为加密算法的密钥
String signature = HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
```
可以在该网站上获得解析结果:https://jwt.io/
![img](C:\Users\acer\Desktop\秒杀系统\images\arch_screen_13.png)
#### JWT实现认证和授权的原理
- 用户调用登录接口,登录成功后获取到JWT的token;
- 之后用户每次调用接口都在http的header中添加一个叫Authorization的头,值为JWT的token;
- 后台程序通过对Authorization头中信息的解码及数字签名校验来获取其中的用户信息,从而实现认证和授权。
#### SpringSecurity
整合springboot的一套认证鉴权框架,