zoukankan      html  css  js  c++  java
  • 消息中间件搬迁

     
    消息队列
    作用:异步,解藕,峰值处理,可恢复,顺序,扩展性
    适用场合:重要不紧急
     
    RocketMq源码部分主要可以分为
    rocketmq-broker,rocketmq-client,rocketmq-common,rocketmq-filterSrv,rocketmq-namesrv和rocketmq-remoting等模块,
    通信框架就封装在rocketmq-remoting模块中
     
     
     
    原理
    Broker
     
    / |
    / |
    Produce - Namesrv - Consume
     
    1,启动Namesrv,Namesrv起来后监听端口,等待Broker、Produer、Consumer连上来,相当于一个路由控制中心
    (Namesrv压力不会太大,平时主要开销是在维持心跳和提供Topic-Broker的关系数据)
    2,Broker启动,跟所有的Namesrv保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有topic信息。注册成功后,namesrv集群中就有Topic跟Broker的映射关系。
    3,收发消息前,先创建topic,创建topic时需要指定该topic要存储在哪些Broker上。也可以在发送消息时自动创建Topic。
    4,Producer发送消息,启动时先跟Namesrv集群中的其中一台建立长连接,并从Namesrv中获取当前发送的Topic存在哪些Broker上,然后跟对应的Broker建长连接,直接向Broker发消息。
     
    RocketMQ消息队列集群中的几个角色
    NameServer:在MQ集群中做的是做命名服务,更新和路由发现 broker服务;
    NameServer相当于配置中心,维护Broker集群、Broker信息、Broker存活信息、主题与队列信息等。
    NameServer是一个几乎无状态的节点,可集群部署,节点之间无任何信息同步,每个Broker与集群内所有的Broker保持长连接。
     
    Broker-Master:broker 消息主机服务器;
    Broker-Slave:broker 消息从机服务器;
    Producer:消息生产者;
    Consumer:消息消费者。
     
     
    其中,RocketMQ集群的一部分通信如下:
    Broker( 集群最核心模块,主要负责Topic消息存储、管理和分发等功能)
    Broker启动后需要完成一次将自己注册至NameServer的操作;随后每隔30s时间定期向NameServer上报Topic路由信息;
    消息生产者Producer作为客户端发送消息时候,需要根据Msg的Topic从本地缓存的TopicPublishInfoTable获取路由信息。
    如果没有则更新路由信息会从NameServer上重新拉取;
    消息生产者Producer根据所获取的路由信息选择一个队列(MessageQueue)进行消息发送;Broker作为消息的接收者接收消息并落盘存储。
    RocketMQ的Broker集群部署模式还挺多的,比如单Master模式、多Master模式、多Master多Slave模式(异步复制)、多Master多Slave模式(同步双写)等。明确个概念,RocketMQ Slave不可以写,可以读,类似于MySQL的主从机制。
     
    对于Producer端RocketMQ采用了轮询的方式保证了负载均衡,
    Consumer端通常采用cluster集群方式消费消息,我们可以自己定义消息在消息端的分配方式。
     
     
    send 方法会设置一个默认的 timeout, 3 秒。
    默认使用 SYNC 模式,另外有 Async 和 OneWay 模式。
    我们需要处理方法签名中的 Client 端的异常,网络异常,Broker 端的异常,线程中断异常。
     
     
    消息存储
    RocketMQ高性能之底层存储设计https://www.jianshu.com/p/d06e9bc6c463
     
    Pull和Push
    对于任何一款消息中间件而言,消费者客户端一般有两种方式从消息中间件获取消息并消费。
    严格意义上来讲,RocketMQ并没有实现PUSH模式,而是对拉模式进行一层包装,名字虽然是 Push 开头,实际在实现时,使用 Pull 方式实现。
    通过 Pull 不断不断不断轮询 Broker 获取消息。当不存在新消息时,Broker 会挂起请求,直到有新消息产生,取消挂起,返回新消息。这样,基本和 Broker 主动 Push 做到接近的实时性(当然,还是有相应的实时性损失)。原理类似 长轮询( Long-Polling )
     
    Pull方式
    由消费者客户端主动向消息中间件(MQ消息服务器代理)拉取消息;采用Pull方式,如何设置Pull消息的频率需要重点去考虑,举个例子来说,可能1分钟内连续来了1000条消息,然后2小时内没有新消息产生(概括起来说就是“消息延迟与忙等待”)。如果每次Pull的时间间隔比较久,会增加消息的延迟,即消息到达消费者的时间加长,MQ中消息的堆积量变大;若每次Pull的时间间隔较短,但是在一段时间内MQ中并没有任何消息可以消费,那么会产生很多无效的Pull请求的RPC开销,影响MQ整体的网络性能;
    Push方式
    由消息中间件(MQ消息服务器代理)主动地将消息推送给消费者;采用Push方式,可以尽可能实时地将消息发送给消费者进行消费。但是,在消费者的处理消息的能力较弱的时候(比如,消费者端的业务系统处理一条消息的流程比较复杂,其中的调用链路比较多导致消费时间比较久。概括起来地说就是“慢消费问题”),而MQ不断地向消费者Push消息,消费者端的缓冲区可能会溢出,导致异常;
     
    通过研究源码可知,RocketMQ的消费方式都是基于拉模式拉取消息的,而在这其中有一种长轮询机制(对普通轮询的一种优化),来平衡上面Push/Pull模型的各自缺点。基本设计思路是:消费者如果第一次尝试Pull消息失败(比如:Broker端没有可以消费的消息),并不立即给消费者客户端返回Response的响应,而是先hold住并且挂起请求(将请求保存至pullRequestTable本地缓存变量中),然后Broker端的后台独立线程—PullRequestHoldService会从pullRequestTable本地缓存变量中不断地去取,具体的做法是查询待拉取消息的偏移量是否小于消费队列最大偏移量,如果条件成立则说明有新消息达到Broker端(这里,在RocketMQ的Broker端会有一个后台独立线程—ReputMessageService不停地构建ConsumeQueue/IndexFile数据,同时取出hold住的请求并进行二次处理),则通过重新调用一次业务处理器—PullMessageProcessor的处理请求方法—processRequest()来重新尝试拉取消息(此处,每隔5S重试一次,默认长轮询整体的时间设置为30s)。
    RocketMQ消息Pull的长轮询机制的关键在于Broker端的PullRequestHoldService和ReputMessageService两个后台线程。对于RocketMQ的长轮询(LongPolling)消费模式后面会专门详细介绍。
     
    在本文前面已经提到过了,从严格意义上说,RocketMQ并没有实现真正的消息消费的Push模式,而是对Pull模式进行了一定的优化,一方面在Consumer端开启后台独立的线程—PullMessageService不断地从阻塞队列—pullRequestQueue中获取PullRequest请求并通过网络通信模块发送Pull消息的RPC请求给Broker端。另外一方面,后台独立线程—rebalanceService根据Topic中消息队列个数和当前消费组内消费者个数进行负载均衡,将产生的对应PullRequest实例放入阻塞队列—pullRequestQueue中。这里算是比较典型的生产者-消费者模型,实现了准实时的自动消息拉取。然后,再根据业务反馈是否成功消费来推动消费进度。
    在Broker端,PullMessageProcessor业务处理器收到Pull消息的RPC请求后,通过MessageStore实例从commitLog获取消息。如1.2节内容所述,如果第一次尝试Pull消息失败(比如Broker端没有可以消费的消息),则通过长轮询机制先hold住并且挂起该请求,然后通过Broker端的后台线程PullRequestHoldService重新尝试和后台线程ReputMessageService的二次处理。
     
     
     
     
     
    高可用
    1⃣️单Master模式:
    无需多言,一旦单个broker重启或宕机,一切都结束了!很显然,线上不可以使用。
     
    2⃣️多Master模式:
    全是Master,没有Slave。当然,一个broker宕机了,应用是无影响的,缺点在于宕机的Master上未被消费的消息在Master没有恢复之前不可以订阅。
     
    3⃣️多Master多Slave模式(异步复制):
    多对Master-Slave,高可用!采用异步复制的方式,主备之间短暂延迟,MS级别。Master宕机,消费者可以从Slave上进行消费,不受影响,但是Master的宕机,会导致丢失掉极少量的消息。
     
    4⃣️多Master多Slave模式(同步双写):
    和上面的区别点在于采用的是同步方式,也就是在Master/Slave都写成功的前提下,向应用返回成功,可见不论是数据,还是服务都没有单点,都非常可靠!缺点在于同步的性能比异步稍低。
     
    多Master多Slave的好处在于,即便集群中某个broker挂了,也可以继续消费,保证了实时性的高可用,但是并不是说某个master挂了,slave就可以升级master,开源版本的rocketmq是不可以的。也就是说,在这种情况下,slave只能提供读的功能,将失去消息负载的能力。
     
    通信
    涉及地方:
    (1)Broker启动后需要完成一次将自己注册至NameServer的操作;随后每隔30s时间定期向NameServer上报Topic路由信息;
    (2)消息生产者Producer作为客户端发送消息时候,需要根据Msg的Topic从本地缓存的TopicPublishInfoTable获取路由信息。如果没有则更新路由信息会从NameServer上重新拉取;
    (3)消息生产者Producer根据(2)中获取的路由信息选择一个队列(MessageQueue)进行消息发送;Broker作为消息的接收者收消息并落盘存储;
    从上面(1)~(3)中可以看出在消息生产者, Broker和NameServer之间都会发生通信(这里只说了MQ的部分通信),因此如何设计一个良好的网络通信模块在MQ中至关重要,它将决定RocketMQ集群整体的消息传输能力与最终的性能。
     
    rocketmq-remoting 模块是 RocketMQ消息队列中负责网络通信的模块,RocketMQ消息队列自定义了通信协议并在Netty的基础之上扩展了通信模块。
    1、Remoting通信模块的类结构图
    (1)RemotingService:为最上层的接口,提供了三个方法:void registerRPCHook(RPCHook rpcHook);
    (2)RemotingClient/RemotingSever:两个接口继承了最上层接口—RemotingService,分别各自为Client和Server提供所必需的方法,
    (3)NettyRemotingAbstract:Netty通信处理的抽象类,定义并封装了Netty处理的公共处理方法;
    (4)NettyRemotingClient/NettyRemotingServer:分别实现了RemotingClient和RemotingServer, 都继承了NettyRemotingAbstract抽象类。RocketMQ中其他的组件(如client、nameServer、broker在进行消息的发送和接收时均使用这两个组件)
     
    2、消息的协议设计与编码解码
    RemotingCommand类的部分成员变量如下:
    Header字段
    类型
    Request说明
    Response说明
    code
    int
    请求操作码,应答方根据不同的请求码进行不同的业务处理
    应答响应码。0表示成功,非0则表示各种错误
    language
    LanguageCode
    请求方实现的语言
    应答方实现的语言
    version
    int
    请求方程序的版本
    应答方程序的版本
    opaque
    int
    相当于reqeustId,在同一个连接上的不同请求标识码,与响应消息中的相对应
    应答不做修改直接返回
    flag
    int
    区分是普通RPC还是onewayRPC得标志
    区分是普通RPC还是onewayRPC得标志
    remark
    String
    传输自定义文本信息
    传输自定义文本信息
    extFields
    HashMap<String, String>
    请求自定义扩展信息
    响应自定义扩展信息
    RocketMQ通信协议的格式:
     
    可见传输内容主要可以分为以下4部分:
    (1)消息长度:总长度,四个字节存储,占用一个int类型;
    (2)序列化类型&消息头长度:同样占用一个int类型,第一个字节表示序列化类型,后面三个字节表示消息头长度;
    (3)消息头数据:经过序列化后的消息头数据;
    (4)消息主体数据:消息主体的二进制字节数据内容;
    消息的编码和解码分别在RemotingCommand类的encode和decode方法中完成
     
    3、消息的通信方式和通信流程
    在RocketMQ消息队列中支持通信的方式主要有以下三种:
    (1)同步(sync)
    (2)异步(async)
    (3)单向(oneway)
     
     
     
     
    异常重试
    消息失败重试机制:
    消息失败,无非涉及到2端:从生产者端发往MQ的失败;消费者端从MQ消费消息的失败;
     
    生产者端的消息失败
    比如网络抖动导致生产者发送消息到MQ失败,可以设置重试次数,setRetryTimesWhenSendFailed(3);
     
    Producer的send方法本身支持内部重试,重试逻辑如下:
     1.至多重试 3 次。
     2.如果发送失败,则轮转到下一个 Broker。

     3.这个方法的总耗时时间不超过 sendMsgTimeout设置的值,默认10s。所以,如果本身向broker发送消息产生超时异常,就不会再做重试。 

     
    以上策略仍然不能保证消息一定发送成功,为保证消息一定成功,建议应用这样做:
    如果调用send同步方法发送失败,则尝试将消息存储到db,由后台线程定时重试,保证消息一定到达Broker。
     
     
    消费者端的消失失败
    分为2种情况,一个是timeout,一个是exception
    timeout,比如由于网络原因导致消息压根就没有从MQ到消费者上,在RocketMQ内部会不断的尝试发送这条消息,直至发送成功为止!(比如集群中一个broker失败,就尝试另一个broker)
    exception,消息正常的到了消费者,结果消费者发生异常,处理失败了。这里涉及到一些问题,需要我们思考下,比如,消费者消费消息的状态有哪些定义?如果失败,MQ将采取什么策略进行重试?假设一次性批量PUSH了10条,其中某条数据消费异常,那么消息重试是10条呢,还是1条呢?而且在重试的过程中,需要保证不重复消费吗?
     
    消息消费的状态,有2种,一个是成功(CONSUME_SUCCESS),一个是失败&稍后重试(RECONSUME_LATER)
    RocketMQ为我们提供了这么多次数的失败重试,但是在实际中也许我们并不需要这么多重试,比如重试3次,还没有成功,我们希望把这条消息存储起来并采用另一种方式处理,而且希望RocketMQ不要在重试呢,因为重试解决不了问题了!
     
     
    消费过程要做到幂等(即消费端去重)
    1.将消息的唯一键,可以是 msgId,也可以是消息内容中的唯一标识字段,例如订单Id等,消费之前判断是否在 
