[JavaWebマイクロサービスアーキテクチャプロジェクト-LeyouMall day11] -RabbitMQを使用して、検索ページと静的ページ間のデータ同期を実現します

0.学習目標

  • 一般的なMQ製品を理解する
  • RabbitMQの5つのメッセージモデルを理解する
  • MQを使用して、検索ページと静的ページ間のデータ同期を実現します

ソースコードとメモ:
リンク:https
://pan.baidu.com/s/1REHmVFclFI9_pu1ESB_sZw抽出コード:voyc

1.RabbitMQ

1.1。検索と商品サービスの問題

現在、商品詳細・検索システムの開発を完了しております。考えてみましょう、問題はありますか?

  • 製品の元のデータはデータベースに保存され、すべての追加、削除、変更、およびチェックはデータベースで完了します。
  • 検索サービスデータのソースはインデックスデータベースです。データベースの商品が変更された場合、インデックスデータベースデータは時間内に更新できません。
  • 製品の詳細はページ上で静的化されており、静的なページデータはデータベース製品によって変更されません。

バックグラウンドで商品の価格を変更しても、検索ページと商品詳細ページには古い価格が表示されますが、これは明らかに間違っています。それを解決する方法は?

2つの解決策があります:

  • 解決策1:バックグラウンドで製品を追加、削除、および変更するときは常に、インデックスデータベースデータと静的ページを同時に変更する必要があります
  • 解決策2:検索サービスと製品ページサービスは外部操作インターフェイスを提供し、バックグラウンドは製品を追加、削除、および変更した後にインターフェイスを呼び出します

上記の2つの方法には、同じ深刻な問題があります。コードカップリング、検索、および製品ページサービスをバックエンドサービスに組み込む必要があり、これ独立はマイクロサービス原則に違反します。

したがって、この問題を別の方法で解決します:メッセージキュー

1.2。メッセージキュー(MQ)

1.2.1。メッセージキューとは

メッセージキュー、つまりMQ、メッセージキュー。
ここに画像の説明を挿入

メッセージキューは典型的なものです:プロデューサー、コンシューマーモデル。プロデューサーは引き続きメッセージキューにメッセージを生成し、コンシューマーは引き続きキューからメッセージを取得します。メッセージの生成と消費は非同期であり、メッセージの送受信のみを考慮しているため、ビジネスロジックの侵入がなく、プロデューサーとコンシューマーの分離が実現されます。

前述の問題を組み合わせる:

  • 製品を追加、削除、変更した後は、インデックスライブラリや静的ページを操作する必要はなく、メッセージを送信するだけで、誰がメッセージを受信するかは関係ありません。
  • 検索サービスと静的ページサービスはメッセージを受信し、それぞれインデックスライブラリと静的ページを処理します。

将来的にコモディティサービスのデータにも依存する他のシステムがある場合、それらはメッセージを監視することもでき、コモディティサービスはコードの変更を必要としません。

1.2.2.AMQPとJMS

MQはメッセージ通信のモデルであり、特定の実装ではありません。MQを実装するには、AMQPとJMSの2つの主流の方法があります。

ここに画像の説明を挿入

2つの違いと接続:

  • JMSは、メッセージ操作を統合するための統合インターフェースを定義します。AMQPは、指定されたプロトコルを介したデータ相互作用の形式を統合することです。
  • JMSはJava言語の使用を制限します。AMQPは単なるプロトコルであり、実装方法を指定しないため、クロスランゲージです。
  • JMSは2つのメッセージモデルを提供します。AMQPメッセージモデルはより豊富です

1.2.3。一般的なMQ製品

ここに画像の説明を挿入

  • ActiveMQ:JMSに基づく
  • RabbitMQ:AMQPプロトコル、erlang言語開発、優れた安定性に基づく
  • RocketMQ:現在ApacheFoundationに引き渡されているJMS、Alibaba製品に基づいています
  • Kafka:分散メッセージングシステム、高スループット

1.2.4.RabbitMQ

RabbitMQは、AMQPに基づくメッセージ管理システムです。

公式サイト:http://www.rabbitmq.com/

公式チュートリアル:http://www.rabbitmq.com/getstarted.html

ここに画像の説明を挿入

ここに画像の説明を挿入

