纯技术每日一题

纯技术每日一题

一、11/4 (token、过期、分布式、多个节点多次调用)

业务背景

小猛同学正在压测,发现个小问题,因为在终端设备上跟鹅厂有紧密合作,调用他们的接口时需要获取到access_token,但是这个access_token过期时间是2小时,过期后需要重新获取。

压测时发现当到达过期时间时,日志看刷出来好几个不一样的access_token,因为这个服务也是分布式部署的,多个节点同时发起了第三方接口请求导致。

虽然以最后一次获取的access_token为准,也没什么不良副作用,但是会导致多次不必要的对第三方接口的调用,也会短时间内造成access_token的 重复无效获取

讨论

  • 调用三方接口获取 只能有一个线程去调取三方接口获取 取到后放入缓存 其他的线程一直从缓存中取

  • 这个感觉应该用分布式锁把。默认从缓存取,缓存没有去获取锁。没拿到,等待一下。拿到了。在判断缓存有没有,有的话直接返回了,没有的话去重新申请,添加到缓存,然后释放锁。其他进程一样操作。

  • 嗯嗯,出现多余的access_token,是因为2个小时到了,多次请求,同时判断需要更新access_token了,那肯定就得只能让一个请求获取到access_token,其他的等待,然后再拿access_token,才能保持一致;这种肯定是是全局锁,加内部锁的,双重锁检查;但是性能会不会出现问题呢,不好说;

  • 你加锁影响并发 你不如生成两个 第一个失效了 就用第二个 ,然后再存一个备用。两个放进去 一个失效了直接取出来第二个

  • token加计时器,大部分走分布式取token,快2小时时走单线程取token。

  • 直接用etcd存储token , 设置失效时间,token失效会直接etcd推送事件,再次刷新token即可

  • 这个让我想起了netty时间伦

  • 不能所有东西都用分布式锁的 那个太影响性能了

  • 提前生成的话,需要程序中创建定时器,这个逻辑不能兜底啊,那如果定时器创建失败或者程序重启呢。还是会有获取多个token问题

  • 你看中间件的源码 有没有什么问题都优先考虑分布式锁 基本上都没有那玩意,锁都是迫不得已的解决办法

  • 为啥没人讨论 etcd的 Watch 机制:即监听机制,Watch 机制支持 Watch 某个固定的 key,也支持 Watch 一个目录(前缀机制),当被 Watch 的 key 或目录发生变化,客户端将收到通知。这个有被用技能啊

  • 虽然以最后一次获取的access_token为准,也没什么不良副作用,但是会导致多次不必要的对第三方接口的调用,也会短时间内造成access_token的 重复无效获取

    这个思考题的题干,是说会导致多次不必要的对第三方的接口的调用,目标是为了减少不必要的调用吧?

优秀方案

  1. 方法一

  2. 没最优的解决方案,最优最合适业务场景的,这个是其他的方式:

    1、不使用db,查询redis 中access_token,存在直接返回;不存在,加分布式锁,同时检查是否存在,存在返回;不存在调用第三方接口,写redis,同时设置对应的过期时间

    2、后台定时任务去刷新access_token,同时失败后去调用一次接口(容错)

    3、图上群友提供答案中为啥需要db?有些同学考虑到redis宕机的场景或者主从数据丢失的场景,也可以用db来进行兜底

二、11/4 (大数据、高并发、mysql同步、库结构不一致)

业务背景

小猛同学在某公司的大数据部门,现在要做一个高并发的同步系统,把某个业务的数据同步到自己的系统中,然后在做各种复杂的操作后提供接口给其他部门查询,每天日同步数据要达到上千万级别。

整个数据同步前期流程是从一个 mysql 库原封不动地同步到另一个 mysql 库里面去(mysql -> mysql)。

小猛同学初步架构设计是:从一个mysql监听到binlog后发送消息到MQ中,另外一个服务在消费MQ中的数据写到自己的库中,但是现在存在一个问题。

多个业务系统执行sql往mysql 里增删改对应的数据,但是小猛发现自己这边库的数据有时候跟业务方的库数据不一致。

讨论

  • mq顺序性可能,也可能消息处理的慢了 导致中间的延迟性的数据不一致
  • 坐听大神们的答案,最近我们公司在搞 三合一,三个库的数据同步一个库
  • 其实是数据迁移涉及两点:(1)历史数据迁移 (2)实时数据迁移
  • 如果要强一致性效率上就比较差了,要强一致性 只能 1个生产者1个队列1个消费者

