SpringBoot 分散トランザクションのベスト エフォート通知

SpringBoot 分散トランザクションのベスト エフォート通知

[[393657]]

環境: springboot.2.4.9 + RabbitMQ3.7.4

ベストエフォート通知とは何ですか?

これは充電のケースです

対話プロセス:

1. アカウント システムが再チャージ システム インターフェイスを呼び出します。

2. 再チャージシステムが支払いを完了すると、アカウントシステムに再チャージ結果通知が送信されます。通知が失敗した場合、再充電システムは戦略に従って通知を繰り返します。

3. アカウントシステムは再チャージ結果通知を受信し、再チャージステータスを変更します。

4. アカウント システムが通知を受信しない場合、再チャージ システムのインターフェイスを積極的に呼び出して再チャージの結果を照会します。

上記の例から、ベスト エフォート通知スキームの目標をまとめることができます。つまり、通知の発信者は、特定のメカニズムを通じてビジネス処理の結果を受信者に通知するために最善の努力をします。具体的には以下が含まれます:

1. 特定のメッセージ繰り返し通知メカニズムがあります。通知の受信者が通知を受け取っていない可能性があるため、メッセージを繰り返すための特定のメカニズムが必要です。

2. メッセージ校正メカニズム。最善の努力にもかかわらず受信者に通知されない場合、または受信者がメッセージを消費した後に再度消費する必要がある場合、受信者は要求を満たすために通知側にメッセージ情報を積極的に問い合わせることができます。

ベストエフォート通知と信頼性の高いメッセージ一貫性の違いは何ですか?

1. さまざまなソリューションのアイデア: 信頼性の高いメッセージの一貫性。通知の発信者は、メッセージが送信され、通知の受信者に届くことを確認する必要があります。メッセージの信頼性は主に通知の発信者によって保証されます。ベスト エフォート通知: 通知の発信者は、通知の受信者にビジネス処理の結果を通知するために最善の努力を払いますが、メッセージが受信されない可能性があります。この場合、通知の受信者は、ビジネス処理の結果を照会するために、イニシエーターのインターフェースを積極的に呼び出す必要があります。通知の信頼性は通知の受信者によって異なります。

2. 両者のビジネスアプリケーションシナリオは異なります。信頼性の高いメッセージの一貫性は、トランザクション プロセスのトランザクション一貫性に重点を置き、非同期方式でトランザクションを完了します。ベスト エフォート通知は、トランザクション後の通知、つまりトランザクション結果を確実に通知することに重点を置いています。

3. さまざまな技術的ソリューション 信頼性の高いメッセージの一貫性を実現するには、送信から受信までのメッセージの一貫性、つまりメッセージの送受信を解決する必要があります。ベストエフォート通知では、送信から受信までのメッセージの一貫性を保証することはできませんが、メッセージ受信の信頼性メカニズムのみを提供します。信頼できるメカニズムは、メッセージの受信者に通知するために最大限の努力をすることです。受信者がメッセージを受信できない場合、受信者は消費について積極的に問い合わせます。

RabbitMQ によるベストエフォート通知

RabbitMQ に関する関連記事: 「SpringBoot RabbitMQ メッセージの信頼性の高い送受信」、「RabbitMQ メッセージ確認メカニズムの確認」。

プロジェクト構造

2 つのサブモジュール: users-mananger (アカウント モジュール)、pay-manager (支払いモジュール)

頼る

  1. <依存関係>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-jpa</artifactId>
  4. </依存関係>
  5. <依存関係>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-web</artifactId>
  8. </依存関係>
  9. <依存関係>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-amqp</artifactId>
  12. </依存関係>
  13. <依存関係>
  14. <グループID>mysql</グループID>
  15. <artifactId>mysql-コネクタ-java</artifactId>
  16. <scope>ランタイム</scope>
  17. </依存関係>

サブモジュール pay-manager

設定ファイル

  1. サーバ:
  2. ポート: 8080
  3. ---  
  4. 春:
  5. ウサギさん:
  6. ホスト: ローカルホスト
  7. ポート: 5672
  8. ユーザー名: ゲスト
  9. パスワード: ゲスト
  10. 仮想ホスト: /
  11. 発行者確認タイプ: 相関
  12. 発行者戻り値: true  
  13. リスナー:
  14. 単純:
  15. 同時実行数: 5
  16. 最大同時実行数: 10
  17. プリフェッチ: 5
  18. 承認モード: 手動
  19. リトライ:
  20. 有効: true  
  21. 初期間隔: 3000
  22. 最大試行回数: 3
  23. デフォルトの再キュー拒否: false  

