徐无忌并发编程笔记:案例实战:并发编程之12306抢票业务重点分析
完成:第一遍
1.抢票业务的瓶颈在哪里?
流量非常大时,数据库是扛不住的,是整个系统的瓶颈
真实场景会从各个方面减少流量,保证最终能够访问数据库的只是很少的一部分流量
一般会从产品层面、应用各层次、限流、消息队列削锋、优先更新缓存再同步入库等等方式来保证只能让一小部分请求进入到后台。
2.由于限流目的,可以对不能通过的请求做哪些措施?
一般我们会限制一秒钟的能够通过的请求数以此来保护系统
,对于不能通过的请求可以:
措施一:拒绝服务(提示友好信息或者错误页面)
措施二:排队或等待
措施三:降级(返回默认数据)
3.如何限制一秒钟的能够通过的请求数?
实现方式一:最简单的是通过计数器进行实现(秒杀活动通过该方式)
比如限流qps为100,从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数器加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝。等到1s结束后,把计数恢复成0,重新开始计数
通过 AtomicInteger底层的incrementAndGet()方法来给计数器加1并返回最新值,通过这个最新值和阈值进行比较
4.什么是计数器限流的弊端即突刺现象?
这种实现方式,相信大家都知道有一个弊端:如果我在单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象”
5.什么是令牌桶算法?
存在一个桶,用来存放固定数量的令牌,以一定的速率往桶中放令牌
请求需要先获取令牌,拿到令牌后才有机会继续执行,否则选择等待可用的令牌、或者直接拒绝
实现思路:可以准备一个队列,用来保存令牌
另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行
通过Google开源的guava包,我们可以轻松的创建一个令牌桶算法的限流器,通过RateLimiter类的create方法,创建限流器,通过acquire()方法获取令牌
6.如何解决并发编程经典的超卖现象?
假如一个车次的余票有100张,买票的过程,首先从数据库中获取到余票为100,然后减1变成99,最后更新库存表,将车票的库存改为99
高并发的情况下,假设两个线程同时下单,此时从数据库中获取到余票都为100,进行减1操作,最后更新库存表,都会将库存改为99。实际上卖出去了两张票,但是库存显示仍为99
解决方法一:加锁机制
将减库存的整个操作进行原子性的包装,通过lock获取成功后,才能去进行获取余票,减一,最后更新这样的操作
具体可以使用ReetrantLock或者synchronized关键字来实现
解决方法二:排队机制
应用一个队列缓存,将多线程变为单线程读写,具体可以使用单线程的线程池 newSingleThreadExecutor
解决方法三:数据库的乐观锁
通过增加版本号的方式实现乐观锁,类似CAS机制
依赖数据库引擎提供的表锁、行锁