优秀方案

  1. 其他:

    1、小猛同学是使用的RocketMQ,在监听binlog的服务中发送消息到RocketMQ的时候,一条数据的增删改路由到同一个messagequeue中保证消息的顺序性;

    2、A同学会问:如何解决监听到binlog后发送消息到RocketMQ的失败问题呢? 答:发送消息时重试、多次重试失败后,提供兜底补偿方案

    3、B同学会问:发送binlog01、binlog02,01发送失败重试,02发送成功到时无序怎么办?答:修改MQ的配置,需要等消息01发送成功后才能发送02;或者修改binlog为row模式,不使用insert update delete等逻辑上的binlog

    4、C同学会问:如何保证MQ中的消息不丢失?答:RocketMQ的master、slave高可用,基于一致性算法raft来写commitlog

    5、D同学会问:如何保证消费messagequeue时消息的有序性?答:单线程消费;如何提升系统性能?答:单线程消费后,路由到队列中,在每个队列一个线程消费。如何解决数据丢失问题?答:ack机制、优雅停机

    6、E同学会问:消费方执行本地落库的事务成功了,但是ack的时候失败,有重复消费怎么办? 答:在消费方来保证幂等

    7、F同学会问:为啥要单独在搞个MQ直接用同步工具不就好了吗?答:比如alibaba otter、离线dataX、kettle等

    8、补充说明:整个数据同步过程中,还是需要定期的比对数据;定期的全量同步数据;

三、11/9(库存超额、预算控制、分布式集群环境)

业务背景

在现今电子商务场景下,预算管理在很多场景下都有广泛应用,比如营销奖品,积分、红包的发放、商品库存管理等。

预算管理要求消耗的物品的数量能够按照预算库存进行消耗,不能超额,尽可能的保障预算的消耗率。

大规模请求量以及分布式环境下,预算管理问题更加突出,如果预算控制不合理,则会出现物品超发或是预算消耗率不高的情况,超发会导致直接的资金损失出现;预算消耗率不高则会导致商品无法卖出、奖品无法发放等问题,从而导致间接的资金损失。

编程要求:根据上面的背景,用伪代码描述如下功能,在分布式集群环境下,有一批积分需要发放给用户,存放在数据库中,实现预算的扣减方法。

讨论

  • 分布式锁+业务处理呗
  • 就是用redis分布式锁+硬编码,拿到请求后先累加再比较,超过了就回退返回超了
  • 将数据库的同步到redis一份,redis中取到后 再去操作,redis中如果取完就降级
  • 1、将积分总量提前维护到redis中
    2、请求进来时判断存在积分对应key
    如果不存在,直接返回
    否则继续执行
    3、请求进来时先从redis中扣除
    库存不足,直接返回
    否则继续执行
    4、放入异步队列
    返回成功(也可以如果队列满,直接返回)
    5、顺序消费异步队列
    6、sql更新数据库剩余积分
    类似 set stock=stock-#{num} where id=#{id} and stock>=#{num}
    更新成功则返回
    否则失效redis中积分总量的key

四、11/9 (高并发、自我保护、限流、单机)

业务背景

互联网服务端应用设计时,在处理高并发请求时,通常都会考虑系统的自我保护能力,一种办法是通过接口自身的限流能力来实现。

要求实现一个通用的限流服务类,能支持在单机环境下,当系统处理的TPS大于某个限流值时返回被限流的结果。

讨论

  • sentinel
  • 提供了一个服务类,但内部是还是集成了sentinel来实现的
  • 请求发送到mq,计数在阈值前一点,进行限流,从mq消费 然后走数据库
  • 令牌桶之类应该都可以

五、11/9 (突发情况、短期大流量、热点问题、瓶颈)

业务背景

小猛最近出去面试,面试官出了一道关于双十一的题目

内容如下:我们工作中有时候会遇到一些突发的情况,比如某电商机构双十一期间的某些热门商品会有降价促销的活动,当这其中某一件商品被数万次点击浏览或者购买时,会有一个短期的较大的流量,造成热点问题。

你觉得后台服务会有哪些瓶颈

  • 如何去解决这些问题?
  • 系统会遇到哪些问题?
  • 如何优化系统的整体架构来解决这些问题?

