SpringBoot --- 実用的な記事

1. ホットデプロイメント

1.1. コンセプト

ホットデプロイメントとは何ですか? 簡単に言えば、プログラムが変更され、サーバーを再起動する必要があるのですが、面倒ですか? 再起動せずに、サーバーは更新されたプログラムを自動的に再ロードします。これがホット デプロイメントです。

ホットデプロイメントの機能はどのように実現されているのでしょうか?これは 2 つの状況に分けられ、非 Springboot プロジェクトと SpringBoot プロジェクトではホットデプロイメントの実装方法が全く異なります。まず、元の非スプリングブート プロジェクトがホット デプロイメントを実装する方法について説明します。

非スプリングブート プロジェクトのホット デプロイメントの実装原則

SpringBoot 以外のプロジェクトを開発する場合、Web プロジェクトを作成し、Tomcat を通じて開始する必要があります。通常、最初に Tomcat サーバーをディスクにインストールし、開発したプログラム構成をインストールされた Tomcat サーバーに公開する必要があります。ホット デプロイメントの効果を実現したい場合、実際には 2 つの方法があります。1 つは、Tomcat サーバーの構成ファイルで構成する方法です。この方法は、使用する IDE ツールとは関係がありません。日食またはアイデア。もう 1 つの方法は、アイデア ツールでの設定など、IDE ツールを使用して設定する方法です。この形式は IDE ツールに依存する必要があります。各 IDE ツールは異なり、対応する設定も異なります。ただし、中心的な考え方は同じで、サーバーを使用してサーバーにロードされているアプリケーションを監視し、変更が見つかった場合は一度リロードするというものです。

上記の非 SpringBoot プロジェクトのホット デプロイメントは非常に単純なプロセスであるようで、ほぼすべての小規模パートナーが自分で作成できます。書けない場合は、最も単純なアイデアを示しますが、実際のデザインはこれより複雑です。たとえば、スケジュールされたタスクを開始し、タスク開始時の各ファイルのサイズを記録し、5 秒ごとに比較して各ファイルのサイズが変化したかどうか、または新しいファイルがあるかどうかを確認します。変化がない場合はリリースし、変化がある場合は現在記録されているファイル情報を更新してからサーバを再起動することでホットデプロイメントを実現できます。もちろん、この処理はこのようにしてはいけません。例えば、出力された文字列「abc」を「cba」に変更すると、比較サイズは変わりませんが、内容は変わりませんので、絶対に機能しません。たとえて言えば、サーバーの再起動はホット デプロイメントではなくコールド スタートであり、その精神を理解しましょう。

このプロセスはそれほど複雑ではないようですが、springboot プロジェクトには他にも紆余曲折はありますか? 本当にあるんです。

springbootプロジェクトのホットデプロイメントの実装原理

実は springboot をベースに開発された Web プロジェクトには、Tomcat サーバーが組み込まれているという注目すべき特徴があります。サーバーは Spring コンテナー内でオブジェクトとして実行されます。当初、Tomcat サーバーがプログラムをロードした後、Tomcat サーバーはプログラムを見つめることを期待していました。変更後、再起動してリロードしますが、現在、Tomcat とプログラムは同じレベルにあり、それらはすべて、 Spring Container. トラブル、直接の管理権限がない、どうすればいいですか? シンプルに、Spring コンテナーで元に開発したプログラム A を見つめるために別のプログラム X を作成するだけで十分ではないでしょうか。実際、プログラム A を見つめるプログラム X を作成するだけです。自分で開発したプログラム A が変更された場合、プログラム X は Tomcat コンテナにプログラム A をリロードするように命令します。これで問題ありません。これには利点があり、Spring コンテナ内のすべてのものをリロードする必要はなく、開発したプログラムの一部をリロードするだけで済みます。これにより効率が向上し、非常に優れています。

このようなプログラム X を思いつく方法について話しましょう。プログラム X は自分で手書きしてはいけません。スプリングブートはすでに完了しており、それに座標をインポートするだけです。

1.2. ホットデプロイメントを手動で開始する

ステップ①:開発者ツールに対応した座標をインポートする

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

ステップ② : プロジェクトをビルドします。ショートカット キーを使用してこの機能を有効にできます。

ここに画像の説明を挿入

ホットデプロイメントをアクティブにする: Ctrl + F9

上記のプロセスは springboot プロジェクトのホットデプロイメントを実現しますが、非常に簡単ですか? しかし、ここでは基礎となる作業工学を普及させる必要があります。

再起動してリロードする

Springboot プロジェクトは実際には実行時に 2 つのプロセスに分割され、ロードされるものの違いに応じて、ベース クラス ローダーとリスタート クラス ローダーに分かれます。

  • 基本クラスローダー:jarパッケージ内のクラスをロードするために使用されます.jarパッケージ内のクラスと設定ファイルは変更されないため、何度ロードしてもロードされた内容は変わりません
  • クラスローダーの再起動: 開発者自身が開発したクラス、設定ファイル、ページ、その他の情報をロードするために使用されます。このタイプのファイルは開発者の影響を受けます。

Springboot プロジェクトが開始されると、基本クラス ローダーが実行され、jar パッケージ内の情報がロードされた後、再起動クラス ローダーが実行されて、開発者が作成したコンテンツがロードされます。ビルドプロジェクトの実行後、jar内の情報は変わらないので基本クラスローダーを再度実行する必要はなく、再起動クラスローダーを実行する、つまり開発者が作成した内容をリロードするだけで済みます。完了 ホット デプロイメント プロセスが実際に再起動クラス ローダー内の情報を再ロードしているとも言えます。

要約する

  1. 開発者ツールを使用して、現在のプロジェクトのホット デプロイメントを有効にします
  2. プロジェクトのビルド操作を使用してプロジェクトをホット デプロイします (Ctrl+F9)
  3. ホット デプロイメントでは、現在の開発者がカスタム開発したリソースのみがロードされ、jar リソースはロードされません。

1.3. ホットデプロイメントを自動的に開始する

自動ホット デプロイメントは、実際にはスイッチを設計することであり、スイッチをオンにすると、IDE ツールが自動的にホット デプロイメントされます。したがって、この操作は IDE ツールに関連しています。以下では、アイデアでホット デプロイメントを設定する例としてアイデアを取り上げます。

ステップ①:自動ビルドプロジェクトを設定する

[ファイル]を開き、[設定...]を選択し、パネルの左側のメニューで[コンパイル]オプションを見つけて、[プロジェクトを自動的にビルドする]にチェックを入れます。これは、プロジェクトを自動的にビルドすることを意味します。

ここに画像の説明を挿入

ステップ②:プログラム実行中の自動構築を許可する

ショートカット キー [Ctrl] + [Alt] + [Shit] + [/] を使用してメンテナンス パネルを開き、項目 1 [レジストリ…] を選択します。

ここに画像の説明を挿入

オプションで「comple」を検索し、該当する項目にチェックを入れます

ここに画像の説明を挿入

これにより、実行中にプログラムが自動的に構築され、ホットデプロイメントの効果が実現されます。

: 文字を入力するたびにサーバーを再構築する場合、これは頻度が高すぎるため、アイデア ツールが 5 秒間フォーカスを失ったときにホット デプロイメントを実行するように設定されています。実はホットデプロイメントは、アイデアツールから他のツールに切り替える際に行われますが、例えばプログラムを変更した後、ブラウザでデバッグする必要がありますが、このときアイデアは自動的にホットデプロイメントを実行します。

要約する

  1. 自動ホットデプロイメントでは自動構築プロジェクトを開始する必要があります
  2. プログラムの実行中にプロジェクトを自動的にビルドするには、自動ホット デプロイメントをオンにする必要があります。

1.4. ホットデプロイ範囲の構成

プロジェクト内のファイルを変更すると、すべてのファイル変更がホット デプロイメントをアクティブ化するわけではないことがわかります。その理由は、開発者ツールに一連の構成があるためです。構成内の条件が満たされると、ホット デプロイメントが開始されます。 . 構成 デフォルトでホットデプロイメントに参加しないディレクトリ情報は以下のとおりです。

  • /META-INF/maven
  • /META-INF/リソース
  • /資力
  • /静的
  • /公共
  • /テンプレート

上記のディレクトリ内のファイルが変更されると、それらはホット デプロイメントに参加しなくなります。構成を変更する場合は、application.yml ファイルを使用して、ホット デプロイメント操作に参加しないファイルを設定できます。