Db或Tair(全局KV存储)中存在,如果不存在则插入入,并消费,否则跳过。(实际过程要考虑原子性问题,判断是否存在可以尝试插入,如果报主键冲突,则插入失败,直接跳过)
msgId一定是全句唯一标识符;
    2.使用业务局面的状态机去重 。
     
     
    顺序性
    这是阿里云上对顺序消息的定义,把顺序消息拆分成了顺序发布和顺序消费
    如何保证顺序
    在MQ的模型中,顺序需要由3个阶段去保障:
    消息被发送时保持顺序
    消息被存储时保持和发送的顺序一致
    消息被消费时保持和存储的顺序一致
     
    >>> 顺序性,消费的消息发往同一个broker的同一个队列上!其次消费者端采用有序Listener即可。
    注意在以前普通消费消息时设置的回调是MessageListenerConcurrently,而顺序消费的回调设置是MessageListenerOrderly。
    RocketMQ底层是如何做到消息顺序消费的,看一看源码你就能大概了解到,至少来说,在多线程消费场景下,一个线程只去消费一个队列上的消息,那么自然就保证了消息消费的顺序性,同时也保证了多个线程之间的并发性。也就是说其实broker并不能完全保证消息的顺序消费,它仅仅能保证的消息的顺序发送而已!
     
    RabbitMQ
    拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
     
    Kafka
    一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。
    写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
     
    RocketMQ消费端有两种类型:MQPullConsumer和MQPushConsumer。
    MQPullConsumer由用户控制线程,主动从服务端获取消息,每次获取到的是一个MessageQueue中的消息。PullResult中的List msgFoundList自然和存储顺序一致,用户需要再拿到这批消息后自己保证消费的顺序。
    对于PushConsumer,由用户注册MessageListener来消费消息,在客户端中需要保证调用MessageListener时消息的顺序性。
     
    RocketMQ不保证消息的顺序性,
    RocketMQ(消息可靠传输)重试机制:网络问题:生产者会无限次重试 / 会是无限次的 业务逻辑:延迟重试
    处理消息重复:保持幂等,消费者通过日志记录(消息ID),大事务 = 小事务 + 异步
    RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。细心的你可能又发现问题了,如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,这时候发现了Prepared消息,它会向消息发送者确认,Bob的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
     
     
    集群消费 & 广播消费
    RocketMQ的消费方式有2种,在默认情况下,就是集群消费,也就是上面提及的消息的负载均衡消费。另一种消费模式,是广播消费。广播消费,类似于ActiveMQ中的发布订阅模式,消息会发给Consume Group中的每一个消费者进行消费。
    默认的是使用集群消费
     
     
    public class Consumer { public static void main(String[] args) throws MQClientException { //声明并初始化一个consumer //需要一个consumer group名字作为构造方法的参数,这里为consumer1 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer1"); //同样也要设置NameServer地址 consumer.setNamesrvAddr("192.168.140.128:9876;192.168.140.129:9876"); * consumer.setMessageModel(MessageModel.BROADCASTING); consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); //设置consumer所订阅的Topic和Tag,*代表全部的Tag consumer.subscribe("TopicTest", "*"); //设置一个Listener,主要进行消息的逻辑处理 consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { for (MessageExt messageExt : msgs) { String messageBody = new String(messageExt.getBody()); System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format( new Date())+"消费响应:msgId : " + messageExt.getMsgId() + ", msgBody : " + messageBody);//输出消息内容 } //返回消费状态 //CONSUME_SUCCESS 消费成功 //RECONSUME_LATER 消费失败,需要稍后重新消费 return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); //调用start()方法启动consumer consumer.start(); System.out.println("Consumer Started."); } }
     
     
     
     
    对比
    >>> 消息队列和dubbo的差异:
    在架构上,RPC和Message Queue的差异点是,Message Queue有一个中间结点Message Queue(broker),可以把消息存储。
     
     
    ActiveMq / RabbitMq
    【AMQP】高级消息队列协议,以字节为流
    【JMS】相当于一个API
     
    Kafka
    Kafka是一个分布式的消息引擎。具有以下特征
    能够发布和订阅消息流(类似于消息队列)
    以容错的、持久的方式存储消息流
    多分区概念,提高了并行能力
     
    在kafka中,每个日志分段文件都对应了两个索引文件——偏移量索引文件和时间戳索引文件(还有其它的诸如事务日志索引文件就不细表了),主要用来提高查找消息的效率。
     
    Kafka高性能
    • partition提升了并发
    • zero-copy
    • 顺序写入
    • 消息聚集batch
    • 页缓存
    业务方对 Kafka producer的优化
    • 增大producer数量
    • ack配置
    • batch
     
    消息中间件如何实现每秒几十万的高并发写入?
     
     
    Kafka的ISR机制是什么?
    现在我们先留着这个问题不说具体怎么解决,先回过头来看一个Kafka的核心机制,就是ISR机制。
    这个机制简单来说,就是会自动给每个Partition维护一个ISR列表,
    这个列表里一定会有Leader,然后还会包含跟Leader保持同步的Follower。
    也就是说,只要Leader的某个Follower一直跟他保持数据同步,那么就会存在于ISR列表里。
    但是如果Follower因为自身发生一些问题,导致不能及时的从Leader同步数据过去,那么这个Follower就会被认为是“out-of-sync”,从ISR列表里踢出去。
    所以大家先得明白这个ISR是什么,说白了,就是Kafka自动维护和监控哪些Follower及时的跟上了Leader的数据同步。
     
    Kafka写入的数据如何保证不丢失?
    所以如果要让写入Kafka的数据不丢失,你需要要求几点:
    每个Partition都至少得有1个Follower在ISR列表里,跟上了Leader的数据同步
    每次写入数据的时候,都要求至少写入Partition Leader成功,同时还有至少一个ISR里的Follower也写入成功,才算这个写入是成功了
    如果不满足上述两个条件,那就一直写入失败,让生产系统不停的尝试重试,直到满足上述两个条件,然后才能认为写入成功
    按照上述思路去配置相应的参数,才能保证写入Kafka的数据不会丢失
    好!现在咱们来分析一下上面几点要求。
    第一条,必须要求至少一个Follower在ISR列表里。
    那必须的啊,要是Leader没有Follower了,或者是Follower都没法及时同步Leader数据,那么这个事儿肯定就没法弄下去了。
    第二条,每次写入数据的时候,要求leader写入成功以外,至少一个ISR里的Follower也写成功。
     
     
    Topic 和 Partition
    在 Kafka 中的每一条消息都有一个 Topic。一般来说在我们应用中产生不同类型的数据,都可以设置不同的主题。
    一个主题一般会有多个消息的订阅者,当生产者发布消息到某个主题时,订阅了这个主题的消费者都可以接收到生产者写入的新消息。
    Kafka 为每个主题维护了分布式的分区(Partition)日志文件,每个 Partition 在 Kafka 存储层面是 Append Log。
    任何发布到此 Partition 的消息都会被追加到 Log 文件的尾部,在分区中的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,也就是我们的 Offset。Offset 是一个 Long 型的数字。
    我们通过这个 Offset 可以确定一条在该 Partition 下的唯一消息。在 Partition 下面是保证了有序性,但是在 Topic 下面没有保证有序性。
     
     
     
    MQ选型
    RabbitMQ 总结
    RabbitMQ 对消息堆积的支持并不好,当大量消息积压的时候,会导致 RabbitMQ 的性能急剧下降。
    RabbitMQ 的性能是这几个消息队列中最差的,大概每秒钟可以处理几万到十几万条消息。如果应用对消息队列的性能要求非常高,那不要选择 RabbitMQ。
    RabbitMQ 使用的编程语言 Erlang,扩展和二次开发成本高。
     
    RocketMQ 有着不错的性能,稳定性和可靠性,具备一个现代的消息队列应该有的几乎全部功能和特性,并且它还在持续的成长中。
    RocketMQ 有非常活跃的中文社区,大多数问题可以找到中文的答案。RocketMQ 使用 Java 语言开发,源代码相对比较容易读懂,容易对 RocketMQ 进行扩展或者二次开发。
    RocketMQ 对在线业务的响应时延做了很多的优化,大多数情况下可以做到毫秒级的响应,如果你的应用场景很在意响应时延,那应该选择使用 RocketMQ。
    RocketMQ 的性能比 RabbitMQ 要高一个数量级,每秒钟大概能处理几十万条消息。
    RocketMQ 的劣势是与周边生态系统的集成和兼容程度不够。
     
    Kafka 性能高效、可扩展良好并且可持久化。它的分区特性,可复制和可容错都是不错的特性。
    Kafka 使用 Scala 和 Java 语言开发,设计上大量使用了批量和异步的思想,使得 Kafka 能做到超高的性能。Kafka 的性能,尤其是异步收发的性能,是三者中最好的,但与 RocketMQ 并没有量级上的差异,大约每秒钟可以处理几十万条消息。
    在有足够的客户端并发进行异步批量发送,并且开启压缩的情况下,Kafka 的极限处理能力可以超过每秒 2000 万条消息。
    但是 Kafka 异步批量的设计带来的问题是,它的同步收发消息的响应时延比较高,因为当客户端发送一条消息的时候,Kafka 并不会立即发送出去,而是要等一会儿攒一批再发送,在它的 Broker 中,很多地方都会使用这种先攒一波再一起处理的设计。当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高。所以,Kafka 不太适合在线业务场景。
    吞吐量:Rocketmq 和 kafka,且都是顺序性往broker写消息 可分布式
    随着业务增多,topic 增多,Kafka的稳定性比RocketMQ要差不少。
    从业务扩展性来看
     
    但凡选择就会受到主观和客观两个因素的影响。我们如何尽量客观的进行架构和框架选型,而避免先有结果而后找理由的文字游戏,下面我分享下我们做MQ选型的过程(这里不是说主观就是不好的,但作为工程师凡事做结构化和量化还是有必要的)。
    3.1 关键需求
    1) 集群支持:为了保证消息中间件的可靠性,需要提供完备的生产者、消费者、消息中间件集群方案;
    2) 持久化的支持:为了避免消息丢失,需要支持消息保存到磁盘文件或其它格式存储;
    3) 消息重试的支持:消息处理失败后的支持失败转存或重试,并提供消息至少投第一次或消息最多投递一次的配置;
    4) 分布式事务的支持:为了保证业务的完整性,选择的中间件需要支持分布式;
    5) 消息的按序消费:在有些场景下,需要消息的消费能够按照发送的同样顺序进行处理从而保证顺序执行;
    6) 消息的延时支持:在2C业务处理或三方数据源对接中,会遇到消息延时投递要求,需要支持延投递;
    7) 消息堆积和回溯功能:在消息中间件持久化保存大量消息时不会对性能有大的影响,支持消息查询、重发,或者按照时间点来重新消费消息,以应对某一段时间消息的重新消费场景。
    3.2 其它需要考虑的因素
    1) 产品与当前技术栈是否匹配,团队人员熟悉源代码更便于对消息中间件的原理理解和后续功能扩展;
    2) 产品的使用广度特别是金融同业客户:同业因为业务同质化校对,场景需求相近,使用的人越多,说明关键场景支持较好,问题在之前暴露的越充分,当我们在使用时碰问题的时候,就比较容易找到对应的解决方案或解决人员;
    3) 产品的高可用性:作为一个金融企业,需要服务的持续可用,作为提高企业弹性的基础消息平台,集群和高可用是一个必不可少的要求;
    4) 产品的稳定性:产品可以持续、稳定的提供服务,不需要经常因为资源泄露或性能衰减等问题而重新启动。
    5) 产品的活跃度:通过github统计数据能看出来这个产品是否经常有人维护,经常有人开发一些新的功能,经常fix一些bug。
    3.3 选型要点及原则
    l 搜寻满足关键需求的框架到候选清单;
    l 从功能和非功能性需求等几个方面对候选框架进行筛选;
    l 在帅选过程中要做好量化记录,避免先有倾向性的结果,后有筛选,这样选型就变成了一场数字游戏;
    l 有时要换个角度思考,常用来做比较的可能就是最好的,如很多MQ框架都与Kafka做比较,那么Kafka有可能就是最通用的框架,如果做选型就要对其是否满足自己的需求做重点分析;
    l 遵循第三眼美女原则,让理性引导感性;
    l 适合的就是最好的,不要但纯追求高性能和功能全面。
     
     
     

  • 相关阅读:
    曾宪杰谈Java在淘宝的应用
    Spring 系列: Spring 框架简介
    Azul发布开源工具jHiccup,为Java提供运行时响应时间分析
    我在赶集网的两个月(完整版)
    汇编基础教程(一)——寄存器介绍(EFlags)
    PowerDesigner 12.5 开启注释列 将Comment(注释)及Name(名称)内容互相COPY的VBS代码 根据名称生成注释(完整示例)
    linux ubuntu SVN improt 项目 简单明了 一看就懂
    android WebView onJsAlert onJsConfirm
    汇编基础教程(二)——常用汇编指令之传送指令
    理解WebKit和Chromium: Chromium移动版(Chromium for Mobile: Android & iOS)
  • 原文地址:https://www.cnblogs.com/novalist/p/11621340.html
Copyright © 2011-2022 走看看