1.3。ダウンロードしてインストールする

別のブログを参照してください:rabbitMQのインストール
https://blog.csdn.net/qq_38454176/article/details/105338529

2.5つのメッセージモデル

RabbitMQは6つのメッセージモデルを提供しますが、6番目のモデルは実際にはMQではなくRPCであるため、学習しません。あと5種類しか残っていません。

RabbitMQの具体的な学習と使用については、別のブログを参照してください:
day72JavaWebフレームワークステージ-RabbitMQメッセージキュー
https://blog.csdn.net/qq_38454176/article/details/105339764

3.プロジェクトの変革

次に、プロジェクトを変換して、検索サービスと製品の静的ページのデータ同期を実現します。

3.1。思考分析

送信者:商品マイクロサービス

  • いつ送られますか?

    商品サービスが商品を書き込むとき(追加、削除、または変更)、他のサービスに通知するためにメッセージを送信する必要があります。

  • 何を送る?

    他のサービスでは、製品を追加、削除、または変更するときに新しい製品データが必要になる場合がありますが、メッセージのコンテンツにすべての製品情報が含まれている場合、データ量が多すぎて、すべてのサービスがすべての情報を必要とするわけではありません。したがって、製品IDのみを送信し、他のサービスはIDに基づいて必要な情報を照会できます。

受信者:マイクロサービスの検索、静的ページのマイクロサービス

メッセージを受信した後はどうすればよいですか?

  • マイクロサービスの検索:
    • 追加/変更:インデックスライブラリに新しいデータを追加します
    • 削除:インデックスデータベースデータを削除します
  • 静的ページマイクロサービス:
    • 追加/変更:新しい静的ページを作成します
    • 削除:元の静的ページを削除します

3.2。商品やサービスに関するメッセージの送信

まず、コモディティマイクロサービスでleyou-item-serviceメッセージの送信実装します。

3.2.1。依存関係を導入する

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

3.2.2。構成ファイル

application.ymlにRabbitMQに関するいくつかの構成を追加します。

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou
    template:
      exchange: leyou.item.exchange
    publisher-confirms: true
  • テンプレート:関連するAmqpTemplate構成
    • 交換:デフォルトのスイッチ名。ここで構成した後、スイッチが指定されていない場合、これはメッセージの送信に使用されます。
  • パブリッシャー確認:メッセージが正しく送信されることを確認するプロデューサー確認メカニズム。送信が失敗した場合、エラー受信が発生し、再試行がトリガーされます。

3.2.3.GoodsServiceの変換

GoodsServiceのmqにメッセージを送信するメソッドをカプセル化します:(AmqpTemplateテンプレートを挿入する必要があります)

private void sendMessage(Long id, String type){
    
    
    // 发送消息
    try {
    
    
        this.amqpTemplate.convertAndSend("item." + type, id);
    } catch (Exception e) {
    
    
        logger.error("{}商品消息发送异常,商品id:{}", type, id, e);
    }
}

ここではスイッチが指定されていないため、デフォルトで構成に送信されます。leyou.item.exchange

注:メッセージの送信が通常のビジネスロジックに影響を与えないように、ここですべての例外を試す必要があります

次に、追加するときに呼び出します。

ここに画像の説明を挿入

変更時に呼び出されます:

ここに画像の説明を挿入

3.3。メッセージを受信する検索サービス

検索サービスがメッセージを受信した後の対処方法:

  • 増加:インデックスライブラリに新しいデータを追加します
  • 削除:インデックスデータベースデータを削除します
  • 変更:インデックスデータベースデータを変更する

インデックスライブラリの新しいメソッドと変更されたメソッドが組み合わされているため、これら2種類のメッセージを一緒に処理し、別々に削除することができます。

3.3.1。依存関係を導入する

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

3.3.2。構成を追加する

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou

ここでは、メッセージは受信のみで送信されないため、テンプレート関連のコンテンツを構成する必要はありません。

3.3.3。リスナーの作成

ここに画像の説明を挿入

コード:

@Component
public class GoodsListener {
    
    

    @Autowired
    private SearchService searchService;