spring:
  devtools:
    restart:
      # 设置不参与热部署的文件或文件夹
      exclude: static/**,public/**,config/application.yml

要約する

  1. ホットデプロイメントに参加しないファイルまたはディレクトリは、構成を通じて変更できます。

1.5. ホットデプロイメントをオフにする

オンライン環境ではホットデプロイメント機能を使用することができないため、強制的に無効化する必要がありますが、設定により無効化することが可能です。

spring:
  devtools:
    restart:
      enabled: false

構成ファイルの層が多すぎると一貫したカバレッジが得られ、最終的に構成エラーが発生することに注意する場合は、構成レベルを上げて、より高いレベルでホット デプロイメントを構成できます。たとえば、コンテナを起動する前に、システムプロパティの設定でホットデプロイメント機能をオフにします。

@SpringBootApplication
public class SSMPApplication {
    
    
    public static void main(String[] args) {
    
    
        System.setProperty("spring.devtools.restart.enabled","false");
        SpringApplication.run(SSMPApplication.class);
    }
}

実際には、オンライン環境のメンテナンス中にコードを変更することは不可能であるため、上記の懸念は少し冗長です。変更してもリソースの消費が削減されるだけです。結局のところ、目を閉じればよいだけです。プロジェクトが変更されたかどうかを確認するため、ホット デプロイメント機能はなく、このスイッチの機能は対応する機能を無効にすることです。

要約する

  1. ホット デプロイメント機能は構成によってオフにすることができ、オンライン プログラムのリソース消費を削減できます。

2、詳細設定

2.1、@ConfigurationProperties

基本の章では、Bean のプロパティをバインドするために使用される @ConfigurationProperties アノテーションについて学びました。開発者は、yml 構成ファイルのオブジェクト形式でいくつかのプロパティを追加できます。

servers:
  ip-address: 192.168.0.1 
  port: 2345
  timeout: -1

次に、データをカプセル化するためのエンティティ クラスを開発し、属性に対応するセッター メソッドを提供することに注意してください。

@Component
@Data
public class ServerConfig {
    
    
    private String ipAddress;
    private int port;
    private long timeout;
}

@ConfigurationProperties アノテーションを使用して、構成内のプロパティ値を開発されたモデル クラスに関連付けます

@Component
@Data
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
    
    
    private String ipAddress;
    private int port;
    private long timeout;
}

このようにして、対応する Bean がロードされるときに、構成プロパティ値を直接ロードできます。しかし、これまでに学んだことは、このフォームを使用してカスタム Bean の属性値をロードするということです。それがサードパーティ Bean の場合はどうなるでしょうか? このフォームを使用して属性値をロードできますか? なぜこの質問が提起されるのでしょうか? その理由は、現在の @ConfigurationProperties アノテーションはクラス定義の上に記述されており、サードパーティが開発した Bean のソースコードは自分で書いたものではなく、ソースコードに @ConfigurationProperties アノテーションを追加することは不可能であるためです。 . この問題を解決するにはどうすればよいですか? この問題については以下で説明しましょう。

@ConfigurationProperties アノテーションを使用すると、特別な形式でサードパーティ Bean のプロパティを実際にロードできます。

ステップ① : @Bean アノテーションを使用してサードパーティ Bean を定義する

@Bean
public DruidDataSource datasource(){
    
    
    DruidDataSource ds = new DruidDataSource();
    return ds;
}

ステップ② : ymlでバインドするプロパティを定義します。この時点ではデータソースはすべて小文字であることに注意してください。

datasource:
  driverClassName: com.mysql.jdbc.Driver

ステップ③ : @ConfigurationProperties アノテーションを使用してプロパティをサードパーティ Bean にバインドします。プレフィックスはすべて小文字のデータソースであることに注意してください

@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource datasource(){
    
    
    DruidDataSource ds = new DruidDataSource();
    return ds;
}

操作方法は全く同じですが、 @ConfigurationProperties アノテーションはクラスだけでなくメソッドにも追加できる点が異なります クラスへの追加は、Springコンテナが管理する現在のクラスのオブジェクトバインディングプロパティに対するものであり、メソッドへの追加は、Spring コンテナーに対するものであり、現在のメソッド オブジェクトの戻り値を管理するプロパティをバインディングします。実際、これらのプロパティは本質的に同じです。

実際、これを行うと、新しい問題が発生しました。現在、クラス アノテーションまたは @Bean 定義を通じて Bean を定義します。@ConfigurationProperties アノテーションを使用すると、Bean のプロパティをバインドできます。ビジネス システムでは、どの Bean がアノテーションを通じて定義されるか@ConfigurationProperties でプロパティをバインドしますか? このアノテーションはクラスだけでなくメソッドにも記述できるため、探すのがさらに面倒になります。この問題を解決するために、Spring は @ConfigurationProperties アノテーションを使用してどの Bean がプロパティにバインドされているかを具体的にマークする新しいアノテーションを提供します。このアノテーションは @EnableConfigurationProperties と呼ばれます。それの使い方?

ステップ① : 構成クラスの @EnableConfigurationProperties アノテーションを開き、@ConfigurationProperties アノテーションを使用してプロパティをバインドするようにクラスをマークします。

@SpringBootApplication
@EnableConfigurationProperties(ServerConfig.class)
public class Springboot13ConfigurationApplication {
    
    
}

ステップ② : @ConfigurationProperties を対応するクラスで直接使用してプロパティ バインディングを行う

@Data
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
    
    
    private String ipAddress;
    private int port;
    private long timeout;
}

違いがないと感じる人もいるのでは?現在プロパティをバインドする ServerConfig クラスは @Component アノテーションを宣言していないことに注意してください。@EnableConfigurationProperties アノテーションを使用する場合、Spring はデフォルトでそのアノテーション付きクラスを Bean として定義するため、 @Component アノテーションを再度宣言する必要はありません。

  • @EnableConfigurationProperties と @Component を同時に使用することはできません

@ConfigurationProperties アノテーションを使用すると、プロンプト メッセージが表示されます

ここに画像の説明を挿入

このプロンプトが表示されたら、座標を追加するだけでリマインダーが消えます。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

要約する

  1. @ConfigurationProperties を使用して、@Bean を使用して宣言されたサードパーティ Bean のプロパティをバインドします。
  2. @EnableConfigurationProperties を使用してプロパティ バインディング用の Bean を宣言した後は、@Component アノテーションを使用して Bean を再度宣言する必要はありません。

2.2. ルーズバインディング/ルーズバインディング

属性バインディングを実行するとき、次の状況が発生する可能性があります。標準の命名を実行するために、開発者はハンプ命名方法に厳密に従って属性名を記述し、次のように yml 構成ファイル内の datasource を dataSource に変更します。

dataSource:
  driverClassName: com.mysql.jdbc.Driver

この時点で、プログラムは通常どおり実行できるようになり、次のようにコード内のプレフィックス データソースを dataSource に変更します。

@Bean
@ConfigurationProperties(prefix = "dataSource")
public DruidDataSource datasource(){
    
    
    DruidDataSource ds = new DruidDataSource();
    return ds;
}

この時点でコンパイル エラーが発生しました。アイデア ツールが原因ではありません。実行後も問題が発生します。構成属性名 dataSource が無効です。

Configuration property name 'dataSource' is not valid:

    Invalid characters: 'S'
    Bean: datasource
    Reason: Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter

Action:
Modify 'dataSource' so that it conforms to the canonical names requirements.

なぜこのような問題が起こるのか、これは springboot がプロパティをバインドする際の重要な知識ポイントです プロパティ名の緩いバインディングは、疎バインディングとも呼ばれます。

ルーズバインディングとは何ですか? 実際、これはスプリングブート プログラミングの人間化された設計の現れであり、構成ファイル内の命名形式と変数名の命名形式の形式互換性を最大限に高めることができます。どこまで互換性があるのでしょうか?ほぼすべての主要な命名形式がサポートされています。次に例を示します。

ServerConfig の ipAddress プロパティ名

@Component
@Data
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
    
    
    private String ipAddress;
}

次の構成プロパティ名のルールと完全に互換性があります

servers:
  ipAddress: 192.168.0.2       # 驼峰模式
  ip_address: 192.168.0.2      # 下划线模式
  ip-address: 192.168.0.2      # 烤肉串模式
  IP_ADDRESS: 192.168.0.2      # 常量模式

上記 4 つのパターンは最終的に属性名 ipAddress と一致するとも言えます。なぜ?その理由は、照合するときに、構成内の名前からダッシュとアンダースコアを削除し、大文字と小文字を無視して Java コード内のプロパティ名と一致させる必要があるためです。上記の 4 つの名前は、アンダースコアと中央の行を削除します。大文字と小文字を区別しないと、その行は ipaddress という単語になり、Java コード内の属性名も大文字と小文字を無視して ipaddress になるため、同等の照合が実行できます。これが、4 つの形式が正常に照合できる理由です。ただし、springboot はダッシュ モードであるケバブ モードの使用を公式に推奨しています。

ここで、命名仕様の問題という知識ポイントを習得しました。表示され始めたプログラミングエラーメッセージを見てみましょう

Configuration property name 'dataSource' is not valid:

    Invalid characters: 'S'
    Bean: datasource
    Reason: Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter

Action:
Modify 'dataSource' so that it conforms to the canonical names requirements.

このうち、Reason はエラーの理由を記述し、仕様名は Kebab モード (case)、つまり - で区切られ、標準文字として小文字の英数字が使用され、文字で始まる必要があります。次に、作成した名前 dataSource に注目してください。これは上記の要件を満たしていません。

最後に、上記のルールは Springboot の @ConfigurationProperties アノテーションの属性バインディングに対してのみ有効であり、@Value アノテーションの属性マッピングに対しては無効です。

要約する

  1. @ConfigurationProperties は、プロパティをバインドする際のプロパティ名の緩やかなバインディングをサポートします。これは、プロパティ名の命名規則に反映されます。
  2. @Value アノテーションは疎結合ルールをサポートしていません
  3. バインディング接頭辞名にはケバブ命名規則を使用すること、つまり、区切り文字としてダッシュを使用することをお勧めします。
  4. バインディング プレフィックスの命名規則: 純粋な小文字、数字、アンダースコアのみが有効な文字として使用できます。

2.3. 一般的に使用される測定単位のバインディング

前の構成では、次の構成値を記述しました。3 番目のタイムアウト タイムアウトは、サーバー操作のタイムアウト時間を表します。現在の値は -1 で、タイムアウトしないことを意味します。

servers:
  ip-address: 192.168.0.1 
  port: 2345
  timeout: -1

ただし、この値の理解は人によって異なります。たとえば、オンライン サーバーはマスター/スレーブ バックアップを完了し、タイムアウト期間を 240 に設定します。240 の単位が秒の場合、タイムアウト期間は 4 分になり、単位が秒の場合、タイムアウト期間は 4 分になります。は分、タイムアウト期間は 4 時間です。このとき、問題は、この誤解をどのように解決するかということです。

コントラクトの強化に加えて、springboot は測定単位を表すために JDK8 で提供される新しいデータ型を最大限に活用し、この問題を根本的に解決します。JDK8 の 2 つの新しいクラス (Duration と DataSize) が次のモデル クラスに追加されます。

@Component
@Data
@ConfigurationProperties(prefix = "servers")
public class ServerConfig {
    
    
    @DurationUnit(ChronoUnit.HOURS)
    private Duration serverTimeOut;
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize dataSize;
}

期間: 時間間隔を示し、時間単位は @DurationUnit アノテーションによって記述できます。たとえば、上記の例で記述されている単位は時間です (ChronoUnit.HOURS)

DataSize : 記憶域スペースを示します。記憶域スペースの単位は @DataSizeUnit アノテーションによって記述できます。たとえば、上記の例で記述されている単位は MB (DataUnit.MEGABYTES) です。

上記の 2 つのユニットを使用すると、同期のずれたコミュニケーションや不完全な文書によって引き起こされる情報の非対称性の問題を効果的に回避し、問題を根本的に解決し、誤読を回避できます。

ドルエーションの一般的な単位は次のとおりです。

ここに画像の説明を挿入

DataSize の一般的な単位は次のとおりです。

ここに画像の説明を挿入

2.4. データの検証

現時点では、プロパティをバインドするときに緩いバインディング ルールに従って記述することができますが、記述するときにモデル クラスのデータ型を認識できないため、コード内で int 型が必要であるなど、型の不一致の問題が発生します。 、「a」を書き込むなど、構成に不正な値が指定されている場合、この種のデータは確実に効果的にバインドされず、エラーが発生します。SpringBoot は、このような問題を効果的に回避できる強力なデータ検証機能を提供します。具体的なデータ検証基準はJAVAEEのJSR303仕様に規定されており、開発者は必要に応じて対応する検証フレームワークを選択することができますが、ここではデータ検証の実装としてHibernateが提供する検証フレームワークを使用します。ライティングアプリケーションの形式は非常に固定されています。

ステップ①:検証フレームワークを開く

<!--1.导入JSR303规范-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>
<!--使用hibernate框架提供的校验器做实现-->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

ステップ② : 検証機能を有効にする必要があるクラスに対して @Validated アノテーションを使用して検証機能を有効にします

@Component
@Data
@ConfigurationProperties(prefix = "servers")
//开启对当前bean的属性注入校验
@Validated
public class ServerConfig {
    
    
}

ステップ③ : 特定のフィールドの入力規則を設定する

@Component
@Data
@ConfigurationProperties(prefix = "servers")
//开启对当前bean的属性注入校验
@Validated
public class ServerConfig {
    
    
    //设置具体的规则
    @Max(value = 8888,message = "最大值不能超过8888")
    @Min(value = 202,message = "最小值不能低于202")
    private int port;
}

データ形式検証を設定することで、不正なデータ読み込みを効果的に回避できます。実際、使い方は非常に簡単で、基本的には形式です。

要約する

  1. Bean プロパティ検証機能を有効にするには 3 つの手順があります。JSR303 および Hibernate 検証フレーム座標をインポートし、@Validated アノテーションを使用して検証機能を有効にし、特定の検証ルールを使用してデータ検証形式を標準化します。

2.5. データ型変換

まず問題について説明します。通常はデータベースに接続しますが、プログラムの実行後に表示されるメッセージは、パスワードが間違っているというものです。

java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)

実際、このエラーを見ると、ほとんどすべての学習者はユーザー名とパスワードが一致していない、つまりパスワードが正しく入力されていないことがわかりますが、問題はパスワードが正しく入力されていないことです。ユーザー名とパスワードの書き方:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
    username: root
    password: 0127

基礎編で属性インジェクションの話をしたときに、型に関する知識について触れましたが、整数に関する知識では、2進数、8進数、16進数をサポートするこのような文があります。

ここに画像の説明を挿入

この問題はここにあります。開発者の目には 0127 は文字列「0127」ですが、springboot の観点からは、これは数値であり、8 進数であるためです。バックグラウンドが String 型でデータを受信する場合、設定ファイルに整数値が設定されている場合、最初は整数として処理され、読み取り後に文字列に変換されます。偶然にも、0127 が 8 進数形式に衝突したため、10 進数の 87 になりました。

注意点は2つあり、1つ目は文字列を引用符で囲むことを習慣化すること、2つ目は0から始まるデータに注意することです。

要約する

  1. yaml ファイル内の数値の定義では、基本的な書き込み形式がサポートされています。文字列を使用する必要がある場合は、引用符を使用して明確にマークしてください。

3. テスト

3.1、負荷テストの特別なプロパティ

テスト プロセス自体は複雑なプロセスではありませんが、多くの場合、テスト中にオンライン状況をシミュレートしたり、特殊な状況をシミュレートしたりする必要があります。現在の環境がオンライン環境に合わせて設定されている場合、例えば以下のような構成となります。

env:
  maxMemory: 32GB
  minMemory: 16GB

ただし、対応する互換性を今すぐテストしたい場合は、次の構成をテストする必要があります

env:
  maxMemory: 16GB
  minMemory: 8GB

この際、テストのたびにテスト用のソースコードapplication.ymlの設定を変更してもよいでしょうか?明らかに違います。各テスト前に変更し、テスト後に元に戻すのは非常に面倒です。そこで、ソース コードに設定されているプロパティをカバーする一時プロパティのセットをテスト環境に作成する必要があると考えました。これにより、テスト ケースは独立した環境と同等になり、独立してテストできるため、より便利になります。

一時的なプロパティ

springboot は開発者のためにこの種の問題を解決する方法をすでに考えており、対応する関数エントリを提供しています。テスト ケース プログラムでは、次のようにプロパティをアノテーション @SpringBootTest に追加することで、一時プロパティをシミュレートできます。

//properties属性可以为当前测试用例添加临时的属性配置
@SpringBootTest(properties = {
    
    "test.prop=testValue1"})
public class PropertiesAndArgsTest {
    
    

    @Value("${test.prop}")
    private String msg;
    
    @Test
    void testProperties(){
    
    
        System.out.println(msg);
    }
}

注釈 @SpringBootTest のプロパティ属性を使用して、一時的なプロパティを現在のテスト ケースに追加し、テスト用のソース コード構成ファイル内の対応するプロパティ値をカバーします。

上記の状況に加えて、前述したように、コマンド ラインを使用して springboot プログラムを起動する場合、コマンド ライン パラメーターを通じてプロパティ値を設定することもできます。また、プログラムをオンラインで開始する場合、通常、いくつかの特別な構成情報が追加されます。運用保守担当者は Java の知識がありませんし、これらの構成情報の具体的な形式の書き方もわかりませんが、開発者として対応する記述内容を提供すれば、その構成情報が正しいかどうかをテストできますか?事前に有効ですか?当時は可能だったか、@SpringBootTest の別のプロパティにアノテーションを付けることで設定されていました。

//args属性可以为当前测试用例添加临时的命令行参数
@SpringBootTest(args={
    
    "--test.prop=testValue2"})
public class PropertiesAndArgsTest {
    
    
    
    @Value("${test.prop}")
    private String msg;
    
    @Test
    void testProperties(){
    
    
        System.out.println(msg);
    }
}

注釈 @SpringBootTest の args 属性を使用して、コマンド ライン パラメーターをシミュレートし、現在のテスト ケースをテストします。

両方が共存したらどうなるでしょうか? 実際、構成プロパティとコマンド ライン パラメーターの読み込み優先順位を考えれば、この結果は自明です。属性ロードの優先順位設定には明確な優先順位があり、次の順序を覚えていますか?

ここに画像の説明を挿入

属性ロードの優先順位では、コマンドラインパラメータの優先順位が11、構成属性の優先順位が3であることが明確に規定されています。結果は、args属性の構成がproperties属性よりも先にロードされることは自明のことです。構成。

要約する

  1. ロード テストの一時プロパティは、@SpringBootTest のプロパティと args プロパティに注釈を付けることで設定できます。この設定の適用範囲は、現在のテスト ケースにのみ適用されます

3.2. 負荷試験のための特別な構成

Spring について学習した後は、実際、Spring 環境では複数の構成ファイルまたは構成クラスを設定でき、複数の構成情報を同時に有効にすることができることを誰もが知っています。ここでの要件は、テスト環境に別の構成クラスを追加することです。その後、テスト環境が開始されると、この構成が有効になります。実際、この方法は Spring 環境で複数の構成情報をロードする方法とまったく同じです。具体的な操作手順は以下の通りです。

ステップ①:テストパッケージテストで専用のテスト環境設定クラスを作成する

@Configuration
public class MsgConfig {
    
    
    @Bean
    public String msg(){
    
    
        return "bean msg";
    }
}

上記の構成は現在の実験効果を実証するためにのみ使用されており、実際の開発ではこのように文字列型データを注入することはできません

ステップ②:テスト環境起動時にテスト環境専用の設定クラスをインポートし、 @Import アノテーションを使用して実現します

@SpringBootTest
@Import({
    
    MsgConfig.class})
public class ConfigurationTest {
    
    

    @Autowired
    private String msg;

    @Test
    void testConfiguration(){
    
    
        System.out.println(msg);
    }
}

この時点で、開発環境に基づく構成をベースに、@Import 属性を通じてテスト環境の追加操作が実装され、1+1 構成環境の効果が実現されます。このようにして、異なるテスト ケースごとに異なる Bean をロードする効果を実現し、テスト ケースの作成を強化し、開発環境の構成には影響を与えません。

要約する

  1. テスト環境専用の構成クラスを定義し、@Import アノテーションを使用して特定のテストにテスト ケースなどの一時的な構成をインポートして、テスト プロセスを容易にします。上記の構成は他のテスト クラス環境に影響を与えません。

3.3、Web環境シミュレーションテスト

テストでプレゼンテーション層の機能をテストするには、ベースと機能が必要です。いわゆる基本は、テスト プログラムを実行するときに Web 環境を起動する必要があり、そうしないと Web 機能をテストできないということです。機能の 1 つは、テスト プログラムに Web リクエストを送信する機能がなければ、Web 機能のテストは実現できないということです。したがって、テスト ケースでプレゼンテーション層インターフェイスをテストする作業は、2 つのことに変換されます。1 つはテスト クラスで Web テストを開始する方法、もう 1 つはテスト クラスで Web リクエストを送信する方法です。

テストクラスでWeb環境を起動する

すべての springboot テスト クラスには標準の @SpringBootTest アノテーションがあり、このアノテーションには webEnvironment と呼ばれる属性があります。このプロパティを通じて、次のようにテスト ケースで Web 環境を開始するように設定できます。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {
    
    	
}

テストクラスでWeb環境を起動する際に、起動するWeb環境に対応するポートを指定することができますが、Springbootでは以下の4つの設定値が用意されています。

  • MOCK: 現在の設定に従って Web 環境を起動するかどうかを確認します。たとえば、Web 環境の起動にサーブレット API を使用する場合、それは適応型構成です。
  • DEFINED_PORT: カスタム ポートを Web サーバー ポートとして使用します
  • RANDOM_PORT: ランダムなポートを Web サーバーのポートとして使用します。
  • NONE: Web 環境を起動しません

上記の設定により、テストプログラム起動時に Web 環境を正常に有効にすることができますが、コードのハードコード設定によるオンライン関数パッケージングテスト時のポート競合による予期せぬ現象を避けるため、テスト時には RANDOM_PORT を使用することをお勧めします。これは、プログラムにはポート 8080 が記述されていますが、オンライン環境ではポート 8080 が占有されているため、コードに記述されているすべてを変更する必要があることを意味します。これは、デッド コードを記述することの代償です。これで、ランダム ポートを使用して、この種の問題の隠れた危険があるかどうかをテストできるようになりました。

テスト環境の Web 環境が構築できたので、次は 2 番目の問題、プログラム コード内で Web リクエストを送信する方法を解決します。

テストクラスでリクエストを送信する

テストクラスでリクエストを送信する場合、実際にはJava APIが対応する機能を提供していますが、普段友人との接触が少ないため、比較的馴染みがありません。Springboot では、開発者が対応する機能を開発しやすいようにパッケージ化し、開発手順を簡略化しています。

ステップ①:テストクラスでWeb仮想呼び出し関数を開き、 @AutoConfigureMockMvc アノテーションを付けることでこの関数の呼び出しを実現する

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
    
    
}

ステップ②:仮想呼び出しを開始するオブジェクトMockMVCを定義し、自動アセンブリでオブジェクトを初期化する

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
    
    

    @Test
    void testWeb(@Autowired MockMvc mvc) {
    
    
    }
}

ステップ③:仮想リクエストオブジェクトを作成し、リクエストのパスをカプセル化し、MockMVCオブジェクトを使用して対応するリクエストを送信します。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
    
    

    @Test
    void testWeb(@Autowired MockMvc mvc) throws Exception {
    
    
        //http://localhost:8080/books
        //创建虚拟请求,当前访问/books
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        //执行对应的请求
        mvc.perform(builder);
    }
}

テストプログラムを実行すると、/books に対応するリクエストが正常に送信できるようになりました。以前のサーバーの IP アドレスとポートは現在の仮想サーバーの IP アドレスとポートを使用しているため、アクセスパスに http://localhost:8080/books を書かないように注意してください。 Web 環境では、指定する必要はありません。リクエストの特定のパスを指定するだけです。

要約する

  1. テスト クラスで Web 層インターフェイスをテストするには、テスト クラスの開始時に Web コンテナーが開始されていることを確認する必要があります。また、@SpringBootTest アノテーションが付けられた webEnvironment 属性を仮想 Web 環境でのテストに使用できるようにする必要があります。
  2. MockMvc オブジェクトをテスト メソッドに挿入します。これにより、仮想リクエストを送信して Web リクエスト呼び出しプロセスをシミュレートできます。

Web環境リクエスト結果比較

テストケースでWeb環境をシミュレートし、Webリクエストの送信に成功したので、リクエスト送信後の送信結果をどのように比較するかという問題を解決します。実際、リクエストの送信後に取得される情報は 1 種類だけで、それがレスポンス オブジェクトです。レスポンスオブジェクトに何が含まれているかを比較することができます。一般的な比較は次のとおりです。

  • 応答ステータスの一致

    @Test
    void testStatus(@Autowired MockMvc mvc) throws Exception {
          
          
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        StatusResultMatchers status = MockMvcResultMatchers.status();
        //预计本次调用时成功的:状态200
        ResultMatcher ok = status.isOk();
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(ok);
    }
    
  • レスポンスボディのマッチング(非jsonデータ形式)

    @Test
    void testBody(@Autowired MockMvc mvc) throws Exception {
          
          
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        ContentResultMatchers content = MockMvcResultMatchers.content();
        ResultMatcher result = content.string("springboot2");
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(result);
    }
    
  • レスポンスボディのマッチング(jsonデータ形式、開発で主流)

    @Test
    void testJson(@Autowired MockMvc mvc) throws Exception {
          
          
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        ContentResultMatchers content = MockMvcResultMatchers.content();
        ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot2\",\"type\":\"springboot\"}");
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(result);
    }
    
  • レスポンスヘッダ情報の照合

    @Test
    void testContentType(@Autowired MockMvc mvc) throws Exception {
          
          
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        HeaderResultMatchers header = MockMvcResultMatchers.header();
        ResultMatcher contentType = header.string("Content-Type", "application/json");
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(contentType);
    }
    

基本的にすべてが揃っており、ヘッダー情報、ボディ情報、ステータス情報がすべて揃っており、完璧な応答結果の比較結果を組み合わせることができます。次の例では、3 種類の情報が同時に照合および検証されます。これも完全な情報照合プロセスです。

@Test
void testGetById(@Autowired MockMvc mvc) throws Exception {
    
    
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions action = mvc.perform(builder);

    StatusResultMatchers status = MockMvcResultMatchers.status();
    ResultMatcher ok = status.isOk();
    action.andExpect(ok);

    HeaderResultMatchers header = MockMvcResultMatchers.header();
    ResultMatcher contentType = header.string("Content-Type", "application/json");
    action.andExpect(contentType);

    ContentResultMatchers content = MockMvcResultMatchers.content();
    ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot\",\"type\":\"springboot\"}");
    action.andExpect(result);
}

要約する

  1. Web 仮想呼び出しは、ローカル仮想リクエストによって返された応答情報を比較できます。この情報は、応答ヘッダー情報の比較、応答本文情報の比較、および応答ステータス情報の比較に分かれています。

3.4. データ層テストのロールバック

現時点では、弊社のテストプログラムはプレゼンテーション層、ビジネス層、データ層のインターフェースに対応した機能テストを完璧に実行できますが、テストのライフサイクルはテストケースの開発完了後、パッケージングの段階で行われます。実行する必要があるライフサイクル。スキップすると、システムに非常に高いセキュリティ リスクが生じるため、テスト ケースを実行する必要があります。しかし、テスト ケースでテスト中にトランザクション コミットが生成されると、テスト中にデータベース データに影響を及ぼし、ガベージ データが生成されるという新たな問題が発生します。このプロセスは私たちが望んでいることではありません。開発者テスト ケースとして実行する必要がありますが、プロセス中に生成されたデータがシステムに痕跡を残すべきではありません。どう対処すればよいですか?

springboot は長い間開発者向けにこの問題を考えており、この問題に対する最も簡単な解決策を提供しています。元のテスト ケースに @Transactional アノテーションを追加すると、現在のテスト ケースのトランザクションがコミットされていないことがわかります。プログラムの実行時、@Transactional アノテーションが表示される @SpringBootTest アノテーションが存在する限り、springboot はこれがテスト プログラムであると判断し、トランザクションをコミットする必要がないため、トランザクションの送信を回避できます。

@SpringBootTest
@Transactional
@Rollback(true)
public class DaoTest {
    
    
    @Autowired
    private BookService bookService;

    @Test
    void testSave(){
    
    
        Book book = new Book();
        book.setName("springboot3");
        book.setType("springboot3");
        book.setDescription("springboot3");

        bookService.save(book);
    }
}

開発者がトランザクションを送信したい場合は、@RollBack アノテーションを追加し、ロールバック ステータスを false に設定してトランザクションを通常どおり送信することもできます。

要約する

  1. Springboot テスト クラスに @Transactional アノテーションを追加して、テスト ケースがトランザクションを送信しないようにします。
  2. 注釈 @Rollback を使用して、スプリングブート テスト クラスの実行結果がトランザクションを送信するかどうかを制御します。これは、注釈 @Transactional とともに使用する必要があります。

3.5. テストケースデータの設定

テスト ケースに固定データを記述するのは明らかに不合理ですが、springboot は、プログラムが実行されるたびにロードされるデータがランダムであることを保証するために、構成でランダムな値を使用するメカニズムを提供します。詳細は次のとおりです。

testcase:
  book:
    id: ${
    
    random.int}
    id2: ${
    
    random.int(10)}
    type: ${
    
    random.int!5,10!}
    name: ${
    
    random.value}
    uuid: ${
    
    random.uuid}
    publishTime: ${
    
    random.long}

現在の構成では、プログラムを実行するたびにランダムなデータのセットを作成できるため、実行のたびにデータが固定値になるという厄介な現象を回避でき、機能のテストに役立ちます。データのロードは、@ConfigurationProperties アノテーションを使用して、以前にロードされたデータの形式で行われます。

@Component
@Data
@ConfigurationProperties(prefix = "testcase.book")
public class BookCase {
    
    
    private int id;
    private int id2;
    private int type;
    private String name;
    private String uuid;
    private long publishTime;
}

乱数値の生成には、生成される数値データの設定範囲など、以下のような細かい制限があります。

ここに画像の説明を挿入

  • ${random.int} はランダムな整数を表します
  • ${random.int(10)} は 10 以内の乱数を意味します
  • ${random.int(10,20)} は 10 から 20 までの乱数を表します
  • () には、[] などの任意の文字を指定できます。

要約する

  1. ランダム データを使用すると、テスト ケースに書き込まれた固定データを置き換えて、テスト ケース内のテスト データの有効性を向上させることができます。

4. データ層ソリューション

4.1、SQL

データ層ソリューションは、Mysql+Druid+MyBatisPlus であると言えます。3 つのテクノロジーは、データ層操作の 3 つのレベルに対応します。

  • データソーステクノロジー: Druid
  • 永続化テクノロジー: MyBatisPlus
  • データベーステクノロジー: MySQL

以下の調査は 3 つのレベルに分かれており、上記の 3 つの側面に対応して、最初のデータ ソース テクノロジーから始めましょう。

4.1.1. データソーステクノロジー

現在、使用しているデータ ソース テクノロジは Druid であり、対応するデータ ソース初期化情報は、次のように実行時にログで確認できます。

INFO 28600 --- [           main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
INFO 28600 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {
    
    dataSource-1} inited

Druid データ ソースを使用しない場合、プログラムは実行後にどのようになりますか? それは独立したデータベース接続オブジェクトですか、それとも他の接続プールのテクニカル サポートはありますか? Druid テクノロジーに対応するスターターを削除し、プログラムを再度実行すると、ログで次の初期化情報が見つかります。

INFO 31820 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
INFO 31820 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.

DruidDataSource に関する情報はありませんが、HikariDataSource に関する情報がログに存在することがわかりました。何の技術か分からなくても、名前からそれがわかります。DataSource で終わる名前は、データソーステクノロジー。このテクノロジーは手動で追加したわけではありません。このテクノロジーはどこから来たのでしょうか? これは、このセクション、スプリングブート埋め込みデータ ソースで説明する知識です。

データ層テクノロジはすべてのエンタープライズレベルのアプリケーションで使用され、それらの間でデータベース接続管理を実行する必要があります。Springboot は開発者の習慣から始まります。開発者がデータ ソース テクノロジを提供する場合は、開発者が提供するものを使用します。開発者が提供しない場合は、データベース接続オブジェクトを 1 つずつ手動で管理することはできません。どうすればよいですか? デフォルトのものをあげておきます。

springboot は、次の 3 つの組み込みデータ ソース テクノロジを提供します。

  • ヒカリCP
  • Tomcat はデータソースを提供します
  • コモンズDBCP

1 つ目の HikartCP は、springboot によって公式に推奨されているデータ ソース テクノロジであり、デフォルトの組み込みデータ ソースとして使用されます。どういう意味ですか?データ ソースを構成しない場合は、これを使用します。

2 つ目は Tomcat が提供する DataSource で、HikartCP を使用せずに Web プログラム開発用の Web サーバーとして Tomcat を使用する場合は、これを使用します。なぜ他の Web サーバーではなく Tomcat を使用するのでしょうか? Web テクノロジーをスターターにインポートすると、組み込みの Tomcat がデフォルトで使用されるため、デフォルトで使用されるテクノロジーであるため、最後まで使用されるとデータ ソースとして使用されます。HikartCP を使用せずに、Tomcat が提供するデフォルトのデータ ソース オブジェクトを使用する方法を誰かが提案しましたか? HikartCP技術の座標は除外してもOKです。

3 番目のタイプの DBCP には、より厳しい使用条件があり、HikartCP も Tomcat の DataSource も使用されていない場合は、デフォルトでこれが使用されます。

springboot の心臓部も断片化しています。接続オブジェクトを自分で管理することはできないと思いますが、お勧めします。開発の世界では本当に最強の支援です。せっかく与えられたものなので、使ってみましょう。これらはどのように設定して使用するのですか? 以前にドルイドを設定したとき、ドルイドのスターターに対応する設定は次のとおりです。

spring:
  datasource:
    druid:	
   	  url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root

デフォルトのデータソースHikariCPに変更した後、次のようにdruidを直接削除するだけです。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root

もちろん、hikari用の設定として記述することもできますが、URLアドレスは以下のように別途設定する必要があります。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root

これがhikariデータソースの構成方法です。ヒカリをさらに設定したい場合は、その独立したプロパティを引き続き設定できます。例えば:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: root
      maximum-pool-size: 50

hikari データソースを使用したくない場合は、tomcat を使用したデータソースまたは DBCP 構成形式は同じです。これを学んだ後、将来データ レイヤーを実行するときに、データ ソース オブジェクトの選択はドルイド データ ソース テクノロジの 1 つの使用法ではなくなり、ニーズに応じて選択できるようになります。

要約する

  1. springboot テクノロジは、Hikari、Tomcat 組み込みデータ ソース、DBCP という 3 つの組み込みデータ ソース テクノロジを提供します。

4.1.2. 永続化テクノロジー

データ ソース ソリューションについて話した後、永続化ソリューションについて話しましょう。Springboot はその最強の補助機能を最大限に活用し、JdbcTemplate と呼ばれる既成のデータ層テクノロジーのセットを開発者に提供します。実はこの技術は springboot の技術を使わなくても使えるので、 springboot が提供しているとは言えませんが、誰が提供したのでしょうか?Spring Technology によって提供されているため、Springboot テクノロジーの範疇にはこのテクノロジーも存在しますが、結局のところ、Springboot テクノロジーは Spring プログラムの開発を加速するために作成されています。

このテクノロジーは実際には、データ層を開発するために jdbc の最も原始的なプログラミング形式に戻っており、次の手順が直接実行されます。

ステップ①:jdbcに対応する座標をインポートします。スターターであることを忘れないでください。

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

ステップ②:JdbcTemplateオブジェクトを自動アセンブルする

@SpringBootTest
class Springboot15SqlApplicationTests {
    
    
    @Test
    void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
    
    
    }
}

ステップ③:JdbcTemplateを使用してクエリ操作を実装する(非エンティティクラスのカプセル化データに対するクエリ操作)

@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
    
    
    String sql = "select * from tbl_book";
    List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
    System.out.println(maps);
}

手順④:JdbcTemplateを使用してクエリ操作(データをエンティティクラスにカプセル化するクエリ操作)を実装する

@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
    
    

    String sql = "select * from tbl_book";
    RowMapper<Book> rm = new RowMapper<Book>() {
    
    
        @Override
        public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
    
    
            Book temp = new Book();
            temp.setId(rs.getInt("id"));
            temp.setName(rs.getString("name"));
            temp.setType(rs.getString("type"));
            temp.setDescription(rs.getString("description"));
            return temp;
        }
    };
    List<Book> list = jdbcTemplate.query(sql, rm);
    System.out.println(list);
}

ステップ⑤:JdbcTemplateを使用して追加・削除・変更操作を実装する

@Test
void testJdbcTemplateSave(@Autowired JdbcTemplate jdbcTemplate){
    
    
    String sql = "insert into tbl_book values(3,'springboot1','springboot2','springboot3')";
    jdbcTemplate.update(sql);
}

JdbcTemplate オブジェクトを設定する場合は、次のように yml ファイルで設定できます。

spring:
  jdbc:
    template:
      query-timeout: -1   # 查询超时时间
      max-rows: 500       # 最大行数
      fetch-size: -1      # 缓存行数

要約する

  1. SpringBoot 組み込み JdbcTemplate 永続化ソリューション
  2. JdbcTemplateを使用するには、spring-boot-starter-jdbcの座標をインポートする必要がある

4.1.3. データベース技術

これまでのところ、springboot は開発者に組み込みのデータ ソース ソリューションと永続化ソリューションを提供してきました。3 つの部分からなるデータ レイヤー ソリューションにはデータベースが 1 つだけ残っています。springboot は組み込みのソリューションも提供しますか? 実際には 1 つではなく 3 つあります。このセクションでは、組み込みデータベース ソリューションについて説明します。

springboot は 3 つの組み込みデータベースを提供します。

  • H2
  • HSQL
  • ダービー

上記の 3 つのデータベースは、独立してインストールされるだけでなく、Tomcat サーバーのような埋め込み形式で spirngboot コンテナ内で実行することもできます。コンテナに埋め込まれて実行するには、Java オブジェクトである必要があります。はい、これら 3 つのデータベースの最下層はすべて Java 言語を使用して開発されています。

私たちは MySQL データベースをよく使っていますが、なぜこれを使用する必要があるのでしょうか? その理由は、これら 3 つのデータベースはすべて組み込みコンテナーの形式で実行できるためです。アプリケーションの実行後にテスト作業を実行する場合、現時点ではテスト データをディスクに保存する必要はありませんが、組み込みデータベースはこれは便利です。メモリ内で実行され、テストの時間になり、実行の時間になり、サーバーがシャットダウンされるとすべてが消えます。これは非常に優れており、外部データベースを維持する手間が省けます。これは組み込みデータベースの最大の利点でもあり、機能テストに便利です。

これらの組み込みデータベースの使い方をH2データベースを例に説明しますが、操作手順も非常にシンプルで、シンプルであるほど使いやすいです。

手順①:H2データベースに対応する座標を合計2つインポート

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

ステップ②:プロジェクトをWebプロジェクトとして設定し、プロジェクト起動時にH2データベースを起動する

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

ステップ③:設定を通じてH2データベースコンソールアクセスプログラムを開くか、他のデータベース接続ソフトウェアを使用して操作します

spring:
  h2:
    console:
      enabled: true
      path: /h2

Web アクセス パス /h2、アクセス パスワード 123456、アクセスに失敗した場合は、まず次のデータ ソースを設定し、プログラムを起動してから、再度 /h2 パスにアクセスすると正常にアクセスできます

datasource:
  url: jdbc:h2:~/test
  hikari:
    driver-class-name: org.h2.Driver
    username: sa
    password: 123456

ステップ④:JdbcTemplateまたはMyBatisPlus技術を使用してデータベースを操作する

@Test
void testJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate){
    
    
    String sql = "select * from tbl_book";
    List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
    System.out.println(maps);
}

実際、データベースを変更しただけであり、他のものには影響はありません。重要な注意点として、オンラインになるときは、メモリ レベル データベースを閉じ、データ永続化ソリューションとして MySQL データベースを使用することを忘れないでください。閉じる方法は、enabled プロパティを false に設定することです。

要約する

  1. H2組み込みデータベースの起動方法、座標の追加、設定の追加
  2. オンラインで実行している場合は、必ず H2 データベースを閉じてください。

SQL 関連のデータ レイヤー ソリューションはこれで終わりで、オプションのテクノロジがさらに豊富になりました。

  • データソーステクノロジー:Druid、Hikari、tomcat DataSource、DBCP
  • 永続化テクノロジー: MyBatisPlus、MyBatis、JdbcTemplate
  • データベーステクノロジー: MySQL、H2、HSQL、Derby

プログラムを開発するときに、上記のテクノロジーのいずれかを選択して、一連のデータベース ソリューションを編成できるようになりました。

4.2、NoSQL

SQL データ レイヤー ソリューションが完了したので、NoSQL データ レイヤー ソリューションについて話しましょう。この NoSQL は何を意味するのでしょうか? 文字通り、「いいえ」は否定を意味します。NoSQL は非リレーショナル データベース ソリューションです。つまり、データは保存および取得する必要がありますが、これらのデータはリレーショナル データベースには保存されません。データはどこに配置する必要がありますか? 当然のことながら、Redis など、データを保存できる他の関連テクノロジーにも含まれています。このセクションの内容は、springboot がこれらのテクノロジーをどのように統合するかについてです。springboot の公式ドキュメントでは、関連テクノロジーの 10 の統合ソリューションが提供されています。国内市場で最も人気のある NoSQL データベース統合ソリューション、すなわち Redis、MongoDB、ES について説明します。

4.2.1、SpringBoot は Redis を統合します

Redis は、キーと値のデータ保存形式を使用するインメモリ NoSQL データベースであり、キーと値のペアの保存形式であるキーと値のデータ保存形式に重点を置いています。MySQL データベースには、MySQL データベースとは異なり、テーブル、フィールド、レコードがありますが、Redis には名前と値が対応するものはなく、データは主にメモリに保存されます。メモリには主に何が保存されますか? 実際、Redis には RDB と AOF というデータ永続化ソリューションがありますが、Redis 自体はデータ永続化のために生まれたものではなく、主にデータをメモリに保存し、データ アクセスを高速化する、メモリ レベルのデータベースです。

Redis はさまざまなデータ ストレージ形式をサポートしています。たとえば、文字列を直接保存することも、マップ コレクションやリスト コレクションを保存することもできます。また、後で異なる形式での一部のデータ操作が必要になります。これは、事前に学習する必要があります。統合されているので、基本的な操作に関連するいくつかの操作を で紹介します。まずインストールして、次に操作して、最後に統合しましょう

4.2.1.1. インストール

Windows版インストールパッケージのダウンロードアドレス:https://github.com/tporadowski/redis/releases

ダウンロードしたインストールパッケージには 2 つの形式があり、1 つはワンクリックインストール用の msi ファイル、もう 1 つは解凍して使用できる zip ファイルです。どちらの形式でも問題ありません。ワンクリック用の msi ファイルは次のとおりです。インストールにはmsiのインストール。

msi とは何ですか? 実際には、これはファイル インストール パッケージであり、ソフトウェアをインストールするだけでなく、ソフトウェアのインストールに必要な機能を関連付けて、それらをパッケージ化して動作させることもできます。例には、インストール シーケンス、インストール パスの作成と設定、システム依存関係の設定、デフォルトでのインストール オプションの設定、インストール プロセスを制御するプロパティなどが含まれます。簡単に言うとワンストップサービスで、導入作業が一度で完了します。

インストールが完了すると、CMD コマンドライン モードで実行する必要がある、Redis を起動するためのコア コマンドである 2 つのコマンドに対応する 2 つのファイルが得られます。

サーバーを起動する

redis-server.exe redis.windows.conf

クライアントを起動する

redis-cli.exe

Redis サーバーの起動に失敗した場合は、最初にクライアントを起動し、次にシャットダウン操作を実行して終了すると、Redis サーバーが正常に実行されます。

4.2.1.2. 基本操作

サーバーが起動したら、MySQL データベースを起動し、SQL コマンド ラインを起動してデータベースを操作するのと同様に、クライアントを使用してサーバーに接続できます。

文字列データを Redis に配置し、最初に名前、年齢などのデータの名前を定義してから、コマンド set を使用してデータを Redis サーバーに設定します

set name test
set age 12

redisから入れたデータを取り出し、名前に従って該当するデータを取得します。該当するデータがない場合は(nil)となります。

get name
get age

上記で使用したデータ ストレージは値に対応する名前であり、データが多すぎて維持できない場合は、他のデータ ストレージ構造を使用できます。たとえば、ハッシュは複数のデータを 1 つの名前で保存できるストレージ モデルであり、各データは独自のセカンダリ ストレージ名を持つこともできます。ハッシュ構造にデータを格納する形式は次のとおりです。

hset a a1 aa1		#对外key名称是a,在名称为a的存储模型中,a1这个key中保存了数据aa1
hset a a2 aa2

ハッシュ構造のデータを取得するコマンドは以下の通りです

hget a a1			#得到aa1
hget a a2			#得到aa2

4.2.1.3. 統合

統合の前に統合の考え方を整理しておくと、テクノロジーを springboot に統合するということは、実際には springboot で対応するテクノロジーの API を使用することになります。2 つのテクノロジーが交わらない場合、統合という概念は存在しません。いわゆる統合では、実際には Springboot テクノロジを使用して他のテクノロジを管理することになりますが、いくつかの問題は避けられません。

まず、対応するテクノロジーの座標をインポートする必要があります。統合後、これらの座標はいくつか変更されます。

次に、どのようなテクノロジーにも関連する設定情報が存在するのが一般的ですが、統合後にこの情報をどのように記述するか、どこに記述するかが問題になります。

第三に、統合前の動作がモード A である場合、統合によって開発者に便利な動作がもたらされなければ、統合の意味がありません。そのため、統合後の動作は簡素化する必要があり、対応する動作方法は当然です。利用可能、異なる

上記の 3 つの質問によると、スプリングブートですべてのテクノロジーの統合を考えるのが一般的なアイデアです。統合プロセス中に、統合ルーチンは徐々に検討され、適用性は非常に高くなります。いくつかのテクノロジーの統合後、基本的には固定観念であると結論付けることができます。

Springboot を起動して Redis を統合しましょう。操作手順は次のとおりです。

ステップ①:springbootをインポートしてredisのスターター座標を統合する

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

上記の座標はモジュール作成時にチェックを入れることで選択でき、NoSQLカテゴリに属します

ステップ②:基本設定を行う

spring:
  redis:
    host: localhost
    port: 6379

redisを運用する場合、どのredisサーバーを運用するかが最も基本的な情報となるため、サーバーアドレスは基本構成情報に属し必須となります。ただし、設定をしなくても現状では使用可能です。上記の 2 つの情報セットにはデフォルト設定があり、それがたまたま上記の設定値になっているためです。

ステップ③ : Springboot を使用して Redis の専用クライアント インターフェイス操作を統合します。これは RedisTemplate です

@SpringBootTest
class Springboot16RedisApplicationTests {
    
    
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void set() {
    
    
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("age",41);
    }
    @Test
    void get() {
    
    
        ValueOperations ops = redisTemplate.opsForValue();
        Object age = ops.get("name");
        System.out.println(age);
    }
    @Test
    void hset() {
    
    
        HashOperations ops = redisTemplate.opsForHash();
        ops.put("info","b","bb");
    }
    @Test
    void hget() {
    
    
        HashOperations ops = redisTemplate.opsForHash();
        Object val = ops.get("info", "b");
        System.out.println(val);
    }
}

Redisを操作する際には、どのようなデータを操作するのかを確認し、データの種類に応じた操作インターフェースを取得する必要があります。たとえば、opsForValue() を使用して文字列型のデータ操作インターフェイスを取得し、opsForHash() を使用してハッシュ型のデータ操作インターフェイスを取得し、残りは対応する API オペレーションを呼び出すだけです。各種データ操作インターフェースは以下のとおりです。

ここに画像の説明を挿入

要約する

  1. springboot 統合の redis 手順
    1. springbootをインポートしてredisのスターター座標を統合します
    2. 基本的な設定を実行する
    3. springboot を使用して Redis の専用クライアント インターフェイス RedisTemplate 操作を統合する

文字列Redisテンプレート

RedisはJavaオブジェクトの保存形式を内部で提供していないため、操作対象のデータがオブジェクト形式で存在する場合、トランスコードして文字列形式に変換して操作します。Springboot では、開発者が文字列ベースのデータ操作を使用しやすくするために、Redis を統合する際に専用の API インターフェイス StringRedisTemplate を提供していますが、これは RedisTemplate の汎用的なデータ固有の操作 API であることが理解できます。

@SpringBootTest
public class StringRedisTemplateTest {
    
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Test
    void get(){
    
    
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String name = ops.get("name");
        System.out.println(name);
    }
}

Redisクライアントの選択

Springboot は、redis テクノロジーを統合して、さまざまなクライアント互換モードを提供します。デフォルトでは lettucs クライアント テクノロジーが提供されますが、必要に応じて jedis クライアント テクノロジーなどの指定されたクライアント テクノロジーに切り替えることもできます。jedis クライアントに切り替える操作手順技術は次のとおりです。

ステップ①:ジェディス座標をインポートする

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

Jedis 座標は springboot によって管理され、バージョン番号を指定する必要はありません

ステップ② : クライアントテクノロジータイプを構成し、jedis に設定します。

spring:
  redis:
    host: localhost
    port: 6379
    client-type: jedis

ステップ③:必要に応じて、対応する構成を設定します

spring:
  redis:
    host: localhost
    port: 6379
    client-type: jedis
    lettuce:
      pool:
        max-active: 16
    jedis:
      pool:
        max-active: 16

レタスとジェディスの違い

  • Jedis は、直接接続モードで Redis サーバーに接続します。マルチスレッド モードで jedis を使用すると、スレッドの安全性の問題が発生します。解決策は、各接続を専用にするように接続プールを構成することで、全体的なパフォーマンスが大幅に向上します。影響を受ける。
  • Lettcus は Netty フレームワークに基づいて Redis サーバーに接続され、StatefulRedisConnection が基礎となる設計で使用されます。StatefulRedisConnection 自体はスレッドセーフであり、同時アクセスのセキュリティ問題を保証できるため、接続は複数のスレッドで再利用できます。もちろん、Lettcus は連携する複数の接続インスタンスもサポートしています

要約する

  1. Springboot は Redis を統合して、文字列データ形式で Redis を操作するための StringRedisTemplate オブジェクトを提供します
  2. Redis クライアント実装テクノロジーを切り替える必要がある場合は、構成の形式で行うことができます。

4.2.2. SpringBoot は MongoDB を統合します

MongoDB は、オープン ソース、高性能、スキーマフリーのドキュメント データベースであり、NoSQL データベース製品の 1 つであり、リレーショナル データベースに最も似た非リレーショナル データベースです。

上記の説明のいくつかの単語のうち、私たちにとって最も馴染みのない単語はパターンがありません。モードレスとは何ですか? 簡単に言えば、データベースには固定されたデータの格納構造はなく、最初のデータには A、B、C の 3 つのフィールドがあり、2 番目のデータには D、E、F の 3 つのフィールドがある場合があります。 3 番目のデータには 3 つのフィールドがある場合があります。データは A、C、E3 フィールドである可能性があります。つまり、データの構造は固定されておらず、モードレスです。これが何の役に立つのかと言う人もいるでしょう。制約がなく、いつでも柔軟に変更できます。上記の特性を踏まえて、MongoDB のアプリケーション側にもいくつかの変更が加えられます。以下に、MongoDB をデータ ストレージとして使用できるいくつかのシナリオを示しますが、MongoDB を使用する必要はありません。

  • タオバオのユーザーデータ
    • 保存場所: データベース
    • 特徴: 永久保存、非常に低い変更頻度
  • ゲーム機器データ、ゲーム小道具データ
    • 保存場所: データベース、Mongodb
    • 特徴:永久保存と一時保存の組み合わせ、変更頻度が高い
  • 生放送データ、報酬データ、ファンデータ
    • 保存場所: データベース、Mongodb
    • 特徴:永久保存と一時保存の組み合わせ、変更頻度が高い
  • IoTデータ
    • 保存場所: Mongodb
    • 特徴: 一時保存、迅速な変更頻度

4.2.2.1. インストール

Windows版インストールパッケージのダウンロードアドレス:https://www.mongodb.com/try/download

ダウンロードしたインストールパッケージも、ワンクリックインストール用のmsiファイルと、解凍して使用できるzipファイルの2つの形式がありますが、どちらの形式でも構いません。ここでは、解凍したzipファイルを使用してインストールします。

解凍後、次のファイルが得られます。このファイルの bin ディレクトリには、すべての mongodb 実行可能コマンドが含まれています

ここに画像の説明を挿入

mongodb は実行時にデータ保存ディレクトリを指定する必要があるため、次のように、通常はインストール ディレクトリに配置されるデータ保存ディレクトリを作成し、ここにデータを保存するデータ ディレクトリを作成します

ここに画像の説明を挿入

インストール プロセス中に次の警告メッセージが表示された場合は、現在のオペレーティング システムに一部のシステム ファイルが不足していることを示しているため、心配する必要はありません。

ここに画像の説明を挿入

以下の解決策に従って、ブラウザで不足している名前に対応するファイルを検索してダウンロードすることができます。ダウンロードしたファイルを Windows インストール ディレクトリの system32 ディレクトリにコピーし、コマンド ラインで regsvr32 コマンドを実行しますをクリックしてこのファイルを登録します。ダウンロードしたファイルの名前に応じて、対応する名前を変更してからコマンドを実行してください。

regsvr32 vcruntime140_1.dll

サーバーを起動する

mongod --dbpath=..\data\db

サーバーを起動するときは、データ ストレージの場所を指定し、パラメータ -dbpath で設定する必要があります。必要に応じてデータ ストレージ パスを設定できます。デフォルトのサービス ポートは 27017 です。

クライアントを起動する

mongo --host=127.0.0.1 --port=27017

4.2.2.2. 基本操作

MongoDB はデータベースですが、その操作は SQL 文を使用しないため、操作方法に馴染みがないかもしれませんが、幸いなことに、Navicat と同様のデータベース クライアント ソフトウェアがいくつかあり、簡単に MongoDB を操作できます。モンゴDB。

同じ種類のソフトウェアは多数ありますが、今回インストールしたソフトウェアは Robo3t です。Robot3t は環境に優しいソフトウェアで、インストールする必要はなく、解凍するだけです。解凍後、インストールディレクトリに移動し、robot3t.exeをダブルクリックしてご利用ください。

ここに画像の説明を挿入

ソフトウェアを開くには、まず MongoDB サーバーに接続し、[ファイル] メニューを選択し、[接続…] を選択する必要があります。

ここに画像の説明を挿入

接続管理インターフェイスに入ったら、左上隅の[作成]リンクを選択して新しい接続設定を作成します

ここに画像の説明を挿入

設定値を入力すると接続できます(デフォルトでは本機のポート27017にそのまま接続できます)

ここに画像の説明を挿入

接続に成功したら、コマンド入力エリアにコマンドを入力してMongoDBを操作します。

データベースの作成: 右ボタンを使用して左側のメニューで作成し、データベース名を入力します

コレクションを作成します。コレクションを右クリックして作成し、コレクションの名前を入力するだけです。コレクションはデータベース内のテーブルの役割と同等です。

新規ドキュメント:(ドキュメントはjson形式に似たデータの一種です)

db.集合名称.insert/save/insertOne(文档)

ドキュメントを削除:

db.集合名称.remove(条件)

ドキュメントを変更します。

db.集合名称.update(条件,{操作种类:{文档}})

クエリドキュメント:

基础查询
查询全部:		   db.集合.find();
查第一条:		   db.集合.findOne()
查询指定数量文档:	db.集合.find().limit(10)					//查10条文档
跳过指定数量文档:	db.集合.find().skip(20)					//跳过20条文档
统计:			  	db.集合.count()
排序:				db.集合.sort({age:1})						//按age升序排序
投影:				db.集合名称.find(条件,{name:1,age:1})		 //仅保留name与age域

条件查询
基本格式:			db.集合.find({条件})
模糊查询:			db.集合.find({域名:/正则表达式/})		  //等同SQL中的like,比like强大,可以执行正则所有规则
条件比较运算:		   db.集合.find({域名:{$gt:值}})				//等同SQL中的数值比较操作,例如:name>18
包含查询:			db.集合.find({域名:{$in:[值1,值2]}})		//等同于SQL中的in
条件连接查询:		   db.集合.find({$and:[{条件1},{条件2}]})	   //等同于SQL中的and、or

4.2.2.3. 統合

MongDB を springboot と統合するにはどうすればよいですか? 実際、springboot がこれほど多くの開発者を使用している理由は、彼のルーチンがほぼまったく同じであるためです。座標をインポートし、設定し、API インターフェイスを使用して操作します。Redis の統合にも同じことが当てはまり、MongoDB の統合にも同じことが当てはまります。

まず、対応するテクノロジーの統合スターター座標をインポートします

次に、必要な情報を設定します

第三に、提供された API を使用して操作します

Springboot を起動して MongoDB を統合しましょう。手順は次のとおりです。

ステップ①:SpringbootをインポートしてMongoDBのスターター座標を統合する

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

上記の座標はモジュール作成時にチェックを入れることで選択することもでき、NoSQLカテゴリにも属します

ステップ②:基本設定を行う

spring:
  data:
    mongodb:
      uri: mongodb://localhost/angyan

MongoDBを動作させるために必要な設定はredisを動作させるために必要な設定と同じであり、最も基本的な情報はどのサーバーを動作させるかですが、異なる点は接続するサーバーのIPアドレスとポートが異なることと、記述形式が異なることです。

ステップ③ : Springboot を使用して MongoDB の専用クライアント インターフェイス MongoTemplate を統合して動作させる

@SpringBootTest
class Springboot17MongodbApplicationTests {
    
    
    @Autowired
    private MongoTemplate mongoTemplate;
    @Test
    void contextLoads() {
    
    
        Book book = new Book();
        book.setId(2);
        book.setName("springboot2");
        book.setType("springboot2");
        book.setDescription("springboot2");
        mongoTemplate.save(book);
    }
    @Test
    void find(){
    
    
        List<Book> all = mongoTemplate.findAll(Book.class);
        System.out.println(all);
    }
}

要約する

  1. Springboot は MongoDB ステップを統合します
    1. SpringbootをインポートしてMongoDBのスターター座標を統合する
    2. 基本的な設定を実行する
    3. Springboot を使用して MongoDB の専用クライアント インターフェイス MongoTemplate 操作を統合する

4.2.3. SpringBoot は ES を統合します

ES (Elasticsearch) は、全文検索に重点を置いた分散型全文検索エンジンです。

では、全文検索とは何でしょうか? たとえば、ユーザーが本を購入したい場合、Java をキーワードとして検索します。本のタイトル、本の紹介、本の著者の名前にさえ含まれていれば、Java がキーワードとして検索されます。 Javaが含まれているため、クエリ結果としてユーザーに返されます。上記のプロセスは全文検索技術を使用しています。検索条件は、特定のフィールドを比較するためにのみ使用されるのではなく、1 つのデータ内のより多くのフィールドを比較するために使用されます。一致する限り、その検索条件はクエリ結果に含まれます。全文検索の目的。ES技術は、上記の効果を実現できる技術です。

全文検索の効果を得るには、データベース内で比較するような操作を使用することは不可能であり、あまりにも非効率的です。ES は、全文検索を実現するためのまったく新しいアイデアを設計しました。具体的な操作プロセスは次のとおりです。

  1. クエリされたフィールド内のデータのすべてのテキスト情報を確認し、複数の単語に分割します

    • たとえば、「中華人民共和国」は「中国」、「人民」、「共和国」の 3 つの単語に分割されますが、この処理を専門用語で単語分割と呼びます。異なる単語分割戦略は異なる分離効果を持ち、異なる単語分割戦略は単語分割デバイスと呼ばれます。
  2. 単語分割した結果を各データのidに対応付けて格納

    • たとえば、ID 1 のデータの名前項目の値は「中華人民共和国」であり、単語分割後、ID 1 に「中国」、ID 1 に「人民」、ID 1 に「共和国」が対応します。 ID 1 へ

    • たとえば、ID 2 のデータの名前項目の値は「人民会議」であり、単語の分割後、「人民」が ID 2、「代表」が ID 2、「議会」に対応することがわかります。 "はID 2に対応します

    • このとき、以下の対応する結果が表示され、上記の形式に従ってすべての文書をセグメント化できます。単語分割のプロセスは 1 つのフィールドだけではなく、クエリに参加するすべてのフィールドに対して実行され、最終結果は表にまとめられることに注意してください。

      単語分割結果のキーワード 対応するID
      中国 1
      人々 1、2
      共和国 1
      代表する 2
      大会 2
  3. クエリを実行する際、クエリ条件として「人」を入力すると、上記の表のデータを比較して id 値 1、2 を取得し、id 値に応じたクエリ結果データを取得できます。

上記のプロセスでは、単語分割結果のキーワード内容は異なりますが、その機能は、データクエリを高速化するために使用されるデータベースのインデックスに似ています。ただし、データベースにおけるインデックスとは、あるフィールドにインデックスを付けるものであり、ここでの単語分割結果のキーワードはフィールドの完全な値ではなく、フィールドの内容の一部にすぎません。また、インデックスを使用すると、インデックスの内容に従ってデータ全体が検索されますが、全文検索における単語分割結果のキーワードクエリでは、データ全体ではなく、データのIDが取得されます。特定のデータを取得したい場合は、再度クエリを実行する必要があるため、この単語分割結果のキーワードには、転置インデックスという新しい名前が付けられました

4.2.3.1. インストール

Windows版インストールパッケージのダウンロードアドレス:https://www.elastic.co/cn/downloads/elasticsearch

ダウンロードしたインストールパッケージはzipファイルになっており、解凍すると以下のファイルが得られます

ここに画像の説明を挿入

  • bin ディレクトリ: すべての実行可能なコマンドが含まれます
  • config ディレクトリ: ES サーバーによって使用される構成ファイルが含まれます
  • jdk ディレクトリ: このディレクトリには、完全な jdk ツールキット、バージョン 17 が含まれています。ES がアップグレードされるときは、バージョンのサポートが不十分であるという問題が発生しないように、最新バージョンの jdk を使用してください。
  • lib ディレクトリ: ES を実行するための依存 jar ファイルが含まれています
  • logs ディレクトリ: ES の実行後に生成されたすべてのログ ファイルが含まれます
  • modules ディレクトリ: ES ソフトウェアのすべての機能モジュールが含まれており、1 つずつの jar パッケージでもあります。jar ディレクトリとは異なり、jar ディレクトリは ES が動作中に依存する jar パッケージであり、モジュールは ES ソフトウェア自体の機能的な jar パッケージです。
  • plugins ディレクトリ: ES ソフトウェアによってインストールされたプラグインが含まれます。デフォルトは空です。

サーバーを起動する

elasticsearch.bat

elasticsearch.bat ファイルをダブルクリックして ES サーバーを起動します。デフォルトのサービス ポートは 9200 です。ブラウザ経由で http://localhost:9200 にアクセスし、次の情報を確認します。これは ES サーバーの通常の起動とみなされます。

{
    
    
  "name" : "CZBK-**********",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "j137DSswTPG8U4Yb-0T1Mg",
  "version" : {
    
    
    "number" : "7.16.2",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "2b937c44140b6559905130a8650c64dbd0879cfb",
    "build_date" : "2021-12-18T19:42:46.604893745Z",
    "build_snapshot" : false,
    "lucene_version" : "8.10.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

4.2.3.2. 基本操作

クエリしたいデータはESに保存されていますが、その形式はデータベースの保存データ形式とは異なります。ES では、最初に転置インデックスを作成する必要があります。このインデックスの機能はデータベースのテーブルと似ており、その後、転置インデックスにデータを追加します。追加されたデータはドキュメントと呼ばれます。したがって、ES 操作を実行するには、最初にインデックスを作成し、次にドキュメントを追加して、後続のクエリ操作を実行できるようにする必要があります。

ES は Rest スタイルのリクエストを通じて操作できます。つまり、リクエストを送信することで操作を実行できます。たとえば、インデックスの作成や削除などの操作をリクエストを送信する形で実行できます。

  • インデックスを作成します。books はインデックス名です。以下も同様です。

    PUT请求		http://localhost:9200/books
    

    リクエストを送信した後、次の情報が表示されれば、インデックスは正常に作成されています。

    {
          
          
        "acknowledged": true,
        "shards_acknowledged": true,
        "index": "books"
    }
    

    既存のインデックスを繰り返し作成するとエラー メッセージが表示され、エラーの理由はreason 属性に記述されます。

    {
          
          
        "error": {
          
          
            "root_cause": [
                {
          
          
                    "type": "resource_already_exists_exception",
                    "reason": "index [books/VgC_XMVAQmedaiBNSgO2-w] already exists",
                    "index_uuid": "VgC_XMVAQmedaiBNSgO2-w",
                    "index": "books"
                }
            ],
            "type": "resource_already_exists_exception",
            "reason": "index [books/VgC_XMVAQmedaiBNSgO2-w] already exists",	# books索引已经存在
            "index_uuid": "VgC_XMVAQmedaiBNSgO2-w",
            "index": "book"
        },
        "status": 400
    }
    
  • クエリインデックス

    GET请求		http://localhost:9200/books
    

    次のように、インデックスをクエリしてインデックスの関連情報を取得します。

    {
          
          
        "book": {
          
          
            "aliases": {
          
          },
            "mappings": {
          
          },
            "settings": {
          
          
                "index": {
          
          
                    "routing": {
          
          
                        "allocation": {
          
          
                            "include": {
          
          
                                "_tier_preference": "data_content"
                            }
                        }
                    },
                    "number_of_shards": "1",
                    "provided_name": "books",
                    "creation_date": "1645768584849",
                    "number_of_replicas": "1",
                    "uuid": "VgC_XMVAQmedaiBNSgO2-w",
                    "version": {
          
          
                        "created": "7160299"
                    }
                }
            }
        }
    }
    

    存在しないインデックスをクエリすると、エラー メッセージが返されます。たとえば、book という名前のインデックスをクエリした後の情報は次のようになります。

    {
          
          
        "error": {
          
          
            "root_cause": [
                {
          
          
                    "type": "index_not_found_exception",
                    "reason": "no such index [book]",
                    "resource.type": "index_or_alias",
                    "resource.id": "book",
                    "index_uuid": "_na_",
                    "index": "book"
                }
            ],
            "type": "index_not_found_exception",
            "reason": "no such index [book]",		# 没有book索引
            "resource.type": "index_or_alias",
            "resource.id": "book",
            "index_uuid": "_na_",
            "index": "book"
        },
        "status": 404
    }
    
  • インデックスの削除

    DELETE请求	http://localhost:9200/books
    

    全て削除後、削除結果を通知

    {
          
          
        "acknowledged": true
    }
    

    繰り返し削除した場合はエラーメッセージが表示され、エラーの具体的な原因もreason属性に記述されます。

    {
          
          
        "error": {
          
          
            "root_cause": [
                {
          
          
                    "type": "index_not_found_exception",
                    "reason": "no such index [books]",
                    "resource.type": "index_or_alias",
                    "resource.id": "book",
                    "index_uuid": "_na_",
                    "index": "book"
                }
            ],
            "type": "index_not_found_exception",
            "reason": "no such index [books]",		# 没有books索引
            "resource.type": "index_or_alias",
            "resource.id": "book",
            "index_uuid": "_na_",
            "index": "book"
        },
        "status": 404
    }
    
  • インデックスを作成してトークナイザーを指定する

    前に作成したインデックスではトークナイザーが指定されていません。インデックスの作成時にリクエスト パラメーターを追加してトークナイザーを設定できます。現在、中国で最も人気のあるワード ブレーカーは IK ワード ブレーカーです。使用する前に、対応するワード ブレーカーをダウンロードして使用してください。IK トークナイザーのダウンロード アドレス: https://github.com/medcl/elasticsearch-analysis-ik/releases

    ワード ブレーカーをダウンロードし、ES インストール ディレクトリの plugins ディレクトリに解凍し、ワード ブレーカーをインストールした後、ES サーバーを再起動する必要があります。IK トークナイザーを使用してインデックス形式を作成します。

    PUT请求		http://localhost:9200/books
    
    请求参数如下(注意是json格式的参数)
    {
          
          
        "mappings":{
          
          							#定义mappings属性,替换创建索引时对应的mappings属性		
            "properties":{
          
          						#定义索引中包含的属性设置
                "id":{
          
          							#设置索引中包含id属性
                    "type":"keyword"			#当前属性可以被直接搜索
                },
                "name":{
          
          						#设置索引中包含name属性
                    "type":"text",              #当前属性是文本信息,参与分词  
                    "analyzer":"ik_max_word",   #使用IK分词器进行分词             
                    "copy_to":"all"				#分词结果拷贝到all属性中
                },
                "type":{
          
          
                    "type":"keyword"
                },
                "description":{
          
          
                    "type":"text",	                
                    "analyzer":"ik_max_word",                
                    "copy_to":"all"
                },
                "all":{
          
          							#定义属性,用来描述多个字段的分词结果集合,当前属性可以参与查询
                    "type":"text",	                
                    "analyzer":"ik_max_word"
                }
            }
        }
    }
    

    作成完了後はトークナイザーを使用せずにインデックスを作成した結果と同じ結果が返されますが、このときインデックス情報を見ると、追加したリクエストパラメータのマッピングがインデックス属性に入っていることがわかります。

    {
          
          
        "books": {
          
          
            "aliases": {
          
          },
            "mappings": {
          
          						#mappings属性已经被替换
                "properties": {
          
          
                    "all": {
          
          
                        "type": "text",
                        "analyzer": "ik_max_word"
                    },
                    "description": {
          
          
                        "type": "text",
                        "copy_to": [
                            "all"
                        ],
                        "analyzer": "ik_max_word"
                    },
                    "id": {
          
          
                        "type": "keyword"
                    },
                    "name": {
          
          
                        "type": "text",
                        "copy_to": [
                            "all"
                        ],
                        "analyzer": "ik_max_word"
                    },
                    "type": {
          
          
                        "type": "keyword"
                    }
                }
            },
            "settings": {
          
          
                "index": {
          
          
                    "routing": {
          
          
                        "allocation": {
          
          
                            "include": {
          
          
                                "_tier_preference": "data_content"
                            }
                        }
                    },
                    "number_of_shards": "1",
                    "provided_name": "books",
                    "creation_date": "1645769809521",
                    "number_of_replicas": "1",
                    "uuid": "DohYKvr_SZO4KRGmbZYmTQ",
                    "version": {
          
          
                        "created": "7160299"
                    }
                }
            }
        }
    }
    

現時点ではすでにインデックスはありますが、インデックス内にデータが存在しないため、まずデータを追加する必要があります データのことをESではドキュメントと呼び、ドキュメント操作は以下で行われます。

  • ドキュメントを追加するには 3 つの方法があります

    POST请求	http://localhost:9200/books/_doc		#使用系统生成id
    POST请求	http://localhost:9200/books/_create/1	#使用指定id
    POST请求	http://localhost:9200/books/_doc/1		#使用指定id,不存在创建,存在更新(版本递增)
    
    文档通过请求参数传递,数据格式json
    {
          
          
        "name":"springboot",
        "type":"springboot",
        "description":"springboot"
    }  
    
  • クエリ文書

    GET请求	http://localhost:9200/books/_doc/1		 #查询单个文档 		
    GET请求	http://localhost:9200/books/_search		 #查询全部文档
    
  • 条件付きクエリ

    GET请求	http://localhost:9200/books/_search?q=name:springboot	# q=查询属性名:查询属性值
    
  • 文書を削除する

    DELETE请求	http://localhost:9200/books/_doc/1
    
  • ドキュメントを変更する (完全な更新)

    PUT请求	http://localhost:9200/books/_doc/1
    
    文档通过请求参数传递,数据格式json
    {
          
          
        "name":"springboot",
        "type":"springboot",
        "description":"springboot"
    }
    
  • 改訂されたドキュメント (部分的に更新)

    POST请求	http://localhost:9200/books/_update/1
    
    文档通过请求参数传递,数据格式json
    {
          
          			
        "doc":{
          
          						#部分更新并不是对原始文档进行更新,而是对原始文档对象中的doc属性中的指定属性更新
            "name":"springboot"		#仅更新提供的属性值,未提供的属性值不参与更新操作
        }
    }
    

4.2.3.3. 統合

ES を SpringBoot と統合するにはどうすればよいですか? 古いルールでは、座標をインポートし、設定を行い、API インターフェイスを使用して操作します。Redis の統合も同様であり、MongoDB の統合も同様であり、ES の統合も同様です。

Springboot を起動して ES を統合しましょう。手順は次のとおりです。

ステップ①:SpringbootをインポートしてESのスターター座標を統合する

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

ステップ②:基本設定を行う

spring:
  elasticsearch:
    rest:
      uris: http://localhost:9200

ESサーバーアドレス、ポート9200を構成します

ステップ③:springbootを使用してESの専用クライアントインターフェースElasticsearchRestTemplateを統合して動作させる

@SpringBootTest
class Springboot18EsApplicationTests {
    
    
    @Autowired
    private ElasticsearchRestTemplate template;
}

上記の動作モードは ES の初期の動作モードであり、使用されるクライアントは Low Level Client と呼ばれますが、このクライアント動作モードでは性能が若干不十分であるため、ES は High Level Client と呼ばれる新しいクライアント動作モードを開発しました。高レベル クライアントは ES バージョンと同期して更新されますが、springboot が最初に ES を統合したときは低レベル クライアントが使用されていたため、エンタープライズ開発は高レベル クライアント モデルに置き換える必要があります。

以下は、ハイレベルクライアントメソッドを使用して ES と springboot を統合する操作手順は次のとおりです。

ステップ①:SpringbootをインポートしてES上位クライアントの座標を統合する 現在、このフォームに対応するスターターはありません

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

ステップ②:プログラミングで接続するESサーバーを設定し、クライアントオブジェクトを取得する

@SpringBootTest
class Springboot18EsApplicationTests {
    
    
    private RestHighLevelClient client;
      @Test
      void testCreateClient() throws IOException {
    
    
          HttpHost host = HttpHost.create("http://localhost:9200");
          RestClientBuilder builder = RestClient.builder(host);
          client = new RestHighLevelClient(builder);
  
          client.close();
      }
}

ES サーバーのアドレスとポート 9200 を構成します。使用後はクライアントを手動で閉じる必要があることに注意してください。現在のクライアントは手動で管理されているため、オートワイヤリングを通じてオブジェクトをロードすることはできません。

ステップ③:クライアントオブジェクトを利用してインデックスの作成などESを操作する

@SpringBootTest
class Springboot18EsApplicationTests {
    
    
    private RestHighLevelClient client;
      @Test
      void testCreateIndex() throws IOException {
    
    
          HttpHost host = HttpHost.create("http://localhost:9200");
          RestClientBuilder builder = RestClient.builder(host);
          client = new RestHighLevelClient(builder);
          
          CreateIndexRequest request = new CreateIndexRequest("books");
          client.indices().create(request, RequestOptions.DEFAULT); 
          
          client.close();
      }
}

高レベルのクライアント操作はリクエストを送信することですべての操作を完了します。ES はさまざまな操作に対してさまざまなリクエスト オブジェクトを設定します。上記の例では、インデックスを作成するオブジェクトは CreateIndexRequest であり、他の操作にも独自の専用の Request オブジェクトがあります。

現在の操作では、どのような ES 操作が実行されるかに関係なく、最初のステップは常に RestHighLevelClient オブジェクトを取得することであり、最後のステップは常にオブジェクトの接続を閉じることであることがわかりました。テストでは、テスト クラスの特性を使用して、開発者が上記の操作を一度に完了できるように支援できますが、ビジネスを作成する場合は、自分で管理する必要があります。上記のコード形式を変換して、テストクラスの初期化メソッドと破棄メソッドを使用してクライアントオブジェクトを維持します。

@SpringBootTest
class Springboot18EsApplicationTests {
    
    
    @BeforeEach		//在测试类中每个操作运行前运行的方法
    void setUp() {
    
    
        HttpHost host = HttpHost.create("http://localhost:9200");
        RestClientBuilder builder = RestClient.builder(host);
        client = new RestHighLevelClient(builder);
    }

    @AfterEach		//在测试类中每个操作运行后运行的方法
    void tearDown() throws IOException {
    
    
        client.close();
    }

    private RestHighLevelClient client;

    @Test
    void testCreateIndex() throws IOException {
    
    
        CreateIndexRequest request = new CreateIndexRequest("books");
        client.indices().create(request, RequestOptions.DEFAULT);
    }
}

現在の記述ははるかに簡略化され、より合理的になっています。次に、上記のモードを使用してすべての ES 操作を再度実行し、結果をテストします。

インデックスを作成します (IK トークナイザー)

@Test
void testCreateIndexByIK() throws IOException {
    
    
    CreateIndexRequest request = new CreateIndexRequest("books");
    String json = "{\n" +
            "    \"mappings\":{\n" +
            "        \"properties\":{\n" +
            "            \"id\":{\n" +
            "                \"type\":\"keyword\"\n" +
            "            },\n" +
            "            \"name\":{\n" +
            "                \"type\":\"text\",\n" +
            "                \"analyzer\":\"ik_max_word\",\n" +
            "                \"copy_to\":\"all\"\n" +
            "            },\n" +
            "            \"type\":{\n" +
            "                \"type\":\"keyword\"\n" +
            "            },\n" +
            "            \"description\":{\n" +
            "                \"type\":\"text\",\n" +
            "                \"analyzer\":\"ik_max_word\",\n" +
            "                \"copy_to\":\"all\"\n" +
            "            },\n" +
            "            \"all\":{\n" +
            "                \"type\":\"text\",\n" +
            "                \"analyzer\":\"ik_max_word\"\n" +
            "            }\n" +
            "        }\n" +
            "    }\n" +
            "}";
    //设置请求中的参数
    request.source(json, XContentType.JSON);
    client.indices().create(request, RequestOptions.DEFAULT);
}

IK トークナイザーはリクエスト パラメーターの形式で設定されます。リクエスト パラメーターを設定するには、リクエスト オブジェクト内のソース メソッドを使用して設定します。パラメーターについては、操作の種類に応じて異なります。リクエストにパラメータが必要な場合、現在のフォームをパラメータの設定に使用できます。

ドキュメントを追加します

@Test
//添加文档
void testCreateDoc() throws IOException {
    
    
    Book book = bookDao.selectById(1);
    IndexRequest request = new IndexRequest("books").id(book.getId().toString());
    String json = JSON.toJSONString(book);
    request.source(json,XContentType.JSON);
    client.index(request,RequestOptions.DEFAULT);
}

ドキュメントの追加に使用されるリクエスト オブジェクトは IndexRequest であり、インデックスの作成に使用されるリクエスト オブジェクトとは異なります。

ドキュメントをバッチで追加するには:

@Test
//批量添加文档
void testCreateDocAll() throws IOException {
    
    
    List<Book> bookList = bookDao.selectList(null);
    BulkRequest bulk = new BulkRequest();
    for (Book book : bookList) {
    
    
        IndexRequest request = new IndexRequest("books").id(book.getId().toString());
        String json = JSON.toJSONString(book);
        request.source(json,XContentType.JSON);
        bulk.add(request);
    }
    client.bulk(bulk,RequestOptions.DEFAULT);
}

バッチ処理を実行するときは、最初に BulkRequest オブジェクトを作成します。これは、リクエスト オブジェクトを格納するためのコンテナとして理解できます。すべてのリクエストが初期化された後、それらを BulkRequest オブジェクトに追加し、その後、BulkRequest オブジェクトのBulk メソッドを使用します。終わらせる。

id でドキュメントをクエリします

@Test
//按id查询
void testGet() throws IOException {
    
    
    GetRequest request = new GetRequest("books","1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    String json = response.getSourceAsString();
    System.out.println(json);
}

ID に基づいてドキュメントをクエリするために使用されるリクエスト オブジェクトは GetRequest です。

条件によるドキュメントのクエリ:

@Test
//按条件查询
void testSearch() throws IOException {
    
    
    SearchRequest request = new SearchRequest("books");

    SearchSourceBuilder builder = new SearchSourceBuilder();
    builder.query(QueryBuilders.termQuery("all","spring"));
    request.source(builder);

    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    SearchHits hits = response.getHits();
    for (SearchHit hit : hits) {
    
    
        String source = hit.getSourceAsString();
        //System.out.println(source);
        Book book = JSON.parseObject(source, Book.class);
        System.out.println(book);
    }
}

条件によってドキュメントをクエリするために使用されるリクエスト オブジェクトは SearchRequest です。クエリを実行するときに SearchRequest オブジェクトの termQuery メソッドが呼び出され、クエリ属性の名前を指定する必要があります。ここでは、差し込みフィールドの使用がサポートされています。 all 属性は、index 属性を定義するときに追加されます。

要約する

  1. springboot 統合 ES の手順
    1. スプリングブートをインポートして ES の高レベル クライアントの座標を統合する
    2. 初期化やシャットダウン操作など、クライアント オブジェクトを手動で管理する
    3. ハイレベルクライアントを使用して、さまざまなリクエストオブジェクトを選択し、さまざまな種類の操作に応じて対応する操作を完了します。

5. サードパーティテクノロジーの統合

5.1、キャッシュ

エンタープライズレベルのアプリケーションの主な機能は情報処理ですが、データを読み取る必要がある場合、データベースへのアクセス効率が限られているため、システム全体のパフォーマンスが低下します。

アプリケーションがデータベースを直接扱うため、アクセス効率が低い

上記の現象を改善するために、開発者は通常、アプリケーションプログラムとデータベースの間に一時的なデータストレージメカニズムを確立し、この領域のデータはメモリに保存され、読み取りと書き込みの速度が速く、問題を効果的に解決できます。データベースのアクセス効率が低い問題についての質問です。この一時的にデータを保存する領域がキャッシュです。

  • キャッシュは、永続的なデータ記憶媒体とデータ アプリケーションの間の一時的なデータ記憶媒体です。

  • キャッシュを使用すると、低速データ読み取りプロセス (ディスク IO など) の数が効果的に削減され、システム パフォーマンスが向上します。

  • キャッシュを使用すると、永続ストレージ メディアからのデータ読み取りの効率が向上するだけでなく、一時的なデータ ストレージ スペースも提供されます。

5.1.1、SpringBoot 組み込みキャッシュ ソリューション

Springboot テクノロジは、開発者がキャッシュ テクノロジを迅速に有効にし、キャッシュされたデータの読み取りやキャッシュへのデータの書き込みなどの高速データ操作にキャッシュ テクノロジを使用できるようにする組み込みのキャッシュ ソリューションを提供します。

ステップ①:springbootが提供するキャッシュ技術に対応したスターターをインポートする

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

ステップ② : キャッシュを有効にし、ブート クラスの上に @EnableCaching アノテーションを付けて、キャッシュを使用するように Springboot プログラムを構成します

@SpringBootApplication
//开启缓存功能
@EnableCaching
public class Springboot19CacheApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springboot19CacheApplication.class, args);
    }
}

ステップ③:操作データにキャッシュを使用するかどうかを設定する

@Service
public class BookServiceImpl implements BookService {
    
    
    @Autowired
    private BookDao bookDao;

    @Cacheable(value="cacheSpace",key="#id")
    public Book getById(Integer id) {
    
    
        return bookDao.selectById(id);
    }
}

ビジネス メソッドで @Cacheable アノテーションを使用して、現在のメソッドの戻り値がキャッシュに配置されることを宣言します。キャッシュの保存場所が指定され、現在のメソッドの戻り値に対応する名前が保存されます。キャッシュ内にあります。上記の例では、value 属性はキャッシュの保存場所を記述しており、これは記憶域スペース名として理解でき、key 属性はキャッシュに保存されているデータの名前を記述しています。#id を使用して id 値を読み取ります。キャッシュ名としての仮パラメータ。

@Cacheable アノテーションを使用した後、現在の操作を実行します。対応する名前にキャッシュにデータがないことがわかった場合は、通常どおりデータを読み取ってキャッシュに入れます。対応する名前にキャッシュにデータがある場合は、現在のビジネス メソッドを実行し、キャッシュ内のデータを直接返します。

5.1.2. 携帯電話認証コードの場合

以下のさまざまなキャッシュ テクノロジのデモンストレーションを容易にするために、携帯電話認証コードのケース環境を作成し、キャッシュを使用して携帯電話認証コードを保存するプロセスをシミュレートします。

携帯電話認証コードの要件は次のとおりです。

  • 携帯電話番号を入力して認証コードを取得し、ユーザーに送信する文書をテキストメッセージ形式で整理します(ページシミュレーション)
  • 携帯電話番号と認証コードを入力して結果を確認します

上記の操作を説明するために、2 つのプレゼンテーション層インターフェイスを作成します。1 つは SMS の送信プロセスをシミュレートするために使用されます。実際には、ユーザーが提供した携帯電話番号に基づいて確認コードが生成され、キャッシュ、もう 1 つは検証コードの検証をシミュレートするために使用されます。プロセスは実際には、受信した携帯電話番号と検証コードを使用して照合し、最終的な照合結果を返します。このケースのシミュレーション コードは以下で直接生成され、上記の例で Springboot によって提供される組み込みキャッシュ テクノロジを使用して、現在のケースの生成が完了します。

ステップ①:springbootが提供するキャッシュ技術に対応したスターターをインポートする

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

ステップ② : キャッシュを有効にし、ブート クラスの上に @EnableCaching アノテーションを付けて、キャッシュを使用するように Springboot プログラムを構成します

@SpringBootApplication
//开启缓存功能
@EnableCaching
public class Springboot19CacheApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springboot19CacheApplication.class, args);
    }
}

ステップ③:認証コードに対応するエンティティクラスを定義し、携帯電話番号と認証コードの2つの属性をカプセル化する

@Data
public class SMSCode {
    
    
    private String tele;
    private String code;
}

ステップ④:ビジネス層インターフェースと検証コード機能の実装クラスを定義する

public interface SMSCodeService {
    
    
    public String sendCodeToSMS(String tele);
    public boolean checkCode(SMSCode smsCode);
}

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @Autowired
    private CodeUtils codeUtils;

    @CachePut(value = "smsCode", key = "#tele")
    public String sendCodeToSMS(String tele) {
    
    
        String code = codeUtils.generator(tele);
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
    
    
        //取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
        String code = smsCode.getCode();
        String cacheCode = codeUtils.get(smsCode.getTele());
        return code.equals(cacheCode);
    }
}

検証コードを取得した後、検証コードが無効になった場合には再度検証コードを取得する必要があるため、検証コードを取得する機能では @Cacheable アノテーションを使用することはできません。キャッシュに値が入れられ、キャッシュに値がある場合はその値が取得されます。ここでの機能は検証コードを生成してキャッシュに入れるだけであり、キャッシュから値を取得する機能はないため、 @Cacheable アノテーションは使用できず、保存する機能だけあればよい@CachePut アノテーションを使用して、キャッシュ内のデータ。

検証コードを検証する機能については、ツールクラスに入れることを推奨します。

ステップ⑤:認証コードの生成戦略と携帯電話番号に応じた認証コードの読み取り機能を定義する

@Component
public class CodeUtils {
    
    
    private String [] patch = {
    
    "000000","00000","0000","000","00","0",""};

    public String generator(String tele){
    
    
        int hash = tele.hashCode();
        int encryption = 20206666;
        long result = hash ^ encryption;
        long nowTime = System.currentTimeMillis();
        result = result ^ nowTime;
        long code = result % 1000000;
        code = code < 0 ? -code : code;
        String codeStr = code + "";
        int len = codeStr.length();
        return patch[len] + codeStr;
    }

    @Cacheable(value = "smsCode",key="#tele")
    public String get(String tele){
    
    
        return null;
    }
}

ステップ⑥:検証コード機能のWeb層インターフェースを定義します。1つの方法は検証コードを取得するために携帯電話番号を提供するために使用され、もう1つの方法は検証のための携帯電話番号と検証コードを提供するために使用されます。

@RestController
@RequestMapping("/sms")
public class SMSCodeController {
    
    
    @Autowired
    private SMSCodeService smsCodeService;
    
    @GetMapping
    public String getCode(String tele){
    
    
        String code = smsCodeService.sendCodeToSMS(tele);
        return code;
    }
    
    @PostMapping
    public boolean checkCode(SMSCode smsCode){
    
    
        return smsCodeService.checkCode(smsCode);
    }
}

5.1.3、SpringBoot は Ehcache キャッシュを統合します

携帯電話認証コードのケースは完了し、springboot はさまざまなキャッシュ技術の統合を開始し、初めて Ehcache 技術を統合します。Ehcache はキャッシュ テクノロジであり、springboot を使用して Ehcache を統合すると、キャッシュ テクノロジの実装が実際に変わります。

ステップ①:Ehcacheの座標をインポートする

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

ここで Ehcache スターターをインポートせず、技術座標をインポートするのはなぜですか? 実は、springboot の統合キャッシュ技術は汎用的な形式であり、どのキャッシュ技術を統合しても実装が変わるだけで、運用方法は同じである。これは、スプリングブート テクノロジーの利点も反映しており、同様のテクノロジーの統合方法を統一します。

ステップ② : Ehcache を使用するようにキャッシュ テクノロジを構成する

spring:
  cache:
    type: ehcache
    ehcache:
      config: ehcache.xml

構成キャッシュのタイプは ehcache ですが、ここで Springboot が統合できる現在のキャッシュ テクノロジには ehcache が含まれているため、このように記述できることを説明する必要があります。実はこのタイプは気軽に書くことができず、ただ名前を書くだけでは統合できません。

ehcacheの設定は独立した設定ファイル形式を持っているため、対応する設定を読み込むためにはehcacheの設定ファイルも指定する必要があります

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <diskStore path="D:\ehcache" />

    <!--默认缓存策略 -->
    <!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
    <!-- diskPersistent:是否启用磁盘持久化-->
    <!-- maxElementsInMemory:最大缓存数量-->
    <!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
    <!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果,可用于记录时效性数据,例如验证码-->
    <!-- timeToLiveSeconds:最大存活时间-->
    <!-- memoryStoreEvictionPolicy:缓存清除策略-->
    <defaultCache
        eternal="false"
        diskPersistent="false"
        maxElementsInMemory="1000"
        overflowToDisk="false"
        timeToIdleSeconds="60"
        timeToLiveSeconds="60"
        memoryStoreEvictionPolicy="LRU" />

    <cache
        name="smsCode"
        eternal="false"
        diskPersistent="false"
        maxElementsInMemory="1000"
        overflowToDisk="false"
        timeToIdleSeconds="10"
        timeToLiveSeconds="10"
        memoryStoreEvictionPolicy="LRU" />
</ehcache>

前のケースでは、データが保存される場所が smsCode に設定されていることに注意してください

@CachePut(value = "smsCode", key = "#tele")
public String sendCodeToSMS(String tele) {
    
    
    String code = codeUtils.generator(tele);
    return code;
}	

この設定では、ehcache 内に smsCode 構成というキャッシュ スペース名が存在することを確認する必要があり、前後で統一する必要があります。エンタープライズ開発プロセスでは、異なるキャッシュ データに適用される異なる名前のキャッシュを設定することによって、異なるキャッシュ戦略が設定されます。

この時点で、Ehcache の springboot 統合は完了しました。元のコードは変更されていないことがわかります。一連の設定を追加するだけで、キャッシュ プロバイダーを変更できます。これは、統一されたキャッシュ操作を提供する springboot の利点でもあります実装の変更 元のコードの記述には影響しません。

要約する

  1. Springboot は Ehcache をキャッシュとして使用し、Ehcache にインポートする必要がある座標を実装します。
  2. 設定を変更し、キャッシュプロバイダーを ehcache として構成し、対応するキャッシュ構成ファイルを提供します。

5.1.4、SpringBoot は Redis キャッシュを統合します

前のセクションでは、springboot の組み込みキャッシュ テクノロジを置き換えるために Ehcache が使用されました。実際、springboot でサポートされているキャッシュ テクノロジは数多くあります。以下では、携帯電話認証コードのケースを実現するためのキャッシュ ソリューションとして redis テクノロジを使用します。

Ehcache を使用するプロセスを比較し、座標を追加し、キャッシュ実装タイプを ehcache に変更し、Ehcache を構成します。キャッシュ用の Redis としても使用するとどうなるでしょうか? 全く同じで、座標を追加し、キャッシュ実装タイプをredisに変更し、redisを設定します。唯一の違いは、別の構成ファイルを作成せずに、redis の構成を yml ファイルで直接構成できることです。

ステップ①:redisの座標をインポートする

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

ステップ② : Redis を使用するようにキャッシュテクノロジーを構成する

spring:
  redis:
    host: localhost
    port: 6379
  cache:
    type: redis

Redis をキャッシュとして構成する必要がある場合は、元の Redis を構成するのではなく、spring.cache.redis ノードに属する関連構成を使用するキャッシュとして Redis を構成し、間違った場所。

spring:
  redis:
    host: localhost
    port: 6379
  cache:
    type: redis
    redis:
      use-key-prefix: false
      key-prefix: sms_
      cache-null-values: false
      time-to-live: 10s

要約する

  1. Springboot は、redis にインポートする必要がある座標を実装するためのキャッシュとして redis を使用します。
  2. 設定を変更し、キャッシュプロバイダーを redis として構成し、対応するキャッシュ構成を提供します。

5.1.5、SpringBoot は Memcached キャッシュを統合します

現在、springboot 組み込みキャッシュ、ehcache、redis の 3 つのキャッシュ ソリューションの構成形式をマスターしましたので、次は中国で人気のあるキャッシュである memcached について勉強してみましょう。

前のルーチンによると、キャッシュの変更は実際には面倒ではありませんが、springboot はキャッシュ ソリューションとして memcached の使用をサポートしていません。つまり、type 属性には memcached 構成オプションがなく、処理メソッドは次のことを行う必要があります。ここで変更されます。統合する前に、まず memcached をインストールします。

インストール

Windows版インストールパッケージダウンロードアドレス:https://www.runoob.com/memcached/window-install-memcached.html

ダウンロードしたインストールパッケージはzipファイルになっており、解凍すると以下のファイルが得られます

ここに画像の説明を挿入

実行可能ファイルには、memcached をシステム サービスとして起動するために使用できる memcached.exe が 1 つだけあり、このファイルを実行すると、次のようなエラー メッセージが表示されます。

ここに画像の説明を挿入

ここで問題が発生する理由は、システム サービスを登録するときに管理者権限を使用する必要があるためです。現在のアカウント権限では不十分であり、サービスのインストールは失敗します。管理者アカウント権限を切り替えてコマンド ラインを開始してください。

次に、次のようにコマンドを実行してサービスを再度インストールします。

memcached.exe -d install

サービスのインストール後、次のようにコマンドを使用してサービスを開始および停止できます。

memcached.exe -d start		# 启动服务
memcached.exe -d stop		# 停止服务

タスクマネージャーでサービスのステータスを切り替えることもできます

キャッシュを Memcached に変更する

memcached は springboot にキャッシュ ソリューションとして含まれていないため、memcached を使用するには手動でハードコーディングする必要があるため、以前のルーチンは適用できず、自分で記述する必要があります。

memcached は現在、Memcached Client for Java、SpyMemcached、Xmemcached の 3 つのクライアント テクノロジを提供しています。その中で、Xmemcached は最も優れたパフォーマンス指標を持つクライアントであり、今回の統合ではこれをクライアント実装テクノロジとして使用します。Xmemcached を使ってみましょう

ステップ①:xmemcachedの座標をインポートする

<dependency>
    <groupId>com.googlecode.xmemcached</groupId>
    <artifactId>xmemcached</artifactId>
    <version>2.4.7</version>
</dependency>

ステップ②:memcachedの設定、memcached設定クラスの作成

@Configuration
public class XMemcachedConfig {
    
    
    @Bean
    public MemcachedClient getMemcachedClient() throws IOException {
    
    
        MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder("localhost:11211");
        MemcachedClient memcachedClient = memcachedClientBuilder.build();
        return memcachedClient;
    }
}

memcached のデフォルトの外部サービス ポートは 11211 です。

ステップ③ : xmemcached クライアントを使用してキャッシュを操作し、MemcachedClient オブジェクトを注入する

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @Autowired
    private CodeUtils codeUtils;
    @Autowired
    private MemcachedClient memcachedClient;

    public String sendCodeToSMS(String tele) {
    
    
        String code = codeUtils.generator(tele);
        try {
    
    
            memcachedClient.set(tele,10,code);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
    
    
        String code = null;
        try {
    
    
            code = memcachedClient.get(smsCode.getTele()).toString();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return smsCode.getCode().equals(code);
    }
}

set オペレーションを使用して値をキャッシュに設定し、get オペレーションを使用して値を取得します。これは、実際には開発者の習慣により一致しています。

上記のコードでは、サーバーの構成がコードにハードコーディングされ、データが抽出されて独立した構成属性に作成されます。

構成プロパティを定義する

次の処理は前期で学習した属性設定方法を使用して実行されますが、今回の操作は原理に関する多くの知識を理解するのに役立ちます。

  • 構成クラスを定義し、必要な構成属性をロードし、構成ファイル内の memcached ノード情報を読み取ります。

    @Component
    @ConfigurationProperties(prefix = "memcached")
    @Data
    public class XMemcachedProperties {
          
          
        private String servers;
        private int poolSize;
        private long opTimeout;
    }
    
  • memcached ノード情報の定義

    memcached:
      servers: localhost:11211
      poolSize: 10
      opTimeout: 3000
    
  • memcached 構成クラスに情報をロードする

@Configuration
public class XMemcachedConfig {
    
    
    @Autowired
    private XMemcachedProperties props;
    @Bean
    public MemcachedClient getMemcachedClient() throws IOException {
    
    
        MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(props.getServers());
        memcachedClientBuilder.setConnectionPoolSize(props.getPoolSize());
        memcachedClientBuilder.setOpTimeout(props.getOpTimeout());
        MemcachedClient memcachedClient = memcachedClientBuilder.build();
        return memcachedClient;
    }
}

要約する

  1. memcached のインストール後、外部からキャッシュ機能を提供するために対応するサービスを開始する必要があります。memcached サービスのインストールには Windows システム管理者の権限が必要です。
  2. springboot は memcached のキャッシュ統合ソリューションを提供していないため、手動コーディングの形式で xmemcached クライアント操作キャッシュを作成する必要があります。
  3. xmemcached 座標をインポートした後、memcached 構成クラスを作成し、キャッシュを操作するための MemcachedClient に対応する Bean を登録します
  4. MemcachedClient オブジェクトの初期化に必要なプロパティは、カスタム構成プロパティ クラスの形式でロードできます。

5.1.6、SpringBoot 統合 Jetcache キャッシュ

現在使用しているキャッシュはAかBのどちらかですが、ABも併用できますか?今すぐ修正してください。springboot のキャッシュへの統合はキャッシュ上にのみ残るため、キャッシュ自体が AB の使用を同時にサポートしていない場合、springboot はそれについて何もできません。キャッシュを併用するには、AB と AB の両方をサポートできるキャッシュを見つける必要があります。キャッシュと併用する場合、そのようなキャッシュはありますか? Ali によって作成された、jetcache が実際に存在します。

厳密に言えば、jetcache はキャッシュ ソリューションではなく、キャッシング フレームワークであり、管理のために他のキャッシュを Jetcache に入れて、AB キャッシュの使用をサポートできるとしか言えません。そして、jetcache は springboot 統合キャッシュのアイデアを指し、全体的なテクノロジーの使用法は springboot のキャッシュ ソリューションのアイデアと非常に似ています。まず Jetcache を使用してから、その中のいくつかの小さな関数について説明します。

実行する前に、jetcache では 2 つのキャッシュを取得してそれらを組み合わせることができないことを明確にしておきます。現在、jetcache は、ローカル キャッシュとリモート キャッシュという 2 種類のキャッシュ スキームをサポートしています。次のとおりです。

  • ローカルキャッシュ (ローカル)
    • リンクされたハッシュマップ
    • カフェイン
  • リモートキャッシュ (リモート)
    • レディス
    • 三つ

実際、何人かの人々から、なぜ Jetcache は 2+2 キャッシュのみをサポートするのですか? と尋ねられました。Ali は主に自社のニーズを満たすためにこのテクノロジーを開発しました。最初は 1+1 だけですが、徐々に 2+2 に変更してください。以下では、LinkedHashMap+Redis ソリューションを使用して、ローカル キャッシュ ソリューションとリモート キャッシュ ソリューションの同時使用を実現します。

5.1.6.1、純粋なリモート ソリューション

ステップ① : springboot をインポートして、jetcache に対応する座標スターターを統合します。現在の座標のデフォルトのリモート ソリューションは redis です。

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.6.2</version>
</dependency>

ステップ②:リモートソリューションの基本構成

jetcache:
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50

このうち、poolConfigは必須項目です。そうでない場合はエラーが報告されます

ステップ 3 : キャッシュを有効にし、ブート クラスの上にある注釈 @EnableCreateCacheAnnotation をマークして、注釈の形式でキャッシュを作成するように Springboot プログラムを構成します。

@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
public class Springboot20JetCacheApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springboot20JetCacheApplication.class, args);
    }
}

ステップ④ : キャッシュオブジェクト Cache を作成し、アノテーション @CreateCache を使用して現在のキャッシュ情報をマークし、Cache オブジェクトの API を使用してキャッシュを操作し、書き込みキャッシュを配置し、読み取りキャッシュを取得します。

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @Autowired
    private CodeUtils codeUtils;
    
    @CreateCache(name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS)
    private Cache<String ,String> jetCache;

    public String sendCodeToSMS(String tele) {
    
    
        String code = codeUtils.generator(tele);
        jetCache.put(tele,code);
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
    
    
        String code = jetCache.get(smsCode.getTele());
        return smsCode.getCode().equals(code);
    }
}

リモート ソリューションを使用して Redis に接続する上記の Jetcache を通じて、キャッシュを操作するときの JetCache のインターフェイス操作が開発者の習慣により一致していることがわかります。キャッシュを使用するときは、まずキャッシュ オブジェクト Cache を取得します。 、データを入力し、データを取り出すのは、よりシンプルで簡単です。また、jetcache でキャッシュを操作する場合、特定のキャッシュ オブジェクトに有効期限を設定し、同じ種類のデータをキャッシュに入れることで有効期間の管理が容易になります。

上記のスキームは、設定で定義されたデフォルトのキャッシュを使用しますが、実際には、このデフォルトは名前であり、自由に書き込んだり追加したりできます。たとえば、別のキャッシュ ソリューションを追加するには、次の構成を参照してください。

jetcache:
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50
    sms:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50

smsという名前のキャッシュを使用したい場合は、キャッシュ作成時にパラメータ領域を指定し、対応するキャッシュを使用することを宣言する必要があります

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @Autowired
    private CodeUtils codeUtils;
    
    @CreateCache(area="sms",name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS)
    private Cache<String ,String> jetCache;

    public String sendCodeToSMS(String tele) {
    
    
        String code = codeUtils.generator(tele);
        jetCache.put(tele,code);
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
    
    
        String code = jetCache.get(smsCode.getTele());
        return smsCode.getCode().equals(code);
    }
}

5.1.6.2、純粋なローカル ソリューション

リモート ソリューションでは、構成内でリモートを示すためにリモートが使用され、ローカルはローカルを示しますが、タイプが異なります。

ステップ①:springbootをインポートしてjetcache starterに対応する座標を統合する

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.6.2</version>
</dependency>

ステップ②:ローカルキャッシュの基本設定

jetcache:
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson

データ取得時のキー照合速度を高速化するために、jetcache には指定されたキー タイプ コンバーターが必要です。簡単に言うと、オブジェクトをキーとして指定した場合、キー型コンバーターを使用して文字列に変換してから保存します。データが取得されると、最初に指定されたオブジェクトを使用して文字列に変換され、次に文字列に従って照合されます。Jetcache は Ali のテクノロジーであるため、キータイプコンバータには Ali の fastjson を使用することをお勧めします。

ステップ③:キャッシュを有効にする

@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
public class Springboot20JetCacheApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springboot20JetCacheApplication.class, args);
    }
}

ステップ④ : キャッシュ オブジェクト Cache を作成するときに、ローカル キャッシュの現在の使用状況をマークします。

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL)
    private Cache<String ,String> jetCache;

    public String sendCodeToSMS(String tele) {
    
    
        String code = codeUtils.generator(tele);
        jetCache.put(tele,code);
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
    
    
        String code = jetCache.get(smsCode.getTele());
        return smsCode.getCode().equals(code);
    }
}

cacheType は、現在のキャッシュがローカル キャッシュを使用するかリモート キャッシュを使用するかを制御します。cacheType=CacheType.LOCAL を構成すると、ローカル キャッシュを使用することになります。

5.1.6.3、ローカル + リモート ソリューション

ローカルとリモートの両方の方法がありますが、2 つのソリューションを一緒に構成するにはどうすればよいですか? 実際には、2 つの構成を組み合わせるだけで十分です。

jetcache:
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50
    sms:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50

キャッシュを作成するときは、cacheType を BOTH に設定します。つまり、ローカル キャッシュとリモート キャッシュが同時に使用されます。

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.BOTH)
    private Cache<String ,String> jetCache;
}

CacheType が構成されていない場合、デフォルト値は REMOTE です。つまり、リモート キャッシュ ソリューションのみが使用されます。Jetcache の設定については、以下の情報を参照してください。

属性 デフォルト 説明する
Jetcache.statIntervalMinutes 0 統計間隔。0 は統計がないことを意味します
Jetcache.hiddenパッケージ なし 名前を自動生成するときに、指定されたパッケージ名のプレフィックスを非表示にします
Jetcache.[ローカル|リモート].${エリア}.type なし キャッシュタイプ、ローカルサポートlinkedhashmap、カフェイン、リモートサポートredis、tair
Jetcache.[ローカル|リモート].${エリア}.keyConvertor なし キーコンバーター、現在は fastjson のみをサポートしています
Jetcache.[ローカル|リモート].${エリア}.valueEncoder ジャワ リモート タイプ キャッシュのみを指定する必要があります。java と kryo はオプションです
Jetcache.[ローカル|リモート].${エリア}.valueDecoder ジャワ リモート タイプ キャッシュのみを指定する必要があります。java と kryo はオプションです
Jetcache.[ローカル|リモート].${エリア}.limit 100 ローカル タイプのキャッシュのみを指定する必要があり、キャッシュ インスタンスの最大要素数も指定する必要があります。
Jetcache.[ローカル|リモート].${エリア}.expireAfterWriteInMillis 无穷大 デフォルトの有効期限 (ミリ秒単位)
Jetcache.local.${エリア}.expireAfterAccessInMillis 0 ローカル キャッシュのみが有効です (ミリ秒単位の最大非アクティブ間隔)

上記のスキームはキャッシュの手動制御のみをサポートしていますが、springcache スキームのメソッド キャッシュは特に便利で、メソッドにアノテーションを追加すると、メソッドは自動的にキャッシュを使用します。Jetcache は、対応する機能、つまりメソッド キャッシュも提供します。

メソッドキャッシュ

Jetcache はメソッド キャッシュ ソリューションを提供しますが、名前が変更されました。対応する操作インターフェイスの上に @Cached 注釈を使用するだけです

ステップ①:springbootをインポートしてjetcache starterに対応する座標を統合する

<dependency>
    <groupId>com.alicp.jetcache</groupId>
    <artifactId>jetcache-starter-redis</artifactId>
    <version>2.6.2</version>
</dependency>

ステップ②:キャッシュを設定する

jetcache:
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
  remote:
    default:
      type: redis
      host: localhost
      port: 6379
      keyConvertor: fastjson
      valueEncode: java
      valueDecode: java
      poolConfig:
        maxTotal: 50
    sms:
      type: redis
      host: localhost
      port: 6379
      poolConfig:
        maxTotal: 50

Redisキャッシュはオブジェクトの保存をサポートしていないため、オブジェクト型のデータがredisに入ったときにどのように型変換を行うかをredis側で設定する必要があります。keyConvertor でキーの型変換方法を示すように設定する必要があり、同時に値の変換型をマークする必要があります。値が redis に入るとき、それは java 型であり、valueEncode を java としてマークし、値を に変換します。 Redis から読み取る場合は java を使用し、valueDecode を java としてマークします。

Redis に出入りするオブジェクト タイプの値を実現するには、Redis に出入りするオブジェクト タイプ データがシリアル化インターフェイスを実装していることを確認する必要があることに注意してください。

@Data
public class Book implements Serializable {
    
    
    private Integer id;
    private String type;
    private String name;
    private String description;
}

ステップ③:キャッシュが有効な場合にメソッドキャッシュ機能を有効にし、どのパッケージでメソッドキャッシュが有効であるかを示すようにbasePackagesを設定します。

@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
//开启方法注解缓存
@EnableMethodCache(basePackages = "com.test")
public class Springboot20JetCacheApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(Springboot20JetCacheApplication.class, args);
    }
}

ステップ④ : アノテーション @Cached を使用して、キャッシュを使用している現在のメソッドをマークします。

@Service
public class BookServiceImpl implements BookService {
    
    
    @Autowired
    private BookDao bookDao;
    
    @Override
    @Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
    public Book getById(Integer id) {
    
    
        return bookDao.selectById(id);
    }
}

5.1.6.4. リモートソリューションのデータ同期

リモートソリューションではredisで保存したデータを複数のクライアントで共有できるため、データの同期が問題となります。Jetcache は、この問題を解決するために 3 つのアノテーションを提供します。それぞれ、更新および削除操作中にキャッシュ データを同期し、キャッシュを読み取るときにデータを定期的にリフレッシュします。

キャッシュを更新する

@CacheUpdate(name="book_",key="#book.id",value="#book")
public boolean update(Book book) {
    
    
    return bookDao.updateById(book) > 0;
}

キャッシュを削除する

@CacheInvalidate(name="book_",key = "#id")
public boolean delete(Integer id) {
    
    
    return bookDao.deleteById(id) > 0;
}

キャッシュを定期的に更新する

@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
@CacheRefresh(refresh = 5)
public Book getById(Integer id) {
    
    
    return bookDao.selectById(id);
}

5.1.6.5、データレポート

Jetcache は、開発者が設定を追加するだけでキャッシュ ヒット情報をすばやく確認できるようにするためのシンプルなデータ レポート機能も提供します。

jetcache:
  statIntervalMinutes: 1

設定後、1分ごとにキャッシュデータのヒット情報がコンソールに出力されます

[DefaultExecutor] c.alicp.jetcache.support.StatInfoLogger  : jetcache stat from 2022-02-28 09:32:15,892 to 2022-02-28 09:33:00,003
cache    |    qps|   rate|   get|    hit|   fail|   expire|   avgLoadTime|   maxLoadTime
---------+-------+-------+------+-------+-------+---------+--------------+--------------
book_    |   0.66| 75.86%|    29|     22|      0|        0|          28.0|           188
---------+-------+-------+------+-------+-------+---------+--------------+--------------

要約する

  1. Jetcache は springcache と同様のキャッシュ ソリューションですが、それ自体にキャッシュ機能はなく、ローカル キャッシュとリモート キャッシュの両方を複数のレベルで使用するキャッシュ ソリューションを提供します。
  2. Jetcache が提供するキャッシュ ソリューションは、現在サポートされているスキームによって制限されており、ローカル キャッシュは 2 つのタイプをサポートし、リモート キャッシュは 2 つのタイプをサポートします。
  3. データがリモート キャッシュに入るときの型変換の問題に注意してください
  4. Jetcache はメソッドのキャッシュを提供し、対応するキャッシュの更新およびリフレッシュ機能を提供します。
  5. Jetcache は、開発者がキャッシュ データ ヒットをリアルタイムで監視できるよう、シンプルなキャッシュ情報ヒット レポートを提供します。

5.1.7、SpringBoot 統合 j2cache キャッシュ

Jetcache は限られた範囲内で多層キャッシュを構築できますが、キャッシュを自由に合わせることができるほど柔軟性がありません。ここで、キャッシュ ソリューションを自由に合わせることができるキャッシュ統合フレームワーク、j2cache を紹介します。Ehcache と redis の統合を例として、このキャッシュ フレームワークの使用方法を説明しましょう。

ステップ① : j2cache、redis、ehcache 座標をインポートする

<dependency>
    <groupId>net.oschina.j2cache</groupId>
    <artifactId>j2cache-core</artifactId>
    <version>2.8.4-release</version>
</dependency>
<dependency>
    <groupId>net.oschina.j2cache</groupId>
    <artifactId>j2cache-spring-boot2-starter</artifactId>
    <version>2.8.0-release</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

j2cache のスターターにはデフォルトで redis 座標が含まれており、二次キャッシュとして redis を使用することが公式に推奨されているため、ここで redis 座標をインポートする必要はありません

手順②:1次キャッシュと2次キャッシュの設定、および1次キャッシュと2次キャッシュ間のデータ転送方法を設定する設定は、j2cache.propertiesというファイルに記述されます。ehcache を使用する場合は、ehcache 構成ファイルを別途追加する必要があります

# 1级缓存
j2cache.L1.provider_class = ehcache
ehcache.configXml = ehcache.xml

# 2级缓存
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379

# 1级缓存中的数据如何到达二级缓存
j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy

ここでの構成は無差別に構成することはできません。公式の構成手順を参照する必要があります。たとえば、第 1 レベルのサプライヤーが ehcache を選択すると、サプライヤー名は単なる ehcache になりますが、第 2 レベルのサプライヤーが Redis を選択すると、専用の Spring 統合 Redis サプライヤー クラス名 SpringRedisProvider を記述する必要があり、この名前はすべての redis パッケージで利用できますが、Spring パッケージでは提供されていません。したがって、j2cache を構成するには、公式ドキュメントの構成を参照する必要があり、使用する前に専用の統合パッケージを見つけて対応する座標をインポートする必要があります。

一次キャッシュと二次キャッシュの最も重要な構成の 1 つは、2 つのキャッシュ間のデータ通信方法です。このタイプの構成は任意ではなく、異なるキャッシュ ソリューションによって提供されるデータ通信方法は大きく異なります。公式ドキュメントを確認して設定してください。

ステップ③:キャッシュを利用する

@Service
public class SMSCodeServiceImpl implements SMSCodeService {
    
    
    @Autowired
    private CodeUtils codeUtils;

    @Autowired
    private CacheChannel cacheChannel;

    public String sendCodeToSMS(String tele) {
    
    
        String code = codeUtils.generator(tele);
        cacheChannel.set("sms",tele,code);
        return code;
    }

    public boolean checkCode(SMSCode smsCode) {
    
    
        String code = cacheChannel.get("sms",smsCode.getTele()).asString();
        return smsCode.getCode().equals(code);
    }
}

j2cache の使い方は、jetcache と似ていますが、使用するためにスイッチをオンにする必要はなく、キャッシュ オブジェクトを直接定義することで使用できます。キャッシュ オブジェクトの名前は CacheChannel です。

j2cache の使用は複雑ではありません。構成は j2cache の核心であり、結局のところ、j2cache は統合されたキャッシュ フレームワークです。キャッシュ関連の設定が多すぎるため、j2cache-core コア パッケージの j2cache.properties ファイルの説明を参照してください。次のように:

#J2Cache configuration
#########################################
# Cache Broadcast Method
# values:
# jgroups -> use jgroups's multicast
# redis -> use redis publish/subscribe mechanism (using jedis)
# lettuce -> use redis publish/subscribe mechanism (using lettuce, Recommend)
# rabbitmq -> use RabbitMQ publisher/consumer mechanism
# rocketmq -> use RocketMQ publisher/consumer mechanism
# none -> don't notify the other nodes in cluster
# xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy
#########################################
j2cache.broadcast = redis

# jgroups properties
jgroups.channel.name = j2cache
jgroups.configXml = /network.xml

# RabbitMQ properties
rabbitmq.exchange = j2cache
rabbitmq.host = localhost
rabbitmq.port = 5672
rabbitmq.username = guest
rabbitmq.password = guest

# RocketMQ properties
rocketmq.name = j2cache
rocketmq.topic = j2cache
# use ; to split multi hosts
rocketmq.hosts = 127.0.0.1:9876

#########################################
# Level 1&2 provider
# values:
# none -> disable this level cache
# ehcache -> use ehcache2 as level 1 cache
# ehcache3 -> use ehcache3 as level 1 cache
# caffeine -> use caffeine as level 1 cache(only in memory)
# redis -> use redis as level 2 cache (using jedis)
# lettuce -> use redis as level 2 cache (using lettuce)
# readonly-redis -> use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available.
# memcached -> use memcached as level 2 cache (xmemcached),
# [classname] -> use custom provider
#########################################

j2cache.L1.provider_class = caffeine
j2cache.L2.provider_class = redis

# When L2 provider isn't `redis`, using `L2.config_section = redis` to read redis configurations
# j2cache.L2.config_section = redis

# Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true)
# NOTICE: redis hash mode (redis.storage = hash) do not support this feature)
j2cache.sync_ttl_to_redis = true

# Whether to cache null objects by default (default false)
j2cache.default_cache_null_object = true

#########################################
# Cache Serialization Provider
# values:
# fst -> using fast-serialization (recommend)
# kryo -> using kryo serialization
# json -> using fst's json serialization (testing)
# fastjson -> using fastjson serialization (embed non-static class not support)
# java -> java standard
# fse -> using fse serialization
# [classname implements Serializer]
#########################################

j2cache.serialization = json
#json.map.person = net.oschina.j2cache.demo.Person

#########################################
# Ehcache configuration
#########################################

# ehcache.configXml = /ehcache.xml

# ehcache3.configXml = /ehcache3.xml
# ehcache3.defaultHeapSize = 1000

#########################################
# Caffeine configuration
# caffeine.region.[name] = size, xxxx[s|m|h|d]
#
#########################################
caffeine.properties = /caffeine.properties

#########################################
# Redis connection configuration
#########################################

#########################################
# Redis Cluster Mode
#
# single -> single redis server
# sentinel -> master-slaves servers
# cluster -> cluster servers (数据库配置无效,使用 database = 0)
# sharded -> sharded servers  (密码、数据库必须在 hosts 中指定,且连接池配置无效 ; redis://user:[email protected]:6379/0)
#
#########################################

redis.mode = single

#redis storage mode (generic|hash)
redis.storage = generic

## redis pub/sub channel name
redis.channel = j2cache
## redis pub/sub server (using redis.hosts when empty)
redis.channel.host =

#cluster name just for sharded
redis.cluster_name = j2cache

## redis cache namespace optional, default[empty]
redis.namespace =

## redis command scan parameter count, default[1000]
#redis.scanCount = 1000

## connection
# Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379

redis.hosts = 127.0.0.1:6379
redis.timeout = 2000
redis.password =
redis.database = 0
redis.ssl = false

## redis pool properties
redis.maxTotal = 100
redis.maxIdle = 10
redis.maxWaitMillis = 5000
redis.minEvictableIdleTimeMillis = 60000
redis.minIdle = 1
redis.numTestsPerEvictionRun = 10
redis.lifo = false
redis.softMinEvictableIdleTimeMillis = 10
redis.testOnBorrow = true
redis.testOnReturn = false
redis.testWhileIdle = true
redis.timeBetweenEvictionRunsMillis = 300000
redis.blockWhenExhausted = false
redis.jmxEnabled = false

#########################################
# Lettuce scheme
#
# redis -> single redis server
# rediss -> single redis server with ssl
# redis-sentinel -> redis sentinel
# redis-cluster -> cluster servers
#
#########################################

#########################################
# Lettuce Mode
#
# single -> single redis server
# sentinel -> master-slaves servers
# cluster -> cluster servers (数据库配置无效,使用 database = 0)
# sharded -> sharded servers  (密码、数据库必须在 hosts 中指定,且连接池配置无效 ; redis://user:[email protected]:6379/0)
#
#########################################

## redis command scan parameter count, default[1000]
#lettuce.scanCount = 1000
lettuce.mode = single
lettuce.namespace =
lettuce.storage = hash
lettuce.channel = j2cache
lettuce.scheme = redis
lettuce.hosts = 127.0.0.1:6379
lettuce.password =
lettuce.database = 0
lettuce.sentinelMasterId =
lettuce.maxTotal = 100
lettuce.maxIdle = 10
lettuce.minIdle = 10
# timeout in milliseconds
lettuce.timeout = 10000
# redis cluster topology refresh interval in milliseconds
lettuce.clusterTopologyRefresh = 3000

#########################################
# memcached server configurations
# refer to https://gitee.com/mirrors/XMemcached
#########################################

memcached.servers = 127.0.0.1:11211
memcached.username =
memcached.password =
memcached.connectionPoolSize = 10
memcached.connectTimeout = 1000
memcached.failureMode = false
memcached.healSessionInterval = 1000
memcached.maxQueuedNoReplyOperations = 100
memcached.opTimeout = 100
memcached.sanitizeKeys = false

要約する

  1. j2cache はキャッシュ フレームワークです。それ自体にはキャッシュ機能はありませんが、さまざまなキャッシュ統合ソリューションを提供します。
  2. j2cache は、キャッシュ間のデータ交換方法だけでなく、複雑な構成を通じてすべてのレベルでキャッシュをセットアップする必要があります。
  3. j2cache 操作インターフェイスは CacheChannel を通じて実装されます

5.1.8、SpringBoot は Caffeine キャッシュを統合します

カフェインキャッシュの 3 つのリサイクル戦略

  • サイズベース: 最大サイズ

  • 時間ベース:

    • expireAfterAccess 最終アクセス有効期限タイマー
    • expireAfterWrite は最後の書き込み時に期限切れになります
  • 参照ベース: ソフト参照と弱い参照のみが期限切れになります。

Caffeine クラスは、ビルダー モードの Builder クラスと同等です。キャッシュは、Caffeine クラスを通じて構成されます。キャッシュの構成には、次のパラメーターがあります。

  • expireAfterWrite: キャッシュへの最後の書き込み後にタイミングを開始し、指定された時間が経過すると期限切れになります。

  • expireAfterAccess: 最後のアクセスまたは書き込み後にタイミングを開始し、指定された時間が経過すると期限切れになります。キーへのアクセス要求が常にある場合、キャッシュが期限切れになることはありません。

  • freshAfterWrite: 書き込み後のリフレッシュ期間。このリフレッシュはアクセスに基づいて受動的にトリガーされます。非同期リフレッシュと同期リフレッシュをサポートしています。expireAfterWrite と組み合わせて使用​​すると、キャッシュにアクセスできない場合でも、キャッシュが確実に削除されます。固定の時間間隔を使用しないと、単独で使用すると OOM が発生しやすくなります。

  • expireAfter: カスタム削除戦略。この戦略では、Caffeine はタイムホイール アルゴリズムを通じてさまざまなキーに対してさまざまな有効期限を実装します。

  • MaximumSize: 同じキャッシュ戦略でキャッシュされたデータ量をキャッシュ数に応じてアクセス順に追い出します。最大サイズ 100 を例にとります。値が 100 を超える場合は、最後にアクセスされたデータ キャッシュが追い出されます。

  • MaximumWeight: キャッシュの重みに従って削除します (重みはキャッシュのサイズを決定するためにのみ使用され、キャッシュが削除されるかどうかを決定するためには使用されません)。MaximumWeight と MaximumSize を同時に使用することはできません。

  • weakKeys: キーは弱い参照として設定され、GC 中に直接削除できます。

  • weakValues: 値は弱い参照として設定され、GC 中に直接削除できます。

  • SoftValues: 値はソフト参照として設定され、メモリがオーバーフローする直前に削除できます。

  • executor: カスタム スレッド プールを選択します。デフォルトのスレッド プール実装は ForkJoinPool.commonPool();

  • 重み付け: 特定のキーの重みを設定します。

  • RecordStats: ヒット率などのキャッシュ統計。

  • removalListener: キャッシュ削除リスナー;

  • Writer: キャッシュの書き込み、更新、および削除のリスナー。

主に Spring キャッシュのアノテーション @Cacheable、@CacheEvict、@CachePut に基づいています

  • @Cacheable : クエリ メソッドで使用されるキャッシュ読み取り操作をトリガーします。キャッシュ内に見つかった場合は直接キャッシュを取り出して返し、そうでない場合はターゲット メソッドを実行して結果をキャッシュします。

  • @CachePut : キャッシュ更新をトリガーするメソッドでは、Cacheable と比較して、このアノテーションのメソッドが常に実行され、メソッドによって返された結果がキャッシュの更新に使用されます。これは、挿入および更新動作のメソッドに適用されます。 。

  • @CacheEvict : キャッシュの無効化、キャッシュ項目の削除、またはキャッシュのクリアをトリガーします。削除メソッドに適用されます。

  • @Caching は、1 つのメソッドで複数のキャッシュを結合します (このアノテーションにより、メソッドで複数のアノテーションを同時に設定できます)

  • @CacheConfig は、クラス レベルでいくつかのキャッシュ関連の共通構成を設定します (他のキャッシュと組み合わせて使用​​されます)。

5.1.8.1、方法 1: Caffeine の依存関係を直接インポートし、Caffeine 関数を使用してキャッシュを実装する

5.1.8.1.1. Maven 関連の依存関係の導入
<dependency>  
  <groupId>com.github.ben-manes.caffeine</groupId>  
    <artifactId>caffeine</artifactId>  
</dependency>
5.1.8.1.2、キャッシュの構成オプションを設定する
@EnableCaching
@Configuration
public class CacheConfig {
    
    
 
    @Bean
    public Cache<String, Object> caffeineCache() {
    
    
        return Caffeine.newBuilder()
                // 设置最后一次写入或访问后经过固定时间过期
                .expireAfterWrite(60, TimeUnit.SECONDS)
                // 初始的缓存空间大小
                .initialCapacity(100)
                // 缓存的最大条数
                .maximumSize(1000)
                .build();
    }
}
5.1.8.1.3、サービスにキャッシュ機能を追加
 
@Slf4j
@Service
public class UserInfoServiceImpl {
    
    
 
    /**
     * 模拟数据库存储数据
     */
    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();
 
    @Autowired
    Cache<String, Object> caffeineCache;
 
    public void addUserInfo(UserInfo userInfo) {
    
    
        userInfoMap.put(userInfo.getId(), userInfo);
        // 加入缓存
        caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
    }
 
    public UserInfo getByName(Integer id) {
    
    
        // 先从缓存读取
        caffeineCache.getIfPresent(id);
        UserInfo userInfo = (UserInfo) caffeineCache.asMap().get(String.valueOf(id));
        if (userInfo != null){
    
    
            return userInfo;
        }
        // 如果缓存中不存在,则从库中查找
        userInfo = userInfoMap.get(id);
        // 如果用户信息不为空,则加入缓存
        if (userInfo != null){
    
    
            caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
        }
        return userInfo;
    }
 
    public UserInfo updateUserInfo(UserInfo userInfo) {
    
    
        if (!userInfoMap.containsKey(userInfo.getId())) {
    
    
            return null;
        }
        // 取旧的值
        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
        // 替换内容
        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
    
    
            oldUserInfo.setAge(userInfo.getAge());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getName())) {
    
    
            oldUserInfo.setName(userInfo.getName());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
    
    
            oldUserInfo.setSex(userInfo.getSex());
        }
        // 将新的对象存储,更新旧对象信息
        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
        // 替换缓存中的值
        caffeineCache.put(String.valueOf(oldUserInfo.getId()),oldUserInfo);
        return oldUserInfo;
    }
 
    @Override
    public void deleteById(Integer id) {
    
    
        userInfoMap.remove(id);
        // 从缓存中删除
        caffeineCache.asMap().remove(String.valueOf(id));
    }
}

