SpringCloud マイクロサービス テクノロジー スタック ダークホースのフォローアップ (インタビュー)

SpringCloud マイクロサービス テクノロジー スタック ダークホース フォローアップ インタビュー

今日の目標

ここに画像の説明を挿入

1. マイクロサービス

1.1. Spring Cloud の共通コンポーネントは何ですか?

問題の説明: このトピックでは主に、SpringCloud コンポーネントの基本的な理解について調べます。

難易度:やさしい

基準語句:

Spring Cloud には多くのコンポーネントが含まれており、多くの機能が繰り返されます。最も一般的に使用されるコンポーネントは次のとおりです。

• レジストリ コンポーネント: Eureka、Nacos など。

• 負荷分散コンポーネント: リボン

•リモート通話コンポーネント: OpenFeign

• ゲートウェイ コンポーネント: Zuul、ゲートウェイ

• サービス保護コンポーネント: Hystrix、Sentinel

• サービス構成管理コンポーネント: SpringCloudConfig、Nacos

1.2. Nacos のサービスレジストリ構造とは?

問題の説明: Nacos データの階層構造の理解と Nacos のソース コードの習熟を調査する

難易度:ノーマル

基準語句:

Nacos は階層型のデータ ストレージ モデルを採用しており、最外層は環境を分離するために使用される Namespace です。次に、サービスをグループ化するために使用されるグループがあります。次はサービス (サービス) です。サービスには複数のインスタンスが含まれますが、異なるコンピューター ルームにある場合があるため、サービスの下に複数のクラスター (クラスター) があり、クラスターの下に異なるインスタンス (インスタンス) があります。

Java コードに対応して、Nacos は多層 Map を使用して表現します。構造は Map<String, Map<String, Service>> で、最も外側の Map のキーは namespaceId で、値は Map です。内側の Map のキーは連結されたグループの serviceName であり、値は Service オブジェクトです。Service オブジェクトの内部には Map があり、キーはクラスター名、値は Cluster オブジェクトです。Cluster オブジェクトは、インスタンスのコレクションを内部的に保持します。

図に示すように:
ここに画像の説明を挿入
簡単な実装コード

package com.nacos;
import org.junit.Test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class NacosStructure {
    
    
    @Test
    public void testNacosStructure() {
    
    
        // 实例
        Instance personInfo = new Instance("personInfo");
        Instance finance = new Instance("finance");

        // 集群(一个地区的机房)
        Cluster SZ = new Cluster("SZ");
        SZ.getInstance(personInfo);
        SZ.getInstance(finance);

        // 其中服务组是环境隔离的
        NameSpace dev01 = new NameSpace("dev01");
        // 集群是部署在服务组中
        dev01.putService("01-personInfo", new Service("personInfo"));

        dev01.putNameSpace("dev01", dev01.getService("01-personInfo"));
        System.out.println(dev01);
    }

}

class NameSpace {
    
    
    private String nameSpaceId;
    private Map<String, Map<String, Service>> nameSpaceMap = new HashMap();
    private Map<String, Service> groupMap = new HashMap<>();

    public void putNameSpace(String nameSpaceId, Map<String, Service> serviceMap) {
    
    
        nameSpaceMap.put(nameSpaceId, serviceMap);
    }

    public void putService(String groupId, Service service) {
    
    
        this.groupMap.put(groupId, service);
    }

    public Map<String, Service> getService(String groupId) {
    
    
        return this.groupMap;
    }

    public NameSpace(String nameSpaceId) {
    
    
        this.nameSpaceId = nameSpaceId;
    }

    @Override
    public String toString() {
    
    
        return "NameSpace{" +
                "nameSpaceId='" + nameSpaceId + '\'' +
                ", nameSpaceMap=" + nameSpaceMap +
                ", groupMap=" + groupMap +
                '}';
    }
}

class Service {
    
    
    private String name;
    Map<String, Cluster> service = new HashMap();

    public Service(String name) {
    
    
        this.name = name;
    }

    /**
     * 往服务中添加集群
     *
     * @param c
     */
    public void putCluster(Cluster c) {
    
    
        this.service.put(c.getName(), c);
    }

    /**
     * 往服务中删除集群
     *
     * @param c
     */
    public void deleteCluster(Cluster c) {
    
    
        this.service.remove(c.getName());
    }

    @Override
    public String toString() {
    
    
        return "Service{" +
                "name='" + name + '\'' +
                ", service=" + service +
                '}';
    }
}

class Cluster {
    
    
    private String name;
    private Set<Instance> instance = new HashSet<>();

    public Cluster(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    /**
     * 往集群中添加实例
     *
     * @param in
     */
    public void getInstance(Instance in) {
    
    
        this.instance.add(in);
    }

    /**
     * 集群中删除实例
     *
     * @param in
     */
    public void removeInstance(Instance in) {
    
    
        this.instance.remove(in);
    }

    @Override
    public String toString() {
    
    
        return "Cluster{" +
                "name='" + name + '\'' +
                ", instance=" + instance +
                '}';
    }
}

class Instance {
    
    
    private String name;

    public Instance(String name) {
    
    
        this.name = name;
    }

    @Override
    public String toString() {
    
    
        return "Instance{" +
                "name='" + name + '\'' +
                '}';
    }
}

1. Nacos のソースコードをダウンロードして実行する

もちろん、Nacos のソース コードを調べるには、パッケージ化された Nacos サーバー jar パッケージを使用して実行することはできません。ソース コードをダウンロードして、自分でコンパイルする必要があります。

1.1. Nacos ソースコードのダウンロード

Nacos GitHub アドレス: https://github.com/alibaba/nacos

ダウンロードしたバージョン 1.4.2 の Nacos ソース コードは、授業前の資料で提供されています。
ここに画像の説明を挿入

他のバージョンの生徒を学習する必要がある場合は、自分でダウンロードすることもできます。

誰もがそのリリース ページを見つけます: https://github.com/alibaba/nacos/tags、1.4.2 バージョンを見つけます:
ここに画像の説明を挿入
クリックして入力し、ソース コード (zip) をダウンロードします。
ここに画像の説明を挿入

1.2. デモプロジェクトのインポート

クラス前の資料では、サービスの登録や検出などのサービスを含むマイクロサービスのデモを提供しています。
ここに画像の説明を挿入
プロジェクトをインポートした後、そのプロジェクト構造を表示します:
ここに画像の説明を挿入
構造の説明:

  • cloud-source-demo: プロジェクトの親ディレクトリ
    • cloud-demo: マイクロサービスの親プロジェクトであり、マイクロサービスの依存関係を管理します
      • order-service: ビジネスでユーザー サービスにアクセスする必要がある Order マイクロサービスは、サービス コンシューマです。
      • user-service: ユーザー マイクロサービス。id に基づいてユーザーにクエリを実行するインターフェイスを公開し、サービス プロバイダーです。

1.3. Nacos ソースコードのインポート

以前にダウンロードした Nacos ソース コードを cloud-source-demo プロジェクト ディレクトリに解凍します。
ここに画像の説明を挿入

次に、IDEA を使用してモジュールとしてインポートします。

1) プロジェクト構造オプションを選択します:
ここに画像の説明を挿入
次に、[モジュールのインポート] をクリックします:
ここに画像の説明を挿入
ポップアップ ウィンドウで、nacos ソース コード ディレクトリを選択します:
ここに画像の説明を挿入
次に、maven モジュールを選択して終了します:
ここに画像の説明を挿入

最後に、[OK] をクリックします。
ここに画像の説明を挿入
インポートされたプロジェクト構造:
ここに画像の説明を挿入

1.4.プロトコンパイル

Nacos の基礎となるデータ通信は、protobuf に基づいてデータをシリアライズおよびデシリアライズします。そして、一貫性サブモジュールで対応する proto ファイルを定義します。
ここに画像の説明を挿入

まず、proto ファイルを対応する Java コードにコンパイルする必要があります。

1.4.1. プロトブフとは

protobuf のフル ネームは、Google が提供するデータ シリアル化プロトコルである Protocol Buffer です。これは Google の公式定義です。

Protocol Buffers は軽量で効率的な構造化データ ストレージ フォーマットであり、構造化データのシリアル化に使用でき、データ ストレージまたは RPC データ交換フォーマットに非常に適しています。通信プロトコル、データ ストレージ、およびその他の分野で、言語に依存せず、プラットフォームに依存せず、拡張可能なシリアル化された構造化データ形式で使用できます。

これは、クロス言語、クロスプラットフォームのデータ伝送フォーマットとして簡単に理解できます。関数は json に似ていますが、パフォーマンスとデータ サイズの両方が json よりもはるかに優れています。

protobuf がクロス言語になる理由は、データ定義の形式が.proto形式であり、protoc に基づいて対応する言語にコンパイルする必要があるためです。

1.4.2. プロトコルのインストール

Protobuf の GitHub アドレス: https://github.com/protocolbuffers/protobuf/releases

使用する Windows バージョンをダウンロードできます。
ここに画像の説明を挿入
さらに、プレクラスの資料には、ダウンロードしたインストール パッケージも含まれています。
ここに画像の説明を挿入
中国語以外のディレクトリに解凍すると、bin ディレクトリの protoc.exe がコンパイルに役立ちます。
ここに画像の説明を挿入
次に、これを配置します。 bin ディレクトリ環境変数パスで構成します。JDK 構成方法を参照できます。
ここに画像の説明を挿入

1.4.3. proto のコンパイル

nacos-1.4.2 の整合性モジュールの下にある src/main ディレクトリに入ります。
ここに画像の説明を挿入

次に、コマンド ウィンドウを開き、次の 2 つのコマンドを実行します。

protoc --java_out=./java ./proto/consistency.proto
protoc --java_out=./java ./proto/Data.proto

図に示すように、
ここに画像の説明を挿入
これらの Java コードは nacos の整合性モジュールでコンパイルされます。
ここに画像の説明を挿入

1.5.実行

nacos サーバーのエントリは、コンソール モジュールの Nacos クラスです。
ここに画像の説明を挿入

スタンドアロンで開始する必要があります。
ここに画像の説明を挿入

次に、新しい SpringBootApplication を作成します。
ここに画像の説明を挿入

次に、アプリケーション情報を入力します。

メインクラス:com.alibaba.nacos.Nacos
VM オプション: -Dnacos.standalone=true

ここに画像の説明を挿入
次に、Nacos のメイン関数を実行します。
ここに画像の説明を挿入

order-service および user-service サービスを開始すると、nacos コンソールを表示できます。
ここに画像の説明を挿入

2. サービス登録

サービスが Nacos に登録されると、ローカル レジストリに保存されます。その構造は次のとおりです。
ここに画像の説明を挿入

まず、最外層は Map で、構造は次のとおりです: Map<String, Map<String, Service>>:

  • key: 環境分離の役割を果たすのは、namespace_id です。名前空間の下に複数のグループが存在する可能性があります
  • Map<String, Service>value:グループとグループ内のサービスを表す別の値。グループ内に複数のサービスを含めることができます
    • key: グループのグループ化を表しますが、キーとして使用する場合の形式は group_name:service_name です
    • 値: userservice、user service など、グループ内の特定のサービス。タイプは でService、内部にも 1 つ含まれておりMap<String,Cluster>、1 つのサービスの下に複数のクラスターが存在する可能性があります。
      • キー: クラスタ名
      • 値:Clusterクラスターの特定の情報を含むタイプ。クラスタには、複数のインスタンス、つまり特定のノード情報が含まれる場合があります。Set<Instance>これには、クラスタ下のインスタンスのコレクションである が 含まれます。
        • インスタンス: インスタンス IP、ポート、ヘルス ステータス、重量などを含むインスタンス情報。

