分布式相关问题总结(精选)

目录

分布式ID生成的集中方案?

雪花算法原理? 

分布式锁在项目中有哪些应用场景?

分布式锁有哪些解决方案?

Redis做分布式锁用什么命令?

Redis做分布式锁死锁有哪些情况,如何解决? 

Redis如何做分布式锁? 

基于ZooKeeper的分布式锁实现原理是什么?

ZooKeeper和Redis做分布式锁的区别? 

MySQL如何做分布式锁? 

计数器算法是什么?

滑动时间窗口算法是什么? 

漏桶限流算法是什么? 

令牌桶限流算法是什么? 

你设计微服务时遵循什么原则? 

CAP定理是什么?

BASE理论是什么?

TCC解决方案是什么?

TCC空回滚是解决什么问题的?

如何解决TCC幂等问题?

如何解决TCC中悬挂问题?

可靠消息服务方案是什么? 

​最大努力通知方案的关键是什么?

什么是分布式系统中的幂等?

幂等有哪些技术解决方案?

对外提供的API如何保证幂等性? 

分布式微服务项目你是如何设计的? 

认证(Authentication)和授权(Authorization)的区别是什么?

Cookie和Session有什么区别?如何使用Session进行身份验证?

什么是Token?什么是JWT?如何基于Token进行身份验证? 

为什么Cookie无法防止CSRF攻击,而Token可以? 

​分布式架构下,Session共享有什么方案?

Kafka的rebalance机制是什么?

Kafka是pull还是push?以及优劣势分析? 

Kafka消息丢失的场景有哪些? 

Kafka中zookeeper的作用是什么? 

Kafka中高性能如何保障? 

简述Kafka架构设计是什么样? 

Zookeeper watch机制是什么? 

Zookeeper的命名服务、配置管理、集群管理分别是什么? 

Zookeeper的数据模型和节点类型有哪些? 

RabbitMQ的架构设计是什么样的?

RabbitMQ事务消息原理?

RabbitMQ如何确保消息发送和消息接收? 

RabbitMQ死信队列、延时队列分别是什么? 


分布式ID生成的集中方案?

分布式Id生成的几种方案_后青春的诗-CSDN博客

雪花算法原理? 

雪花算法原理(SnowFlake)_后青春的诗-CSDN博客

分布式锁在项目中有哪些应用场景?

1.系统是一个分布式系统,比如集群,Java的锁已经锁不住了。

        比如在生产中,我们为了避免单点故障,一般都是通过集群部署,前面有一个负载均衡,后面有几个一模一样的服务,启动这样的相同的好几个服务,如果为了保证服务请求的串行,一般我们加的synchronized或者lock这种锁来处理,这种属于JVM级别的锁,如果每个服务都用这种锁的话,那么他分散在不同的JVM集群中,它就不是一把锁了,就失去了锁的互斥性,这样也会导致并发的问题,所以此时我们需要用到分布式锁。

2.操作共享资源,比如操作库里唯一的用户数据。

        比如有订单系统,用户系统,优惠券系统,它们都需要修改用户的数据,也就是多个系统要修改同一份数据,这个时候就需用用分布式锁来控制,因为多个系统可能分布在不同的服务器上。

3.同步访问,即多个进程同时操作共享资源。

分布式锁有哪些解决方案?

1.Redis的分布式锁,很多大公司会基于Redis做一些扩展开发,setnx key value,Redisson。

2.基于Zookeeper,临时节点,顺序节点。

3.基于数据库,比如Mysql,主键或唯一索引的唯一性来确定是否拿到锁(互斥性),多个请求去访问数据库中的同一条数据,根据主键或唯一索引的唯一性来确保只能有一个请求是最终成功的。

Redis做分布式锁用什么命令?

SETNX

格式:setnx key value 将 key 的值设为 value,当且仅当key不存在,才可以设置成功。

若给定的key已经存在,则setnx不做任何动作,操作失败。

SETNX是【Set if Not exists】(如果不存在)的简写。

用法示例:

加锁:set key value nx ex 10s 主要是为了防止死锁

释放锁:delete key

Redis做分布式锁死锁有哪些情况,如何解决? 

情况1:加锁之后,但是并没有释放锁。如果没有释放锁,这时在通过setnx去加锁的话是加不上的。其他的程序永远都加不上锁,就会造成死锁。

解决:这时需要加释放锁的操作,如delete key。

情况2:加锁之后,程序还没有执行释放锁,这时程序挂了,造成死锁。

解决:需要用key的过期机制。在程序挂了之后的一段时间内,这个时候key是有效的,过了这个时间窗口,其他的程序就可以加锁了,这就是让key过期,这样就避免了死锁的发生。