5.1.8.2. 方法 2: Caffeine と Spring Cache の依存関係を導入し、SpringCache アノテーション メソッドを使用してキャッシュを実装する

5.1.8.2.1. Maven 関連の依存関係の導入
 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
5.1.8.2.2、キャッシュ管理クラスの構成
@EnableCaching
@Configuration  
public class CacheConfig {
    
      
  
    /**  
     * 配置缓存管理器  
     *  
     * @return 缓存管理器  
     */  
    @Bean("caffeineCacheManager")  
    public CacheManager cacheManager() {
    
      
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();  
        cacheManager.setCaffeine(Caffeine.newBuilder()  
                // 设置最后一次写入或访问后经过固定时间过期  
                .expireAfterAccess(60, TimeUnit.SECONDS)  
                // 初始的缓存空间大小  
                .initialCapacity(100)  
                // 缓存的最大条数  
                .maximumSize(1000));  
        return cacheManager;  
    }  
}
5.1.8.2.3. サービスへのキャッシュ機能の追加
@Slf4j
@Service
@CacheConfig(cacheNames = "caffeineCacheManager")
public class UserInfoServiceImpl {
    
    
 
    /**
     * 模拟数据库存储数据
     */
    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();
 
    @CachePut(key = "#userInfo.id")
    public void addUserInfo(UserInfo userInfo) {
    
    
        userInfoMap.put(userInfo.getId(), userInfo);
    }
 
