小厂后端十连问(附答案)(二)

tony_ybliu
发布于 2022-5-30 17:31
浏览
0收藏

 

7. ThreadLocal的使用场景有哪些?原理?内存泄漏? 
ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。

ThreadLocal的应用场景

  • 数据库连接池
  • 会话管理中使用
    ThreadLocal内存结构图:

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
ThreadLocal原理

  • Thread对象中持有一个ThreadLocal.ThreadLocalMap的成员变量。
  • ThreadLocalMap内部维护了Entry数组,每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型值。
  • 每个线程在往ThreadLocal里设置值的时候,都是往自己的ThreadLocalMap里存,读也是以某个ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程隔离。
    ThreadLocal 内存泄露问题

先看看一下的TreadLocal的引用示意图哈,

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用,如下

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
弱引用:只要垃圾回收机制一运行,不管JVM的内存空间是否充足,都会回收该对象占用的内存。
弱引用比较容易被回收。因此,如果ThreadLocal(ThreadLocalMap的Key)被垃圾回收器回收了,但是因为ThreadLocalMap生命周期和Thread是一样的,它这时候如果不被回收,就会出现这种情况:ThreadLocalMap的key没了,value还在,这就会造成了内存泄漏问题

如何解决内存泄漏问题?使用完ThreadLocal后,及时调用remove()方法释放内存空间。

 

8、kafka是如何保证消息的有序性? 
kafka这样保证消息有序性的:

  • 一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。(全局有序性
  • 写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。

 

大家可以看下消息队列的有序性是怎么推导的哈:

 

消息的有序性,就是指可以按照消息的发送顺序来消费。有些业务对消息的顺序是有要求的,比如先下单再付款,最后再完成订单,这样等。假设生产者先后产生了两条消息,分别是下单消息(M1),付款消息(M2),M1比M2先产生,如何保证M1比M2先被消费呢。

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
为了保证消息的顺序性,可以将将M1、M2发送到同一个Server上,当M1发送完收到ack后,M2再发送。如图:

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
这样还是可能会有问题,因为从MQ服务器到服务端,可能存在网络延迟,虽然M1先发送,但是它比M2晚到。

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
那还能怎么办才能保证消息的顺序性呢?将M1和M2发往同一个消费者,且发送M1后,等到消费端ACK成功后,才发送M2就得了。

 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
消息队列保证顺序性整体思路就是这样啦。比如Kafka的全局有序消息,就是这种思想的体现: 就是生产者发消息时,1个Topic只能对应1个Partition,一个 Consumer,内部单线程消费。

但是这样吞吐量太低,一般保证消息局部有序即可。在发消息的时候指定Partition Key,Kafka对其进行Hash计算,根据计算结果决定放入哪个Partition。这样Partition Key相同的消息会放在同一个Partition。然后多消费者单线程消费指定的Partition

 

9、Nacos的选举机制了解嘛?说下Raft算法? 
Nacos作为配置中心的功能是基于Raft算法来实现的。

Raft 算法是分布式系统开发首选的共识算法,它通过“一切以领导者为准”的方式,实现一系列值的共识和各节点日志的一致。

Raft选举过程涉及三种角色和任期(Term):

  • Follower:默默地接收和处理来自Leader的消息,当等待Leader心跳信息超时的时候,就主动站出来,推荐自己当Candidate。
  • Candidate:向其他节点发送投票请求,通知其他节点来投票,如果赢得了大多数(N/2+1)选票,就晋升Leader。
  • Leader:负责处理客户端请求,进行日志复制等操作,每一轮选举的目标就是选出一个领导者;领导者会不断地发送心跳信息,通知其他节点“我是领导者,我还活着,你们不要发起新的选举,不用找个新领导者来替代我。”
  • Term:这跟民主社会的选举很像,每一届新的履职期称之为一届任期
    领导选举过程

1.在初始时,集群中所有的节点都是Follower状态,都被设定一个随机选举超时时间(一般150ms-300ms):
 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
2.如果Follower在规定的超时时间,都没有收到来自Leader的心跳,它就发起选举:将自己的状态切为 Candidate,增加自己的任期编号,然后向集群中的其它Follower节点发送请求,询问其是否选举自己成为Leader:
 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
3.其他节点收到候选人A的请求投票消息后,如果在编号为1的这届任期内还没有进行过投票,那么它将把选票投给节点A,并增加自己的任期编号:
 小厂后端十连问(附答案)(二)-鸿蒙开发者社区
4.当收到来自集群中过半节点的接受投票后,A节点即成为本届任期内 Leader,他将周期性地发送心跳消息,通知其他节点我是Leader,阻止Follower发起新的选举:
 小厂后端十连问(附答案)(二)-鸿蒙开发者社区

10、聊一聊TCC补偿机制 
TCC是分布式事务的一种解决方案。它采用了补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。TCC(Try-Confirm-Cancel)包括三段流程:

  • try阶段:尝试去执行,完成所有业务的一致性检查,预留必须的业务资源。
  • Confirm阶段:该阶段对业务进行确认提交,不做任何检查,因为try阶段已经检查过了,默认Confirm阶段是不会出错的。
  • Cancel 阶段:若业务执行失败,则进入该阶段,它会释放try阶段占用的所有业务资源,并回滚Confirm阶段执行的所有操作。
    下面再拿用户下单购买礼物作为例子来模拟TCC实现分布式事务的过程:

 

假设用户A余额为100金币,拥有的礼物为5朵。A花了10个金币,下订单,购买10朵玫瑰。余额、订单、礼物都在不同数据库。


TCC的Try阶段:

  • 生成一条订单记录,订单状态为待确认。
  • 将用户A的账户金币中余额更新为90,冻结金币为10(预留业务资源)
  • 将用户的礼物数量为5,预增加数量为10。
  • Try成功之后,便进入Confirm阶段
  • Try过程发生任何异常,均进入Cancel阶段
     小厂后端十连问(附答案)(二)-鸿蒙开发者社区
    TCC的Confirm阶段:
  • 订单状态更新为已支付
  • 更新用户余额为90,可冻结为0
  • 用户礼物数量更新为15,预增加为0
  • Confirm过程发生任何异常,均进入Cancel阶段
  • Confirm过程执行成功,则该事务结束
     小厂后端十连问(附答案)(二)-鸿蒙开发者社区
    TCC的Cancel阶段:
  • 修改订单状态为已取消
  • 更新用户余额回100
  • 更新用户礼物数量为5
     小厂后端十连问(附答案)(二)-鸿蒙开发者社区
  • TCC的优点是可以自定义数据库操作的粒度,降低了锁冲突,可以提升性能
  • TCC的缺点是应用侵入性强,需要根据网络、系统故障等不同失败原因实现不同的回滚策略,实现难度大,一般借助TCC开源框架,ByteTCC,TCC-transaction等。

 

文章转自公众号:小白debug

分类
标签
已于2022-5-30 17:31:35修改
收藏
回复
举报
回复