Redis如何做分布式锁? 

假设有两个服务A、B都希望获得锁,执行过程大致如下:

Step1:服务A为了获得锁,向Redi发起如下命令:SET productId:lock 0xx9p022 NX EX 30000,其中,“productId”由自己定义,可以是与本次业务有关的Id,“0xx9p022”是一串随机值,必须保证全局唯一,“NX”指的是当且仅当key(也就是案例中的“productId:lock”)在Redis中不存在时,返回执行成功,否则执行失败。“EX 30000”指的是在30秒后,key将被自动删除。执行命令后返回成功,表明服务成功的获得了锁。

Step2:服务B为了获得锁,向Redis发起同样的命令:SET productId:lock 0xx22211 NX EX 30000,由于Redis中已经存在同名的key,且并未过期,因此命令执行失败,服务B未能获得锁。服务B进入循环请求状态,比如每隔1秒钟(自行设置)向Redis发送请求,直到执行成功并获得锁。

Step3:服务A的业务代码执行时长超过了30秒,到时key超时,因此Redis自动删除了key。此时服务B再次发送命令执行成功,假设本次请求中设置的value值为00001111。此时需要在服务A中对key进行续期,也就是通过watch dog来进行监管。

Step4:服务A执行完毕,为了释放锁,服务A会主动向Redis发起删除key的请求。注意:在删除key之前,一定要判断服务A持有的value与Redis内存储的value是否一致。比如当前场景下,Redis中的锁早就不是服务A持有的那一把了,而是有服务B创建的,如果贸然使用服务A持有的key来删除锁,则会误将服务B的锁释放掉。此外,由于删除锁时涉及到一系列判断逻辑,因此一般使用lua脚本,具体lua脚本如下:

if redis.call("get", KEYS[1]) == ARGV[1] then
	return redis.call("del", KEYS[1])
else
	return 0
end

基于ZooKeeper的分布式锁实现原理是什么?

顺序节点特性:

        使用ZooKeeper的顺序节点特性,假如我们在/lock/目录下创建3个节点,ZK集群会按照发起创建的顺序来创建节点,节点分别为/lock/000000001、/lock/000000002、/lock/000000003,最后一位数是依次递增的,节点名称由zk来完成。

临时节点特性:

        ZK中还有一种名为临时节点的节点,临时节点由某个客户端创建,当客户端与ZK集群断开连接,则该节点自动被删除。EPHEMERAL_SEQUENTIAL为临时顺序节点。

        根据ZK中节点是否存在,可以作为分布式锁的锁状态,以此来实现一个分布式锁,下面是分布式锁的基本逻辑:

1.客户端1调用create()方法创建名为“/业务ID/lock-”的临时顺序节点。

2.客户端1调用getChildren("业务ID")方法来获取所有已经创建的子节点。

3.客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,就是看自己创建的序列号是否排第一,如果是第一,那么就认为这个客户端1获得了锁,在它前面没有别的客户端拿到锁。

4.如果创建的节点不是所有节点中需要最小的,那么则监视比自己创建节点的序列号小的最大的节点,进入等待。直到下次监视的子节点变更的时候,再进行子节点的获取,判断是否获取锁。

ZooKeeper和Redis做分布式锁的区别? 

Redis:

 1.Redis只保证最终一致性,副本间的数据复制是异步进行(Set是写,Get是读,Redis集群一般是读写分离架构,存在主从同步延迟情况),主从切换之后可能有部分数据没有复制过去【丢失锁】的情况,故对数据一致性要求比较强的业务不推荐使用Redis,而是推荐使用ZooKeeper。

2.Redis集群各方法的响应时间均为最低。随着并发量和业务数量的提升其响应时间会有明显上升(公网集群影响因素偏大),但是极限qps(每秒查询率)可以达到最大且基本无异常。

ZooKeeper:

1.使用ZooKeeper集群,锁原理是使用ZooKeeper的临时顺序节点,临时顺序节点的生命周期在Client与集群的Session结束时结束。因此,如果某个Client节点存在网络问题,与ZooKeeper集群断开连接,Session超时同样会导致锁被错误的释放(导致被其他线程错误的持有),因此ZooKeeper也无法保证完全一致。

2.ZooKeeper具有较好的稳定性,响应时间抖动很小,没有出现异常。但是随着并发量和业务数量的提升其响应时间和qps会明显下降。

总结:

(1)ZooKeeper每次进行锁操作前都要创建若干节点,完成后要释放节点,会浪费很多时间。

(2)Redis只是简单的数据操作,没有这个问题。