    @Cacheable(key = "#id")
    public UserInfo getByName(Integer id) {
    
    
        return userInfoMap.get(id);
    }
 
    @CachePut(key = "#userInfo.id")
    public UserInfo updateUserInfo(UserInfo userInfo) {
    
    
        if (!userInfoMap.containsKey(userInfo.getId())) {
    
    
            return null;
        }
        // 取旧的值
        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
        // 替换内容
        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
    
    
            oldUserInfo.setAge(userInfo.getAge());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getName())) {
    
    
            oldUserInfo.setName(userInfo.getName());
        }
        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
    
    
            oldUserInfo.setSex(userInfo.getSex());
        }
        // 将新的对象存储,更新旧对象信息
        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
        // 返回新对象信息
        return oldUserInfo;
    }
 
    @CacheEvict(key = "#id")
    public void deleteById(Integer id) {
    
    
        userInfoMap.remove(id);
    }
}

5.2. タスク

5.2.1、クォーツ

Quartz テクノロジーは、比較的成熟した時間制限付きタスクのフレームワークです。何と言うか? これは少し面倒で、使用したことがある人なら誰でも、構成が少し複雑であることを知っています。springboot がそれを統合すると、一連の構成が簡素化され、多くの構成にデフォルト設定が採用されるため、開発段階が大幅に簡素化されます。Springboot を学習して Quartz を統合する前に、いくつかの Quartz の概念を普及させてください。

  • 作業 (ジョブ): 作業の特定の実行を定義するために使用されます。
  • ジョブ詳細 (JobDetail): 通常の作業に関連する情報を記述するために使用されます。
  • トリガー(Trigger):作業内容とスケジューラの対応を記述します。
  • スケジューラー (スケジューラー): 作業をトリガーする実行ルールを記述するために使用され、通常は cron 式を使用してルールを定義します。