エンティティクラス

チャージ金額とアカウント情報を記録する

  1. @実在物
  2. @テーブル(名前= "t_pay_info" )
  3. パブリッククラス PayInfo はSerializable を実装します{
  4. @ID
  5. プライベートな Long ID;
  6. プライベート BigDecimal マネー;
  7. プライベート Long accountId ;
  8. }

DAOとサービス

  1. パブリックインターフェース PayInfoRepository は JpaRepository<PayInfo, Long> を拡張します {
  2. PayInfo findByOrderId(文字列 orderId);
  3. }
  1. @サービス
  2. パブリッククラス PayInfoService {
  3.      
  4. @リソース
  5. プライベート PayInfoRepository payInfoRepository ;
  6. @リソース
  7. プライベート RabbitTemplate rabbitTemplate ;
  8.      
  9. // データが保存された後にメッセージを送信します(メッセージは確認モードまたはトランザクションモードで送信できます)
  10. @トランザクション
  11. パブリックPayInfo savePayInfo(PayInfo payInfo) {
  12. payInfo.setId(System.currentTimeMillis());
  13. PayInfo 結果 = payInfoRepository.save(payInfo);
  14. 相関データ correlationData = new CorrelationData(UUID.randomUUID().toString().replaceAll( "-" , "" ));
  15. 試す {
  16. rabbitTemplate.convertAndSend( "pay-exchange" "pay.#" 、新しいObjectMapper().writeValueAsString(payInfo)、相関データ);
  17. } キャッチ (AmqpException | JsonProcessingException e) {
  18. e.printStackTrace();
  19. }
  20. 結果を返します
  21. }
  22.      
  23. パブリックPayInfo queryByOrderId(String orderId) {
  24. payInfoRepository.findByOrderId(orderId)を返します
  25. }
  26.      
  27. }

お支払いが完了したらメッセージを送信してください。

コントローラーインターフェース

  1. @レストコントローラ
  2. @RequestMapping( "/payInfos" )
  3. パブリッククラス PayInfoController {
  4. @リソース
  5. プライベート PayInfoService payInfoService ;
  6.      
  7. // 支払いインターフェース
  8. @PostMapping( "/pay" )
  9. パブリックオブジェクト支払い(@RequestBody PayInfo payInfo) {
  10. payInfoService.savePayInfo(payInfo);
  11. 戻る  「支払いが送信されました。結果を待っています」 ;
  12. }
  13.      
  14. @GetMapping( "/queryPay" )
  15. パブリックオブジェクトクエリペイ(文字列オーダーID) {
  16. payInfoService.queryByOrderId(orderId)を返します
  17. }
  18.      
  19. }

サブモジュール users-manager

アプリケーション構成

  1. サーバ:
  2. ポート: 8081
  3. ---  
  4. 春:
  5. ウサギさん:
  6. ホスト: ローカルホスト
  7. ポート: 5672
  8. ユーザー名: ゲスト
  9. パスワード: ゲスト
  10. 仮想ホスト: /
  11. 発行者確認タイプ: 相関
  12. 発行者戻り値: true  
  13. リスナー:
  14. 単純:
  15. 同時実行数: 5
  16. 最大同時実行数: 10
  17. プリフェッチ: 5
  18. 承認モード: 手動
  19. リトライ:
  20. 有効: true  
  21. 初期間隔: 3000
  22. 最大試行回数: 3
  23. デフォルトの再キュー拒否: false  

エンティティクラス

  1. @実在物
  2. @テーブル(名前= "t_users" )
  3. パブリッククラスUsers{
  4. @ID
  5. プライベートな Long ID;
  6. プライベート文字列;
  7. プライベート BigDecimal マネー;
  8. }

アカウント情報フォーム

  1. @実在物
  2. @テーブル(名前= "t_users_log" )
  3. パブリッククラスUsersLog{
  4. @ID
  5. プライベートな Long ID;
  6. プライベート文字列 orderId ;
  7. // 0: 支払い中、1: 支払い済み、2: キャンセル済み
  8. @(columnDefinition = "int default 0" )
  9. プライベート整数ステータス = 0 ;
  10. プライベート BigDecimal マネー;
  11. プライベートDate createTime ;
  12. }