MySQL如何做分布式锁? 

        在MySql中创建一行表,设置一个主键或者UNIQUE这个KEY就是要锁的KEY,所以同一个KEY在Mysql表里只能插入一次,这样对锁的竞争就交给了数据库,处理同一个KEY数据库保证了只有一个节点能插入成功,其他节点都会插入失败。

        DB分布式锁的实现:通过主键Id或者唯一索引的唯一性进行加锁,说白了就是加锁的形式是向一张表中插入一条数据,该条数据的Id就是一把分布式锁,例如当一次请求插入了一条Id为1的数据,其他想要进行插入数据的并发请求必须等第一次请求执行完成后删除这条Id为1的数据才能继续插入,实现了分布式锁的功能。

lock和unlock的实现思路,代码:

def lock:
	exec sql:insert into locked-table (xxx) values (xxx)
	if result == true :
		return true
	else :
		return false
		
def unlock:
	exec sql:delete from lockedOrder where order_id='order_id'

计数器算法是什么?

         计数器算法,是指在指定的时间周期内累加访问次数,达到设定的阈值时,触发限流策略。下一个时间周期进行访问时,访问次数清零。此算法无论是在单机还是分布式环境下实现都非常简单,使用redis的incr原子自增性,在结合key的过期时间,就可以轻松实现。

        从上图我们可以看出,我们设置一分钟的阈值是100,在0:00到1:00内请求数是60,当到1:00时,请求数清零,从0开始计算,这时在1:00到2:00之间我们能处理的最大请求数为100,超过100个的请求,系统都拒绝。

        这个算法有一个临界问题,比如在上图中,在0:00到1:00内,只在0:50有60个请求,而在1:00到2:00之间,只有1:10有60个请求,虽然在两个一分钟内,都没有超过100个请求,但是在0:50到1:10这20秒内,确实有120个请求,虽然在每个周期内都没有超过阈值,但是在这20秒内,已经远远超过了我们原来设置的1分钟内100个请求的阈值。

滑动时间窗口算法是什么? 

        为了解决计数器算法的临界值的问题,发明了滑动窗口算法。在TCP网络通信协议中,就采用了滑动时间窗口算法来解决网络拥堵问题。

        滑动时间窗口是将计数器算法中的实际周期切分成多个小的时间窗口,分别在每个小的时间窗口中记录访问次数,然后根据时间将窗口往前滑动并删除过期的小时间窗口。最终只需要统计滑动窗口范围内的小时间窗口的总的请求数即可。

         在上图中,假设我们设置一分钟的请求阈值是100,我们将一分钟拆分成4个小时间窗口,这样,每个小的时间窗口只能处理25个请求,我们用虚线方框表示滑动时间窗口,当前窗口的大小是2,也就是在窗口内最多能处理50个请求。随着时间的推移,滑动窗口也随着时间往前移动,比如上图开始时,窗口是0:00到0:30的这个范围,过了15秒后,窗口是0:15到0:45的这个范围,窗口中的请求重新清零,这样就很好的解决了计数器算法的临界值问题。

        在滑动时间窗口算法中,我们的小窗口划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。

漏桶限流算法是什么? 

        漏桶算法的原理就像它的名字一样,我们维持一个漏斗,它有恒定的流出速度,不管水流流入的速度有多块,漏斗出水的速度始终保持不变,类似于消息中间件,不管消息的生产者请求量有多大,消息的处理能力取决于消费者。

        漏桶的容量=漏桶的流出速度*可接受的等待时长。在这个容量范围内的请求可以排队等待系统的处理,超过这个容量的请求,才会被抛弃。

在漏桶限流算法中,存在下面集中情况:

1.当请求速度大于漏桶的流出速度时,也就是请求量大于当前服务所能处理的最大极限值时,触发限流策略。

2.请求速度小于或等于漏桶的流出速度时,也就是服务的处理能力大于或等于请求量时,正常执行。

漏桶算法有一个缺点:当系统在短时间内有突发的大流量时,漏桶算法就处理不了了。 

令牌桶限流算法是什么? 

        令牌桶算法,是增加一个大小固定的容器,也就是令牌桶,系统以恒定的速率向令牌桶中放入令牌,如果有客户端来请求,先需要从令牌桶中拿一个令牌,拿到令牌,才有资格访问系统,这是令牌桶中少一个令牌。当令牌桶满的时候,在向令牌桶生成令牌时,令牌会被抛弃。 

        在令牌桶算法中,存在以下几种情况:

1.请求速度大于令牌的生成速度:那么令牌桶中的令牌会被取完,后续再进来的请求,由于拿不到令牌,会被限流。

2.请求速度等于令牌的生成速度:那么此时系统处于平稳状态。

3.请求速度小于令牌的生成速度:那么此时系统的访问量远远低于系统的并发能力,请求可以被正常处理。