簡単に言うと、日常的に行うこと、つまり仕事ですが、単純に作業することは不可能で、いくつかの詳細な情報を設定する必要があります。作業をいつ実行するかというスケジューラの設定は、単純にジョブを実行する時刻を設定することと理解できます。仕事とスケジュールは両方とも独立して定義されていますが、それらはどのように組み合わされるのでしょうか? トリガーを使用します。それで全部です。Springboot を起動して Quartz を統合しましょう。

ステップ① : Springboot をインポートして Quartz スターターを統合する

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

ステップ②:タスクBeanを定義し、Quartzの開発仕様に従って作成し、QuartzJobBeanを継承する

public class MyQuartz extends QuartzJobBean {
    
    
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    
    
        System.out.println("quartz task run...");
    }
}

ステップ③ : Quartz 構成クラスを作成し、ジョブ詳細 (JobDetail) とトリガー (Trigger) Bean を定義します

@Configuration
public class QuartzConfig {
    
    
    @Bean
    public JobDetail printJobDetail(){
    
    
        //绑定具体的工作
        return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
    }
    @Bean
    public Trigger printJobTrigger(){
    
    
        ScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        //绑定对应的工作明细
        return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(schedBuilder).build();
    }
}

対応する特定の作業を作業の詳細に設定するには、 newJob() オペレーションを使用して、対応する作業タスク タイプを渡します。