アカウント再チャージ記録テーブル(重複排除)

DAOとサービス

  1. パブリックインターフェースUsersRepositoryはJpaRepository<Users, Long>を拡張します。
  2. }
  3. パブリックインターフェースUsersLogRepositoryはJpaRepository<UsersLog, Long>を拡張します。
  4. ユーザーログ findByOrderId(文字列 orderId);
  5. }

サービスクラス

  1. @サービス
  2. パブリッククラスUsersService{
  3. @リソース
  4. プライベートUsersRepository usersRepository;
  5. @リソース
  6. プライベートUsersLogRepository usersLogRepository;
  7.      
  8. @トランザクション
  9. パブリックブール型 updateMoneyAndLogStatus(Long id, String orderId) {
  10. ユーザーログ usersLog = usersLogRepository.findByOrderId(orderId);
  11. (usersLog != null && 1 == usersLog.getStatus()) の場合 {
  12. 新しい RuntimeException( "paid" ) をスローします。
  13. }
  14. ユーザー users = usersRepository.findById(id).orElse( null );
  15. ユーザー == null場合
  16. 新しい RuntimeException( "アカウントが存在しません" ) をスローします。
  17. }
  18. users.setMoney(users.getMoney(). add (usersLog.getMoney()));
  19. usersRepository.save(ユーザー);
  20. ユーザーログのステータスを設定します(1);
  21. usersLog リポジトリを保存します。
  22. 戻る 真実;
  23. }
  24.      
  25. @トランザクション
  26. パブリックブール値 saveLog(UsersLog usersLog) {
  27. ユーザーログにIdを設定します。
  28. usersLog リポジトリを保存します。
  29. 戻る 真実;
  30. }
  31. }

メッセージ監視

  1. @成分
  2. パブリッククラス PayMessageListener {
  3.      
  4. プライベート静的最終ロガー logger = LoggerFactory.getLogger(PayMessageListener.class);
  5.      
  6. @リソース
  7. プライベートUsersService usersService;
  8.      
  9. @SuppressWarnings( "チェックなし" )
  10. @RabbitListener(キュー = { "ペイキュー" })
  11. @RabbitHandler
  12. パブリックvoid 受信(メッセージ メッセージ、チャネル チャネル) {
  13. 長い配信タグ = message.getMessageProperties().getDeliveryTag();
  14. バイト[] buf = null ;
  15. 試す {
  16. buf = message.getBody();
  17. logger.info( "受信したメッセージ: {}" , new String(buf, "UTF-8" )) ;
  18. Map<String, Object> 結果 = new JsonMapper().readValue(buf, Map.class);
  19. Long id = (( Integer ) result.get( "accountId" )) + 0L ;
  20. 文字列 orderId = (文字列) result.get( "orderId" ) ;
  21. usersService.updateMoneyAndLogStatus(id、orderId);
  22. チャネル.basicAck(配信タグ、 true );
  23. } キャッチ (例外 e) {
  24. logger.error( "メッセージ受信中に例外が発生しました: {}、例外メッセージ: {}" 、 e.getMessage()、 new String(buf、 Charset.forName( "UTF-8" ))) ;
  25. e.printStackTrace();
  26. 試す {
  27. // このような異常なメッセージは、手動で調査するためにデッドレターキューに入れる必要があります。
  28. チャネル.basicReject(配信タグ、 false );
  29. } キャッチ (IOException e1) {
  30. logger.error( "メッセージ再エントリキュー例外を拒否: {}" , e1.getMessage());
  31. e1.printStackTrace();
  32. }
  33. }
  34. }
  35. }