令牌桶算法,由于有一个桶的存在,可以处理短时间大流量的场景。这是令牌桶和漏桶的一个区别。

你设计微服务时遵循什么原则? 

1.单一职责原则:让每个微服务能独立,有界限的工作,每个服务只关注自己的业务,做到高内聚。 

2.服务自治原则:每个服务要能做到独立开发、独立测试、独立构建、独立部署、独立运行。与其他服务进行解耦。

3.轻量级通信原则:让每个服务之间的调用是轻量级的,并且能够跨平台、跨语言。比如采用RESFUL风格,利用消息队列进行通信等。

4.粒度进化原则:对每个服务的粒度把控,其实没有统一标准,这个得结合我们解决的具体业务问题。不要过度设计。服务的粒度随着业务和用户的发展而发展。

总结起来:软件是为业务服务的,好的系统不是设计出来的,而是进化出来的。

CAP定理是什么?

概念解释:

C:一致性(Consistency),数据在多个副本节点中保持一致,可以理解成两个用户访问两个系统A和B,当A系统数据有变化时,及时同步给B系统,让两个用户看到的数据是一致的。

A:可用性(Availability),系统对外提供服务必须一直处于可用状态,在任何故障下,客户端都能在合理时间内获得服务端非错误的相应。

P:分区容错性(Partition tolerance),在分布式系统中遇到任何网络分区故障,系统仍然能对外提供服务。网络分区,可以这样理解,在分布式系统中,不同的节点分布在不同的子网络中,有可能子网络中只有一个节点,在所有网络正常的情况下,由于某些原因导致这些节点之间的网络出现故障,造成整个节点环境被切分成了不同的独立区域,这就是网络分区。

CAP定理:指的是一个分布式系统最多只能同时满足C一致性(Consistency)、A可用性(Availability)和P分区容错性(Partition tolerance)这三项中的两项。

 原理解释:

 

BASE理论是什么?

TCC解决方案是什么?

TCC(Try-Confirm-Cancel)是一种常见的分布式事务解决方案,它将一个事务拆分成三个步骤:

1.T(Try):业务检查阶段,这阶段主要进行业务校验和检查或者资源预留;也可以是直接进行业务操作。

2.C(Confirm):业务确认阶段,这阶段对Try阶段校验过的业务或者预留的资源进行确认。

3.C(Cancel):业务回滚阶段,这阶段和上面的C(Confirm)是互斥的,用于释放Try阶段预留的资源或者业务。

TCC空回滚是解决什么问题的?

        在没有调用TCC资源Try方法的情况下,调用了二阶段的Cancel方法。比如当Try请求由于网络延迟或故障等原因,没有执行,结果返回了异常,那么此时Cancel就不能正常执行,因为Try没有对数据进行修改,如果Cancel进行了对数据的修改,那么就会导致数据不一致。

        解决思路关键就是要识别出这个空回滚。思路很简单,就是需要知道Try阶段是否执行,如果执行了,那就是正常回滚;如果没有执行,那就是空回滚。建议TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条。再额外增加一张分支事务记录表,其中有全局事务ID和分支事务ID,第一阶段Try方法里会插入一条记录,表示Try阶段执行了。Cancel接口里面读取该记录,如果记录存在,则正常回滚;如果该记录不存在,则是空回滚。

如何解决TCC幂等问题?

         为了保证TCC二阶段提交重试机制不会引发数据不一致,要求TCC的二阶段Confirm和Cancel接口保证幂等,这样就不会重复使用或者释放资源。如果幂等控制没有做好,很有可能导致数据不一致等严重问题。解决思路在上述分支事务记录中增加执行状态,每次执行前都查询该状态。

如何解决TCC中悬挂问题?

 悬挂就是对于一个分布式事务,其二阶段Cancel接口比Try接口先执行。

        出现原因是在调用分支事务Try时,由于网络发生拥堵,造成了超时,TM就会通知RM回滚该分布式事务,可能回滚完成后,Try请求才到达参与者真正执行,而一个Try方法预留的业务资源,只有该分布式事务才能使用,该分布式事务第一阶段预留的业务资源就再也没有人能够处理了,对于这种情况,我们就称为悬挂,即业务资源预留后无法继续处理。

        解决思路是如果二阶段执行完成,那一阶段就不能再继续执行。在执行一阶段事务时判断在该全局事务下,判断分支事务记录表中是否已经有二阶段事务记录,如果有则不执行Try。

可靠消息服务方案是什么? 

可靠消息最终一致性方案指的是:当事务的发起方(事务参与者,消息发送者)执行完本地事务后,同是发出一条消息,事务参与方(事务参与者,消息的消费者)一定能够接受消息并可以成功处理自己的事务。