トリガーはタスクにバインドする必要があります。forJob() オペレーションを使用して、バインドされた作業詳細オブジェクトを渡します。ここで、作業詳細の名前を設定して名前バインディングを使用することも、対応するメソッド バインディングを直接呼び出すこともできます。トリガーの中核となるルールは実行時間ですが、ここではスケジューラを使用して実行時間を定義し、実行時間の記述方法には cron 式を使用します。cron表現のルールについては関連講座を参照して学習してください 少し複雑で、形式をランダムに設定することもできず、形式を書いただけでは使えません。

要約する

  1. Quartz の Springboot 統合では、JobDetail オブジェクトと Trigger オブジェクトの 2 つのオブジェクトを含む、Quartz に対応するコア オブジェクトを Spring コンテナ管理に引き渡します。
  2. JobDetail オブジェクトはジョブの実行情報を記述し、QuartzJobBean 型のオブジェクトにバインドする必要があります。
  3. Trigger オブジェクトはトリガーを定義するもので、どの JobDetail をバインドするかを指定し、同時に実行周期スケジューラを設定する必要があります。

5.2.2、タスク

タイミング タスクの特性に応じて、Spring はタイミング タスクの開発を極限まで簡素化します。どのように言って?スケジュールされたタスクを実行するには、コンテナにこの関数があることを常に伝えてから、対応する Bean に定期的にタスクを実行するタイミングを伝える必要があります。とても簡単です。その方法を一緒に見てみましょう。

手順①:スケジュールタスク機能をオンにし、ブートクラスでスケジュールタスク機能のスイッチをオンにし、アノテーション @EnableScheduling を使用する

@SpringBootApplication
//开启定时任务功能
@EnableScheduling
public class SpringbootTaskApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringbootTaskApplication.class, args);
    }
}

手順②:Beanを定義する 定期的に実行するオペレーションの上に @Scheduled アノテーションを付けて実行時間を定義します 実行時間の記述方法は引き続き cron 式です

@Component
public class MyBean {
    
    
    @Scheduled(cron = "0/1 * * * * ?")
    public void print(){
    
    
        System.out.println(Thread.currentThread().getName()+" :spring task run...");
    }
}

完了、これでスケジュールされたタスクの構成が完了しました。全体的には何も不足しているように感じられますが、すべての情報を Bean に抽出するのではなく、アノテーション バインディングを直接使用して定期的にタスクを実行します。

タイミングタスクの設定方法は設定ファイルを通じて実行できます

spring:
  task:
   	scheduling:
      pool:
       	size: 1							# 任务调度线程池大小 默认 1
      thread-name-prefix: ssm_      	# 调度线程名称前缀 默认 scheduling-      
        shutdown:
          await-termination: false		# 线程池关闭时等待所有任务完成
          await-termination-period: 10s	# 调度线程关闭前最大等待时间,确保最后一定关闭

要約する

  1. Spring タスクは、スケジュールされたタスク機能を有効にするには、アノテーション @EnableScheduling を使用する必要があります。

  2. cron式で記述されたスケジュール実行タスクの実行期間を設定します。

5.3. メール

電子メールの送信方法を学ぶ前に、電子メールの運用プロセスの標準を規定する 3 つの概念を理解してください。

  • SMTP (Simple Mail Transfer Protocol): Simple Mail Transfer Protocol、電子メールを送信するための転送プロトコル
  • POP3 (Post Office Protocol - バージョン 3):電子メールを受信するための標準プロトコル
  • IMAP (Internet Mail Access Protocol): POP3 の代替プロトコルである Internet Message Protocol

簡単に言うと、SMPT は電子メール送信の標準、POP3 は電子メール受信の標準、そして IMAP は POP3 のアップグレードです。通常、運用プログラムでの電子メールの操作は電子メールの送信であるため、SMTP の使用が中心であり、電子メールの受信のほとんどは電子メール クライアントを通じて行われるため、電子メールを開発するためのコードはほとんどありません。メールの内容を読み取って解析し、メール機能の統一処理を行う場合を除きます。たとえば、人事メールボックスは求職者から履歴書を受け取り、統合された方法で読み取り、処理できます。しかし、独立した履歴書配信システムを作ってみてはいかがでしょうか? というと、なんとも奇妙な要件なのですが、メールを受信したい場合は、送信者の記述形式を統一する必要があるのですが、これが少し難しく、外部からの攻撃を受けやすくなってしまうのです。 。ホワイトリストを使ってメールを受信して​​解析できるのであれば、ホワイトリストに登録されている人専用のシステムを開発した方が安全です。Springboot が javamail を統合して電子メールを送信する方法を見てみましょう。

5.3.1. 簡単なメールの送信

ステップ①:springbootをインポートしてjavamail starterを統合する

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

ステップ②:メールボックスのログイン情報を設定する

spring:
  mail:
    host: smtp.126.com
    username: [email protected]
    password: test

Java プログラムは電子メールの送信にのみ使用され、電子メール機能は電子メール プロバイダーによって提供されるため、ここで他の人の電子メール サービスを使用するには、対応する情報を設定する必要があります。

ホスト構成はメール サービスを提供するホスト プロトコルであり、現在のプログラムはメールの送信のみに使用されるため、構成は smtp プロトコルです。

password は電子メール アカウントのログイン パスワードではなく、システムのセキュリティを確保するために電子メール プロバイダーによって提供される暗号化されたパスワードです。そうしないと、外部担当者がそのアドレスから設定ファイルにアクセスしてダウンロードし、電子メールのパスワードを直接取得した場合、セキュリティ上の大きなリスクが発生します。パスワードの取得方法はメールプロバイダーごとに異なるため、ここでは省略します。メールプロバイダーの設定ページでPOP3やIMAPなどのキーワードを検索すると、該当する取得場所が見つかります。次の例は参考用です。

ここに画像の説明を挿入

ステップ③ : JavaMailSender インターフェースを使用してメールを送信する

@Service
public class SendMailServiceImpl implements SendMailService {
    
    
    @Autowired
    private JavaMailSender javaMailSender;

    //发送人
    private String from = "[email protected]";
    //接收人
    private String to = "[email protected]";
    //标题
    private String subject = "测试邮件";
    //正文
    private String context = "测试邮件正文内容";

    @Override
    public void sendMail() {
    
    
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from+"(小甜甜)");
        message.setTo(to);
        message.setSubject(subject);
        message.setText(context);
        javaMailSender.send(message);
    }
}

メール送信に必要な情報(送信者、受信者、タイトル、本文)をSimpleMailMessageオブジェクトにカプセル化し、ルールに従って送信者のニックネームを設定できます。

5.3.2. 複数のコンポーネントからなる電子メール (添付ファイル、複雑なテキスト) の送信

単純なメールを送信する場合は、対応する 4 つの基本情報を入力するだけで済みますが、複雑なメールを送信する場合は、メール オブジェクトを変更する必要があります。MimeMessage を使用して特別な電子メールを送信できます。

Webページの本文メールを送信する

@Service
public class SendMailServiceImpl2 implements SendMailService {
    
    
    @Autowired
    private JavaMailSender javaMailSender;

    //发送人
    private String from = "[email protected]";
    //接收人
    private String to = "[email protected]";
    //标题
    private String subject = "测试邮件";
    //正文
    private String context = "<img src='ABC.JPG'/><a href='https://www.angyan.cn'>点开有惊喜</a>";

    public void sendMail() {
    
    
        try {
    
    
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message);
            helper.setFrom(to+"(小甜甜)");
            helper.setTo(from);
            helper.setSubject(subject);
            helper.setText(context,true);		//此处设置正文支持html解析

            javaMailSender.send(message);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

添付ファイル付きのメールを送信する

@Service
public class SendMailServiceImpl2 implements SendMailService {
    
    
    @Autowired
    private JavaMailSender javaMailSender;

    //发送人
    private String from = "[email protected]";
    //接收人
    private String to = "[email protected]";
    //标题
    private String subject = "测试邮件";
    //正文
    private String context = "测试邮件正文";

    public void sendMail() {
    
    
        try {
    
    
            MimeMessage message = javaMailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message,true);		//此处设置支持附件
            helper.setFrom(to+"(小甜甜)");
            helper.setTo(from);
            helper.setSubject(subject);
            helper.setText(context);

            //添加附件
            File f1 = new File("springboot_23_mail-0.0.1-SNAPSHOT.jar");
            File f2 = new File("resources\\logo.png");

            helper.addAttachment(f1.getName(),f1);
            helper.addAttachment("test.png",f2);

            javaMailSender.send(message);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

要約する

  1. Javamail の Springboot 統合により、電子メールを送信するためのクライアント オブジェクト JavaMailSender の初期化プロセスが実際に簡素化され、構成の形式で情報をロードすることで開発プロセスが簡素化されます。

5.4. メッセージ

5.4.1. メッセージの概念

広い意味では、ニュースは実際には情報ですが、情報とは異なります。通常、情報はデータの集合として定義されますが、メッセージはデータの特性だけでなく、メッセージの送信元と受信者の概念も持ちます。通常、メッセージを送信する側はメッセージのプロデューサーと呼ばれ、メッセージを受信する側はメッセージのコンシューマと呼ばれます。このように比較してみると、実はニュースと情報には大きな違いがあることが分かります。

なぜプロデューサーとコンシューマーを設定するのでしょうか? ニュースについて語るというのはこういうことだ。通常、情報はデータの集合ですが、メッセージにはプロデューサーとコンシューマーが存在するため、メッセージに含まれる情報は二重に解釈される可能性があり、プロデューサーがメッセージを送信すると、プロデューサーがメッセージを送信したと理解されることも、メッセージがメッセージを送信したことも理解されます。プロデューサーがコマンドを送信したことがわかり、コンシューマーがメッセージを受信すると、コンシューマーがメッセージを取得したことがわかり、コンシューマがコマンドを取得したことがわかります。これと比較すると、情報は基本データであり、コマンドは次の動作アクションに関連付けることができるため、受信したメッセージに基づいて動作アクションを取得し、その動作アクションを使用することと同等であることがわかります。さらなる操作のためにビジネス ロジックに編成できます。一般に、ニュースは実際には情報の集合ですが、ニュースには新しい意味が与えられるだけです。ニュースには流れがあり、行動の流れに基づいて新しい解釈をもたらす方向性のある流れであるからです。このメッセージの特別な解決策に基づいて、開発者はそれをコード内の命令に置き換えることができます。

メッセージの理解に関して、初心者はメッセージ内のデータが非常に複雑であると常に考えていますが、これは誤解です。たとえば、受信者に送信内容を翻訳するように求めるメッセージを送信しました。この操作では印刷操作ではなく翻訳操作を実行する必要があるため、初心者はメッセージに翻訳されたテキストが含まれると考えるでしょう。実はこの現象は少し拡大解釈されており、送信されたメッセージには翻訳されたテキストしか含まれていませんが、このメッセージを受信するように別の人を制御することで、何をすべきかを確認することができます。たとえば、翻訳されるテキストは A プログラムにのみ送信され、A プログラムは翻訳操作のみを実行できるため、複雑なビジネスを完了するために単純な情報を送信でき、メッセージのさまざまな件名を受信することでさまざまな操作が実行されます。 , 代わりに、メッセージ内のデータの操作動作を定義します。もちろん、開発者がメッセージに操作タイプの情報を含めたい場合は、それも可能ですが、メッセージの内容はより単純かつ単数にすることができます。

メッセージのプロデューサーとコンシューマーの動作モードに関しては、メッセージは同期消費と非同期メッセージの 2 つのモードに分けることもできます。

いわゆる同期メッセージとは、プロデューサーがメッセージを送信し、コンシューマーがそれを処理するのを待ち、コンシューマーが処理を完了した後、その結果をプロデューサーに通知し、プロデューサーが下方向にビジネスを実行し続けることを意味します。このモードは、カード作成者のビジネス実行の継続性にとって多すぎるため、現在のエンタープライズ レベルの開発では、通常、上記のビジネス シナリオはメッセージの形式で処理されません。

いわゆる非同期メッセージとは、プロデューサーがメッセージを送信した後、コンシューマーの処理が完了するのを待たずに、プロデューサーが他のアクションを実行し続けることを意味します。たとえば、プロデューサはログ メッセージをログ システムに送信しますが、送信後、プロデューサはログ システムの実行結果に注意を払うことなく、他の作業を行うことができます。ログシステムは、受信したログ情報に基づいて業務実行を継続するため、単にログを記録するだけでも、ログを記録してアラームを送信するだけでも、プロデューサーには関係なく、プロデューサーの業務実行効率が大幅に向上します。また、同じプロデューサーから送信されたメッセージを処理する複数のコンシューマーを追加することで、システムの高い同時実行性が向上し、システムの作業効率が向上し、ユーザー エクスペリエンスが向上します。さまざまな問題により特定のコンシューマがダウンしても、ビジネスには影響を与えないため、システムの高可用性が向上します。

5.4.2、メッセージ処理のための Java 標準仕様

現在、エンタープライズ レベルの開発で広く使用されているメッセージ処理テクノロジには、次の 3 種類があります。

  • JMS
  • AMQP
  • MQTT

なぜ 3 つのテクノロジーではなく 3 つのカテゴリーがあるのですか? これらはすべて仕様ですので、JDBC 技術は仕様であり、仕様開発に基づいて開発が行われ、実装クラスに動作が依存すると考えます。たとえば、MySQL は JDBC 実装を提供し、最終的な動作は実装クラスに依存します。実装。そして、これら 3 種類の仕様はいずれも非同期メッセージを処理するためのものであり、メッセージの設計本質にも準拠し、非同期サービスを処理します。上記3つのメッセージ仕様を普及させる

5.4.2.1、JMS

仕様であるJMS(Java Message Service)はJDBC仕様に相当し、メッセージサービスに関するAPIインターフェースを提供します。

JMSメッセージモデル

JMS 仕様で指定されているメッセージには 2 つのモデルがあります。それは、ポイントツーポイント モデルパブリッシュ/サブスクライブ モデルです

ピアツーポイントモデル: ピア 2 ピア。プロデューサーは、通常はキュー モデルを使用して、メッセージを保持するコンテナにメッセージを送信し、キューを使用してメッセージを保存します。キューのメッセージを消費できるのは 1 人のコンシューマだけです。または、メッセージが時間内に消費されず、タイムアウトが発生します。このモデルでは、生産者と消費者は 1 対 1 で結び付けられます。

パブリッシュ- サブスクライブ モデル: パブリッシュ - サブスクライブの場合、プロデューサーはメッセージを保持するコンテナーにメッセージを送信します。メッセージもキュー モデルを使用して保存されます。ただし、メッセージは複数のコンシューマーによって消費される可能性があり、プロデューサーとコンシューマーは完全に独立しており、お互いの存在を認識する必要はありません。

上記の分類は、メッセージの生成および消費プロセスとは区別されており、メッセージに含まれる情報に応じて、さまざまなカテゴリに分類することもできます。

JMS メッセージの種類

メッセージに含まれるデータの種類に応じて、メッセージは 6 種類のメッセージに分類できます。

  • メール
  • マップメッセージ
  • バイトメッセージ
  • ストリームメッセージ
  • オブジェクトメッセージ
  • メッセージ (メッセージヘッダーと属性のみ)

JMS はさまざまなタイプのメッセージとさまざまな消費方法を推奨しており、使用ニーズに応じてさまざまなタイプのメッセージを選択できます。しかし、これは後に述べる批判にもなっている。全体として、JMS は典型的な保守的であり、すべてが J2EE 仕様に基づいており、一連の仕様が定義され、いくつかの標準が定義され、各標準で多数の API が提供されます。現在、JMS 仕様で実装されているメッセージミドルウェア技術は、ActiveMQ、Redis、HornetMQ など、王室御用達でなめている人もいるくらいたくさんあります。ただし、RabbitMQ、RocketMQ など、JMS の標準設計を参照しているものの、その仕様を完全には満たしていない非標準実装もいくつかあります。

5.4.2.2、AMQP

JMS の出現により、メッセージ ミドルウェアに対する強力な規範的なサポートが提供されましたが、JMS によって設定される非常に複雑な複数タイプのメッセージ処理メカニズムなど、使用の過程で批判も受けてきました。本来は分類して対処するのは良いことだったのに、なぜ批判されたのか。その理由は、JMS の設計が J2EE 仕様であり、Java 開発の観点から考えているためです。しかし、現実は非常に複雑であることがよくあります。たとえば、.NET で開発されたシステム A と Java で開発されたシステム B があり、システム A からシステム B にビジネス メッセージを送信したいとします。その結果、双方のデータ形式が一致しないため、送信できません。操作された。JMS は統一されたデータ形式ではないでしょうか? 6種類のデータを用意しており、必ず最適なデータが見つかります。いいえ、どれも使用できません。システム A の基礎となる言語は Java 言語によって開発されていないため、これらのオブジェクトはまったくサポートされません。つまり、既存の業務システムAを使って開発を続けたいと思ったら、それはもう不可能であり、Java言語で開発されたAシステムを打破してやり直す必要があるということです。

このとき、誰かが「あなたはとても複雑ですが、なぜそんなに種類が多いのですか?」と提案しました。誰もがサポートするメッセージ データ型を見つけることが、このクロスプラットフォームの問題を解決するのではないでしょうか? 誰もがそう思い、そこで AMQP が誕生しました。

上記の説明だけから、AMQP の登場により、メッセージ配信に使用されるメッセージの種類の問題が解決され、複雑さが簡素化されたことがはっきりとわかりますが、JMS の操作 API を完全に覆すわけではないため、AMQP は単にデータ伝送の形式を規定するプロトコル。

AMQP (アドバンスト メッセージ キューイング プロトコル): プロトコル (アドバンスト メッセージ キューイング プロトコル。メッセージ ブローカーの仕様でもある) であり、ネットワーク交換のデータ形式を標準化し、JMS 操作と互換性があります。

アドバンテージ

クロスプラットフォーム、サーバープロバイダー、プロデューサー、コンシューマーは異なる言語を使用して実装できます

JMS メッセージの種類

AMQP メッセージ タイプ: byte[]

AMQP は JMS メッセージ モデルに基づいてさらに拡張されており、ポイントツーポイント モデルとパブリッシュ/サブスクライブ モデルに加えて、さまざまなメッセージ送信に適応するためにいくつかの新しいメッセージ モデルが開発されています。

AMQP メッセージ モデル

  • 直接交換
  • ファンアウト交換
  • 話題の交換
  • ヘッダー交換
  • システム交換

現在、AMQP プロトコルを実装するメッセージ ミドルウェア テクノロジは多数あり、RabbitMQ、StormMQ、RocketMQ など、いずれも比較的人気のあるテクノロジです。

5.4.2.3、MQTT

MQTT (Message Queuing Telemetry Transport) メッセージ キュー テレメトリ送信は、小型デバイス向けに設計されており、モノのインターネット (IOT) エコシステムの主要コンポーネントの 1 つです。JavaEE エンタープライズレベルの開発とは関係がないため、ここではあまり説明しません。

上記の 3 つの J2EE エンタープライズ アプリケーションで広く使用されている 3 つの非同期メッセージング テクノロジに加えて、無視できないテクノロジがもう 1 つあります。Kafka です。

5.4.2.4、カフカ

高スループットの分散パブリッシュ/サブスクライブ メッセージング システムである Kafka は、リアルタイム メッセージング機能を提供します。Kafka テクノロジーは、メッセージ ミドルウェアの主な機能を備えた製品ではありませんが、パブリッシュ/サブスクライブ動作モードを備えており、メッセージ ミドルウェアとしても使用でき、現在エンタープライズ レベルの開発では珍しくありません。

このセクションでは、上記の内容のいくつかの実装スキームを中心に、springboot によるさまざまなメッセージ ミドルウェアの統合について説明します。使用前にさまざまなメッセージ ミドルウェアをインストールする必要があるため、すべての学習者の学習難易度を軽減するために、次のコンテンツが Windows システムにインストールされます。基本的な手順は以前の NoSQL ソリューションと同じで、最初にインストールしてから統合します。

5.4.3. 買い物注文のために携帯電話のテキストメッセージを送信する場合

以下のさまざまなメッセージ ミドルウェア テクノロジのデモンストレーションを容易にするために、ショッピング プロセス中に注文が発生したときにユーザーに SMS が送信されるケース環境を作成し、メッセージ ミドルウェアを使用して携帯電話のテキスト メッセージを送信するプロセスをシミュレートします。 。

携帯電話認証コードの要件は次のとおりです。

  • 発注業務の実行時(このプロセスを模擬)、メッセージサービスを呼び出し、送信するオーダーIDをメッセージミドルウェアに渡します。

  • メッセージ処理サービスは、送信される注文 ID を受信した後、注文 ID を出力します (テキスト メッセージの送信をシミュレートします)。

    データの読み書きを伴わないため、ビジネス層とプレゼンテーション層のみを開発し、SMS処理のビジネスコードを独自に開発します。

受注事業

ビジネス層インターフェース

public interface OrderService {
    
    
    void order(String id);
}

受信した注文 ID をシミュレートし、発注業務を実行します。パラメーターは仮想設定であり、実際には注文に対応するエンティティ クラスである必要があります。

ビジネス層の実装

@Service
public class OrderServiceImpl implements OrderService {
    
    
    @Autowired
    private MessageService messageService;
    
    @Override
    public void order(String id) {
    
    
        //一系列操作,包含各种服务调用,处理各种业务
        System.out.println("订单处理开始");
        //短信消息处理
        messageService.sendMessage(id);
        System.out.println("订单处理结束");
        System.out.println();
    }
}

ビジネス層はメッセージ処理サービスMessageServiceを転送します

プレゼンテーション層サービス

@RestController
@RequestMapping("/orders")
public class OrderController {
    
    

    @Autowired
    private OrderService orderService;

    @PostMapping("{id}")
    public void order(@PathVariable String id){
    
    
        orderService.order(id);
    }
}

プレゼンテーション層の外部開発インターフェイス。オーダーIDを渡すだけです(シミュレーション)

SMS処理事業

ビジネス層インターフェース

public interface MessageService {
    
    
    void sendMessage(String id);
    String doMessage();
}

SMS 処理ビジネス レイヤー インターフェイスには 2 つの操作があり、処理される注文 ID をメッセージ ミドルウェアに送信します。もう 1 つの操作は現在、メッセージを処理するように設計されています。実際のメッセージ処理プロセスは手動で実行する必要はなく、自動的に実行する必要があります。導入後の具体的な設計へ

ビジネス層の実装

@Service
public class MessageServiceImpl implements MessageService {
    
    
    private ArrayList<String> msgList = new ArrayList<String>();

    @Override
    public void sendMessage(String id) {
    
    
        System.out.println("待发送短信的订单已纳入处理队列,id:"+id);
        msgList.add(id);
    }

    @Override
    public String doMessage() {
    
    
        String id = msgList.remove(0);
        System.out.println("已完成短信发送业务,id:"+id);
        return id;
    }
}

SMS 処理ビジネス層の実装では、このセットを使用して最初にメッセージ キューをシミュレートし、その効果を観察します。

プレゼンテーション層サービス

@RestController
@RequestMapping("/msgs")
public class MessageController {
    
    

    @Autowired
    private MessageService messageService;

    @GetMapping
    public String doMessage(){
    
    
        String id = messageService.doMessage();
        return id;
    }
}

ショートメッセージ処理プレゼンテーション層インターフェースはメッセージを処理するためのエントリを一時的に開発しますが、この業務は対応するビジネス層で設計された模擬インターフェースであり、実際の業務ではこのインターフェースを設計する必要はありません。

Springboot を起動して、JMS 仕様を厳密に満たす ActiveMQ をはじめとするさまざまなメッセージ ミドルウェアを統合しましょう

5.4.4、SpringBoot は ActiveMQ を統合します

ActiveMQ は、MQ 製品の中でもベテランの製品です。初期の標準 MQ 製品の 1 つです。AMQP プロトコルが登場する前は、メッセージ ミドルウェア市場の大部分を占めていました。その後、AMQP シリーズ製品の登場により、その勢いは弱まりました。現在のところ、オンラインで運営されている一部の製品に登場するだけで、新製品の開発に使用されることはほとんどありません。

5.4.4.1. インストール

Windows版インストールパッケージのダウンロードアドレス:https ://activemq.apache.org/components/classic/download/

ダウンロードしたインストールパッケージはzipファイルになっており、解凍すると以下のファイルが得られます

ここに画像の説明を挿入

サーバーを起動する

activemq.bat

bin ディレクトリの win32 または win64 ディレクトリで activemq.bat コマンドを実行するだけです。独自のオペレーティング システムに応じて選択できます。デフォルトの外部サービス ポートは 61616 です。

Web管理サービスにアクセスする

ActiveMQ が開始されると、Web コンソール サービスが開始され、これを通じて ActiveMQ を管理できます。

http://127.0.0.1:8161/

Web 管理サービスのデフォルトのポートは 8161 です。アクセス後、次のように ActiveMQ の管理インターフェイスを開くことができます。

ここに画像の説明を挿入

最初にアクセス ユーザー名とパスワードを入力します。初期化ユーザー名とパスワードは同じです。両方とも admin です。ログインに成功した後、次のように管理バックグラウンド インターフェイスに入ります。

ここに画像の説明を挿入

上記のインターフェイスが表示されれば、ActiveMQ サービスが正常に開始されたとみなされます。

起動に失敗しました

ActiveMQ の起動時に複数のポートが占有されます。通常の起動情報は次のとおりです。

wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
jvm 1    |   Copyright 1999-2006 Tanuki Software, Inc.  All Rights Reserved.
jvm 1    |
jvm 1    | Java Runtime: Oracle Corporation 1.8.0_172 D:\soft\jdk1.8.0_172\jre
jvm 1    |   Heap sizes: current=249344k  free=235037k  max=932352k
jvm 1    |     JVM args: -Dactivemq.home=../.. -Dactivemq.base=../.. -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStorePassword=password -Djavax.net.ssl.keyStore=../../conf/broker.ks -Djavax.net.ssl.trustStore=../../conf/broker.ts -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties -Dactivemq.conf=../../conf -Dactivemq.data=../../data -Djava.security.auth.login.config=../../conf/login.config -Xmx1024m -Djava.library.path=../../bin/win64 -Dwrapper.key=7ySrCD75XhLCpLjd -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=9364 -Dwrapper.version=3.2.3 -Dwrapper.native_library=wrapper -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1
jvm 1    | Extensions classpath:
jvm 1    |   [..\..\lib,..\..\lib\camel,..\..\lib\optional,..\..\lib\web,..\..\lib\extra]
jvm 1    | ACTIVEMQ_HOME: ..\..
jvm 1    | ACTIVEMQ_BASE: ..\..
jvm 1    | ACTIVEMQ_CONF: ..\..\conf
jvm 1    | ACTIVEMQ_DATA: ..\..\data
jvm 1    | Loading message broker from: xbean:activemq.xml
jvm 1    |  INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$1@5f3ebfe0: startup date [Mon Feb 28 16:07:48 CST 2022]; root of context hierarchy
jvm 1    |  INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[D:\soft\activemq\bin\win64\..\..\data\kahadb]
jvm 1    |  INFO | KahaDB is version 7
jvm 1    |  INFO | PListStore:[D:\soft\activemq\bin\win64\..\..\data\localhost\tmp_storage] started
jvm 1    |  INFO | Apache ActiveMQ 5.16.3 (localhost, ID:CZBK-20210302VL-10434-1646035669595-0:1) is starting
jvm 1    |  INFO | Listening for connections at: tcp://CZBK-20210302VL:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector openwire started
jvm 1    |  INFO | Listening for connections at: amqp://CZBK-20210302VL:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector amqp started
jvm 1    |  INFO | Listening for connections at: stomp://CZBK-20210302VL:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector stomp started
jvm 1    |  INFO | Listening for connections at: mqtt://CZBK-20210302VL:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector mqtt started
jvm 1    |  INFO | Starting Jetty server
jvm 1    |  INFO | Creating Jetty connector
jvm 1    |  WARN | ServletContext@o.e.j.s.ServletContextHandler@7350746f{
    
    /,null,STARTING} has uncovered http methods for path: /
jvm 1    |  INFO | Listening for connections at ws://CZBK-20210302VL:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector ws started
jvm 1    |  INFO | Apache ActiveMQ 5.16.3 (localhost, ID:CZBK-20210302VL-10434-1646035669595-0:1) started
jvm 1    |  INFO | For help or more information please see: http://activemq.apache.org
jvm 1    |  WARN | Store limit is 102400 mb (current store usage is 0 mb). The data directory: D:\soft\activemq\bin\win64\..\..\data\kahadb only has 68936 mb of usable space. - resetting to maximum available disk space: 68936 mb
jvm 1    |  INFO | ActiveMQ WebConsole available at http://127.0.0.1:8161/
jvm 1    |  INFO | ActiveMQ Jolokia REST API available at http://127.0.0.1:8161/api/jolokia/

占有されているポートは 61616、5672、61613、1883、61614 です。起動に失敗した場合は、まず該当するポートを管理してください。以下は、あるポートが占有されているというエラーメッセージですが、例外がスローされる位置から、ポート5672の起動時にポートが占有されていることが分かり、java.net.BindException: Address selected in use: JVM_Bindが表示されます。 。

wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
jvm 1    |   Copyright 1999-2006 Tanuki Software, Inc.  All Rights Reserved.
jvm 1    |
jvm 1    | Java Runtime: Oracle Corporation 1.8.0_172 D:\soft\jdk1.8.0_172\jre
jvm 1    |   Heap sizes: current=249344k  free=235038k  max=932352k
jvm 1    |     JVM args: -Dactivemq.home=../.. -Dactivemq.base=../.. -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStorePassword=password -Djavax.net.ssl.keyStore=../../conf/broker.ks -Djavax.net.ssl.trustStore=../../conf/broker.ts -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Djava.util.logging.config.file=logging.properties -Dactivemq.conf=../../conf -Dactivemq.data=../../data -Djava.security.auth.login.config=../../conf/login.config -Xmx1024m -Djava.library.path=../../bin/win64 -Dwrapper.key=QPJoy9ZoXeWmmwTS -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=14836 -Dwrapper.version=3.2.3 -Dwrapper.native_library=wrapper -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1
jvm 1    | Extensions classpath:
jvm 1    |   [..\..\lib,..\..\lib\camel,..\..\lib\optional,..\..\lib\web,..\..\lib\extra]
jvm 1    | ACTIVEMQ_HOME: ..\..
jvm 1    | ACTIVEMQ_BASE: ..\..
jvm 1    | ACTIVEMQ_CONF: ..\..\conf
jvm 1    | ACTIVEMQ_DATA: ..\..\data
jvm 1    | Loading message broker from: xbean:activemq.xml
jvm 1    |  INFO | Refreshing org.apache.activemq.xbean.XBeanBrokerFactory$1@2c9392f5: startup date [Mon Feb 28 16:06:16 CST 2022]; root of context hierarchy
jvm 1    |  INFO | Using Persistence Adapter: KahaDBPersistenceAdapter[D:\soft\activemq\bin\win64\..\..\data\kahadb]
jvm 1    |  INFO | KahaDB is version 7
jvm 1    |  INFO | PListStore:[D:\soft\activemq\bin\win64\..\..\data\localhost\tmp_storage] started
jvm 1    |  INFO | Apache ActiveMQ 5.16.3 (localhost, ID:CZBK-20210302VL-10257-1646035577620-0:1) is starting
jvm 1    |  INFO | Listening for connections at: tcp://CZBK-20210302VL:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600
jvm 1    |  INFO | Connector openwire started
jvm 1    | ERROR | Failed to start Apache ActiveMQ (localhost, ID:CZBK-20210302VL-10257-1646035577620-0:1)
jvm 1    | java.io.IOException: Transport Connector could not be registered in JMX: java.io.IOException: Failed to bind to server socket: amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600 due to: java.net.BindException: Address already in use: JVM_Bind
jvm 1    |      at org.apache.activemq.util.IOExceptionSupport.create(IOExceptionSupport.java:28)
jvm 1    |      at org.apache.activemq.broker.BrokerService.registerConnectorMBean(BrokerService.java:2288)
jvm 1    |      at org.apache.activemq.broker.BrokerService.startTransportConnector(BrokerService.java:2769)
jvm 1    |      at org.apache.activemq.broker.BrokerService.startAllConnectors(BrokerService.java:2665)
jvm 1    |      at org.apache.activemq.broker.BrokerService.doStartBroker(BrokerService.java:780)
jvm 1    |      at org.apache.activemq.broker.BrokerService.startBroker(BrokerService.java:742)
jvm 1    |      at org.apache.activemq.broker.BrokerService.start(BrokerService.java:645)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerService.afterPropertiesSet(XBeanBrokerService.java:73)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1748)
jvm 1    |      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1685)
jvm 1    |      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1615)
jvm 1    |      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
jvm 1    |      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
jvm 1    |      at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
jvm 1    |      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
jvm 1    |      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
jvm 1    |      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
jvm 1    |      at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:756)
jvm 1    |      at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
jvm 1    |      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
jvm 1    |      at org.apache.xbean.spring.context.ResourceXmlApplicationContext.<init>(ResourceXmlApplicationContext.java:64)
jvm 1    |      at org.apache.xbean.spring.context.ResourceXmlApplicationContext.<init>(ResourceXmlApplicationContext.java:52)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory$1.<init>(XBeanBrokerFactory.java:104)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:104)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:67)
jvm 1    |      at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:71)
jvm 1    |      at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:54)
jvm 1    |      at org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:87)
jvm 1    |      at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:63)
jvm 1    |      at org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:154)
jvm 1    |      at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:63)
jvm 1    |      at org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:104)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.apache.activemq.console.Main.runTaskClass(Main.java:262)
jvm 1    |      at org.apache.activemq.console.Main.main(Main.java:115)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
jvm 1    |      at java.lang.Thread.run(Thread.java:748)
jvm 1    | Caused by: java.io.IOException: Failed to bind to server socket: amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600 due to: java.net.BindException: Address already in use: JVM_Bind
jvm 1    |      at org.apache.activemq.util.IOExceptionSupport.create(IOExceptionSupport.java:34)
jvm 1    |      at org.apache.activemq.transport.tcp.TcpTransportServer.bind(TcpTransportServer.java:146)
jvm 1    |      at org.apache.activemq.transport.tcp.TcpTransportFactory.doBind(TcpTransportFactory.java:62)
jvm 1    |      at org.apache.activemq.transport.TransportFactorySupport.bind(TransportFactorySupport.java:40)
jvm 1    |      at org.apache.activemq.broker.TransportConnector.createTransportServer(TransportConnector.java:335)
jvm 1    |      at org.apache.activemq.broker.TransportConnector.getServer(TransportConnector.java:145)
jvm 1    |      at org.apache.activemq.broker.TransportConnector.asManagedConnector(TransportConnector.java:110)
jvm 1    |      at org.apache.activemq.broker.BrokerService.registerConnectorMBean(BrokerService.java:2283)
jvm 1    |      ... 46 more
jvm 1    | Caused by: java.net.BindException: Address already in use: JVM_Bind
jvm 1    |      at java.net.DualStackPlainSocketImpl.bind0(Native Method)
jvm 1    |      at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106)
jvm 1    |      at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
jvm 1    |      at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190)
jvm 1    |      at java.net.ServerSocket.bind(ServerSocket.java:375)
jvm 1    |      at java.net.ServerSocket.<init>(ServerSocket.java:237)
jvm 1    |      at javax.net.DefaultServerSocketFactory.createServerSocket(ServerSocketFactory.java:231)
jvm 1    |      at org.apache.activemq.transport.tcp.TcpTransportServer.bind(TcpTransportServer.java:143)
jvm 1    |      ... 52 more
jvm 1    |  INFO | Apache ActiveMQ 5.16.3 (localhost, ID:CZBK-20210302VL-10257-1646035577620-0:1) is shutting down
jvm 1    |  INFO | socketQueue interrupted - stopping
jvm 1    |  INFO | Connector openwire stopped
jvm 1    |  INFO | Could not accept connection during shutdown  : null (null)
jvm 1    |  INFO | Connector amqp stopped
jvm 1    |  INFO | Connector stomp stopped
jvm 1    |  INFO | Connector mqtt stopped
jvm 1    |  INFO | Connector ws stopped
jvm 1    |  INFO | PListStore:[D:\soft\activemq\bin\win64\..\..\data\localhost\tmp_storage] stopped
jvm 1    |  INFO | Stopping async queue tasks
jvm 1    |  INFO | Stopping async topic tasks
jvm 1    |  INFO | Stopped KahaDB
jvm 1    |  INFO | Apache ActiveMQ 5.16.3 (localhost, ID:CZBK-20210302VL-10257-1646035577620-0:1) uptime 0.426 seconds
jvm 1    |  INFO | Apache ActiveMQ 5.16.3 (localhost, ID:CZBK-20210302VL-10257-1646035577620-0:1) is shutdown
jvm 1    |  INFO | Closing org.apache.activemq.xbean.XBeanBrokerFactory$1@2c9392f5: startup date [Mon Feb 28 16:06:16 CST 2022]; root of context hierarchy
jvm 1    |  WARN | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.apache.activemq.xbean.XBeanBrokerService#0' defined in class path resource [activemq.xml]: Invocation of init method failed; nested exception is java.io.IOException: Transport Connector could not be registered in JMX: java.io.IOException: Failed to bind to server socket: amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600 due to: java.net.BindException: Address already in use: JVM_Bind
jvm 1    | ERROR: java.lang.RuntimeException: Failed to execute start task. Reason: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
jvm 1    | java.lang.RuntimeException: Failed to execute start task. Reason: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
jvm 1    |      at org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:91)
jvm 1    |      at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:63)
jvm 1    |      at org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:154)
jvm 1    |      at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:63)
jvm 1    |      at org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:104)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.apache.activemq.console.Main.runTaskClass(Main.java:262)
jvm 1    |      at org.apache.activemq.console.Main.main(Main.java:115)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
jvm 1    |      at java.lang.Thread.run(Thread.java:748)
jvm 1    | Caused by: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
jvm 1    |      at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:164)
jvm 1    |      at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1034)
jvm 1    |      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:555)
jvm 1    |      at org.apache.xbean.spring.context.ResourceXmlApplicationContext.<init>(ResourceXmlApplicationContext.java:64)
jvm 1    |      at org.apache.xbean.spring.context.ResourceXmlApplicationContext.<init>(ResourceXmlApplicationContext.java:52)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory$1.<init>(XBeanBrokerFactory.java:104)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:104)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:67)
jvm 1    |      at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:71)
jvm 1    |      at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:54)
jvm 1    |      at org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:87)
jvm 1    |      ... 16 more
jvm 1    | ERROR: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
jvm 1    | java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
jvm 1    |      at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:164)
jvm 1    |      at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1034)
jvm 1    |      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:555)
jvm 1    |      at org.apache.xbean.spring.context.ResourceXmlApplicationContext.<init>(ResourceXmlApplicationContext.java:64)
jvm 1    |      at org.apache.xbean.spring.context.ResourceXmlApplicationContext.<init>(ResourceXmlApplicationContext.java:52)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory$1.<init>(XBeanBrokerFactory.java:104)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory.createApplicationContext(XBeanBrokerFactory.java:104)
jvm 1    |      at org.apache.activemq.xbean.XBeanBrokerFactory.createBroker(XBeanBrokerFactory.java:67)
jvm 1    |      at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:71)
jvm 1    |      at org.apache.activemq.broker.BrokerFactory.createBroker(BrokerFactory.java:54)
jvm 1    |      at org.apache.activemq.console.command.StartCommand.runTask(StartCommand.java:87)
jvm 1    |      at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:63)
jvm 1    |      at org.apache.activemq.console.command.ShellCommand.runTask(ShellCommand.java:154)
jvm 1    |      at org.apache.activemq.console.command.AbstractCommand.execute(AbstractCommand.java:63)
jvm 1    |      at org.apache.activemq.console.command.ShellCommand.main(ShellCommand.java:104)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.apache.activemq.console.Main.runTaskClass(Main.java:262)
jvm 1    |      at org.apache.activemq.console.Main.main(Main.java:115)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1    |      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1    |      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1    |      at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1    |      at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
jvm 1    |      at java.lang.Thread.run(Thread.java:748)
wrapper  | <-- Wrapper Stopped
请按任意键继续. . .

