ElasticSearch - マイクロサービス プロジェクトでの RabbitMQ に基づく ES と MySQL データの非同期同期 (テスト ポイント)

目次

1. データの同期

1.1. データ同期とは何ですか?

1.2. データ同期が直面する問題を解決する

1.3. 解決策

1.3.1. 同期呼び出し

1.3.2. 非同期通知 (推奨)

1.3.3. ビンログの監視

1.3. RabbitMQ に基づくデータ同期

1.3.1. 需要

1.3.2. 「ホテル検索サービス」でexchange、queue、routingKeyを宣言し、同時に監視を有効にします。

1.3.3. 「ホテル管理サービス」にメッセージを投稿する

1.3.4. マイクロサービスの開始とテスト


1. データの同期


1.1. データ同期とは何ですか?

elasticsearch データがデータベース (mysql など) から取得されることはわかっています。mysql のデータを es にインポートするコードを作成すると、このインポート後に mysql のデータは変更されません。将来的にはビジネスに問題が発生するでしょう。を実行すると、データベース内のデータが追加、変更、削除されるため、mysqlのデータは必ず変更され、esがその変更に追従しないと問題が発生します。

たとえば、モール システムでは、ダブル 11 でデータベース内の商品の価格が下がったが、es は古い価格のままであり、ユーザーが検索するときに表示される商品の価格は変わっておらず、ユーザーはソフトウェアの変更を検討する必要があるかもしれません~  

したがって、mysql データが変更されると、es も同期的に変更できるようにする必要があります。これがデータ同期です。

Ps: 実際、es だけでなくデータ同期の問題が発生するだけでなく、redis や mysql などのデータベースの二重書き込みを伴うあらゆる状況でもデータ同期の問題が発生します。

1.2. データ同期が直面する問題を解決する

現在、一つのプロジェクトに取り組んでいて、全ての業務が一つのプロジェクトに書かれている場合は、業務を遡って追加・変更・削除する際にesを更新するだけで済むので扱いやすいと思います。

しかし、マイクロサービス アーキテクチャ プロジェクトに取り組んでいる場合、異なるビジネスが異なるマイクロサービス上に存在することがよくあります。たとえば、「製品データ管理ビジネス」と「製品データ検索ビジネス」が 2 つの異なるマイクロサービス上に存在することは間違いなく、クロス マイクロサービス プロジェクトは不可能です。直接運営されています。

1.3. 解決策

1.3.1. 同期呼び出し

2 つのマイクロサービスがあるとします。1 つはホテル データ管理サービス、もう 1 つはホテル データ検索サービスです。これら 2 つのサービスは相互にデータベースにアクセスできない、つまり、ホテル管理サービスは mysql にのみアクセスできると仮定します。サービスは es にのみアクセスでき、これもマイクロサービスの標準と仕様に準拠しています。

ここでの 1 つの方法は、同期的に呼び出すことです。手順は次のとおりです。

1. たとえば、ユーザーが新しい操作を実行すると、まずデータがデータベースに書き込まれます。

2. データベースが書き込まれた後、ホテル検索サービスの「インデックス データベースの更新」インターフェイスが呼び出されます。

3. 更新します。

最終更新後、es は検索サービスに応答をフィードバックし、検索サービスは管理サービスに応答をフィードバックし、さらにユーザーに応答を返すという一連の処理が順番に実行されるため、同期呼び出しとも呼ばれます。

欠陥:

1. データ結合、ビジネス結合:当初はデータベースを書いただけで、書いたら終わりでしたが、今度はデータベースのコードを記述し、次に「インデックス ライブラリの更新」インターフェイスを呼び出すコードを追加する必要があります。これにより、ビジネスが呼び出されます。このインターフェースの変更は明らかに私の新しいビジネスとは関係がありません。ビジネスが結合されたため、将来的にパフォーマンスに影響が出るのは避けられません。

2. パフォーマンスへの影響 (カップリングによって引き起こされる問題):当初、データベースの書き込みには 50 ミリ秒かかりましたが、データベースの書き込み後も、バックグラウンドでこのインターフェイスを呼び出して返される応答を待つ必要があります。このインターフェイスは es での応答を待つ必要があります。ここでも 50 ミリ秒かかる場合、かかる合計時間はこれら 3 つのステップの合計ではなく、100 ミリ秒に達します。

3. 一つの行動が全身に影響を与える(カップリングによる問題): 2.と3.のどこかで例外が発生すると、ビジネス全体が破綻してしまいます。

同期呼び出しには非常に多くの問題があるため、他の解決策を検討する必要があります。

1.3.2. 非同期通知 (推奨)

ここでは、mq を使用してそれを実現する必要があります。手順は次のとおりです。

1. 誰かが新しい操作を行うときは、最初にデータベースを書き込みます。

2. 書き込み後、サービスインターフェイスを呼び出さず、他のサービスに通知するメッセージを mq に送信します。「ここに新しいデータを追加しました~」。ここでステップ全体が終了します。

では、このニュースを誰が監視するのか、監視した後はどうするのかということですが、私には関係ありますか?これで業務の結合が解除されるので、他のサービスが何秒かかるかは、私には関係ありません、データベースの書き込みとメッセージの送信が完了するので、パフォーマンスも向上します改善しました、さらに他のサービスがかかっても異常が発生しても私には関係ありません。

欠陥:

1. このように、mq メッセージの信頼性にさらに依存します。

2. 新しいミドルウェアが導入されると、実装の複雑さもある程度増加します。

ただし、これらの欠点は同期呼び出しに比べれば大したことはないため、これもより推奨される解決策です。

1.3.3. ビンログの監視