各サービスが Nacos に登録されると、このマップに情報が整理されて保存されます。

2.1. サービス登録インターフェース

Nacosはサービス登録用のAPIインターフェースを提供しており、クライアントはこのインターフェースにリクエストを送信するだけでサービス登録を実現できます。

**インターフェースの説明:** Nacos サービスにインスタンスを登録します。

リクエストの種類:POST

リクエストパス:/nacos/v1/ns/instance

リクエストパラメータ:

名前 タイプ 必須ですか? 説明
IP はい サービス インスタンス IP
ポート 整数 はい サービス インスタンス ポート
名前空間 ID いいえ 名前空間 ID
重さ ダブル いいえ ウェイト
有効 ブール値 いいえ オンラインですか
健康 ブール値 いいえ 健康かどうか
metadata いいえ 拡張情報
クラスタ名 いいえ クラスター名
サービス名 はい サービス名
グループ名 いいえ グループ名
一時的な ブール値 いいえ 一時的なインスタンスですか

エラーコード:

エラーコード 説明 セマンティクス
400 要求の形式が正しくありません クライアント要求の構文エラー
403 禁断 アクセス拒否
404 見つかりません リソースが見つかりません
500 内部サーバーエラー 内部サーバーエラー
200 OK 普通

2.2. クライアント

まず、サービス登録のエントリを見つける必要があります。
Nacos がインスタンスを導入するためのパスは次のとおりです:/nacos/v1/ns/instance次に、同じパスを見つけて、src/main/java/com/alibaba/nacos/naming/controllers/InstanceController.javaディレクトリでそれ
ここに画像の説明を挿入
を見つける必要があります: リクエスト タイプが POST の場合、PostMapping を見つける必要があります。メソッド register は、登録センターのエントリです。
ここに画像の説明を挿入

1.3. Nacos は Ali 内の何十万ものサービス登録の圧力をどのようにサポートしていますか?

問題の説明: Nacos のソース コードの習熟度を調べる

難易度: 難しい

基準語句:

Nacos は内部で登録要求を受信すると、すぐにデータを書き込むのではなく、サービス登録タスクをブロッキング キューに入れ、すぐにクライアントに応答します。次に、スレッド プールを使用してブロッキング キュー内のタスクを読み取り、インスタンスの更新を非同期的に完了することで、同時書き込み機能を向上させます。
ここに一時的なインスタンスがあります

1.4. Nacos は読み取りと書き込みの同時競合をどのように回避しますか?

問題の説明: Nacos のソース コードの習熟度を調べる

難易度: 難しい

基準語句:

Nacos がインスタンス リストを更新するときは、 CopyOnWriteテクノロジを使用します. まず、古いインスタンス リストをコピーし、コピーしたインスタンス リストを更新してから、更新されたインスタンス リストで古いインスタンス リストを上書きします。

このように、更新プロセス中に、インスタンス リストの読み取り要求は影響を受けず、ダーティ リードの問題は発生しません。
コンカレント処理に相当し、変更するのは新インスタンスリスト(旧リストのコピー)、読み込むのは旧インスタンスリストです。この 2 つは互いに影響しません。

同じサービスの複数のインスタンス、シリアル実行に同期ロックを使用して、書き込みのセキュリティを確保します.
ここに画像の説明を挿入
インスタンスの登録は、書き込み
ここに画像の説明を挿入
のセキュリティを確保するために、シングル スレッドを非同期的に呼び出してシングル スレッドを使用します.
ここに画像の説明を挿入

1.5. Nacos と Eureka の違いは何ですか?

問題の説明: Nacos と Eureka の基礎となる実装の習熟度を調査する

難易度: 難しい

基準語句:

Nacos と Eureka との類似点と相違点は、次の点から説明できます。

  • インターフェース方法: Nacos と Eureka の両方が、サービスの登録や検出などの機能を実現するために使用される Rest スタイルの API インターフェースを外部に公開します。
  • インスタンス タイプ: Nacos インスタンスは永続インスタンスと一時インスタンスに分けられます。Eureka は一時インスタンスのみをサポートします。
  • ヘルス検出: Nacos は一時インスタンスにハートビート モード検出を使用し、永続インスタンスにはアクティブ リクエストを使用します。Eureka はハートビート モードのみをサポートします。
  • サービス検出: Nacos はタイミング プルとサブスクリプション プッシュの 2 つのモードをサポートします; Eureka はタイミング プル モードのみをサポートします

1.6. Sentinel の電流制限と Gateway の電流制限の違いは何ですか?

問題の説明: 電流制限アルゴリズムの習熟度を調べる

難易度: 難しい

参照語:
現在の制限: アプリケーション サーバーの要求を制限して、要求が多すぎることによるサーバーの過負荷やダウンタイムを回避します。

現在の制限アルゴリズムには、次の 3 つの一般的な実装があります。
1. スライディング時間ウィンドウ
2. トークン バケット アルゴリズム
3. リーキー バケット アルゴリズム
ゲートウェイは、Redis に基づくトークン バケット アルゴリズムを使用します。

ただし、Sentinel の内部はより複雑です。

  • デフォルトの電流制限モードは、スライディング タイム ウィンドウ アルゴリズムに基づいています。
  • キューイングの現在の制限モードは、リーキー バケット アルゴリズムに基づいています。
  • ホットスポット パラメータの現在の制限は、トークン バケット アルゴリズムに基づいています

固定ウィンドウ カウンター アルゴリズム
固定ウィンドウ カウンター アルゴリズムの概念は次のとおりです: ● 時間を複数のウィンドウに分割し
ます。● カウンタが現在の制限しきい値を超えると、しきい値を超えるすべての要求が破棄されます。