这里强调两点:

1.可靠消息:发起方一定得把消息传递到消费者。

2.最终一致性:最终发起方的业务处理和消费方的业务处理的完成,达成最终一致。 

最大努力通知方案的关键是什么?

1.有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。

2.消息校对机制。如果尽最大努力也没有通知到消息接收方,或者接收方消息消费后要再次消费,此时可由接收方主动向通知方查询消息信息来满足需求。 

什么是分布式系统中的幂等?

幂等(idempotent、idempotence)是一个数据与计算机学概念,常见于抽象代数中。

在编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

幂等有哪些技术解决方案?

 1.查询操作

        查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作。

2.删除操作

        删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果有多个)

 3.唯一索引

        防止新增脏数据。比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建多个资金账户,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录。要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可)。

4.token机制

        防止页面重复提交。

业务要求:页面的数据只能被点击提交一次。

发生原因:由于重复点击或着网络重发,或者nginx重发等情况会导致数据被重复提交。

解决办法:集群环境采用token加redis(redis单线程的,处理需要排队);单JVM环境:采用token加redis或token加JVM锁。

处理流程: 

(1)数据提交前要向服务器申请token,token放到redis或jvm内存,token有效时间;

(2)提交后后台校验token,同时删除token,生成新的token返回。

token特点:要申请,一次有效性,可以限流。

注意:redis要用删除操作来判断token,删除成功代表token校验通过。

5.TraceId

        操作时是唯一的。

对外提供的API如何保证幂等性? 

举例说明:银联提供的付款接口,需要接入商户提交付款请求时附带source来源,seq序列号。

source+seq在数据库里面做唯一索引,防止多次付款(并发时,只能处理一个请求)。

重点:对外提供接口为了支持幂等调用,接口有两个字段必须传,一个是来源source,一个是来源方序列号seq,这两个字段在提供方系统里面做联合唯一索引,这样当第三方调用时,先在本方系统里面查询一下,是否已经处理过,返回相应的处理结果;没有处理过,进行相应处理,返回结果。

注意:为了幂等的友好性,一定要先查询一下,是否处理过该笔业务,不查询直接插入业务系统,会报错,但实际已经处理。 

分布式微服务项目你是如何设计的? 

一般设计成两层:业务层和能力层(中台),业务层接收用户请求,然后通过调用能力层来完成业务逻辑。 