优秀方案

  1. 官方思路:

    主要问题:某一热点Key的流量集中达到某一主机上,缓存集群开始出现机器的宕机,此时读请求读不到数据读db,DB被击穿,引起业务雪崩

    解决方案:

    核心思路:你的系统需要能够在热点缓存突然发生的时候,直接发现他,然后瞬间立马实现毫秒级的自动负载均衡

    1、你如何自动发现热点缓存问题?

    比如通过大数据的flink技术来计算,一旦发现某一秒内的请求达到了某个阀值,判断为热点key,写入到zk中

    2、热点缓存自动加载为JVM本地缓存

    我们的业务系统可以去监听zk的node节点,把数据加载出来后放入到系统的本地缓存,比如ehcache、自己实现的LRU Cache Map都可以

    3、限流熔断保护

    每个服务实例加入熔断机制,请求达到一定阀值后直接返回一个空白的页面,不让请求访问缓存集群和db

六、11/10 (收集 GPS信息,持久化并推送)

业务背景

服务后台实时收集千万级别在线终端、手机和pad等移动终端的位置点gps信息;

然后根据gps所在城市区域,持久化并推送分发给不同的订阅用户。有哪些比较好的推送方案呢?

讨论

  • 推送到终端,只能想到轮询和长连接
  • websocket分布式集群,推送的时候根据机器或者其他的角色身份、地区来区分判断推送吧
  • 就定时调度也就够了吧
  • 后台 长连接 根据城市 code 来分组不同的用户连接信息。然后就可以根据不同的城市用户,推送不同的消息了吧?

七、11/11 (订单系统、下单、减库存)

业务背景

小猛同学最近入职了一家新的公司,业务线也刚起步,要做一个订单系统,在下单扣减库存这块小猛有些疑惑,如下:
1、是先下单扣库存?
2、还是支付完再扣库存?
3、还是预扣库存?

大家思考一下,上述三种方式分别适合什么样的场景,有什么优缺点?

讨论

  • 选择3,不然会出现支付完成,库存缺货的情况
  • 预扣做补偿吧?可以有人工介入的空间
  • 1,3流程上有啥区别么,理解不了
  • 超时取消订单啊,死信队列,超时消息
  • 预购库存就是有人下单还是加入购物车立即在数据库数量减1吗?
  • 1适合限时秒杀,2适合拼团,3适合正常下单
  • 那为什么不能有购物车功能呢?放入购物车中,付款的时候刷新购物车?这样是不是2是最好的呢?每次看的时候刷新购物车,这样还能和以前做一个对比
  • 加入购物车,还没有到下单流程那里,只有提交订单,才做预扣库存
  • 1 的情况,可能存在在提交订单的时候,库存是有物品的,但是在订单提交完成后,库存没有物品了,导致这个订单缺货的情况

优秀方案

八、11/19 (高峰、MQ卡死、降级)

业务背景

小猛同学公司线上有一个这样的系统,在某次高峰期间 MQ 中间件故障的情况下,触发了降级机制,结果降级机制触发之后运行了一小会儿,突然系统就完全卡死,无法响应任何请求。

给大家简单介绍一下这个系统的整体架构,这个系统简单来说就是有一个非常核心的行为,就是往 MQ 里写入数据,但是这个往 MQ 里写入的数据是非常核心及关键的,绝对不容许有丢失。

所以最初就设计了一个降级机制,如果一旦 MQ 中间件故障,那么这个系统立马就会把核心数据写入本地磁盘文件。

但是如果说在高峰期并发量比较高的情况下,接收到一条数据立马同步写本地磁盘文件,这个性能绝对是极其差的,会导致系统自身的吞吐量瞬间大幅度下降,这个降级机制是绝对无法在生产环境运行的,因为系统就会被高并发请求压垮。

大家思考一下,如何优化系统的整体架构来解决上面提到的问题?

讨论

  • 参考mysql的Change Buffer自己设计一个被
  • 所以这个问题的重点在于磁盘io,并不是mq

优秀方案

九、11/19 (幂等、重复调用)

业务背景

工作中如果没有唯一标识,怎么做幂等

比如活动关联优惠券,领取优惠券的时候通过用户id和活动id两个参数,如果上一次领券后想要防止重复调用,这时该怎么做

用什么来做防止极短时间重复请求

讨论

  • 优惠券为什么不设置主键
  • 用户id和活动id绑定,如果活动id为空,就是没参加活动,否则就默认一张优惠卷,获取优惠卷的时候判断是否已经获取了且优惠劵剩余数量是不为0,获取成功的时候随机个金额
  • redis 存一下关联关系 数据库表 加 redis 双重校验 , 快速判断

十、11/19(Redis、固定已知前缀)

业务背景

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来并且说明使用的方法的底层原理?

优秀方案

posted @ 2021-11-04 23:19  小么VinVin  阅读(173)  评论(1编辑  收藏  举报