ここに画像の説明を挿入

スライディング ウィンドウ カウンター アルゴリズム
スライディング ウィンドウ カウンター アルゴリズムは、ウィンドウを
n 個の小さな間隔に分割します
。制限 しきい値は 3 のままです。時間枠 (1 秒) 内の要求がしきい値を超えると、超過した要求フロー制限
ウィンドウは、要求の現在の時刻 (currentTime) に従って移動し、ウィンドウ範囲は最初のタイム ゾーンです。 after (currentTime-Interval) currentTime があるタイムゾーンで開始および終了します。

ここに画像の説明を挿入
Token Bucket Algorithm Token
Bucket Algorithm 説明:
● トークンは固定レートで生成され、トークン バケットに格納されます. トークン バケットがいっぱいになると、余分なトークンは破棄されます. ● リクエストが入った後、最初にトークンを取得しようとする必要があります
.バケット トークン、トークンが取得された後にのみ処理できます
● トークン バケットにトークンがない場合、リクエストは待機または破棄されます
ここに画像の説明を挿入

Leaky Bucket Algorithm Leaky
Bucket Algorithm 説明: .
● 各リクエストをストレージ用のリーキー バケットへの「水滴」として扱います;
● 「リーキー バケット」は、「リーキー バケット」が空 満杯なら「水漏れ」は止まります
●「水漏れバケツ」が満杯の場合、余分な「水滴」はそのまま捨てられます。
リクエストがバケットに入っていることがわかります待って

ここに画像の説明を挿入

Sentinel がリーキー バケットを実装する場合、キュー待機モードを採用します。
すべてのリクエストをキューに入れ、しきい値で許可された時間間隔に従って順番に実行します。複数の同時リクエストが待機する必要があり、

予想待機時間 = 最新のリクエストの予想待機時間 + 許容間隔。

リクエストの予想待機時間が最大時間を超える場合、リクエストは拒否されます。
例: QPS=5 は、キュー内の要求が 200 ミリ秒ごとに処理されることを意味し、タイムアウト = 2000 は、2000 ミリ秒以上待機すると予想される要求が拒否され、例外がスローされることを意味します。
ここに画像の説明を挿入
電流制限アルゴリズムの比較

ここに画像の説明を挿入

1.7. Sentinel のスレッド分離と Hystix のスレッド分離の違いは何ですか?

問題の説明: スレッド分離スキームの習熟度を調査する

難易度:ノーマル

基準語句:

デフォルトでは、Hystix はスレッド プールに基づいてスレッド分離を実装します。分離された各ビジネスは、独立したスレッド プールを作成する必要があります。スレッドが多すぎると、追加の CPU オーバーヘッドが発生します。パフォーマンスは平均的ですが、分離はより強力です。

Sentinel はセマフォ (カウンター) に基づくスレッド分離です. スレッド プールを作成する必要はありません. パフォーマンスは優れていますが, 分離は平均的です.

2.MQ記事

2.1. 他の MQ ではなく、RabbitMQ を選択した理由は?

写真に示すように:
ここに画像の説明を挿入

言葉:

Kafka はスループットが高いことで有名ですが、データの安定性は平均的であり、メッセージの順序は保証されません。弊社のログ収集も利用しており、業務モジュールにはRabbitMQを利用しています。

Alibaba の RocketMQ は Kafka の原理に基づいており、Kafka の短所を補い、スループットが高いという利点を継承しており、現在、そのクライアントは主に Java です。しかし、アリババのオープンソース製品は安定性が心配で、使っていません。

RabbitMQ は並行処理言語 Erlang をベースに開発されており、スループットは Kafka ほどではありませんが、当社では十分です。さらに、メッセージの信頼性が高く、メッセージの遅延が非常に少なく、クラスター構築がより便利です。複数のプロトコルをサポートし、さまざまな言語のクライアントを備えているため、より柔軟です。Spring の RabbitMQ のサポートも比較的良好で、より使いやすく、当社のニーズに沿っています。

当社の並行性と安定性の要件を考慮して、RabbitMQ を選択しました。

2.2. RabbitMQ はメッセージが失われないことをどのように保証しますか?

言葉:

RabbitMQ は、メッセージ配信中に問題が発生する可能性があるさまざまな場所に的を絞ったソリューションを提供します。

  • プロデューサーがメッセージを送信すると、ネットワークの問題により、メッセージが交換に届かない場合があります。
    • RabbitMQ はパブリッシャー確認メカニズムを提供します
      • プロデューサーがメッセージを送信した後、ConfirmCallback 関数を記述できます。
      • メッセージがスイッチに正常に到達すると、RabbitMQ は ConfirmCallback を呼び出してメッセージの送信者に通知し、ACK を返します。
      • メッセージがスイッチに届かない場合、RabbitMQ は ConfirmCallback も呼び出してメッセージの送信者に通知し、NACK を返します。
      • タイムアウト後にメッセージが正常に送信されなかった場合にも、例外がスローされます。
  • メッセージが交換に到達した後、キューに到達できなかった場合、メッセージも失われます。
    • RabbitMQ はパブリッシャーの返品メカニズムを提供します
      • プロデューサーは ReturnCallback 関数を定義できます
      • メッセージがスイッチに到着したがキューに入っていない場合、RabbitMQ は ReturnCallback を呼び出して送信者に失敗の理由を通知します。
  • メッセージがキューに到着した後、MQ のダウンタイムによってメッセージが失われる場合もあります。
    • RabbitMQ は、永続化機能、クラスタ マスター スレーブ バックアップ機能を提供します。
      • メッセージの永続性、RabbitMQ はスイッチ、キュー、およびメッセージをディスクに永続化し、ダウンタイム後に再起動するとメッセージを復元できます
      • ミラー クラスタとアービトレーション キューの両方が、マスター/スレーブ バックアップ機能を提供できます. マスター ノードがダウンすると、スレーブ ノードは自動的にマスターに切り替わり、データはまだ
  • メッセージがコンシューマに配信された後、コンシューマが不適切に処理した場合、メッセージも失われる可能性があります
    • SpringAMQP は、RabbitMQ に基づいて、コンシューマー確認メカニズム、コンシューマー再試行メカニズム、およびコンシューマー障害処理戦略を提供します。
      • 消費者の確認メカニズム:
        • コンシューマはメッセージを正常に処理し、例外が発生しない場合、Spring は RabbitMQ に ACK を返し、メッセージは削除されます
        • コンシューマーがメッセージの処理に失敗し、例外をスローし、クラッシュし、Spring が NACK を返すか結果を返さず、メッセージが異常ではない
      • コンシューマー再試行メカニズム:
        • デフォルトでは、コンシューマーが処理に失敗すると、メッセージは再び MQ キューに返されてから、他のコンシューマーに配信されます。Spring が提供するコンシューマーの再試行メカニズムは、処理が失敗した後に NACK を返さず、コンシューマーでローカルに直接再試行します。複数回の再試行が失敗した後、メッセージはコンシューマーの失敗処理戦略に従って処理されます。キューに頻繁にメッセージが入ることによる余分なプレッシャーを回避できます。
      • 消費者の失敗戦略:
        • コンシューマーがローカル再試行に複数回失敗すると、メッセージはデフォルトで破棄されます。
        • Spring provides the Republish strategy. 複数回の再試行が失敗し、再試行回数が使い果たされた後、メッセージは指定された例外スイッチに再配信され、問題の特定に役立つ例外スタック情報が運ばれます。

2.3. RabbitMQ はメッセージの蓄積をどのように回避しますか?

言葉:

メッセージの蓄積の問題の原因は、多くの場合、メッセージの送信速度がコンシューマ メッセージの処理速度を上回っているためです。したがって、解決策は次の 3 点に他なりません。

  • コンシューマ処理速度の向上
  • コンシューマーを追加する
  • キュー メッセージ ストレージの上限を増やす

1) 消費者処理速度の向上

コンシューマーの処理速度はビジネス コードによって決まるため、次のようなことができます。

  • ビジネス コードを可能な限り最適化して、ビジネス パフォーマンスを向上させる
  • メッセージの受信後、スレッド プールを開き、複数のメッセージを同時に処理します。

利点: 低コスト、コードを変更するだけ

短所: スレッド プールを有効にすると、パフォーマンス オーバーヘッドが追加されます。これは、高頻度で低遅延のタスクには適していません。タスクの実行期間が長いサービスにおすすめです。

2) 消費者を追加する

キューは、複数のコンシューマをバインドしてタスクを一緒に競合させます。これにより、メッセージ処理の速度が自然に向上します。

長所:お金で解決できる問題は問題ではない。シンプルで無骨を実現

短所:問題はお金がないことです。コストが高すぎる

3) キュー メッセージ ストレージの上限を増やす

RabbitMQ のバージョン 1.8 以降、新しいキュー モードが追加されました: Lazy Queue

この種のキューは、メッセージをメモリに保存せず、メッセージを受信した後に直接ディスクに書き込みます。理論的にはストレージの制限はありません。メッセージの蓄積の問題を解決できます。

利点: より安全なディスク ストレージ、無制限のストレージ、メモリ ストレージに起因するページ アウトの問題の回避、およびより安定したパフォーマンス。

短所: ディスク ストレージは IO パフォーマンスによって制限され、メッセージの適時性はメモリ モードほど良くありませんが、影響は大きくありません。

2.4. RabbitMQ はメッセージの順序をどのように保証しますか?

言葉:

実際、RabbitMQ はキュー ストレージであり、当然、先入れ先出しの特性を備えており、メッセージの送信が規則的である限り、理論的には受信も規則的です。ただし、複数のコンシューマーがキューにバインドされている場合、メッセージがポーリングされてコンシューマーに配信される可能性があり、コンシューマーの処理順序は保証されません。

したがって、メッセージの順序を確保するには、次の点を実行する必要があります。

  • メッセージ送信の順序を保証する
  • 順序付けられた一連のメッセージが同じキューに送信されるようにする
  • キューに含まれるコンシューマーが 1 つだけであることを確認する

2.5. MQ メッセージの繰り返し消費を防ぐ方法は?

言葉:

メッセージが繰り返し消費される理由はさまざまであり、避けられません。したがって、コンシューマ側から始めるしかありません.メッセージ処理のべき等性が保証されている限り、メッセージは繰り返し消費されません.

冪等性を保証する多くのソリューションがあります。

  • 各メッセージに一意の ID を追加し、メッセージ テーブルとメッセージ ステータスをローカルに記録し、メッセージを処理するときにデータベース テーブルの一意の ID に基づいて判断します。
  • 同様に、メッセージテーブルを記録し、メッセージステータスフィールドを使用して、楽観ロックに基づく判断を実現し、冪等性を保証します
  • ビジネス自体の冪等性に基づいています。たとえば、id の削除によると、クエリ ビジネスは本質的に冪等です; 追加と変更のビジネスは、データベース ID の一意性に基づいて、または冪等性を確保するための楽観的ロック メカニズムに基づいて考えることができます。本質は、メッセージ テーブル スキームに似ています。

2.6. RabbitMQ の高可用性を確保するには?

言葉:

RabbitMQ の高可用性を実現するには、次の 2 つのポイントに他なりません。

  • スイッチ、キュー、およびメッセージの永続化を適切に行う
  • RabbitMQ のミラー クラスタを構築し、マスター スレーブ バックアップを適切に実行します。もちろん、ミラー化されたクラスターの代わりにクォーラム キューを使用することもできます。

