目录
服务器反应慢
线上服务器cup飙升,如果定位Java代码?
服务器变慢如何诊断处理?
线上接口负载剧增,快扛不住了,解决方法是什么?
秒杀设计
从全局角度如何设计一个秒杀系统
秒杀活动里遇到的问题
如何设计百万并发场景里的枪优惠券
如何设计春节抢红包的金额的算法。
如果设计订单超时自动取消
时间轮
服务器反应慢
线上服务器cup飙升,如果定位Java代码?
使用top命令找到CPU高的进程
使用SP-MP命令找到进程下CPU高的线程ID
printf命令把tid转换16进制数
使用jstack命令输出线程运行状态日志信息。
服务器变慢如何诊断处理?
CPU利用率
CPU利用率过高,说明服务器当前处理的指令比较多,当cup处理不过来的时候,指令执行效率就会下降,用户感觉程序处理变慢了。解决方法查看线上服务器cup飙升,如果定位Java代码。
CPU利用率过低,说明程序使用资源过低,可以增加线程数量提升程序性能。
磁盘io效率
程序运行过程里会直接或间接磁盘io操作,例如程序直接读写磁盘文件,或者依赖第三方组件对磁盘进程持久化存储,此时磁盘io效率就会对程序产生影响。
使用iostat命令查看,如果磁盘负载较高,可以针对性优化。例如,使用缓存减少io次数,使用顺序写代替随机写,减少寻址开销,使用mmap,减少内存拷贝次数
内存瓶颈
内存是一块临时存储数据的组件,所有CPU指令都需要在内存里读写,内存的合理使用减少程序和磁盘io的频率,减少网络io频率,提升io性能。
jvm对内存合理分配能够避免频繁的GC,当内存使用率高的时候,可以使用dump命令检查jvm内存,使用mat工具分析找出大对象,排查是否存在内存泄漏。如果堆内对象正常,可能是堆外内存被大量使用,这个时候使用pmap检查进程内存分配情况,如果CPU和内存使用正常,这时使用jstat查看cg日志,分析用户线程暂停时间,gc次数等指标.
线上接口负载剧增,快扛不住了,解决方法是什么?
增加缓存。因为增加缓存是解决系统性能问题的最快速,最高效的方案,他能够快速提升系统的吞吐量,但是需要修改源代码。零时的解决方案是增加节点,将程序部署到新的节点上,在流量入口增加限流和分发,但是增加节点会增加成本。
当接口负载扛不住的时候,可以优先增加缓存缓解负载压力,例如将读取频繁的数据写到缓冲区里,将动态页面静态化。如果压力还是很大,再增加限流策略,例如消息队列,如果压力过大,在考虑增加服务节点。
秒杀设计
从全局角度如何设计一个秒杀系统
秒杀是电商系统常见业务模式,为了吸引用户刺激消费而设计的一种促销活动。例如整点秒杀,单个商品秒杀。
特点,
1.瞬时流量大,秒杀活动结束后流量消失,所以不能用堆机器的方式提高QPS成本太高。QPS,queries per second,每秒能够发出的查询数量,是衡量服务器性能的指标,用来评估系统处理高并发的能力。
2.秒杀商品库存量少,但是抢购的人多。
3.在秒杀时间没有到之前,刷新量非常大,导致静态资源访问剧增。
秒杀活动前的准备工作,运营策划一场秒杀活动,盘点秒杀商品的库存,防止活动中出现资源没有准备好的情况,然后在秒杀中台创建秒杀活动,入录秒杀数据。使用缓存完成交互,例如库存数量,商品编号。业务方根据实际情况对秒杀活动进行评估,压力测试。
秒杀活动里遇到的问题
1.需要满足高并发,响应快,用户量很大,并发量很高,对服务的要求是抗的住并发。我们根据压力测试的结果,提前准备好服务器。测量tps瓶颈点,看看是否到达一个好的状态,在压测的过程里对,提前对代码薄弱点做优化。Redis数据预加载,配置专用mq,前端页面静态化,cdn加速,带宽扩展,服务器集群负载均衡配置。这样一套组合下来可以支持高并发场景里的快速响应。
2.防止超卖。因为秒杀的是精品,需要防止由于技术原因导致超卖,库存变负数。在秒杀活动之前把数据同步到Redis里,在开始活动的时候,用户进行抢购,我们通过操作Redis对商品库存进行减扣,把减扣的消息发送到消息队列里,之后交给具体的业务系统进行处理,例如,订单,支付服务等,在处理完成之后,再和MySQL数据库进行交互,完成库存减扣的数据同步。由于库存先从Redis更新再同步到数据库,如果同一个时间有多个用户抢单,就会减扣多个库存,当抢单的数量超过Redis的缓存的库存数量时,就出现了超卖问题。如何避免超卖问题?把减扣库存的操作,分2个步骤完成,判断库存是否充足和减库存,这2个动作使用lua脚本执行,保证这2个操作的原子性,避免超卖。
少买问题,库存减扣成功但是交易失败,导致用户购买失败,例如Redis减扣的库存,但是减扣成功的消息没有发送到业务系统进行后续处理,导致订单生成失败。如何解决?在消息队列里设计一个重试策略,例如,消息发送失败之后,再3次重试,如果超过重试次数消息发送还是没有成功,将消息持久化到磁盘,使用补偿机制来轮询,直到发送成功再进行后续的业务处理。
3.防止恶意刷单,防止机器人恶意抢单,导致真正的用户抢不到。使用限流解决。秒杀是一种很快就结束的活动,不用担心因为限流影响用户体验。限流具体有2种策略,1.用户ID限流 ,例如一个ID1s内只能请求10次 2.同一个IP地址限流,例如,一个IP地址最大只能请求10次。做了限流之后防止一些无效的请求发送的服务器,从而减轻服务器压力。隐藏秒杀的连接,避免爬虫爬取到,对秒杀连接进行加密处理,只有时间到了才可以真实连接。例如不能以递增的方式来构建URL,避免找到规律提前拼接好。
4.页面访问量大,提前将页面静态化。资源静态化将URL唯一化处理,对页面提前缓存,保证用户在请求url的时候,不再解析请求头和重组Http,直接找到静态资源返回即可,不与后台服务做数据交互,提高页面访问效率。另外可以将静态资源放在cdn上。cdn通过就近分发原理,可以提高资源的响应速度和命中率。还可以开启资源压缩减少传输数据量,提高速度。
5秒杀入口开关设计,活动开始之前把按钮隐藏或禁用,或者显示为秒杀倒计时,在秒杀活动开始前再开放,这样可以限制一些无效请求发送到后端,减轻的服务器流量压力。
6.订单的后续逻辑设置为异步。将秒杀成功的消息放入mq里,后续服务可以慢一些反馈给用户。
7.订单失败补偿。在用户秒杀成功后,必须保证订单处理一定成功,不能因为程序原因,导致订单丢失,我们使用mq方式进行驱动,如果消费失败,就发起重试,还需要有报警机制,保证及时监控到预警。
8.服务降级。由于技术或人为原因,导致秒杀价格设置错误。例如1元一个iPhone。这种情况我们需要及时止损,不再允许用户下单。提前设置好降级开关,通知用户,例如秒杀活动已经结束。出现了技术性错误,服务器出现故障,导致大量请求无法处理。这种情况是我们意料不到的。这个时候开启服务降级及时止损。虽然会造成一些投诉,但是我们可以通过手动补偿优惠券,红包等方式安抚用户。
如何设计百万并发场景里的枪优惠券
在618,双11期间,商家会在某个时间点发送大量优惠券。抢到优惠券之后,用户下单的时候,可以减扣支付金额。
抢券,在活动发布之前把优惠券的库存存储到Redis里面,之后抢券的整个过程和缓存交互。保证抢券的速度快。
业务效验,
通用业务逻辑处理,优惠券是否在有效期内,是否在在本平台发放,用户是否具有领取权限。
个性业务逻辑处理,数据库交互,rpc交互,这些操作比较耗时。
把不耗时的业务逻辑放在前面,让他抵挡大部分请求,然后再进行耗时操作。例如,规定每个用户只能领取一次,在领券之前,判断用户是否重复领券,若没有领券,才能减扣优惠券的数量,将优惠券发送到用户账号。
重复领券,使用Redis的zset操作或者布隆过滤器,使用lua脚本保证这些操作的原子性。
扣减券的库存,因为一直有人在抢券,所以券的库存数量一直在发生变化,怎么同步到数据库?他是可以接受延迟的,可以使用定时任务定时读取Redis里的数据同步到数据库里。
追加券的库存,使用mq将追加的库存数量同步到Redis。
发送业务mq,在扣减库存成功之后,我们通过业务系统发送一条mq消息,交给后续业务进一步处理,例如,记录这个券被谁领取了,这样的业务可以设计成异步执行,因为可以接受延迟。
发券到账
如何设计春节抢红包的金额的算法。
春节的时候,平台推出的抢红包活动。抢红包的设计流程。
1.设置红包的总金额和红包个数
2.提前计算出每个红包的金额
3.分享红包,用户抢红包
抢红包和秒杀下单的场景类似。在红包发出去后,用户看到的是抢红包和晒红包的过程。抢到的红包类似在后台事先分配好一个商品。在我们发红包的时候,相当于发放了一个虚拟商品,也会生成一个订单。在生成订单之后,就会调用支付平台的支付接口。完成支付之后,红包就创建好了。每个红包有一个唯一的标识码,方便系统识别。
在红包发出去之前,系统根据设置好的红包个数和总金额,预先生成每个红包的金额,并且给每一个红包设置一个令牌。用户在抢红包的时候,只要读取计算好的结果即可。
这时可以把红包发送的微信群里,用户可以开始抢红包,例如100元10个红包,就会生成10个令牌,先抢到的用户获得一个令牌,并且获取一个对应的金额。
金额算法,随机法,二倍均值法,每次都从保底金额0.01元到最大值的区间取值。最大值=红包的剩余金额/剩余红包的个数*2,(0.01,m/n*2)
如果设计订单超时自动取消
使用时间轮来轮询超时订单,时间轮算法,采用环状数组+链表来管理延迟任务,计算出订单的超时时间,再加入时间轮里。
使用死信队列,利用mq里的延迟消息功能,为新的订单设置一个超时投递时间,再把消息发送的mq服务器。消息发送到mq服务器后不会立刻投递,到达延时时间后再投递,此时投递出去的消息相当于一个取消订单的通知。
时间轮
是一种用来存储定时任务的环状数组,工作原理,2部分组成,一个是环状数组,一个是遍历环状数组的指针。定义一个固定长度的环状数组,数组的每一个元素代表一个时间刻度,假设是1s,如果长度是8就代表8s,然后有一个指针按照顺时间方向无限循环遍历这个数组,每隔最小时间单位前进一个数组索引,这个指针转一圈代表8s,转2圈代表16s,数组里的的元素是用来存储定时任务的容器。当我们向时间轮里添加一个定时任务的时候,根据定时任务的执行时间计算出数组下标。有可能在某个时间刻度上存在多个定时任务,这时采用双向链表的方式存储。
当指针指向某个数组的时候,就会把这个数组里存储的任务取出来,遍历链表逐个执行里面的任务,如果某个定时任务的执行时间大于数组所表示的长度,使用一个圈数来表示该任务延迟执行时间。
使用时间轮管理定时任务的好处,
1.降低定时任务的添加和删除的时间复杂度,提高性能
2.保证每次执行定时任务的时间复杂度都是O1,在定时任务密集的情况下,性能好
缺点,对于执行时间严格的任务,时间轮不合适,因为时间轮算法的精度取决于最小时间单元的粒度,假设1s为一个时间刻度,那么小于1s的任务无法被时间轮调度。