- 主要解决问题
- 项目架构
- 数据库设计
- 项目启动
- 1.后端返回秒杀时间
- 2.解决跨域问题
- 3.Redis使用Cacheable
- 4.秒杀倒计时
- 5.如何防止提前下单
- 6.如何防止重复提交
- 7.网关限流
- 计数器算法
- 漏桶算法
- *令牌桶算法
- 8.请求下单数据一致性
- 9.sql出错导致feign超时调用
- 10.如何保证消息不丢失
- sentinel防服务雪崩
- 流量控制
- 熔断降级
- 高并发
- 线程安全(超卖)
- 事务一致性(分布式事务)
- 防止提前下单
- 倒计时实现
CREATE DATABASE shop
-- 商品表
CREATE TABLE goods(
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
info TEXT,
price DECIMAL(10,2),
save INT NOT NULL,
begin_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL
);
-- 订单表
CREATE TABLE orders(
id INT PRIMARY KEY AUTO_INCREMENT,
oid VARCHAR(30) NOT NULL UNIQUE,
gid INT NOT NULL,
uid INT NOT NULL,
gnumber TINYINT NOT NULL DEFAULT 1,
all_price DECIMAL(10,2) NOT NULL,
create_time TIMESTAMP DEFAULT NOW(),
STATUS TINYINT DEFAULT 0
)
项目启动
Nacos
cmd D:
cd Program Files\nacos\bin
startup.cmd -m standalone
http://192.168.52.1:8848/nacos/index.html
启动Sentinel
cd Program Files
java -jar sentinel-dashboard-1.8.1.jar
localhost:8080
账号密码sentinel
Reids
启动CentOS8
cd usr/local/redis/bin
./redis-server redis.conf
./redis-cli
auth 000
RabbitMQ
启动CentOS7
/usr/local/software/rabbitmq_software/rabbitmq_server-3.7.16/sbin/rabbitmq-server -detached
http://192.168.32.129:15672/
启动前后端各服务
如果用前端js,时间就参考本机的时间,为了保证所有时间一致,使用服务器返回时间
基本所有时间有关的都由服务器返回
2.解决跨域问题浏览器和gateway网关之间会产生跨域问题
因为浏览器的请求会先到网关
spring:
cloud:
gateway:
# gateway的全局跨域请求配置
globalcors:
cors-configurations:
'[/**]':
allow-credentials: true
allowed-originPatterns: "*"
allowed-headers: "*"
allowed-methods:
- OPTIONS
- GET
- POST
3.Redis使用Cacheable
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
<version>2.6.6version>
dependency>
@Cacheable
:自动从Redis中找,没有从数据库查完放到Redis
@CachePut
:一旦这个方法被调用,直接删除这个指定的key
太方便了
序列化问题:增加配置类
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config=config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
return config;
}
}
4.秒杀倒计时
- 客户端进入商品页面,随即从服务器获取时间
- 获取该场次时间,相减获得时间差
- 使用
setTimeout()
让服务器时间每隔一秒加一秒 - 时间差小于等于0,则改变按钮,开始秒杀
有一个专门处理时间的服务器,这个服务器可以被其他服务调用,这样保证了在集群情况下,各个服务器中的时间误差达到微妙级别
5.如何防止提前下单方案一:
下单前,查询缓存,获得当前商品的秒杀开始时间,看当前时间是否在秒杀时间之内,就可以下单;(redis中key过多,需要判断)
方案二:
通过Redis记录当前秒杀时间段的商品集合,这样Redis中key不会太多
- 管理员添加秒杀商品,存入数据库,再在Rides的该时间段的集合中加入该商品
- Redis开定时任务,时间到了更变当前秒杀商品集合
- 有下单请求,先在Redis的当前时间集合中查看是否有该商品Id,有就说明该商品正在秒杀时间段,完成购买;没有就说明不在,返回错误。
方案一:
商品页面生成时,会在页面隐藏一个UUID,提交请求后把这个UUID存入Redis,如果这个UUID已存在,就是重复提交
(只能防止误触碰;如果有人恶意提交不能判断)
方案二:
提交必须输入验证码
- 防止重复提交
- 防止恶意提交(秒杀器、脚本)(普通验证码容易被破解;现在是行为验证码)
- 拉长服务器的请求处理时间
每次请求计数器加1,达到一定阈值,停止接收请求,单位时间后清零,这样可以控制在单位时间内接收的请求数量。
如果单位请求分布不均匀,会超出阈值
漏桶算法- 水(请求)从上方倒入水桶,从水桶下方流出(被处理);
- 来不及流出的水存在水桶中(缓冲),以固定速率流出;
- 水桶满后水溢出(丢弃)。
这个算法的核心是:缓存请求、匀速处理、多余的请求丢弃。
漏桶算法对突发流量不做额外处理,无法应对存在突发特性的流量
*令牌桶算法令牌桶可以看成一个集合,有当前令牌数和最大令牌数两个参数
按照一定的速率往令牌桶中放令牌
请求还可以根据自身的量级来拿令牌数;
还可以根据其他因素限流。比如说普通用户要3个令牌,vip只要一个令牌;
桶放在Redis中,Key不同类,可以根据IP、URL限流
lua脚本
实现
路由网关先查询Redis中是否存在令牌,如果存在再取领牌(count–);这是两条命令,会发生安全性问题。Redis执行命令是单线程可以不用加锁,使用lua脚本或Redis事务
令牌桶的结构
hash结构,key为需要限流的关键属性(url),value为多个参数
- 当前剩余的令牌数
- 最大令牌数
- 每秒产生的令牌数
- 下一次可以产生令牌的时间
设计要点
- 令牌的生成方式:不用线程添加,而是每次请求主动计算要生成多少
- 令牌的预支设计:为了让重量级的请求有机会执行;预支的是时间,而不是令牌数;比如每秒生成60个令牌,如今我有30个令牌,这个请求要50个令牌,就会预支到0.5秒后的时间,而在这0.5秒之中如果有个请求在0.2需要预支令牌,告知它需要等待0.3秒;
单机
- 使用sync加锁,锁在业务层,但是事务靠aop加强,如果锁释放事务没提交却有线程修改,还是会出现问题,耗时15s
- 使用redis分布式锁,IO消耗性能,107s
- 使用mysql锁:开启数据库事务,使用
select ... for update
给查询的行加上排他锁,保证读和该原子性,耗时11s - mysql乐观锁:尝试扣减库存,修改时这条数据会自动加锁,返回是否被修改,然后执行后面的订单业务,耗时6s
- redis的lua脚本:把库存放到redis,使用lua脚本在reids查询并下单,最后把redis数据同步到数据库,无锁,耗时4s
sql错误,导致消息一直不能被消费,消费者就一直消费(.ListenerExecutionFailedException),feign调用超时出错
10.如何保证消息不丢失- 交换机持久化:在上游服务配置声明交换机时的参数
- 队列持久化:在下游服务接收消息,在声明队列时
durable
参数设置
- 消息持久化? 这个不用配置;因为底层
MessageProperties
的持久化策略默认是MessageDeliveryMode.PERSISTENT
,初始化时默认消息时持久化的
限制业务访问的qps,避免服务因流量突增而故障,预防服务挂掉
熔断降级欢迎分享,转载请注明来源:内存溢出
评论列表(0条)