コントローラーインターフェース

  1. @レストコントローラ
  2. @RequestMapping( "/users" )
  3. パブリッククラスUsersController{
  4.      
  5. @リソース
  6. プライベート RestTemplate 残りのテンプレート ;
  7. @リソース
  8. プライベートUsersService usersService;
  9.      
  10. @PostMapping( "/pay" )
  11. パブリックオブジェクトpay(Long id, BigDecimal money)は例外をスローします{
  12. HttpHeaders ヘッダー = new HttpHeaders();
  13. headers.setContentType(MediaType.APPLICATION_JSON);
  14. 文字列 orderId = UUID.randomUUID().toString().replaceAll( "-" , "" );
  15. Map<String, String> パラメータ = new HashMap<>();
  16. params.put( "accountId" , String.valueOf(id));
  17. params.put( "orderId" 、orderId);
  18. params.put( "お金" 、money.toString());
  19.          
  20. ユーザーログ usersLog = 新しいユーザーログ() ;
  21. usersLog.setCreateTime(新しい日付());
  22. usersLog.setOrderId(注文ID);
  23. usersLog.setMoney(お金);
  24. ユーザーログのステータスを0に設定します。
  25. usersService.saveLog(usersLog);
  26. HttpEntity<String> requestEntity = new HttpEntity<String>(new ObjectMapper().writeValueAsString(params), headers);
  27. restTemplate.postForObject( "http://localhost:8080/payInfos/pay" 、 requestEntity、 String.class )を返します
  28. }
  29.      
  30. }

上記は2つのサブモジュールのコード全体です

テスト

初期データ

アカウントサブモジュールコンソール

支払いサブモジュールコンソール

データテーブルデータ

完了! ! !

<<:  エッジコンピューティングは業界のデジタル変革に貢献します

>>:  気をつけてください、あなたの声が盗まれます!テンセント朱雀研究所の最新研究結果が明らかに:音声セキュリティを過小評価すべきではない

推薦する

同意しますか?コンピューティングの未来は分散化です!

[51CTO.com クイック翻訳] 分散アプリケーションは何も新しいものではありません。最初の分散...

aim2game-6ドル/2gメモリ/50g SSD/2Tトラフィック/ニューヨーク

aim2gameは2009年3月に設立され、主にMCホスティング事業を営み、その後VPS事業も手掛け...

6 億人のユーザーと 600 億ドルの市場規模を誇るオンライン ビデオにとって、防御壁はどこにあるのでしょうか?

1930年代には『ウォータールー橋』『風と共に去りぬ』『ミッキーマウスとドナルドダック』など優れた映...

インターフェース監視用の Prometheus エクスポーターを開発する

ブラックボックス監視については皆さんもよくご存知だと思います。ブラックボックスの監視には black...

「枢機卿」周鴻義の1分20秒のプレー:李延紅を覚醒させる

百度360戦争は検索「三国志」時代の到来を告げる360は検索市場で勢いを増しており、年末までに商用化...

忘れられがちなSEOのヒント: キーワード調査

みなさんこんにちは。SEOを行う際、キーワードリサーチに重点が置かれます。多くの友人がこの点で良い仕...

企業のWeiboマーケティングを成功させるための8つの提案

Weiboマーケティングは必須です。しかし、ほとんどの企業はWeiboアカウントを開設して認証を受け...

ninjahawk-512M メモリ KVM/フェニックスデータセンター/月額 7 ドル

Ninjahawk は、米国ニューヨークに登録されているホスティング プロバイダーです。サーバー構成...

クラウド移行を始める前に尋ねるべき3つの質問

多くの場合、クラウド移行は最初は停滞しているか、遅すぎるように感じられますが、実際にはビジネスが速す...

SEO 最適化に関して、SEO レポートはどのように書けばよいですか?

月収10万元の起業の夢を実現するミニプログラム起業支援プランSEO にとって、それはオンライン マー...

chicagovps-$60/3 年/2g メモリ/50g ハード ドライブ/2IP/2T トラフィック

Chicagogovs がサイバー マンデーの超割引を発表しました。2G メモリ、50G ハード ド...

formohost: アジア、アメリカ、ヨーロッパ、オセアニアの36の国と地域でVPSと専用サーバーを運営しています

米国デラウェア州に登録されているホスティング会社であるformohostは、世界36か所以上のデータ...

格安ドメイン名: 7 月、新規登録、複数のサフィックスが 0.99 ドルから

毎月、安いドメイン名について思い出せたらいいなと思っています。忘れてしまったら、興味のある人が私に思...

貧弱なウェブサイト構築はマーケティングプロモーションの突然の死につながる

中小企業が有料検索エンジン広告を通じて自社の製品やサービスを宣伝することは効果的でしょうか? その答...

北京インターネット監視センターは、一時的にアクセス不能になっていた「茶観坊」ウェブサイトの調査を開始した。

北京新聞(記者 林葉) 北京新聞は10月21日と22日、インターネット上に「ホテル予約チェック」のウ...