认证(Authentication)和授权(Authorization)的区别是什么?

         Authentication(认证)是验证你身份的凭据(例如用户名/用户ID和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication被称为身份/用户验证。

        Authorization(授权)发生在Authentication(认证)之后。授权,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问,比如admin,有些对系统资源操作比如删除、添加、更新只能特定用户才具有。

        这两个一般在我们的系统中被结合在一起使用,目的就是为了保护我们系统的安全性。

Cookie和Session有什么区别?如何使用Session进行身份验证?

        Session的主要作用就是通过服务端记录用户的状态。

        Cookie数据保存在客户端(浏览器端),Session数据保存在服务器端。相对来说Session安全性更高。如果使用Cookie的话,一些敏感信息不要写入Cookie中,最好能将Cookie信息加密然后使用到的时候再去服务器端解密。

那么,Session是如何进行身份验证的?

        很多时候我们都是通过SessionId来指定特定的用户,SessionId一般会选择存放在服务端。举个例子:用户成功登录系统,然后返回给客户端具有SessionId的Cookie,当用户向后端发起请求的时候会把SessionId带上,这样后端就知道你的身份状态了。关于这种认证更详细的过程如下:

用户向服务器发送用户名和密码用于登录系统。

服务器验证通过后,服务器为用户创建一个Session,并将Session信息存储起来。

服务器向用户返回一个SessionId,写入用户的Cookie。

当用户保持登录状态时,Cookie将与每个后续请求一起被发送出去。

服务器可以将存储在Cookie上的SessionId与存储在内存中或者数据库中的Session信息进行比较,以验证用户的身份,返回给用户客户端响应信息的时候会附带用户当前的状态。

使用Session时候需要注意以下几点:

依赖Session的关键业务一定要确保客户端开启了Cookie。

注意Session的过期时间(一般Session的过期时间默认是30分钟)。

什么是Token?什么是JWT?如何基于Token进行身份验证? 

 JWT详细介绍:https://blog.csdn.net/qq_26420601/article/details/117474930

用户向服务器发送用户名和密码用于登录系统。

身份验证服务响应并返回了签名的JWT,上面包含了用户是谁的内容。

用户以后每次向后端发送请求都在Header中带上JWT。

服务端检查JWT并从中获取用户相关信息。

为什么Cookie无法防止CSRF攻击,而Token可以? 

分布式架构下,Session共享有什么方案?

Kafka的rebalance机制是什么?

消费者分区分配策略 

Range范围分区(默认的)

RoundRobin轮询分区

Sticky策略

触发Reblance的时机 

Reblance的触发条件有3个:

  • 组成员个数发生变化。例如有新的consumer实例加入该消费组或者离开组。
  • 订阅的Topic个数发生变化。
  • 订阅Topic的分区数发生变化。 

Coordinator协调过程 

消费者如何发现协调者

消费者如何确定分配策略 

Kafka是pull还是push?以及优劣势分析? 

Kafka最初考虑的问题是,consumer应该从brokes拉取消息还是brokes将消息推送到consumer,也就是pull还是push。

Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉去消息。

一些消息系统,比如Scribe和Apache Flume采用了push模式,将消息推送到下游的consumer。

这样做有好处也有坏处:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。

消息系统致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。

最终Kafka还是选取了传统的pull模式。

pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉去数据。

push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。

如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。

pull模式下,consumer就可以根据自己的消费能力去决定这些策略。

pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到消息到达。

为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息达到(当然也可以阻塞知道消息的数量达到某个特定的量,这样就可以批量发送了)

Kafka消息丢失的场景有哪些? 

生产者在生产过程中的消息丢失。

broker在故障后的消息丢失。

消费者在消费过程中的消息丢失。

ACK机制 

ack有3个可选值,分别是1,0,-1

ack=0:生产者在生产过程中的消息丢失

简单来说就是,producer发送一次就不再发送了,不管是否发送成功。

ack=1:broker在故障后的消息丢失

简单来说就是,producer只要收到一个分区副本成功写入的通知就认为推送消息成功了。这里有一个地方需要注意,这个副本必须是leader副本。只有leader副本成功写入了,producer才会认为消息发送成功。

注意:ack的默认就是1。这个默认值其实就是吞吐量与可靠性的一个这种方案。生产上我们可以根据实际情况进行调整,比如如果你要追求高吞吐量,那么就要放弃可靠性。

ack=-1:生产者和存储者不会丢失数据

简单来说就是,producer只有收到分区内所有副本的成功写入的通知才认为托推送消息成功了。

offset机制

kafka消费者的三种消费语义:
at-most-once:最多一次,可能丢数据

at-least-once:最少一次,可能重复消费数据

exact-once message:精确一次

Kafka中zookeeper的作用是什么? 

Zookeeper是分布式协调,注意它不是数据库。

kafka中使用了zookeeper的分布式锁和分布式配置及统一命令的分布式协调解决方案。

在kafka的broker集群中的controller的选择,是通过zookeeper的临时节点争抢获得的。

brokerId等如果自增的话也是通过zookeeper的节点version实现的全局唯一。

kafka中broker中的状态数据也是存储在zookeeper中,不过这里要注意,zookeeper不是数据库,所以存储的属于元数据,而新旧版本变化中,就把曾经的offset从zookeeper中迁移除了zookeeper。 

Kafka中高性能如何保障? 

        首先,性能的瓶颈是IO,这个是不能逾越的鸿沟,虽然broker在持久化数据的时候已经最大努力的使用了磁盘的顺序读写,更进一步的性能优化是零拷贝的使用,也就是从磁盘日志到消费者客户端的数据传递,因为kafka是mq,对于msg不具备加工处理,所以得以实现,然后就是大多数分布式系统一样,总要做tradeoff,在速度与可用性/可靠性中挣扎,ACK的0、1、-1级别就是在性能和可靠性中权衡。 

简述Kafka架构设计是什么样? 

1.broker

kafka集群包含一个或多个服务器,服务器节点称为broker。

broker存储topic的数据。如果某个topic有N个partition,集群有N个broker,那么每个broker存储该topic的一个partition。

如果某个topic有N个partition,集群有(N+M)个broker,那么其中有N个broker存储该topic的一个partition,剩下的M个broker不存储该topic的partiton数据。

如果某个topic有N个partition,集群中broker数量少于N个,那么一个broker存储该topic的一个或多个partition。在实际生产环境中,尽量避免这种情况的发生,这种情况容易导致kafka集群数据不均衡。

2.Topic

每条发布到kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上,但用户只需指定消息的Topic即可产生或消费数据而不关心数据存于何处)。类似于数据库的表名。

3.Partition

 topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。partition中的数据是有序的,不同的partition间的数据丢失了数据的顺序。如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数量设为1。

4.Producer

 生产者即数据的发布者,该角色将消息发布到Kafka的topic中。broker接收到生产者发送的消息后,broker将该消息追加到当前用于追加数据的segment存储文件中。生产者发送的消息,存储到一个partition中,生产者也可以指定数据存储的partition。