2.7. MQ を使用すると、どのような問題を解決できますか?

言葉:

RabbitMQ は、次のような多くの問題を解決できます。

  • 分離: MQ ベースの非同期通知に対するいくつかのビジネス関連のマイクロサービス呼び出しを変更すると、マイクロサービス間のビジネス結合を分離できます。また、ビジネスパフォーマンスも向上します。
  • トラフィック ピーク クリッピング: 突然のビジネス リクエストをバッファとして MQ に入れます。バックエンド業務は、自身の処理能力に応じて MQ からメッセージを取得し、タスクを 1 つずつ処理します。フローカーブがより滑らかになります
  • 遅延キュー: RabbitMQ のデッド レター キューまたは DelayExchange プラグインに基づいて、メッセージの送信後にメッセージの受信を遅らせる効果を実現できます。

3.Redisの記事

3.1. Redis と Memcache の違いは何ですか?

  • redis支持更丰富的数据类型(より複雑なアプリケーション シナリオをサポート): Redis は単純な k/v タイプのデータをサポートするだけでなく、リスト、セット、zset、ハッシュなどのデータ構造のストレージも提供します。memcache は単純なデータ型 String をサポートしています。
  • Redis支持数据的持久化、メモリ内のデータはディスクに保持でき、再起動時にロードして再度使用できますが、Memecacheはすべてのデータをメモリに保存します。
  • 集群模式: memcached にはネイティブ クラスター モードがなく、クラスターにデータを書き込むためにクライアントに依存する必要がありますが、redis は現在、クラスター モードをネイティブにサポートしています。
  • Redis使用单线程: Memcached は、マルチスレッドのノンブロッキング IO 多重化ネットワーク モデルです。Redis は、シングルスレッドの多重化 IO 多重化モデルを使用します。
    ここに画像の説明を挿入

3.2. Redis シングルスレッドの問題

インタビュアー: Redis はシングル スレッドを使用していますが、高い同時実行性を確保するにはどうすればよいですか?

インタビュースピーチ:

Redis が高速である主な理由は次のとおりです。

  1. 完全にメモリベース
  2. データ構造がシンプルで、データ操作もシンプル
  3. 複数の I/O 多重化モデルを使用して、CPU リソースを最大限に活用する

インタビュアー: これを行う利点は何ですか?

インタビュースピーチ:

シングル スレッドの利点は次のとおりです。

  • コードがより明確になり、処理ロジックがよりシンプルになります
  • さまざまなロックの問題を考慮する必要がなく、ロック解除操作がなく、ロックによるパフォーマンスの消費がありません
  • マルチプロセスやマルチスレッドによるCPU切り替えがなく、CPUリソースをフル活用

3.2. Redis の永続化スキームとは?

関連情報:

1) RDB の永続性

RDB の永続化には、save または bgsave を使用できます. メイン プロセスのビジネスをブロックしないために、bgsave が一般的に使用されます. プロセス:

  • Redis プロセスは子プロセスを fork します (親プロセスのメモリ データと一致します)。
  • 親プロセスはクライアント要求コマンドを処理し続けます
  • 子プロセスによって、メモリ内のすべてのデータを一時 RDB ファイルに書き込みます。
  • 書き込み操作が完了すると、古い RDB ファイルは新しい RDB ファイルに置き換えられます。

以下は、RDB の永続性に関連する構成の一部です。

  • save 60 10000: 60 秒以内に 10,000 個のキーが変更された場合、RDB 永続化を実行します。
  • stop-writes-on-bgsave-error yes: Redis が RDB 永続化の実行に失敗した場合 (通常はオペレーティング システムのメモリ不足が原因)、Redis はクライアントからのデータ書き込み要求を受け入れなくなります。
  • rdbcompression yes: RDB ファイルを生成するときに、それらを同時に圧縮します。
  • dbfilename dump.rdb: RDB ファイルに dump.rdb という名前を付けます。
  • dir /var/lib/redis: RDB ファイルを/var/lib/redisディレクトリに保存します。

もちろん、実際には、通常はstop-writes-on-bgsave-error設定を に設定しfalse、同時に、Redis が RDB 永続化の実行に失敗したときに監視システムがアラームを送信するようにします。これにより、クライアントの書き込み要求を無礼に拒否する代わりに手動介入を解決できます。

RDB 永続性の利点:

  • RDB の永続ファイルは小さく、Redis のデータ リカバリは高速です
  • 子プロセスは親プロセスに影響を与えず、親プロセスは引き続きクライアント コマンドを処理できます。
  • 子プロセスをフォークする場合はコピーオンライト方式を採用しており、多くの場合、メモリ消費量が少なく、比較的効率が良い。

RDB 永続性の欠点:

  • 子プロセスが fork されると、コピー オン ライト方式が採用されますが、このときに Redis がさらに書き込みを行うと、メモリの使用量が増加したり、メモリ オーバーフローが発生したりする可能性があります。
  • RDB ファイルの圧縮によりファイル サイズは縮小されますが、通過時に追加の CPU が消費されます。
  • ビジネス シナリオでデータの耐久性 (耐久性) が重視される場合は、RDB の永続性を使用しないでください。たとえば、Redis が RDB 永続化を 5 分ごとに実行する場合、Redis が予期せずクラッシュすると、最大 5 分間のデータが失われます。

2) AOF 永続性