デフォルトでは、mysql の binlog はオフになっていますが、これをオンにすると、mysql が追加、削除、または変更を行うたびに、対応する操作が binlog に記録されます。

その後、canal などのミドルウェアを使用して binlog を監視し、変更が発見されると、対応するマイクロサービスにすぐに通知され、データが変更されたことがわかり、更新することができます。

アドバンテージ:

このソリューションでは、ミドルウェアにメッセージを送信したり、インターフェイスを呼び出したりしないため、結合度は最も低くなります。

短所:

1. binlog をオンにすると、mysql への負荷が増加します。

2.新しいミドルウェアを導入します。

1.3. RabbitMQ に基づくデータ同期

1.3.1. 需要

現在、「ホテル管理サービス」と「ホテル検索サービス」の 2 つのマイクロサービスがあります。

ユーザーは「ホテル管理サービス」を操作してデータの追加・削除・変更を行いますが、「ホテル検索サービス」のesデータについても同様のデータ変更操作が必要です。

ここでは、MQ の非同期メソッドを使用してデータ同期を実現します。

1.3.2. 「ホテル検索サービス」でexchange、queue、routingKeyを宣言し、同時に監視を有効にします。

「ホテル検索サービス」では、追加、削除、変更に関するメッセージを受信するエクスチェンジを宣言し、追加と変更用に 1 つのキューを含む 2 つのキューを宣言します (es では、追加と変更が可能であるため、これら 2 つに 1 つのキューが使用されます)。使用されるものは同じ DSL ステートメントが実装されています)、もう 1 つは削除に使用されます (ここでは Bean の形式でコンテナーに注入しました)。

public class MqConstants {

    //主题交换机
    public static final String EXCHANGE_TOPIC = "hotel.topic";
    //增加 or 修改酒店 队列
    public static final String INSERT_QUEUE = "hotel.insert.queue";
    //删除酒店 队列
    public static final String DELETE_QUEUE = "hotel.delete.queue";
    //增加 or 修改酒店 routingKey
    public static final String INSERT_KEY = "hotel.insert.key";
    //删除酒店 routingKey
    public static final String DELETE_KEY = "hotel.delete.key";

}
@Configuration
public class MqConfig {

    @Bean
    public TopicExchange hotelTopicExchange() {
        return new TopicExchange(MqConstants.EXCHANGE_TOPIC, true, false);
    }

    @Bean
    public Queue hotelInsertQueue() {
        return new Queue(MqConstants.INSERT_QUEUE, true);
    }

    @Bean
    public Queue hotelDeleteQueue() {
        return new Queue(MqConstants.DELETE_QUEUE, true);
    }

    @Bean
    public Binding hotelInsertBinding() {
        return BindingBuilder.bind(hotelInsertQueue()).to(hotelTopicExchange()).with(MqConstants.INSERT_KEY);
    }

    @Bean
    public Binding hotelDeleteBinding() {
        return BindingBuilder.bind(hotelDeleteQueue()).to(hotelTopicExchange()).with(MqConstants.DELETE_KEY);
    }

}

注: このクラス (MqConfig) のパッケージはスタートアップ クラスと同じレベルである必要があります。そうしないと、スイッチとキューが失敗したと宣言されます。

最後に、@RabbitListener を使用してキューをリッスンできます。

@Component
public class MqListener {

    @Autowired
    private IHotelService hotelService;

    @RabbitListener(queues = MqConstants.INSERT_QUEUE)
    public void HotelInsertOrUpdateListener(Long id) {
        hotelService.insertHotelById(id);
    }

    @RabbitListener(queues = MqConstants.DELETE_QUEUE)
    public void HotelDeleteListener(Long id) {
        hotelService.deleteHotelById(id);
    }

}

Ps: このクラス (MyListener、リスナー クラス) には @Component アノテーション (Spring によって管理) が必要です。そうでない場合、宣言されたスイッチとキューは無効になります。

1.3.3. 「ホテル管理サービス」にメッセージを投稿する

ホテル管理サービスでは、ユーザーがホテルを追加、削除、変更すると、データベースの情報が変更され、追加、削除、変更のメッセージが MQ に公開されます。

Ps: ホテル全体のデータをここに送信しないでください。大きすぎるとキューがいっぱいになる可能性があります (Mq はメモリストレージに基づいているため、キューの上限が設定されます)。ここでは ID だけを送信してください。 esからidを取得したら次に進み、追加・削除・修正に対応します。

    @PostMapping
    public void saveHotel(@RequestBody Hotel hotel){
        // 新增酒店
        hotelService.save(hotel);
        rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_TOPIC, MqConstants.INSERT_KEY, hotel.getId());
    }

    @PutMapping()
    public void updateById(@RequestBody Hotel hotel){
        //修改酒店信息
        if (hotel.getId() == null) {
            throw new InvalidParameterException("id不能为空");
        }
        hotelService.updateById(hotel);
        rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_TOPIC, MqConstants.INSERT_KEY, hotel.getId());
    }

    @DeleteMapping("/{id}")
    public void deleteById(@PathVariable("id") Long id) {
        //删除酒店信息
        hotelService.removeById(id);
        rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_TOPIC, MqConstants.DELETE_KEY, id);
    }

1.3.4. マイクロサービスの開始とテスト

a) ホテル管理ページで、次のように「7 Days Inn (Shanghai Xinzhuang Metro Station Branch)」を「7 Days Inn (工事中のため、慎重に進めてください)」に変更します。

b) ホテル検索ページで「建設」のキーワードを検索すると、「ホテル検索サービス」と同期した「ホテル管理サービス」の更新情報が表示されます。

おすすめ

転載: blog.csdn.net/CYK_byte/article/details/133383912