5.Consumer

消费者可以从broker中读取数据。消费者可以消费多个topic中的数据。

6.Consumer Group

每个consumer属于一个特定的Consumer Group(可以为每个Consumer指定group name,若不指定group name则属于默认的group)。这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发送任意一个consumer)的手段。一个topic可以有多个Consumer Group。Topic的消息会复制给consumer。如果需要实现广播,只要每个consumer有一个独立的Consumer Group就可以了。要实现单播只要所有的consumer在同一个Consumer Group。用Consumer Group还可以将consumer进行自由的分组而不需要多次发送消息到不同的Topic。

7.Leader

每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据读写的partition。

8.Follower

Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。

9.offset

kafka的储存文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然 the first offset就是00000000.kafka。

Kafka天生是分布式的,满足AKF的XYZ轴特点,扩展性、可靠性、高性能没的说。而且,kafka具备自己的特色,比如动态ISR集合,是在强一致性,过半一致性之外的另一个实现手段。 

Zookeeper watch机制是什么? 

Zookeeper是用来协调(同步)分布式进程的服务,提供了一个简单高性能的协调内核,用户可以在此之上构建更多复杂的分布式协调功能。

多个分布式进程通过Zookeeper提供的API来操作共享的Zookeeper内存数据对象ZNode来达成某种一致的行为或结果,这种模式本质上是基于状态共享的并发模式,与Java的多线程并发模型一致,它们线程或进程都是“共享式内存通信”。

Java没有直接提供某种响应式通知接口来监控某个对象状态的变化,要么浪费CPU时间毫无响应的轮询重试,或基于Java提供的某种主动通知(Notify)机制(内置队列)来响应状态变化,但这种机制是需要循环阻塞调用。

而Zookeeper实现这些分布式进程的状态(ZNode的Data、Children)共享时,基于性能的考虑采用了类似的异步阻塞的主动通知模式即watch机制,是的分布式进程之间的“共享状态通信”更加实时高效,其实这也是Zookeeper的主要任务决定的——协调。Consul虽然也实现了watch机制,但它是阻塞的长轮询。 

Zookeeper的watch特性

1.watch是一次性的,每次都需要重新注册,并且客户端在会话异常结束时不会收到任何通知,而快速重连时仍不影响接收通知。 

2.watch的回调执行都是顺序执行的,并且客户端在没有收到关注数据的变化事件通知之前是不会看到最新的数据,另外需要注意不要在watch回调逻辑中阻塞整个客户端的watch回调。

3.watch是轻量级的,watchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径。Zookeeper服务端只会通知客户端发生了什么,并不会告诉具体内容。

Zookeeper状态

Disconnected:客户端是断开连接的状态,不能连接服务集合中的任意一个

SyncConnected:客户端是连接状态,连接其中的一个服务

AuthFailed:鉴权失败

ConnectedReadOnly:客户端连接只读的服务器

SaslAuthenticated:SASL认证

Expired:服务器已经过期了该客户的Session

Zookeeper事件类型

None:无

NodeCreated:节点创建

NodeDeleted:节点删除

NodeDataChanged:节点数据改变

NodeChildrenChanged:子节点改变(添加/删除)

Watcher使用的注意事项

watcher是一次触发器,假如需要持续监听数据变化,需要在每次获取时设置watcher。

会话过期:当客户端会话过期时,该客户端注册的watcher会失效。

事件丢失:在接收通知和注册监视点之间,可能会丢失事件,但Zookeeper的状态变更和数据变化,都会记录在状态元数据信息和Zookeeper数据节点上,所以能够获取最终一致的Zookeeper信息状态。

避免watcher过多:服务器会对每一个注册watcher事件的客户端发送通知,通知通过Socket连接的方式发送,当watcher过多时,会产生一个尖峰的通知。

Zookeeper的命名服务、配置管理、集群管理分别是什么? 

分布式协调 

大于等于一的情况下,才会有协调,在协调的事务进行分类得到一些名词,语义能够接受就可以。 

命名服务

通过使用有序节点的特性做到协调命名规则

通过zk的事务ID递增,做到有序行命名规则

通过使用自己点做map映射,做到1:N的命名映射,比如DNS

顺序关系、映射关系 

配置管理

配置、元数据、状态等语义可以通过ZK的节点1MB存储,或者通过zk的节点目录结构特性存储,并且通过watch机制,满足配置变化的全局通知能力。

集群管理 

通过zk的排他性,有序性

满足分布式锁、分布式选主、队列锁

串行化回调调度

分布式调度等 

Zookeeper的数据模型和节点类型有哪些? 

Zookeeper数据模型