5.4.4.2. 統合

ステップ① : Springboot をインポートして ActiveMQ スターターを統合する

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

ステップ②:ActiveMQサーバーアドレスの設定

spring:
  activemq:
    broker-url: tcp://localhost:61616

ステップ③:JmsMessagingTemplateを使用してActiveMQを操作する

@Service
public class MessageServiceActivemqImpl implements MessageService {
    
    
    @Autowired
    private JmsMessagingTemplate messagingTemplate;

    @Override
    public void sendMessage(String id) {
    
    
        System.out.println("待发送短信的订单已纳入处理队列,id:"+id);
        messagingTemplate.convertAndSend("order.queue.id",id);
    }

    @Override
    public String doMessage() {
    
    
        String id = messagingTemplate.receiveAndConvert("order.queue.id",String.class);
        System.out.println("已完成短信发送业务,id:"+id);
        return id;
    }
}

メッセージを送信するには、メッセージの種類を文字列に変換してから送信する必要があるため、メッセージの送信場所と特定のメッセージの内容を定義するのは、convertAndSend です。ここでは、id が次のように使用されます。メッセージの内容。

メッセージを受信するには、まずメッセージを受信し、それから指定されたデータ型に変換する必要があります。つまり、receiveAndConvert です。受信したメッセージを読み取る場所を提供することに加えて、受信したメッセージの特定の型も指定する必要があります。変換されたデータ。

ステップ④ : サーバー起動後にメッセージリスナーを使用して指定された場所を監視し、メッセージが表示されたらすぐにメッセージを消費します

@Component
public class MessageListener {
    
    
    @JmsListener(destination = "order.queue.id")
    @SendTo("order.other.queue.id")
    public String receive(String id){
    
    
        System.out.println("已完成短信发送业务,id:"+id);
        return "new:"+id;
    }
}

注釈 @JmsListener を使用して、ActiveMQ で指定された名前のメッセージ キューをリッスンする現在のメソッドを定義します。

現在のメッセージ キューが処理され、現在のメッセージを別のキューに渡し続ける必要がある場合は、注釈 @SendTo を使用するだけで、継続的に実行するための順次メッセージ キューを構築できます。

ステップ ⑤ : メッセージ モデルをポイントツーポイント モデルからパブリッシュ/サブスクライブ モデルに切り替えます。jms 設定を変更するだけです。

spring:
  activemq:
    broker-url: tcp://localhost:61616
  jms:
    pub-sub-domain: true

pub-sub-domain のデフォルト値は false、つまりポイントツーポイント モデルですが、true に変更するとパブリッシュ/サブスクライブ モデルになります。

要約する

  1. springboot は ActiveMQ を統合し、クライアント操作メッセージ キューとして JmsMessagingTemplate オブジェクトを提供します
  2. ActiveMQ を操作するには、ActiveMQ サーバーのアドレスを設定する必要があります。デフォルトのポートは 61616 です。
  3. エンタープライズ開発中、通常、リスナーはメッセージ キュー内のメッセージを処理するために使用され、リスナーの設定にはアノテーション @JmsListener が使用されます。
  4. jms の pub-sub-domain 属性を構成して、ポイントツーポイント モデルとパブリッシュ/サブスクライブ モデルの間でメッセージ モデルを切り替える

5.4.5. SpringBoot は RabbitMQ を統合します

RabbitMQ は MQ 製品の中でも最も人気のある製品の 1 つであり、AMQP プロトコルに準拠しています。RabbitMQ の基礎となる実装言語は Erlang を使用するため、RabbitMQ をインストールするには最初に Erlang をインストールする必要があります。

Erlang のインストール

Windows版インストールパッケージのダウンロードアドレス:https ://www.erlang.org/downloads

ダウンロードが完了すると、exe インストール ファイルが取得されます。ワンクリックで簡単にインストールできます。インストール後に再起動する必要があります。再起動する必要があります。

インストール プロセス中に、Windows コンポーネントに依存するプロンプトが表示される場合があります。プロンプトに従ってダウンロードしてインストールするだけで、次のようにすべて自動的に実行されます。

ここに画像の説明を挿入

Erlang のインストール後に環境変数を設定する必要があります。そうしないと、RabbitMQ はインストールされた Erlang を見つけることができません。必要な設定項目は以下のとおりであり、JDKの設定環境変数の機能に相当します。

  • ERLANG_HOME

5.4.5.1. インストール

Windows版インストールパッケージのダウンロードアドレスhttps ://rabbitmq.com/install-windows.html

ダウンロードが完了すると、exe インストール ファイルが取得され、ワンクリックのフール スタイル インストールが行われます。インストールが完了すると、次のファイルが取得されます。

ここに画像の説明を挿入

サーバーを起動する

rabbitmq-service.bat start		# 启动服务
rabbitmq-service.bat stop		# 停止服务
rabbitmqctl status				# 查看服务状态

sbin ディレクトリで Rabbitmq-service.bat コマンドを実行するだけです。start パラメータは起動を示し、stop パラメータは終了を示します。デフォルトの外部サービス ポートは 5672 です。

注: Rabbitmq を開始するプロセスは、実際には、rabbitmq に対応するシステム サービスを開始することであり、実行するには管理者権限が必要です。

Web管理サービスにアクセスする

RabbitMQ は Web コンソール サービスも提供しますが、この機能は使用する前に有効にする必要があるプラグインです。

rabbitmq-plugins.bat list							# 查看当前所有插件的运行状态
rabbitmq-plugins.bat enable rabbitmq_management		# 启动rabbitmq_management插件

プラグインを起動した後、プラグインの実行状態で実行されているかどうかを確認できます。実行後、ブラウザを通じてサービス バックグラウンド管理インターフェイスを開くことができます

http://localhost:15672

Web 管理サービスのデフォルトのポートは 15672 です。アクセス後、次のように RabbitMQ の管理インターフェイスを開くことができます。

ここに画像の説明を挿入

最初にアクセス ユーザー名とパスワードを入力します。初期化ユーザー名とパスワードは同じです。両方ともゲストです。ログインに成功した後、次のように管理バックグラウンド インターフェイスに入ります。

ここに画像の説明を挿入

5.4.5.2. 統合(直接モデル)

RabbitMQ は AMQP プロトコルを満たすため、さまざまなメッセージ モデルがさまざまなプロダクションに対応します。まず、開発には最も単純な直接モデルを使用します。

ステップ① : springboot をインポートして amqp スターターを統合します。amqp プロトコルはデフォルトで Rabbitmq スキームとして実装されます

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

ステップ②:RabbitMQのサーバーアドレスを設定する

spring:
  rabbitmq:
    host: localhost
    port: 5672

手順③:ダイレクト接続モードのシステム設定を初期化する

RabbitMQ の異なるモデルは異なるスイッチを使用するため、キューやスイッチなどの RabbitMQ 関連のオブジェクトを初期化する必要があります。

@Configuration
public class RabbitConfigDirect {
    
    
    @Bean
    public Queue directQueue(){
    
    
        return new Queue("direct_queue");
    }
    @Bean
    public Queue directQueue2(){
    
    
        return new Queue("direct_queue2");
    }
    @Bean
    public DirectExchange directExchange(){
    
    
        return new DirectExchange("directExchange");
    }
    @Bean
    public Binding bindingDirect(){
    
    
        return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct");
    }
    @Bean
    public Binding bindingDirect2(){
    
    
        return BindingBuilder.bind(directQueue2()).to(directExchange()).with("direct2");
    }
}

キュー Queue と直接接続されたスイッチ DirectExchange を作成した後、対応するキューをスイッチ経由で操作できるように、それらの間の関係 Binding をバインドする必要があります。

ステップ④:AmqpTemplateを使用してRabbitMQを操作する

@Service
public class MessageServiceRabbitmqDirectImpl implements MessageService {
    
    
    @Autowired
    private AmqpTemplate amqpTemplate;

    @Override
    public void sendMessage(String id) {
    
    
        System.out.println("待发送短信的订单已纳入处理队列(rabbitmq direct),id:"+id);
        amqpTemplate.convertAndSend("directExchange","direct",id);
    }
}

amqp プロトコルのオペレーション API インターフェイスの名前は、jms 仕様のオペレーション API インターフェイスに非常に似ていますが、渡されるパラメータは大きく異なります。

ステップ⑤ : サーバー起動後にメッセージリスナーを使用して指定された場所を監視し、メッセージが表示されたらすぐにメッセージを消費します

@Component
public class MessageListener {
    
    
    @RabbitListener(queues = "direct_queue")
    public void receive(String id){
    
    
        System.out.println("已完成短信发送业务(rabbitmq direct),id:"+id);
    }
}

注釈 @RabbitListener を使用して、RabbitMQ で指定された名前のメッセージ キューをリッスンする現在のメソッドを定義します。

5.4.5.3. 統合(トピックモデル)

ステップ①:同上

ステップ②:同上

ステップ③:テーマモードのシステム設定を初期化する

@Configuration
public class RabbitConfigTopic {
    
    
    @Bean
    public Queue topicQueue(){
    
    
        return new Queue("topic_queue");
    }
    @Bean
    public Queue topicQueue2(){
    
    
        return new Queue("topic_queue2");
    }
    @Bean
    public TopicExchange topicExchange(){
    
    
        return new TopicExchange("topicExchange");
    }
    @Bean
    public Binding bindingTopic(){
    
    
        return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.id");
    }
    @Bean
    public Binding bindingTopic2(){
    
    
        return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.orders.*");
    }
}

トピック モードは routingKey マッチング モードをサポートしており、* は単語に一致することを意味し、# は任意のコンテンツに一致することを意味するため、トピック スイッチを介してメッセージを異なるキューに配信できます。詳細については、RabbitMQ シリーズのコースを参照してください。

一致キー トピック。*。* トピック。#
トピック.オーダー.id 真実 真実
order.topic.id 間違い 間違い
topic.sm.order.id 間違い 真実
トピック.sm.id 間違い 真実
トピックID.オーダー 真実 真実
トピックID 間違い 真実
トピック.オーダー 間違い 真実

ステップ④:AmqpTemplateを使用してRabbitMQを操作する

@Service
public class MessageServiceRabbitmqTopicImpl implements MessageService {
    
    
    @Autowired
    private AmqpTemplate amqpTemplate;

    @Override
    public void sendMessage(String id) {
    
    
        System.out.println("待发送短信的订单已纳入处理队列(rabbitmq topic),id:"+id);
        amqpTemplate.convertAndSend("topicExchange","topic.orders.id",id);
    }
}

メッセージの送信後、現在提供されている routingKey を、スイッチのバインド時に設定された routingKey と照合します。ルールの照合が成功すると、メッセージは対応するキューに入ります。

ステップ⑤:サーバー起動後にメッセージリスナーを使用して指定されたキューをリッスンする

@Component
public class MessageListener {
    
    
    @RabbitListener(queues = "topic_queue")
    public void receive(String id){
    
    
        System.out.println("已完成短信发送业务(rabbitmq topic 1),id:"+id);
    }
    @RabbitListener(queues = "topic_queue2")
    public void receive2(String id){
    
    
        System.out.println("已完成短信发送业务(rabbitmq topic 22222222),id:"+id);
    }
}

注釈 @RabbitListener を使用して、RabbitMQ で指定された名前のメッセージ キューをリッスンする現在のメソッドを定義します。

要約する

  1. Springboot は RabbitMQ を統合し、クライアント操作メッセージ キューとして AmqpTemplate オブジェクトを提供します
  2. ActiveMQ を操作するには、ActiveMQ サーバーのアドレスを設定する必要があります。デフォルトのポートは 5672 です。
  3. エンタープライズ開発中、通常、リスナーはメッセージ キュー内のメッセージを処理するために使用され、リスナーの設定にはアノテーション @RabbitListener が使用されます。
  4. RabbitMQ には 5 つのメッセージ モデルがあり、使用されるキューは同じですが、スイッチが異なります。スイッチが異なり、対応するメッセージ入力戦略も異なります。

5.4.6、SpringBoot は RocketMQ を統合します

RocketMQ は Ali によって開発され、後に Apache Foundation に寄付されました。現在、Apache Foundation のトップ プロジェクトの 1 つであり、現在市場で最も人気のある MQ 製品の 1 つです。AMQP プロトコルに準拠しています。

5.4.6.1. インストール

Windows版インストールパッケージのダウンロードアドレス:https ://rocketmq.apache.org/

ダウンロードが完了すると、zip 圧縮ファイルが生成されるので、解凍して使用します。

RocketMQ をインストールした後、次のように環境変数を構成する必要があります。

  • ROCKETMQ_HOME
  • NAMESRV_ADDR (推奨): 127.0.0.1:9876

NAMESRV_ADDRについては、初心者向けに設定することをお勧めします。コマンドで対応する値を設定することもできますが、操作が少し面倒なので、設定することをお勧めします。この項目は、システムが RocketMQ の知識を学習した後、柔軟に制御できます。

RocketMQ 動作モード

RocketMQ では、ビジネスを処理するサーバーはブローカーと呼ばれ、プロデューサーとコンシューマーはブローカーと直接通信せず、指定されたサーバーを介して通信します。ブローカーが開始されると、ブローカーはオンラインであることをネーミング サーバーに通知し、すべてのブローカー情報がネーミング サーバーに保存されます。プロデューサーとコンシューマーがブローカーに接続する必要がある場合、ネーム サーバーを通じてビジネスを処理する対応するブローカーを見つけるため、ネーム サーバーは構造全体で情報センターの役割を果たします。また、ブローカーが起動する前に、まずネーム サーバーを起動する必要があります。

ここに画像の説明を挿入

サーバーを起動する

mqnamesrv		# 启动命名服务器
mqbroker		# 启动broker

bin ディレクトリで mqnamesrv コマンドを実行してネーミング サーバーを起動します。デフォルトの外部サービス ポートは 9876 です。

bin ディレクトリで mqbroker コマンドを実行してブローカー サーバーを起動します。環境変数に NAMESRV_ADDR が設定されていない場合は、mqbroker コマンドを実行する前に set コマンドで NAMESRV_ADDR の値を設定する必要があり、この項目を設定する必要があります始めるたびに。

テストサーバーの起動ステータス

RocketMQ は、サーバー機能をテストするためのテスト プログラムのセットを提供します。これは、bin ディレクトリで tools コマンドを実行することで使用できます。

tools org.apache.rocketmq.example.quickstart.Producer		# 生产消息
tools org.apache.rocketmq.example.quickstart.Consumer		# 消费消息

5.4.6.2、統合(非同期メッセージ)

ステップ① : Springboot をインポートして RocketMQ スターターを統合します。この座標は springboot によって維持されません。

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.2.1</version>
</dependency>

ステップ②:RocketMQのサーバーアドレスを設定する

rocketmq:
  name-server: localhost:9876
  producer:
    group: group_rocketmq

デフォルトのプロデューサー・コンシューマー・グループ・グループを設定します。