appendonly yes構成アイテムを使用して、AOF 永続性を有効にすることができます。Redis が AOF 永続化を実行すると、受信した書き込みコマンドが AOF ファイルの末尾に追加されるため、Redis は AOF ファイル内のコマンドを再生する限り、データベースを元の状態に復元できます。
  RDB 永続性と比較して、AOF 永続性の明らかな利点は、データの耐久性を向上できることです。write()AOF モードでは、Redis がクライアントから書き込みコマンドを受け取るたびに、コマンドをAOF ファイルの最後に書き込むためです。
  ただし、Linux では、write()データがファイルに転送された後、データはすぐにディスクにフラッシュされず、OS のファイル システム バッファーに一時的に格納されます。適切なタイミングで、OS はバッファ内のデータをディスクにフラッシュします (ファイルの内容をディスクにフラッシュする必要がある場合は、 または を呼び出すことができますfsync()) fdatasync()
  設定項目を通じてappendfsync、Redis がコマンドをディスクに同期する頻度を制御できます。

  • always: Redis がコマンドを AOF ファイルに書き込むたびに、コマンドをディスクにフラッシュするためにwrite()呼び出されます。fsync()これにより、最高のデータ耐久性が保証されますが、システムに大きなオーバーヘッドがかかる可能性があります。
  • no: Redis はコマンドをwrite()AOF ファイルにのみ送信します。これにより、OS はいつコマンドをディスクにフラッシュするかを決定できます。
  • everysec: AOF ファイルにコマンドを書き込むだけでなくwrite()、Redis はそれを毎秒実行しますfsync()実際には、Redis のパフォーマンスを大幅に低下させることなく、データの永続性をある程度保証できるこの設定を使用することをお勧めします。

ただし、AOF の永続性には欠点がないわけではありません。Redis は受信した書き込みコマンドを AOF ファイルに追加し続けるため、AOF ファイルがどんどん大きくなります。大きな AOF ファイルはディスク容量を消費し、Redis の再起動が遅くなります。この問題を解決するために、適切な状況下で、Redis は AOF ファイルを書き換えて、ファイル内の冗長なコマンドを削除し、AOF ファイルのサイズを縮小します。AOF ファイルの書き換え中に、Redis はサブプロセスを開始し、サブプロセスは AOF ファイルの書き換えを担当します。
  次の 2 つの構成項目を使用して、Redis が AOF ファイルを書き換える頻度を制御できます。

  • auto-aof-rewrite-min-size 64mb
  • auto-aof-rewrite-percentage 100

上記の 2 つの構成の影響: AOF ファイルのサイズが 64MB を超え、AOF ファイルのサイズが最後の書き換え後のサイズの少なくとも 2 倍である場合、Redis は AOF の書き換えを実行します。

アドバンテージ:

  • 持続頻度が高く、データの信頼性が高い
  • 追加のメモリや CPU の消費なし

欠点:

  • ファイルサイズが大きい
  • ファイルが大きいと、サービス データ リカバリの効率が低下します

インタビューのスピーチ:

Redis は 2 つのデータ永続化方法を提供します。1 つは RDB で、もう 1 つは AOF です。デフォルトでは、Redis は RDB 永続性を使用します。

RDB 永続ファイルはサイズが小さいですが、一般的にデータを保存する頻度が低く、信頼性が低く、データが失われやすいです。さらに、RDB は Fork 関数を使用して、データの書き込み時にメイン プロセスをコピーします。これにより、追加のメモリ消費が発生する可能性があり、ファイルの圧縮にも追加の CPU 消費が発生します。

ROF の永続性は、高い信頼性で 1 秒に 1 回永続化できます。ただし、永続ファイルはサイズが大きいため、データ リカバリ中にファイルを読み取るのに時間がかかり、効率がやや低くなります。

3.3. Redis のクラスタリング方法は何ですか?

インタビューのスピーチ:

Redis クラスターは、マスター/スレーブ クラスターとフラグメント化されたクラスター分けることができます

マスター/スレーブ クラスタは、通常、1 つのマスターと複数のスレーブで構成され、マスター ライブラリはデータの書き込みに使用され、スレーブ ライブラリはデータの読み取りに使用されます。Sentry と組み合わせると、メイン データベースがダウンしたときにマスターを再選択できます。その目的は、Redis の高可用性を確保することです

シャード クラスターはデータ シャードです. 複数の Redis ノードでクラスターを形成し、16383 スロットを異なるノードに割り当てます。データを格納するときは、キーのハッシュ操作を使用してスロット値を取得し、対応するノードに格納します。ストレージ データはノード自体ではなくスロットに向けられているため、クラスターを動的にスケーリングできます。目的は、Redis がより多くのデータを保存できるようにすることです。

1) マスタスレーブクラスタ

マスター/スレーブ クラスターは、読み書き分離クラスターでもあります。通常、1 つのマスターと複数のスレーブです。

Redis のレプリケーション (レプリケーション) 機能により、ユーザーは Redis サーバーに基づいてサーバーのレプリカをいくつでも作成できます。ここで、レプリケートされたサーバーはマスター サーバー (マスター) であり、レプリケーションによって作成されたサーバー レプリカはスレーブ サーバー (スレーブ) です。

マスター サーバーとスレーブ サーバー間のネットワーク接続が正常である限り、マスター サーバーとスレーブ サーバーは同じデータを保持し、マスター サーバーは自身に発生するデータ更新を常にスレーブ サーバーに同期するため、データが確実に更新されます。マスター サーバーとスレーブ サーバーの数は同じです。

  • データの書き込みは、マスター ノードを介してのみ実行できます
  • どのノードからでもデータの読み込みが可能
  • 構成されている場合哨兵节点、マスターがダウンすると、センチネルはスレーブ ノードから新しいマスターを選択します。

マスター/スレーブ クラスタには、次の 2 つのタイプがあります。
ここに画像の説明を挿入

センチネルを含むクラスター:
ここに画像の説明を挿入

2) 断片化クラスター

マスタースレーブ クラスタでは、各ノードがすべての情報を保存する必要があるため、バレル効果が形成されやすくなります。また、データ量が多い場合、1 台のマシンでは需要を満たすことができません。この時点で、シャード クラスターを使用します。
ここに画像の説明を挿入