    /**
     * 处理insert和update的消息
     *
     * @param id
     * @throws Exception
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "leyou.create.index.queue", durable = "true"),
            exchange = @Exchange(
                    value = "leyou.item.exchange",
                    ignoreDeclarationExceptions = "true",
                    type = ExchangeTypes.TOPIC),
            key = {
    
    "item.insert", "item.update"}))
    public void listenCreate(Long id) throws Exception {
    
    
        if (id == null) {
    
    
            return;
        }
        // 创建或更新索引
        this.searchService.createIndex(id);
    }

    /**
     * 处理delete的消息
     *
     * @param id
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "leyou.delete.index.queue", durable = "true"),
            exchange = @Exchange(
                    value = "leyou.item.exchange",
                    ignoreDeclarationExceptions = "true",
                    type = ExchangeTypes.TOPIC),
            key = "item.delete"))
    public void listenDelete(Long id) {
    
    
        if (id == null) {
    
    
            return;
        }
        // 删除索引
        this.searchService.deleteIndex(id);
    }
}

3.3.4。createおよびdeleteインデックスメソッドの記述

ここでは、インデックスを作成および削除するため、SearchServiceの2つのメソッドを拡張して、インデックスを作成および削除する必要があります。

public void createIndex(Long id) throws IOException {
    
    

    Spu spu = this.goodsClient.querySpuById(id);
    // 构建商品
    Goods goods = this.buildGoods(spu);

    // 保存数据到索引库
    this.goodsRepository.save(goods);
}

public void deleteIndex(Long id) {
    
    
    this.goodsRepository.deleteById(id);
}

インデックスの作成方法は、以前にデータをインポートしたテストクラスからコピーおよび変更できます。

3.4。静的ページサービスはメッセージを受信します

商品静的ページサービスによるメッセージ受信後の処理:

  • 増加:新しい静的ページを作成します
  • 削除:元の静的ページを削除します
  • 変更:新しい静的ページを作成し、元のページを上書きします

ただし、静的ページを作成するために作成したメソッドには、前のページを上書きする機能もあります。したがって、追加および変更されたメッセージを1つの方法で処理し、削除されたメッセージを別の方法で処理できます。

3.4.1。依存関係を導入する

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

3.4.2。構成を追加する

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou

ここでは、メッセージは受信のみで送信されないため、テンプレート関連のコンテンツを構成する必要はありません。

3.4.3。リスナーの作成

ここに画像の説明を挿入

コード:

@Component
public class GoodsListener {
    
    

    @Autowired
    private GoodsHtmlService goodsHtmlService;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "leyou.create.web.queue", durable = "true"),
            exchange = @Exchange(
                    value = "leyou.item.exchange",
                    ignoreDeclarationExceptions = "true",
                    type = ExchangeTypes.TOPIC),
            key = {
    
    "item.insert", "item.update"}))
    public void listenCreate(Long id) throws Exception {
    
    
        if (id == null) {
    
    
            return;
        }
        // 创建页面
        goodsHtmlService.createHtml(id);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "leyou.delete.web.queue", durable = "true"),
            exchange = @Exchange(
                    value = "leyou.item.exchange",
                    ignoreDeclarationExceptions = "true",
                    type = ExchangeTypes.TOPIC),
            key = "item.delete"))
    public void listenDelete(Long id) {
    
    
        if (id == null) {
    
    
            return;
        }
        // 删除页面
        goodsHtmlService.deleteHtml(id);
    }
}

3.4.4。ページメソッドの追加と削除

public void deleteHtml(Long id) {
    
    
    File file = new File("C:\\project\\nginx-1.14.0\\html\\item\\", id + ".html");
    file.deleteOnExit();
}

3.5。テスト

3.5.1。RabbitMQコンソールを表示する

プロジェクトを再起動し、RabbitMQ管理インターフェイスにログインします:http://192.168.56.101:15672

ご覧のとおり、スイッチは次のように作成されています。
ここに画像の説明を挿入

キューも作成されました:

ここに画像の説明を挿入

そして、キューはスイッチにバインドされています。
ここに画像の説明を挿入

3.5.2。データを変更して試してください

バックグラウンドで商品データの価格を変更し、検索ページと商品詳細ページで統一されているかどうかを確認します。

おすすめ

転載: blog.csdn.net/qq_38454176/article/details/105338853