ステップ③:RocketMQTemplateを使用してRocketMQを操作する

@Service
public class MessageServiceRocketmqImpl implements MessageService {
    
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public void sendMessage(String id) {
    
    
        System.out.println("待发送短信的订单已纳入处理队列(rocketmq),id:"+id);
        SendCallback callback = new SendCallback() {
    
    
            @Override
            public void onSuccess(SendResult sendResult) {
    
    
                System.out.println("消息发送成功");
            }
            @Override
            public void onException(Throwable e) {
    
    
                System.out.println("消息发送失败!!!!!");
            }
        };
        rocketMQTemplate.asyncSend("order_id",id,callback);
    }
}

非同期メッセージを送信するには、asyncSend メソッドを使用します。

ステップ④ : サーバー起動後にメッセージリスナーを使用して指定された場所を監視し、メッセージが表示されたらすぐにメッセージを消費します

@Component
@RocketMQMessageListener(topic = "order_id",consumerGroup = "group_rocketmq")
public class MessageListener implements RocketMQListener<String> {
    
    
    @Override
    public void onMessage(String id) {
    
    
        System.out.println("已完成短信发送业务(rocketmq),id:"+id);
    }
}

RocketMQ リスナーは標準形式に従って開発され、RocketMQListener インターフェイスを実装する必要があり、ジェネリック タイプはメッセージ タイプです。

注釈 @RocketMQMessageListener を使用して、現在のクラスが RabbitMQ 内の指定されたグループと指定された名前のメッセージ キューをリッスンすることを定義します。

要約する

  1. Springboot は RocketMQ を統合し、RocketMQTemplate オブジェクトをクライアントとして使用してメッセージ キューを操作します
  2. RocketMQ を操作するには、RocketMQ サーバーのアドレスを設定する必要があります。デフォルトのポートは 9876 です。
  3. エンタープライズ開発では通常、リスナーを使用してメッセージ キュー内のメッセージを処理し、リスナーの設定ではアノテーション @RocketMQMessageListener を使用します。

5.4.7. SpringBoot は Kafka を統合します

5.4.7.1. インストール

Windows版インストールパッケージのダウンロードアドレスhttps ://kafka.apache.org/downloads

ダウンロードが完了するとtgz圧縮ファイルが得られますので、解凍ソフトを使用して解凍してご利用ください Windowsバージョン2.8.1の使用を推奨します。

サーバーを起動する

kafkaサーバーの機能はRocketMQにおけるブローカーに相当し、kafkaの動作にもネーミングサーバーと同様のサービスが必要です。kafkaのインストールディレクトリにはzookeeperという登録センターとして機能するネーミングサーバーに似たツールが存在しますので、該当する講座で知識を習得してください。

zookeeper-server-start.bat ..\..\config\zookeeper.properties		# 启动zookeeper
kafka-server-start.bat ..\..\config\server.properties				# 启动kafka

bin ディレクトリの下の Windows ディレクトリでzookeeper-server-start コマンドを実行して、登録センターを起動します。デフォルトの外部サービス ポートは 2181 です。

bin ディレクトリの下の Windows ディレクトリで kafka-server-start コマンドを実行して、kafka サーバーを起動します。デフォルトの外部サービス ポートは 9092 です。

トピックを作成する

以前の他の MQ 製品の操作と同様に、kakfa もトピック操作に基づいており、操作前にトピックを初期化する必要があります。

# 创建topic
kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic angyan
# 查询topic
kafka-topics.bat --zookeeper 127.0.0.1:2181 --list					
# 删除topic
kafka-topics.bat --delete --zookeeper localhost:2181 --topic angyan

テストサーバーの起動ステータス

Kafka は、サーバー機能をテストするためのテスト プログラムのセットを提供します。これは、bin ディレクトリの下の Windows ディレクトリでコマンドを実行することで使用できます。

kafka-console-producer.bat --broker-list localhost:9092 --topic angyan							# 测试生产消息
kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic angyan --from-beginning	# 测试消息消费

5.4.7.2. 統合

ステップ① : springboot をインポートして Kafka のスターターを統合します。この座標は springboot によって維持されます

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

ステップ②:Kafkaのサーバーアドレスを設定する

spring:
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: order

デフォルトのプロデューサーコンシューマーグループIDを設定します。

ステップ③:KafkaTemplateを使用してKafkaを操作する

@Service
public class MessageServiceKafkaImpl implements MessageService {
    
    
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    @Override
    public void sendMessage(String id) {
    
    
        System.out.println("待发送短信的订单已纳入处理队列(kafka),id:"+id);
        kafkaTemplate.send("angyan2022",id);
    }
}

send メソッドを使用してメッセージを送信するには、トピック名を渡す必要があります。

ステップ④ : サーバー起動後にメッセージリスナーを使用して指定された場所を監視し、メッセージが表示されたらすぐにメッセージを消費します

@Component
public class MessageListener {
    
    
    @KafkaListener(topics = "test")
    public void onMessage(ConsumerRecord<String,String> record){
    
    
        System.out.println("已完成短信发送业务(kafka),id:"+record.value());
    }
}

@KafkaListener アノテーションを使用して、Kafka で指定されたトピックのメッセージを監視する現在のメソッドを定義します。受信したメッセージはオブジェクト ConsumerRecord にカプセル化され、データは ConsumerRecord オブジェクトから取得できます。

要約する

  1. springbootはKafkaを統合し、KafkaTemplateオブジェクトをクライアントとして使用してメッセージキューを操作します

  2. Kafka を操作するには、Kafka サーバーのアドレスを設定する必要があります。デフォルトのポートは 9092 です。

  3. エンタープライズ開発中、通常、リスナーはメッセージ キュー内のメッセージを処理するために使用され、アノテーション @KafkaListener はリスナーを設定するために使用されます。受信したメッセージは仮パラメータ ConsumerRecord オブジェクトに格納されます

6. モニタリング

6.1. モニタリングの意義

  • 監視サービスのステータスがダウンしています
  • サービス実行インジケーター (メモリ、仮想マシン、スレッド、リクエストなど) を監視します。
  • 監視ログ
  • 管理サービス(オフラインサービス)

現代のインターネットプログラムは、規模がますます大きくなり、機能も複雑化し、より良い顧客体験を追求する必要があるため、監視すべき情報量は比較的多くなっています。現在のインターネット プログラムのほとんどはマイクロサービスに基づいているため、プログラムの動作を保証するために複数のサービスが必要となるため、最初に監視する指標は、サービスが正常に実行されているかどうか、つまり、監視サービスの状態がダウンタイムの状態に対処しているかどうかです

サービスが停止していることが判明したら、アプリケーション機能全体への影響を避けるために、対応するソリューションをただちに提供する必要があります。第二に、インターネット プログラムでは膨大な数の顧客がサービスを受けるため、顧客のリクエストが短期間にサーバーに到達すると、さまざまなプログラムの動作指標に変動が生じます。例えば、メモリの使用量が多く、リクエストへの応答や処理がタイムリーに行えないなど、監視すべき 2 番目に重要な指標である監視サービス稼働指標が挙げられます

ソフトウェアはユーザーのアクセス要件を提供し、対応する機能を完了しますが、バックグラウンド動作が安定しているかどうか、顧客の使用に影響を及ぼさない機能上の隠れた危険性がないかどうか、これらも注意深く監視する必要があります。システムの動作を確認するには、ログが有効な手段です。多数のログの中から開発者や運用保守担当者が気になるログ情報を見つけた場合、簡単、迅速、効果的に閲覧するログを絞り込むことも監視システム側で考慮すべき課題です。監視される 3 番目の指標は、監視プログラムの実行ログです

プログラムは常にスムーズに動作することが期待されますが、サーバーへの攻撃やサーバーのメモリオーバーフローなどの予期せぬ事態により、サーバーがダウンし、現在のサービスでは利用ニーズに応えることができなくなり、サーバーの起動と停止を迅速に制御することも、プログラムの動作過程で避けられない問題です。これが 4 番目の監視項目、管理サービスの状態です

上記はモニタリングの問題を広い視点で考えたものですが、新機能の導入や定期的な料金更新の催促など、まだまだ細かい点はたくさんありますが、この機能は設定後すぐに実行されるわけではありません。が起動されますが、現在の機能は true ですか? まず、クイック クエリでこの機能がオンになっていることが判明した場合、これも監視などで解決すべき問題です。モニタリングというのは実はとても大切な仕事なのだそうです。

上記の説明から、モニタリングが非常に重要であることがわかります。具体的なモニタリングはどのように実施すればよいのでしょうか?実際のプログラム運用の観点からも進める必要がある。たとえば、現在、プログラムの動作をサポートするサービスが 3 つあり、各サービスには独自の動作ステータスがあります。

このとき、監視情報の問い合わせと表示は3つの異なるプログラムで行う必要がありますが、3つのサービスは1つのプログラムの動作を担っており、これらを1つのプラットフォームで結合して表示できない場合、監視の負荷が膨大になり、情報が表示されなくなります。対称性が悪く、3台の監視端末で継続的にデータを確認する必要がある。ビジネスが 30、300、3000 と拡大したらどうなるでしょうか? 複数の監視対象サービスに対応する監視指標情報を集約する別のプラットフォームが必要と思われます。その方が監視業務の発展に役立ちます。

新しいプログラムは監視に特化しているため、監視対象プログラムが積極的に情報を報告しているのか、それとも監視プログラムが積極的に情報を取得しているのか、という新たな問題が生じます。監視プログラムが能動的に情報を取得できないということは、監視プログラムがかなり前に報告した情報を監視プログラムが参照している可能性があることを意味し、監視プログラムがダウンした場合、監視プログラムはダウンしたかどうかを区別できなくなります。長い間情報が不足しており、まだオフラインです。したがって、監視プログラムには、監視対象サービスに関する情報を取得する要求をアクティブに開始する機能が必要です。

監視プログラムがサービスを監視したい場合は、積極的に相手の情報を取得します。監視プログラムは、どのプログラムが自分自身で監視されているかをどのようにして知るのでしょうか? 監視プログラムでは誰を監視するかを設定することはできないため、インターネット上のすべてのプログラムを監視することができ、情報セキュリティが保証されません。合理的なアプローチは、監視対象プログラムの開始時に監視プログラムに報告し、監視できることを監視プログラムに伝えることだけです。アクティブなレポート操作は監視対象プログラム側で実行する必要があるようです。そのためには、対応する監視プログラムを監視対象プログラム内で構成する必要があります。

監視対象プログラムはさまざまな指標データを監視プログラムに提供できますが、各指標は企業の機密情報を表しており、すべての指標は運用担当者や保守担当者を含む誰でも見ることができるわけではないため、監視対象の指標が公開されているかどうかを確認する必要があります。監視システムを詳細に設定する必要があります。

以上が監視システムの基本的な処理である。

要約する

  1. 監視は非常に重要な仕事であり、プログラムが正常に動作することを保証するための基本的な手段です。
  2. 監視プロセスは監視プログラムを通じて実行され、すべての監視対象プログラムの情報が集約されて一元的に表示されます。
  3. 監視対象プログラムは、監視されていることを積極的に報告すると同時に、どのインジケータを監視するかを設定する必要があります。

6.2. ビジュアルモニタリングプラットフォーム

Springboot は、監視システムの一般的な指標のほとんどを抽出し、監視の一般的な概念を提案します。そこで、ある同志が監視という一般的な考え方に基づいて汎用性の高い監視システムを作成し、springboot 監視の核となる考え方に基づいて作成されたため、このプログラムは Spring Boot Adminと名付けられました

Spring Boot Admin は、SpringBoot アプリケーションを管理および監視するためのオープンソース コミュニティ プロジェクトです。このプロジェクトにはクライアントとサーバーの 2 つの部分が含まれており、監視プラットフォームはサーバーを指します。作成したプログラムを監視する必要がある場合は、作成したプログラムをクライアントにし、サーバーのアドレスを設定した後、サーバーは HTTP リクエストを通じてクライアントから対応する情報を取得し、UI インターフェイスを通じて対応する情報を表示できます。

さて、この一連の監視プログラムを開発してみましょう. まず、サーバーを作成します. 実際、サーバーは、何らかの情報を受信した後に情報を表示する Web プログラムとして理解できます。

サーバー開発

ステップ①:springboot adminに対応するスターターをインポートし、バージョンは現在使用されているspringbootのバージョンと一致し、Webプロジェクトとして設定します

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>2.5.4</version>
</dependency>

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

ステップ② : @EnableAdminServer アノテーションをブートクラスに追加し、現在のアプリケーションが起動後に SpringBootAdmin サーバーとして使用されることを宣言します。

@SpringBootApplication
@EnableAdminServer
public class SpringbootAdminServerApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringbootAdminServerApplication.class, args);
    }
}

クライアント開発

クライアント プログラムの開発は、実際には基本的にサーバー側の開発のアイデアと似ていますが、構成がいくつか異なります。

ステップ①:springboot adminに対応するスターターをインポートし、バージョンは現在使用されているspringbootのバージョンと一致し、Webプロジェクトとして設定します

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.5.4</version>
</dependency>

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

ステップ② : 現在のクライアントが情報をアップロードするサーバーを設定し、yml ファイルを通じて設定します。

spring:
  boot:
    admin:
      client:
        url: http://localhost:8080

ご覧のとおり、現在 1 つのプログラムが監視されています。クリックすると詳細情報が表示されます。

ここに画像の説明を挿入

現在、監視サーバーにどのような情報を公開するかの設定がないため、現時点で有効な情報はありません。情報を表示するには、次の 2 つの設定セットを実行する必要があります。

  1. 指定した情報をサーバーに公開します

  2. サーバーが HTTP リクエストの形式で対応する情報を取得できるようにします。

    構成は次のとおりです。

server:
  port: 80
spring:
  boot:
    admin:
      client:
        url: http://localhost:8080
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: "*"

springbootadmin クライアントは、デフォルトで 13 セットの情報をサーバーに開きますが、これらの情報の 1 つを除き、他の情報を HTTP リクエストを通じて表示することはできません。したがって、表示される情報には基本的にコンテンツがなく、1 つのコンテンツのみが表示されます。それは以下の健康情報です。

ここに画像の説明を挿入

しかし、それでも、健康情報には何も表示されません。その理由は、健康情報には、現在のアプリケーションでどのようなテクノロジーが使用されているかを説明する情報が含まれているためです。構成を通じて、すべての健康情報の詳細を開いて表示できます。

management:
  endpoint:
    health:
      show-details: always

詳しい健康情報は以下の通りです。

ここに画像の説明を挿入

現在、健康情報以外の情報は参照できません。その理由は、他の 12 種類の情報はデフォルトでは HTTP リクエストによる表示用にサーバーに提供されないためです。そのため、コンテンツ項目の表示を有効にし、* を使用してすべてのコンテンツを表示できることを示す必要があります。引用符を忘れずに含めてください。

endpoints:
  web:
    exposure:
      include: "*"

設定後にサーバーページを更新すると、すべての情報が表示されます。

ここに画像の説明を挿入

上記インターフェースに表示される情報量は非常に多く、パフォーマンス指標の監視、ロードされたBeanリスト、ロードされたシステムプロパティ、ログ表示制御などを含む13グループの情報が含まれます。

複数のクライアントを構成する

現在のサーバーが複数のクライアント プログラムを監視できるように、クライアントを構成することでクライアント座標を他の Springboot プログラムに追加できます。各クライアントには異なる監視情報が表示されます。

監視パネルに入ります。ロードしたアプリケーションに機能がある場合、以前にロードした空のプロジェクトとは異なる 3 つの情報が監視パネルに表示されます。

  • 開発者定義の
    クラスは

  • 現在のアプリケーション構成に対するすべてのリクエストをマップで表示できます。

  • パフォーマンス インジケーターでは、現在のアプリケーションの一意のリクエスト パス
    統計

要約する

  1. 監視サーバーを開発するには、座標をインポートし、@EnableAdminServer アノテーションをブート クラスに追加して、Web プログラムとして構成する必要があります。
  2. 監視対象クライアントを開発するには、座標をインポートし、サーバーのサーバー アドレスを構成し、オープン インジケータを設定する必要があります。
  3. クライアントが監視対象インジケータを開いている場合、監視プラットフォームでさまざまな監視対象インジケータを見つけることができます。

6.3. モニタリングの原則

モニタリングでマッピング インジケーターを参照すると、現在のシステムで実行できるすべてのリクエスト パスを確認できます。そのほとんどは /actuator で始まります。

第一に、これらのリクエスト パスは開発者自身によって書かれたものではありません。そして第二に、このパスは何を表すのでしょうか? このパスにアクセスできるようになったので、ブラウザ経由でこのリクエストを送信して、どのような情報を取得できるかを確認できます。

リクエストを送信すると、以下のような一連のjson情報を取得できます

{
    
    
    "_links": {
    
    
        "self": {
    
    
            "href": "http://localhost:81/actuator",
            "templated": false
        },
        "beans": {
    
    
            "href": "http://localhost:81/actuator/beans",
            "templated": false
        },
        "caches-cache": {
    
    
            "href": "http://localhost:81/actuator/caches/{cache}",
            "templated": true
        },
        "caches": {
    
    
            "href": "http://localhost:81/actuator/caches",
            "templated": false
        },
        "health": {
    
    
            "href": "http://localhost:81/actuator/health",
            "templated": false
        },
        "health-path": {
    
    
            "href": "http://localhost:81/actuator/health/{*path}",
            "templated": true
        },
        "info": {
    
    
            "href": "http://localhost:81/actuator/info",
            "templated": false
        },
        "conditions": {
    
    
            "href": "http://localhost:81/actuator/conditions",
            "templated": false
        },
        "shutdown": {
    
    
            "href": "http://localhost:81/actuator/shutdown",
            "templated": false
        },
        "configprops": {
    
    
            "href": "http://localhost:81/actuator/configprops",
            "templated": false
        },
        "configprops-prefix": {
    
    
            "href": "http://localhost:81/actuator/configprops/{prefix}",
            "templated": true
        },
        "env": {
    
    
            "href": "http://localhost:81/actuator/env",
            "templated": false
        },
        "env-toMatch": {
    
    
            "href": "http://localhost:81/actuator/env/{toMatch}",
            "templated": true
        },
        "loggers": {
    
    
            "href": "http://localhost:81/actuator/loggers",
            "templated": false
        },
        "loggers-name": {
    
    
            "href": "http://localhost:81/actuator/loggers/{name}",
            "templated": true
        },
        "heapdump": {
    
    
            "href": "http://localhost:81/actuator/heapdump",
            "templated": false
        },
        "threaddump": {
    
    
            "href": "http://localhost:81/actuator/threaddump",
            "templated": false
        },
        "metrics-requiredMetricName": {
    
    
            "href": "http://localhost:81/actuator/metrics/{requiredMetricName}",
            "templated": true
        },
        "metrics": {
    
    
            "href": "http://localhost:81/actuator/metrics",
            "templated": false
        },
        "scheduledtasks": {
    
    
            "href": "http://localhost:81/actuator/scheduledtasks",
            "templated": false
        },
        "mappings": {
    
    
            "href": "http://localhost:81/actuator/mappings",
            "templated": false
        }
    }
}

各データセットにはリクエストパスがあり、ここでリクエストパスには以前に確認されたヘルスが含まれており、このリクエストを送信することで一連の情報が取得されます

{
    
    
    "status": "UP",
    "components": {
    
    
        "diskSpace": {
    
    
            "status": "UP",
            "details": {
    
    
                "total": 297042808832,
                "free": 72284409856,
                "threshold": 10485760,
                "exists": true
            }
        },
        "ping": {
    
    
            "status": "UP"
        }
    }
}

現在の情報と監視パネル内のデータには対応関係があります

モニタリングで表示されている情報は、実際にはリクエスト送信後に取得したjsonデータを表示していることが分かります。上記の操作に従って、/actuator で始まるリンク アドレスをさらに送信して、より多くのデータを取得できます。これらのデータが集約されて、監視プラットフォームに表示されるすべてのデータが形成されます。

ここで重要な情報を取得します。監視プラットフォームに表示される情報は、実際には監視対象のアプリケーションにリクエストを送信することによって取得されます。誰がこれらのリクエストを開発したのでしょうか? 監視対象アプリケーションの pom ファイルを開きます。このファイルにより、springboot admin の対応するクライアントがインポートされ、このリソースに actuator という名前のパッケージがインポートされます。監視対象アプリケーションが上記リクエストパスを外部に提供できるのは、このパッケージの追加によるものです。

エンドポイントともいえるアクチュエータには一連の監視情報が記述されており、SpringBootAdminには複数の組み込みエンドポイントが用意されており、エンドポイントにアクセスすることで対応する監視情報を取得したり、必要に応じてエンドポイント情報をカスタマイズしたりすることも可能です。リクエストパスを送信することで/actuatorアプリケーションのすべてのエンドポイント情報にアクセスでき、エンドポイントに詳細情報がある場合はリクエストを送信して/actuator/端点名称詳細情報を取得できます。すべてのエンドポイント情報の説明を以下に示します。

ID 説明 デフォルトで有効になっています
監査イベント 現在のアプリケーションの監査イベント情報を公開します。 はい
アプリケーション内のすべての Spring Bean の完全なリストを表示します。 はい
キャッシュ 利用可能なキャッシュを公開します。 はい
条件 構成クラスおよび自動構成クラスで評価された条件と、それらが一致した、または一致しなかった理由を示します。 はい
構成プロップ すべての @ConfigurationProperties の証明リストを表示します。 はい
環境 Spring ConfigurableEnvironment でプロパティを公開します。 はい
フライウェイ 適用された Flyway データベース移行を表示します。 はい
health 显示应用程序健康信息
httptrace 显示 HTTP 追踪信息(默认情况下,最后 100 个 HTTP 请求/响应交换)。
info 显示应用程序信息。
integrationgraph 显示 Spring Integration 图。
loggers 显示和修改应用程序中日志记录器的配置。
liquibase 显示已应用的 Liquibase 数据库迁移。
metrics 显示当前应用程序的指标度量信息。
mappings 显示所有 @RequestMapping 路径的整理清单。
scheduledtasks 显示应用程序中的调度任务。
sessions 允许从 Spring Session 支持的会话存储中检索和删除用户会话。当使用 Spring Session 的响应式 Web 应用程序支持时不可用。
shutdown 正常关闭应用程序。
threaddump 执行线程 dump。
heapdump 返回一个 hprof 堆 dump 文件。
jolokia 通过 HTTP 暴露 JMX bean(当 Jolokia 在 classpath 上时,不适用于 WebFlux)。
logfile 返回日志文件的内容(如果已设置 logging.file 或 logging.path 属性)。支持使用 HTTP Range 头来检索部分日志文件的内容。
prometheus 以可以由 Prometheus 服务器抓取的格式暴露指标。

​ 上述端点每一项代表被监控的指标,如果对外开放则监控平台可以查询到对应的端点信息,如果未开放则无法查询对应的端点信息。通过配置可以设置端点是否对外开放功能。使用enable属性控制端点是否对外开放。其中health端点为默认端点,不能关闭。

management:
  endpoint:
    health:						# 端点名称
      show-details: always
    info:						# 端点名称
      enabled: true				# 是否开放

​ 为了方便开发者快速配置端点,springboot admin设置了13个较为常用的端点作为默认开放的端点,如果需要控制默认开放的端点的开放状态,可以通过配置设置,如下:

management:
  endpoints:
    enabled-by-default: true	# 是否开启默认端点,默认值true

​ 上述端点开启后,就可以通过端点对应的路径查看对应的信息了。但是此时还不能通过HTTP请求查询此信息,还需要开启通过HTTP请求查询的端点名称,使用“*”可以简化配置成开放所有端点的WEB端HTTP请求权限。

management:
  endpoints:
    web:
      exposure:
        include: "*"

​ 整体上来说,对于端点的配置有两组信息,一组是endpoints开头的,对所有端点进行配置,一组是endpoint开头的,对具体端点进行配置。

management:
  endpoint:		# 具体端点的配置
    health:
      show-details: always
    info:
      enabled: true
  endpoints:	# 全部端点的配置
    web:
      exposure:
        include: "*"
    enabled-by-default: true

总结

  1. 被监控客户端通过添加actuator的坐标可以对外提供被访问的端点功能

  2. 端点功能的开放与关闭可以通过配置进行控制

  3. web端默认无法获取所有端点信息,通过配置开放端点功能

6.4、自定义监控指标

端点描述了被监控的信息,除了系统默认的指标,还可以自行添加显示的指标,下面就通过3种不同的端点的指标自定义方式来学习端点信息的二次开发。

INFO端点

​ info端点描述了当前应用的基本信息,可以通过两种形式快速配置info端点的信息

  • 配置形式

    在yml文件中通过设置info节点的信息就可以快速配置端点信息

    info:
      appName: @project.artifactId@
      version: @project.version@
      company: 昂焱数据
      author: ayshuju
    

    配置完毕后,对应信息显示在监控平台上

    也可以通过请求端点信息路径获取对应json信息

  • 编程形式

    通过配置的形式只能添加固定的数据,如果需要动态数据还可以通过配置bean的方式为info端点添加信息,此信息与配置信息共存

    @Component
    public class InfoConfig implements InfoContributor {
          
          
        @Override
        public void contribute(Info.Builder builder) {
          
          
            builder.withDetail("runTime",System.currentTimeMillis());		//添加单个信息
            Map infoMap = new HashMap();		
            infoMap.put("buildTime","2006");
            builder.withDetails(infoMap);									//添加一组信息
        }
    }
    

Health端点

​ health端点描述当前应用的运行健康指标,即应用的运行是否成功。通过编程的形式可以扩展指标信息。

@Component
public class HealthConfig extends AbstractHealthIndicator {
    
    
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
    
    
        boolean condition = true;
        if(condition) {
    
    
            builder.status(Status.UP);					//设置运行状态为启动状态
            builder.withDetail("runTime", System.currentTimeMillis());
            Map infoMap = new HashMap();
            infoMap.put("buildTime", "2006");
            builder.withDetails(infoMap);
        }else{
    
    
            builder.status(Status.OUT_OF_SERVICE);		//设置运行状态为不在服务状态
            builder.withDetail("上线了吗?","你做梦");
        }
    }
}

​ 当任意一个组件状态不为UP时,整体应用对外服务状态为非UP状态。

Metrics端点

​ metrics端点描述了性能指标,除了系统自带的监控性能指标,还可以自定义性能指标。

@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
    
    
    @Autowired
    private BookDao bookDao;

    private Counter counter;

    public BookServiceImpl(MeterRegistry meterRegistry){
    
    
        counter = meterRegistry.counter("用户付费操作次数:");
    }

    @Override
    public boolean delete(Integer id) {
    
    
        //每次执行删除业务等同于执行了付费业务
        counter.increment();
        return bookDao.deleteById(id) > 0;
    }
}

​ 在性能指标中就出现了自定义的性能指标监控项

自定义端点

​ 可以根据业务需要自定义端点,方便业务监控

@Component
@Endpoint(id="pay",enableByDefault = true)
public class PayEndpoint {
    
    
    @ReadOperation
    public Object getPay(){
    
    
        Map payMap = new HashMap();
        payMap.put("level 1","300");
        payMap.put("level 2","291");
        payMap.put("level 3","666");
        return payMap;
    }
}

​ 由于此端点数据spirng boot admin无法预知该如何展示,所以通过界面无法看到此数据,通过HTTP请求路径可以获取到当前端点的信息,但是需要先开启当前端点对外功能,或者设置当前端点为默认开发的端点。

总结

  1. エンドポイントのインジケーターはカスタマイズできますが、インジケーターごとに機能に応じてカスタマイズ方法が異なります。
  2. 情報エンドポイントは、構成方法とプログラムによる方法の両方を通じてエンドポイント インジケーターを追加できます。
  3. ヘルスエンドポイントはプログラム的にエンドポイントインジケーターを追加するため、対応するインジケーターの起動ステータスを追加するロジックの設定に注意する必要があります。
  4. メトリクス指標 ビジネスに監視操作を追加して指標を設定する
  5. エンドポイントをカスタマイズしてインジケーターを追加できます

おすすめ

転載: blog.csdn.net/shuai_h/article/details/130030579