クラスターの特徴:

  • 各ノードは異なるデータを保持します

  • すべての redis ノードは互いに相互接続され (PING-PONG メカニズム)、内部的にバイナリ プロトコルを使用して伝送速度と帯域幅を最適化します。

  • ノードの障害は、クラスタ内の半分以上のノードが障害を検出した場合にのみ有効になります。

  • クライアントは redis ノードに直接接続され、データにアクセスするためにクラスター内の使用可能なノードに接続するために中間プロキシ層は必要ありません。

  • redis-cluster は、すべての物理ノードを [0-16383] スロット (スロット) にマップして、動的スケーリングを実現します

Redis の各ノードの高可用性を確保するために、図に示すように、ノードごとにレプリケーション (スレーブ ノード) を作成することもできます。
ここに画像の説明を挿入

障害が発生すると、マスターとスレーブは時間内に切り替えることができます。
ここに画像の説明を挿入

3.4. Redis の一般的なデータ型は何ですか?

複数のタイプのデータ構造をサポートします。主な違いは、値ストレージのデータ形式が異なることです。

  • string: 最も基本的なデータ型で、512M までの安全なバイナリ文字列です。

  • list: 追加された順序で順序を維持する文字列のリスト。

  • set: 要素が重複していない順序付けされていない文字列のコレクション。

  • ソート済みセット: 文字列のソート済みコレクション。

  • ハッシュ: キーと値のペアの形式

3.5. Redis トランザクションメカニズムについて話す

関連情報:

参考:http://redisdoc.com/topic/transaction.html

Redis のトランザクション機能は、MULTI、EXEC、DISCARD、WATCH の 4 つのプリミティブによって実現されます。Redis はトランザクション内のすべてのコマンドをシリアル化し、それらを順番に実行します。ただし、Redis トランザクションはロールバック操作をサポートしていません. コマンドが正しく実行されなかった後、正しいコマンドが実行され続けます.

  • MULTI: トランザクションを開始するために使用され、常に OK を返します。MULTI の実行後、クライアントは任意の数のコマンドをサーバーに送信し続けることができます. これらのコマンドはすぐには実行されませんが、コマンド キューに入れられて実行されます.
  • EXEC: コマンド キュー内のすべてのコマンドを順番に実行します。すべてのコマンドの戻り値を返します。トランザクションの実行中、Redis は他のトランザクションのコマンドを実行しません。
  • DISCARD: コマンド キューをクリアし、トランザクションの実行を放棄します。クライアントはトランザクション状態から抜け出します。
  • WATCH: Redis のオプティミスティック ロック メカニズムは、比較と設定 (CAS) の原則を使用して、1 つまたは複数のキーを監視できます。キーの 1 つが変更されると、後続のトランザクションは実行されません。

トランザクションを使用すると、次の 2 種類のエラーが発生する場合があります。

  • エンキューされたコマンドは、EXEC が実行される前に破損する可能性があります。たとえば、コマンドは構文エラー (引数の数が間違っている、引数名が間違っているなど) や、メモリ不足 (サーバーが最大メモリ制限maxmemoryset
    • Redis 2.6.5 から、サーバーはコマンドのエンキューに失敗したことを記録し、クライアントが EXEC コマンドを呼び出すと、実行を拒否し、トランザクションを自動的に放棄します。
  • コマンドは、EXEC 呼び出し後に失敗する場合があります。たとえば、トランザクション内のコマンドは、文字列キーに対して list コマンドを使用するなど、間違ったタイプのキーを処理する可能性があります。
    • トランザクション内のいくつかのコマンドが実行中にエラーを生成した場合でも、トランザクション内の他のコマンドは引き続き実行され、ロールバックされません。

Redis がロールバック (ロールバック) をサポートしていないのはなぜですか?

このアプローチの利点は次のとおりです。

  • Redis コマンドが失敗する可能性があるのは、構文が正しくない (これらの問題はエンキュー時に検出できない) か、コマンドが間違ったタイプのキーで使用されているためです: つまり、実用的な観点から、失敗するコマンドが原因です。プログラミング エラーによるものであり、これらのエラーは開発プロセスで発見される必要があり、本番環境では表示されません。
  • ロールバックをサポートする必要がないため、Redis の内部をシンプルかつ高速に保つことができます。

プログラマー自身が原因で発生したエラーを回避するメカニズムはなく、そのようなエラーは通常、本番環境では発生しないため、Redis はロールバックなしでトランザクションを処理するためのよりシンプルで高速な方法を選択します。

インタビューのスピーチ:

Redis トランザクションは、実際には一連の Redis コマンドをキューに入れ、実行中に他のトランザクションによって中断されることなく、それらをバッチで実行します。ただし、リレーショナル データベース トランザクションとは異なり、Redis トランザクションはロールバック操作をサポートしていません. トランザクションでコマンドの実行に失敗した場合でも、他のコマンドは実行されます.

ロールバックできない問題を補うために、Redis はトランザクションがエンキューされたときにコマンドをチェックし、コマンドが異常な場合はトランザクション全体を破棄します。

したがって、プログラマーのプログラミングが正しい限り、理論上、Redis はロールバックすることなくすべてのトランザクションを正しく実行します。

インタビュアー: トランザクションの実行中に Redis がクラッシュした場合はどうなりますか?

Redis には永続化メカニズムがありますが、信頼性の問題から、通常は AOF 永続化を使用します。トランザクションのすべてのコマンドも AOF ファイルに書き込まれますが、EXEC コマンドが実行される前に Redis がダウンした場合、AOF ファイル内のトランザクションは不完全になります。redis-check-aofプログラムを使用して、AOF ファイル内の不完全なトランザクション情報を削除し、サーバーがスムーズに起動できるようにします。

おすすめ

転載: blog.csdn.net/sinat_38316216/article/details/129883049