あなたはRoketMQを知っている必要があります

1.概要

この記事では、すべてのキーポイントRocketMQとカフカの総合的な比較を紹介しようと、私はあなたが読むために何かを得ることができると思います。

以前はMetaQ呼ばRocketMQ、MeataQのリリースバージョン3.0は、自然に、自分のデザインのアイデアをRocketMQ時間と改名し、カフカに似ていますが、カフカと異なるがはるかにJavaのスカラ座よりも起因する国内の聴衆に、Java開発の使用されていますそのため、多くのJava言語ベースの同社の最初の選択肢をRocketMQ。同じRocketMQとカフカは、Apache Foundationのトップレベルのプロジェクト、彼らは非常に高く、コミュニティ活動、非常に高速な反復プロジェクトの更新です。

2.はじめに例

2.1プロデューサー

public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {

        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.start();

        for (int i = 0; i < 128; i++)
            try {
                {
                    Message msg = new Message("TopicTest",
                        "TagA",
                        "OrderID188",
                        "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
                    SendResult sendResult = producer.send(msg);
                    System.out.printf("%s%n", sendResult);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        producer.shutdown();
    }
}

優れたプロデューサーの直接の定義、作成したメッセージは、あなたはsendメソッドを呼び出すことができます。

2.2消費者

public class PushConsumer {

    public static void main(String[] args) throws InterruptedException, MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1");
        consumer.subscribe("TopicTest", "*");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        //wrong time format 2017_0422_221800
        consumer.setConsumeTimestamp("20181109221800");
        consumer.registerMessageListener(new MessageListenerConcurrently() {

            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }
}

3.RocketMQアーキテクチャの原則

RocketMQのためにいくつかの質問をスローします:

  • RocketMQトピックとどのようなキューの、とカフカのパーティションの違いは何ですか?

  • どのように何のRocketMQネットワークモデル、およびカフカコントラスト?

  • RocketMQメッセージストアモデルは、どのように信頼性の高いストレージを確保するために、どのようにカフカを比較することは何ですか?

3.1 RocketMQチャート

あなたはRoketMQを知っている必要があります

RocketMQアーキテクチャ図の場合は、一般的な外観とカフカに大きな違いはありませんでしたが、多くの細部に多くの違いがあり、次は一つ一つを教えてくれます。

3.2 RocketMQ用語集

我々はプロデューサーのそれぞれが複数のトピックに対応することができ、ブローカーからより多くのプロデューサー、複数のマスターブローカーを、持っている3.1アーキテクチャは、各消費者は、より多くのトピックを消費することができます。

ネームサーバトピックから情報を引っ張ってくるブローカー情報はネームサーバ、消費者やブローカーに報告されます。

  • プロデューサー:メーカーメッセージ、クライアントのブローカーにメッセージを送信

  • 消費:消費者ニュース、ブローカーのクライアントからのメッセージを読みます

  • ブローカ:中間メッセージ処理ノード、こことカフカ異なる、カフカブローカのマスターとスレーブの概念がない、一般的に読み取るためのマスタノードは、マスタノードによる書き込み要求とバックアップ他のノードデータ、書き込みにRocketMQのみマスタブローカノードをすることができアーキテクチャからメインのMySQL - 不良またはいくつかの他の特別なパターンは幾分類似ノードから読み取るために使用されます。

  • トピック:メッセージの件名は、メッセージプロデューサを送って、メッセージを入力し、消費者が自分のメッセージを読みます。

  • グループ:グループの分割ProducerGroup、ConsumerGroup、生産者と消費者のクラスを代表して、一般的には、同じサービスグル​​ープとして使用することができますが、一般的に送信し、メッセージが同じで消費します。

  • タグ:カフカは、概念ではない2つのメッセージの種類に属し、タグ、一般的に関連するビジネスTopic_Orderを使用し、このような順序のメッセージキューと、同じタグを使用することができ、タグはTag_食品順に分けることができ、Tag_の服の注文ように。

  • キュー:各内部キューを発注されたカフカパーティションに呼び出され、それは一般的な読み取りには、RocketMQ中のキューの読み取りと書き込みの2種類に分け、キューの同じ番号を記入し、そうでない場合は多くの問題があるだろうさ。

  • ネームサーバ:カフカはブローカー保存アドレス情報のZooKeeperを使用して、ブローカーの選挙のリーダーとしてRocketMQで採用なし選挙ブローカー戦略、あまりにストレージステートレスネームサーバを使用するので、ネームサーバはステートレスであるため、クラスターアップロードデータは、ノード間のすべてのノードに送信する必要がある場合など、通信しないであろう。

私の友人の多くは、ステートレスが何を求めていますか?データは実際に店ステートフルセッションデータをするつもりされているかどうかの状態が永続的で、ステートレスサービスは、メモリサービス、ネームサーバサービス自体は、メモリが、その後、全てのデータがメモリに格納され、再起動であることを理解することができますそれは失われます。

3.3トピックとキュー

RocketMQ内の各メッセージが、異なるメッセージを区別するために使用されるトピックがあります。テーマは、一般的に生産者がトピックにメッセージをパブリッシュする際、複数の加入者のメッセージを持って、テーマの消費者に加入し、新たなメッセージプロデューサの書き込みを受けることができます。

でより多くのキュートピックあずかるために、これは我々がメッセージチャネルの最小単位を読む/送ること実際に、私たちは、指定された書き込みにメッセージを送信する必要があるキュー、プルメッセージは、彼らはまた、プルを指定する必要がありますキュー、我々は待ち行列の次元キューたちの秩序に基づいてメッセージの順序を維持できるように、あなたがやりたいならば、あなたは秩序あるグローバルキューサイズに必要なすべてのデータがキューに注文されますので、こと、1に設定されています。

あなたはRoketMQを知っている必要があります

私たちのプロデューサーキューは、上記画像における戦略のいくつかによって選択されます。

  • 非メッセージ・シーケンス:非連続的なメッセージは、典型的には、直接伝送の仕方によって回転送信されます。

  • 注文ニュース:そのような私たちの共通の受注ID、ユーザIDとキーによると、私たちのシーケンシャルことを保証するために、ハッシュ、同じキュー内のデータの同じ種類でした。

私たちは、このような均等に分布し、共通または整合性ハッシュとしてキューを選択する消費者いくつかの戦略、の同じセットに基づいて行われます。

注消費者がオフライン表示されたときにまたはオンライン、次のようである重量バランスを、実行する必要が、リバランス、RocketMQ再バランス機構は次のとおりです。

  • 情報ブローカーにプルするタイミング、の話題

  • 20代は、すべての重量バランスを行います

  • ランダムに選択された現在の主ブローカトピック、より多くの場合、その後、ブローカブローカが存在することになるため、すべての主ブローカは、選択されていないすべての重量バランスことが留意されるべきです。

  • 現在のすべてのマシンIDブローカー、現在ConsumerGroupを取得します。

  • 次に、ポリシーの割り当て。

リバランスを行うには時間があるので、ので、ここではキュー2人の消費者を消費し、同時に発生する可能性があるので、メッセージの配信が繰り返されます。

メカニズムとRocketMQのバランスをとる異なるカフカの重量は、コーディネーターが認識されると、カフカが完全に消費者やコーディネーターの接触を通じてであるリバランス、コンシューマ・グループの変化は、平衡信号の心臓手続きを送信され、その後、1 ConsumerLeaderによって再バランス選択し、[すべての消費者へのコーディネーターによって結果が通知。

読み取りと書き込みの数と3.3.1キューの矛盾

このような多くの時間が、我々はマシンのキューを設定し、消費者の多くを読んだとき、いつものように、読み取りと書き込みのキュー設定の矛盾の数は何の問題もないと思っ接触RocketMQの初めに、キューを読み、RocketMQへの書き込みの2種類に分かれていますしかし、メッセージ中に発見された実際の状況は、消費し、何もメッセージは表示されません消費することはできません。

  • キューの数は、それが消費者に割り当てることがないため、データは、この支出の一部ではありません、読み取りキューIDよりも大きい場合、キュー書き込みの数は、書き込みキューを読むよりも大きい場合。

  • キューの数は、読み取り、書き込みをキューよりも数が多い場合には、その後、これ以上のキューメッセージの数がで配信されているよりはないだろう。

基本的に読み取りと書き込みのキューは、これが統一されるように指示していない理由を同じサイズに設定されますが、ユーザーは同じエラーを設定するには、容易ではないためため、この機能は、私の意見では、明らかに無用RocketMQ。

この質問はで問題の良い答えRocketMQを受信して​​いません。

3.4消費モデル

メッセージキュー消費モデルは、一般に、2つのタイプ、プッシュベースのメッセージ(プッシュ)モデルとプルベースモデル(ポーリング)に分割されます。

状態報道機関レコードによる消費に基づいて、プッシュモデルをメッセージング。メッセージエージェントは、消費者にメッセージをプッシュします後、消費されたものとしてメッセージをマークするが、このアプローチは、優れた処理セマンティクス消費を保証することはできません。消費者庁では、我々は消費としてそれをマークする場合は、消費者や、ネットワークの理由のためにプロセスをハングアップは、このメッセージは表示されませんので、例えば、私たちは消費者にメッセージを送信する必要がある場合、ニュースは永久に失われました。私たちは生産者が応答メッセージを受け取るこの方法を使用する場合、レコードの消費状態にメッセージブローカ必要が、これは望ましいことではありません。

RocketMQの提供に使用されるRocketMQの学生確かに助けることはできませんが、考えて、2つの消費者はそうではありませんか?

MQPullConsumer そして  MQPushConsumer これを  MQPushConsumer 、それが私たちのプッシュモデルではありませんか?実際には、これら2つのモデルが、クライアントは次のように実装の違いがあるというメッセージを引くためのイニシアチブをとる、次のとおりです。

  • MQPullConsumer:プルオフセットプルメッセージとどのように多くのメッセージが各プルの量、クライアントプルで制御されているどのくらいの、プル特定のメッセージへのすべての受信メッセージのニーズ。

  • MQPushConsumer:クライアントは、アクティブプルメッセージですが、メッセージは、サーバーの進展によって保存され、消費者が定期的に次回の個人消費は、一般的に使用PushConsumerで、消費の最後のポイントを発見されるので、どこに自分の消費を報告我々は直接使用することができますどのくらいのデータがオフセットとプル、気にしません。

3.4.1クラスタ消費と消費者の放送

我々は2つの消費パターン、消費者のクラスタ、ラジオ消費に分け:

  • クラスタ消費は:同じグループIDがクラスタに属し、一般的に、メッセージは、任意のプロセスにおいて消費者であろう。

  • ブロードキャスト消費者:クライアントはたびに、消費者からの最新ニュースではなく、最後に再起動されますので、ブロードキャストメッセージの消費、すべての消費者のニュースでは、クラスタ化されているが、放送の消費量は、サーバーのコストに記憶されたオフセットために注意を払う必要がありますが、高すぎますオフセットを保存します。

3.5ネットワークモデル

ネイティブソケットは、ネットワーク通信にカフカで使用され、RocketMQはネッティーネットワークフレームワークを使用して、今より多くのミドルウェア主として次のように、直接ネイティブのソケットを選択しますが、ネッティーフレームワークを使用することはありませんいくつかの理由のために:

  • APIの使い方は簡単で、ミドルウェアのロジックに集中するために、ネットワークの詳細についてはあまりケアを必要としません。

  • 高性能。

  • 成熟し安定したが、バグのJDK NIOが修正されました。

一つのことですが、スレッドモデル効率的なネットワーク通信ネットワークは、一方であることを確認したい、私たちは共通の1 + N(1つのアクセプタスレッド、N数IOスレッドを)持っているフレームを選択し、1 + N + M(1つの1つのアクセプタ図に示すように、モデル1 + N1 + N2 + Mを使用してモデル、RocketMQ等スレッド、N個のIOスレッド、ワーカースレッドM):

あなたはRoketMQを知っている必要があります

アクセプタスレッド、IOスレッドN1を、コーデックシェークハンド、SSL認証に使用N2スレッド、サービス処理に使用されるMスレッド。利点は、コーデック、およびSSLの認証といくつかは、別のスレッドプールのスレッドに時間のかかる操作であってもよいが、当社の事業およびIOスレッドを占有しませんでしょう。

3.6信頼性の高い分散型ストレージ・モデル

良いニュースシステム、高性能ストレージとして、高可用性が不可欠です。

3.6.1高性能ログ保存

RocketMQ和Kafka的存储核心设计有很大的不同,所以其在写入性能方面也有很大的差别,这是16年阿里中间件团队对RocketMQ和Kafka不同Topic下做的性能测试:

あなたはRoketMQを知っている必要があります

从图上可以看出:

  • Kafka在Topic数量由64增长到256时,吞吐量下降了98.37%。

  • RocketMQ在Topic数量由64增长到256时,吞吐量只下降了16%。

    这是为什么呢?kafka一个topic下面的所有消息都是以partition的方式分布式的存储在多个节点上。同时在kafka的机器上,每个Partition其实都会对应一个日志目录,在目录下面会对应多个日志分段。所以如果Topic很多的时候Kafka虽然写文件是顺序写,但实际上文件过多,会造成磁盘IO竞争非常激烈。

那RocketMQ为什么在多Topic的情况下,依然还能很好的保持较多的吞吐量呢?我们首先来看一下RocketMQ中比较关键的文件:

あなたはRoketMQを知っている必要があります

这里有四个目录(这里的解释就直接用RocketMQ官方的了):

  • commitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。单个文件大小默认1G ,文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件;

  • config:保存一些配置信息,包括一些Group,Topic以及Consumer消费offset等信息。

  • consumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能,由于RocketMQ是基于主题topic的订阅模式,消息消费是针对主题进行的,如果要遍历commitlog文件中根据topic检索消息是非常低效的。Consumer即可根据ConsumeQueue来查找待消费的消息。其中,ConsumeQueue(逻辑消费队列)作为消费消息的索引,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息大小size和消息Tag的HashCode值。consumequeue文件可以看成是基于topic的commitlog索引文件,故consumequeue文件夹的组织方式如下:topic/queue/file三层组织结构,具体存储路径为:HOME \store\index\${fileName},文件名fileName是以创建时的时间戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引,IndexFile的底层存储设计为在文件系统中实现HashMap结构,故rocketmq的索引文件其底层实现为hash索引。

我们发现我们的消息主体数据并没有像Kafka一样写入多个文件,而是写入一个文件,这样我们的写入IO竞争就非常小,可以在很多Topic的时候依然保持很高的吞吐量。有同学说这里的ConsumeQueue写是在不停的写入呢,并且ConsumeQueue是以Queue维度来创建文件,那么文件数量依然很多,在这里ConsumeQueue的写入的数据量很小,每条消息只有20个字节,30W条数据也才6M左右,所以其实对我们的影响相对Kafka的Topic之间影响是要小很多的。我们整个的逻辑可以如下:

あなたはRoketMQを知っている必要があります

Producer不断的再往CommitLog添加新的消息,有一个定时任务ReputService会不断的扫描新添加进来的CommitLog,然后不断的去构建ConsumerQueue和Index。

注意:这里指的都是普通的硬盘,在SSD上面多个文件并发写入和单个文件写入影响不大。

读取消息

Kafka中每个Partition都会是一个单独的文件,所以当消费某个消息的时候,会很好的出现顺序读,我们知道OS从物理磁盘上访问读取文件的同时,会顺序对其他相邻块的数据文件进行预读取,将数据放入PageCache,所以Kafka的读取消息性能比较好。

RocketMQ读取流程如下:

  • 先读取ConsumerQueue中的offset对应CommitLog物理的offset

  • 根据offset读取CommitLog

ConsumerQueue也是每个Queue一个单独的文件,并且其文件体积小,所以很容易利用PageCache提高性能。而CommitLog,由于同一个Queue的连续消息在CommitLog其实是不连续的,所以会造成随机读,RocketMQ对此做了几个优化:

  • Mmap映射读取,Mmap的方式减少了传统IO将磁盘文件数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区之间来回进行拷贝的性能开销

  • 使用DeadLine调度算法+SSD存储盘

  • 由于Mmap映射受到内存限制,当不在Mmmap映射这部分数据的时候(也就是消息堆积过多),默认是内存的40%,会将请求发送到SLAVE,减缓Master的压力

3.6.2 可用性

3.6.2.1 集群模式

我们首先需要选择一种集群模式,来适应我们可忍耐的可用程度,一般来说分为三种:

  • 单Master:这种模式,可用性最低,但是成本也是最低,一旦宕机,所有都不可用。这种一般只适用于本地测试。

  • 单Master多SLAVE:这种模式,可用性一般,如果主宕机,那么所有写入都不可用,读取依然可用,如果master磁盘损坏,可以依赖slave的数据。

  • 多Master:这种模式,可用性一般,如果出现部分master宕机,那么这部分master上的消息都不可消费,也不可写数据,如果一个Topic的队列在多个Master上都有,那么可以保证没有宕机的那部分可以正常消费,写入。如果master的磁盘损坏会导致消息丢失。

  • 多Master多Slave:这种模式,可用性最高,但是维护成本也最高,当master宕机了之后,只会出现在这部分master上的队列不可写入,但是读取依然是可以的,并且如果master磁盘损坏,可以依赖slave的数据。

一般来说投入生产环境的话都会选择第四种,来保证最高的可用性。

3.6.2.2 消息的可用性

当我们选择好了集群模式之后,那么我们需要关心的就是怎么去存储和复制这个数据,rocketMQ对消息的刷盘提供了同步和异步的策略来满足我们的,当我们选择同步刷盘之后,如果刷盘超时会给返回FLUSH_DISK_TIMEOUT,如果是异步刷盘不会返回刷盘相关信息,选择同步刷盘可以尽最大程度满足我们的消息不会丢失。

除了存储有选择之后,我们的主从同步提供了同步和异步两种模式来进行复制,当然选择同步可以提升可用性,但是消息的发送RT时间会下降10%左右。

3.6.3 Dleger

我们上面对于master-slave部署模式已经做了很多分析,我们发现,当master出现问题的时候,我们的写入怎么都会不可用,除非恢复master,或者手动将我们的slave切换成master,导致了我们的Slave在多数情况下只有读取的作用。RocketMQ在最近的几个版本中推出了Dleger-RocketMQ,使用Raft协议复制CommitLog,并且自动进行选主,这样master宕机的时候,写入依然保持可用。

3.7 定时/延时消息

定时消息和延时消息在实际业务场景中使用的比较多,比如下面的一些场景:

  • 订单超时未支付自动关闭,因为在很多场景中下单之后库存就被锁定了,这里需要将其进行超时关闭。

  • 需要一些延时的操作,比如一些兜底的逻辑,当做完某个逻辑之后,可以发送延时消息比如延时半个小时,进行兜底检查补偿。

  • 在某个时间给用户发送消息,同样也可以使用延时消息。

在开源版本的RocketMQ中延时消息并不支持任意时间的延时,需要设置几个固定的延时等级,目前默认设置为: 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h ,从1s到2h分别对应着等级1到18,而阿里云中的版本(要付钱)是可以支持40天内的任何时刻(毫秒级别)。我们先看下在RocketMQ中定时任务原理图:

あなたはRoketMQを知っている必要があります

  • Step1:Producer在自己发送的消息上设置好需要延时的级别。

  • Step2: Broker发现此消息是延时消息,将Topic进行替换成延时Topic,每个延时级别都会作为一个单独的queue,将自己的Topic作为额外信息存储。

  • Step3: 构建ConsumerQueue

  • Step4: 定时任务定时扫描每个延时级别的ConsumerQueue。

  • Step5: 拿到ConsumerQueue中的CommitLog的Offset,获取消息,判断是否已经达到执行时间

  • Step6: 如果达到,那么将消息的Topic恢复,进行重新投递。如果没有达到则延迟没有达到的这段时间执行任务。

可以看见延时消息是利用新建单独的Topic和Queue来实现的,如果我们要实现40天之内的任意时间度,基于这种方案,那么需要40 24 60 60 1000个queue,这样的成本是非常之高的,那阿里云上面的支持任意时间是怎么实现的呢?这里猜测是持久化二级TimeWheel时间轮,二级时间轮用于替代我们的ConsumeQueue,保存Commitlog-Offset,然后通过时间轮不断的取出当前已经到了的时间,然后再次投递消息。具体的实现逻辑需要后续会单独写一篇文章。

3.8 事务消息

事务消息同样的也是RocketMQ中的一大特色,其可以帮助我们完成分布式事务的最终一致性,有关分布式事务相关的可以看我以前的很多文章都有很多详细的介绍。

あなたはRoketMQを知っている必要があります

具体使用事务消息步骤如下:

  • Step1:调用sendMessageInTransaction发送事务消息

  • Step2:  如果发送成功,则执行本地事务。

  • Step3:  如果执行本地事务成功则发送commit,如果失败则发送rollback。

  • Step4:  如果其中某个阶段比如commit发送失败,rocketMQ会进行定时从Broker回查,本地事务的状态。

事务消息的使用整个流程相对之前几种消息使用比较复杂,下面是事务消息实现的原理图:

あなたはRoketMQを知っている必要があります

  • Step1: 发送事务消息,这里也叫做halfMessage,会将Topic替换为HalfMessage的Topic。

  • Step2: 发送commit或者rollback,如果是commit这里会查询出之前的消息,然后将消息复原成原Topic,并且发送一个OpMessage用于记录当前消息可以删除。如果是rollback这里会直接发送一个OpMessage删除。

  • Step3: 在Broker有个处理事务消息的定时任务,定时对比halfMessage和OpMessage,如果有OpMessage且状态为删除,那么该条消息必定commit或者rollback,所以就可以删除这条消息。

  • Step4: 如果事务超时(默认是6s),还没有opMessage,那么很有可能commit信息丢了,这里会去反查我们的Producer本地事务状态。

  • Step5: 根据查询出来的信息做Step2。

我们发现RocketMQ实现事务消息也是通过修改原Topic信息,和延迟消息一样,然后模拟成消费者进行消费,做一些特殊的业务逻辑。当然我们还可以利用这种方式去做RocketMQ更多的扩展。

4.总结

这里让我们在回到文章中提到的几个问题:

  • RocketMQ的topic和队列是什么样的,和Kafka的分区有什么不同?

  • どのように何のRocketMQネットワークモデル、およびカフカコントラスト?

  • RocketMQメッセージストアモデルは、どのように信頼性の高いストレージを確保するために、どのようにカフカを比較することは何ですか?

おそらく、この記事を読んで、あなたの心は答えを持っています

おすすめ

転載: blog.51cto.com/14230003/2458094