Zookeeper的数据模型,在结构上和标准文件系统的非常相似,拥有一个层次命名空间,都是采用树形层次结构,Zookeeper树中的每个节点被称为Znode。

和文件系统的目录树一样,Zookeeper树中的每个节点可以拥有子节点。但也有不同之处:

 节点类型

RabbitMQ的架构设计是什么样的?

是AMQP的实现,相关概念定义

 Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输。

Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。

Queue:消息的载体,每个消息都会被投到一个或多个队列。

Binding:绑定,它的作用就是把Exchange和Queue按照路由规则绑定起来。

Routing Key:路由关键字,Exchange根据这个关键字进行消息投递。

vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。

Producer:消息生产者,就是投递消息的程序。

Consumer:消息消费者,就是接收消息的程序。

Channel:消息通道,在客户端的每个连接里,可建立多个channel。

核心概念

在mq领域中,producer将msg发送到queue,然后consumer通过消费queue完成P.C解耦。

Kafka是由producer决定msg发送到哪个queue。

rabbitmq是由Exchange决定msg应该怎么样发送到目标queue,这就是binding及对应的策略。 

Exchange 

Direct Exchange:直接匹配,通过Exchange名称+RoutingKey来发送与接受消息。

Fanout Exchange:广播订阅,向所有的消费者发布消息,但是只有消费者将队列绑定到该路由器才能收到消息,忽略Routing Key。 

Topic Exchange:主题匹配订阅,这里的主题指的是Routing Key,Routing Key可以采用通配符,如:*或#,Routing Key命名采用.来分隔多个词,只有消息将队列绑定到该路由器且指定Routing Key符合匹配规则时才能收到消息。

Headers Exchange:消息头订阅,消息发布前,为消息定义一个或多个键值对的消息头,然后消费者接收消息同时需要定义类似的键值对请求头:(如:x-match=all或者x_match=any),只有请求头与消息头匹配,才能接收消息,忽略Routing Key。

默认的Exchange:如果用空字符串去声明一个Exchange,那么系统就会使用“amq.direct”这个Exchange,我们创建一个queue时,默认的都会有一个和新建queue同名的Routing Key绑定到这个默认的Exchange上去。

复杂与精简

在众多的MQ中间件中,首先学习Rabbitmq的时候,就理解他是一个单机的mq组件,为了系统的解耦,可以自己在业务层面做AKF。

其在内卷能力做的非常出色,这得益于AMQP,也就是消息的传递形式、复杂度Exchange和queue的binding实现,这对于PC有很大的帮助。 

RabbitMQ事务消息原理?

事务V.S确认

 确认是对一件事的确认

事务是对批量的确认

增删改查中,事务是对于增删改查的保证

发送方事务

开启事务,发送多条数据,事务提交或回滚是原子的,要么都提交,要么都回滚。

消费方事务 

消费方是读取行为,那么事务体现在哪里尼

rabbitmq的消费行为会触发queue中的msg是否删除、是否重新放回队列等行为,类似增删改

所以,消费方的ack是要手动提交的,且最终确定以事务的提交和回滚决定。 

RabbitMQ如何确保消息发送和消息接收? 

消息发送确认 

1.ConfirmCallback方法

ConfirmCallback是一个回调接口,消息发送到Broker后触发回调,确认消息是否到达Broker服务器,也就是只确认是否正确到达Exchange中。

2.ReturnCallback方法

通过实现ReturnCallback接口,启动消息失败返回,此接口是在交换器路由不到队列时触发回调,该方法可以不使用,因为交换器和队列是在代码里面绑定的,如果消息成功投递到Broker后几乎不存在绑定队列失败,除非你代码写错了。

消息接收确认

RabbitMQ消息确认机制(ACK)默认是自动确认的,自动确认会在消息发送给消费者后立即确认,但存在丢失消息的可能,如果消费端消费逻辑抛出异常,假如你用回滚了也只是保证了数据的一致性,但是消息还是丢了,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。

消息确认模式有:

AcknowledgeMode.NONE:自动确认。

AcknowledgeMode.AUTO:根据情况确认。

AcknowledgeMode.MANUAL:手动确认。

消费者收到消息后,手动调用Basic.Ack或Basic.Nack或Basic.Reject后,RabbitMQ收到这些消息后,才认为本次投递完成。

Basic.Ack命令:用于确认当前消息

Basic.Nack命令:用于否定当前消息(注意:这是AMQ 0-9-1的RabbitMQ的扩展)。

Basic.Reject命令:用于拒绝当前消息。

Nack,Reject后都有能力要求是否requeue消息或者进入死信队列。

RabbitMQ死信队列、延时队列分别是什么? 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想天涯~路在脚下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值