Springboot2.x AOPはキャッシュロックと分散ロックを実装します

Springboot2.x AOPはキャッシュロックと分散ロックを実装します

[[410877]]

Springboot2.x AOPは重複送信を防ぐためにキャッシュロックと分散ロックを実装しています。

私はバックエンドシステムで長年の経験を持っています。ユーザーがネットワーク状態が悪い状態でフォームを送信すると、送信が繰り返し発生する可能性があります。したがって、フォームが再度送信されないようにする必要があります。

Google の Guave Cache

  1. <依存関係>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </依存関係>
  5. <依存関係>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-aop</artifactId>
  8. </依存関係>
  9. <依存関係>
  10. <groupId>com.google.guava</groupId>
  11. <artifactId>グアバ</artifactId>
  12. <バージョン>21.0</バージョン>
  13. </依存関係>

注釈インターフェース

  1. パッケージ com.ouyue.xiwenapi.annotation;
  2.  
  3. java.lang.annotation.* をインポートします。
  4.  
  5. /**
  6. * @クラス名:${}
  7. * @説明:TODO
  8. * @著者:[email protected]
  9. * @日付
  10. */
  11. @ターゲット(要素タイプ.METHOD)
  12. @保持(保持ポリシー.RUNTIME)
  13. @文書化済み
  14. @継承
  15. パブリック@interface GuaveLock
  16. {
  17.  
  18. 文字列キー()デフォルト  "" ;
  19.  
  20. /**
  21. * 有効期限 TODO guava を使用しているため、現時点ではこのプロパティは無視します。 Redisを統合するために必要
  22. *
  23. * @author フライ
  24. */
  25. int expire()デフォルト5;
  26. }

AOPの応用

  1. パッケージ com.ouyue.xiwenapi.config;
  2.  
  3. com.google.common.cache.Cache をインポートします。
  4. com.google.common.cache.CacheBuilder をインポートします。
  5. com.ouyue.xiwenapi.annotation.GuaveLock をインポートします。
  6. org.aspectj.lang.ProceedingJoinPoint をインポートします。
  7. org.aspectj.lang.annotation.Around をインポートします。
  8. org.aspectj.lang.annotation.Aspect をインポートします。
  9. org.aspectj.lang.reflect.MethodSignature をインポートします。
  10. org.springframework.context.annotation.Configuration をインポートします。
  11. org.springframework.util.StringUtils をインポートします。
  12.  
  13. java.lang.reflect.Method をインポートします。
  14. java.util.concurrent.TimeUnitをインポートします。
  15.  
  16. /**
  17. * @クラス名:${}
  18. * @説明:TODO
  19. * @著者:[email protected]
  20. * @日付
  21. */
  22. @側面
  23. @構成
  24. パブリッククラスLockMethodAopConfigure {
  25. プライベート静的最終Cache<String, Object> CACHES = CacheBuilder.newBuilder()
  26. // 最大キャッシュサイズ: 100
  27. .最大サイズ(1000)
  28. // 書き込みキャッシュを5秒で期限切れになるように設定します
  29. .expireAfterWrite(5, 時間単位.SECONDS)
  30. 。建てる();
  31.  
  32. @Around( "実行(public * *(..)) && @annotation(com.ouyue.xiwenapi.annotation.GuaveLock)" )
  33. パブリックオブジェクトインターセプター(ProceedingJoinPoint pjp) {
  34. メソッド署名署名 = (メソッド署名) pjp.getSignature();
  35. メソッド method = signature.getMethod();
  36. GuaveLock の localLock = method.getAnnotation(GuaveLock.class);
  37. 文字列キー= getKey(localLock.key ( ), pjp.getArgs());
  38. if (!StringUtils.isEmpty(キー)) {
  39. CACHES.getIfPresent(キー) != null場合
  40. throw new RuntimeException( "リクエストを繰り返さないでください" );
  41. }
  42. // 最初のリクエストの場合は、キーの現在のオブジェクトをキャッシュにプッシュします
  43. CACHES.put(キー,キー);
  44. }
  45. 試す {
  46. pjp.proceed()を返します
  47. } キャッチ (Throwable スロー可能) {
  48. 新しい RuntimeException( "サーバー例外" ) をスローします。
  49. ついに
  50. // TODO 効果を示すために、CACHES.invalidate( key );コードはここでは呼び出されません
  51. }
  52. }
  53.  
  54. /**
  55. *キー生成戦略は、柔軟性が必要な場合は、インターフェースと実装クラスの形式で記述できます(TODOは後で説明します)
  56. *
  57. * @param keyExpress式
  58. * @param argsパラメータはMD5を使用して暗号化できます
  59. * @return生成されたキー 
  60. */
  61. プライベート文字列getKey(文字列keyExpress、オブジェクト[]引数) {
  62. ( int i = 0; i < args.length; i++) {
  63. keyExpress = keyExpress.replace ( "arg[" + i + "]" 、 args[i].toString());
  64. }
  65. keyExpressを返します
  66. }
  67. }

コントローラ

  1. @レストコントローラ
  2. @RequestMapping( "/ビジネス" )
  3. パブリッククラス BusinessController {
  4. @GuaveLock(キー= "ビジネス:arg[0]" )
  5. @GetMapping
  6. パブリック文字列クエリ(@RequestParam 文字列トークン) {
  7. 戻る  「成功 - 」 + トークン;
  8. }
  9. }

上記は基本的にメモリレベルのキャッシュであり、分散システムでは満たすことができません。したがって、分散システムでそれらを使用できる必要があります。

Redis キャッシュロックの実装

ドキュメント

  1. <依存関係>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </依存関係>
  5. spring.redis.host=ローカルホスト
  6. spring.redis.port=6379

レディロック

  1. プレフィックス: キャッシュ内のキーのプレフィックス
  2. 有効期限: 有効期限、デフォルト値は5秒
  3. timeUnit: タイムアウトの単位。デフォルトは秒です。
  4. 区切り文字: キー区切り文字。異なるパラメータ値を区切ります。
  1. パッケージ com.ouyue.xiwenapi.annotation;
  2.  
  3. java.lang.annotation.* をインポートします。
  4. java.util.concurrent.TimeUnitをインポートします。
  5.  
  6. /**
  7. * @クラス名:${}
  8. * @説明:TODO
  9. * @著者:[email protected]
  10. * @日付
  11. */
  12. @ターゲット(要素タイプ.METHOD)
  13. @保持(保持ポリシー.RUNTIME)
  14. @文書化済み
  15. @継承
  16. パブリック@interface RedisLock {
  17. /**
  18. * Redisロックキーのプレフィックス
  19. *
  20. * @return redis ロックキープレフィックス
  21. */
  22. 文字列プレフィックス()デフォルト  "" ;
  23.  
  24. /**
  25. * 有効期限の秒数、デフォルトは5秒
  26. *
  27. * @returnポーリングロック時間
  28. */
  29. int expire()デフォルト5;
  30.  
  31. /**
  32. * タイムアウト単位
  33. *
  34. * @return
  35. */
  36. TimeUnit timeUnit()デフォルトTimeUnit.SECONDS;
  37.  
  38. /**
  39. * <p>キーセパレーター (デフォルト:)</p>
  40. * <p>生成されたキー: N:SO1008:500</p>
  41. *
  42. * @return文字列
  43. */
  44. 文字列区切り文字()デフォルト  ":" ;
  45. }

CacheParam アノテーション

  1. パッケージ com.ouyue.xiwenapi.annotation;
  2.  
  3. java.lang.annotation.* をインポートします。
  4.  
  5. /**
  6. * @クラス名:${}
  7. * @説明:TODO
  8. * @著者:[email protected]
  9. * @日付
  10. */
  11. @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
  12. @保持(保持ポリシー.RUNTIME)
  13. @文書化済み
  14. @継承
  15. パブリック@interface CacheParam {
  16. /**
  17. * フィールド名
  18. *
  19. * @return文字列
  20. */
  21. 文字列()デフォルト  "" ;
  22. }

キー生成戦略

  1. パッケージ com.ouyue.xiwenapi.component;
  2.  
  3. org.aspectj.lang.ProceedingJoinPoint をインポートします。
  4.  
  5. パブリックインターフェースCacheKeyGenerator {
  6. /**
  7. * AOPパラメータを取得し、指定されたキャッシュキーを生成する 
  8. *
  9. * @param pjp PJP
  10. * @returnキャッシュキー 
  11. */
  12. 文字列 getLockKey(ProceedingJoinPoint pjp);
  13. }

鍵生成戦略(実装)

  1. パッケージ com.ouyue.xiwenapi.service;
  2.  
  3. com.ouyue.xiwenapi.annotation.CacheParam をインポートします。
  4. com.ouyue.xiwenapi.annotation.RedisLock をインポートします。
  5. com.ouyue.xiwenapi.comonet.CacheKeyGenerator をインポートします。
  6. org.aspectj.lang.ProceedingJoinPoint をインポートします。
  7. org.aspectj.lang.reflect.MethodSignature をインポートします。
  8. org.springframework.util.ReflectionUtils をインポートします。
  9. org.springframework.util.StringUtils をインポートします。
  10.  
  11. java.lang.annotation.Annotation をインポートします。
  12. java.lang.reflect.Field をインポートします。
  13. java.lang.reflect.Method をインポートします。
  14. java.lang.reflect.Parameterをインポートします。
  15.  
  16. /**
  17. * @クラス名:${}
  18. * @説明:TODO
  19. * @著者:[email protected]
  20. * @日付
  21. */
  22. パブリッククラスLockKeyGeneratorはCacheKeyGeneratorを実装します{
  23. @オーバーライド
  24. パブリック文字列 getLockKey(ProceedingJoinPoint pjp) {
  25. メソッド署名署名 = (メソッド署名) pjp.getSignature();
  26. メソッド method = signature.getMethod();
  27. RedisLock lockAnnotation = method.getAnnotation(RedisLock.class);
  28. 最終 Object[] args = pjp.getArgs();
  29. 最終的なパラメータ[]パラメータ = method.getParameters();
  30. StringBuilder ビルダー = new StringBuilder();
  31. // TODO デフォルトの解析メソッドには、CacheParam アノテーション付きプロパティが含まれています。エンティティオブジェクトを解析しようとしない場合
  32. ( int i = 0; i < パラメータ長さ; i++) {
  33. 最終的なCacheParamアノテーション = パラメータ[i].getAnnotation(CacheParam.class);
  34. if (アノテーション == null ) {
  35. 続く;
  36. }
  37. builder.append(lockAnnotation.delimiter()).append(args[i]);
  38. }
  39. StringUtils.isEmpty(builder.toString()) の場合 {
  40. 最終的な Annotation[][] パラメータ Annotations = method.getParameterAnnotations();
  41. ( int i = 0; i < パラメータ注釈のさ; i++) {
  42. 最終的なオブジェクトオブジェクト = args[i];
  43. 最終的なフィールド[] fields = object.getClass().getDeclaredFields();
  44. for (フィールド フィールド: フィールド) {
  45. 最終的な CacheParam アノテーション = field.getAnnotation(CacheParam.class);
  46. if (アノテーション == null ) {
  47. 続く;
  48. }
  49. フィールドをアクセス可能に設定( true );
  50. builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object));
  51. }
  52. }
  53. }
  54. lockAnnotation.prefix() + builder.toString()を返します
  55. }
  56. }

ロックインターセプター (AOP)

  1. パッケージ com.ouyue.xiwenapi.config;
  2.  
  3. com.ouyue.xiwenapi.annotation.RedisLock をインポートします。
  4. com.ouyue.xiwenapi.comonet.CacheKeyGenerator をインポートします。
  5. org.aspectj.lang.ProceedingJoinPoint をインポートします。
  6. org.aspectj.lang.annotation.Around をインポートします。
  7. org.aspectj.lang.annotation.Aspect をインポートします。
  8. org.aspectj.lang.reflect.MethodSignature をインポートします。
  9. org.springframework.beans.factory.annotation.Autowired をインポートします。
  10. org.springframework.context.annotation.Configuration をインポートします。
  11. org.springframework.data.redis をインポートします。接続.RedisStringCommands;
  12. org.springframework.data.redis.core.RedisCallback をインポートします。
  13. org.springframework.data.redis.core.StringRedisTemplate をインポートします。
  14. org.springframework.data.redis.core.types.Expiration をインポートします。
  15. org.springframework.util.StringUtils をインポートします。
  16.  
  17. java.lang.reflect.Method をインポートします。
  18.  
  19. /**
  20. * @クラス名:${}
  21. * @説明:TODO
  22. * @著者:[email protected]
  23. * @日付
  24. */
  25. @側面
  26. @構成
  27. パブリッククラスLockMethodInterceptor {
  28. オートワイヤード
  29. パブリックLockMethodInterceptor(StringRedisTemplate lockRedisTemplate、CacheKeyGenerator cacheKeyGenerator) {
  30. this.lockRedisTemplate = lockRedisTemplate;
  31. this.cacheKeyGenerator = キャッシュキージェネレータ;
  32. }
  33.  
  34. プライベート最終 StringRedisTemplate lockRedisTemplate;
  35. プライベート最終 CacheKeyGenerator cacheKeyGenerator;
  36.  
  37.  
  38. @Around( "実行(public * *(..)) && @annotation(com.ouyue.xiwenapi.annotation.RedisLock)" )
  39. パブリックオブジェクトインターセプター(ProceedingJoinPoint pjp) {
  40. メソッド署名署名 = (メソッド署名) pjp.getSignature();
  41. メソッド method = signature.getMethod();
  42. RedisLock ロック = method.getAnnotation(RedisLock.class);
  43. StringUtils.isEmpty(lock.prefix()) の場合 {
  44. 新しい RuntimeException をスローします ( "ロック キーは null ではありません..." );
  45. }
  46. 最終的な文字列 lockKey = cacheKeyGenerator.getLockKey(pjp);
  47. 試す {
  48. // ネイティブAPIを使用して分散ロックを実装する
  49. 最終的なブール値の成功 = lockRedisTemplate。実行((RedisCallback<Boolean>) connection -> connection . set (lockKey.getBytes(), new byte[0], Expiration. from (lock.expire(), lock.timeUnit()), RedisStringCommands.SetOption.SET_IF_ABSENT));
  50. 成功の場合
  51. // TODO 論理的には、カスタム CacheLockException をスローする必要があります。ここでは怠けましょう
  52. throw new RuntimeException( "リクエストを繰り返さないでください" );
  53. }
  54. 試す {
  55. pjp.proceed()を返します
  56. } キャッチ (Throwable スロー可能) {
  57. 新しい RuntimeException( "システム例外" ) をスローします。
  58. }
  59. ついに
  60. // TODO これを実証したい場合は、このコードをコメント化する必要があります。実際には、開いたままにしておく必要があります。
  61. //Redisテンプレートをロックします。削除(lockKey);
  62. }
  63. }
  64. }

聞く

  1. パッケージ com.ouyue.xiwenapi.controller;
  2.  
  3. com.ouyue.xiwenapi.annotation.CacheParam をインポートします。
  4. com.ouyue.xiwenapi.annotation.GuaveLock をインポートします。
  5. com.ouyue.xiwenapi.annotation.RedisLock をインポートします。
  6. org.springframework.web.bind.annotation.GetMapping をインポートします。
  7. org.springframework.web.bind.annotation.RequestMapping をインポートします。
  8. org.springframework.web.bind.annotation.RequestParam をインポートします。
  9. org.springframework.web.bind.annotation.RestController をインポートします。
  10.  
  11. /**
  12. * @クラス名:${}
  13. * @説明:TODO
  14. * @著者:[email protected]
  15. * @日付
  16. */
  17.  
  18. @レストコントローラ
  19. @RequestMapping( "/ビジネス" )
  20. パブリッククラス BusinessController {
  21. @GuaveLock(キー= "ビジネス:arg[0]" )
  22. @GetMapping
  23. パブリック文字列クエリ(@RequestParam 文字列トークン) {
  24. 戻る  「成功 - 」 + トークン;
  25. }
  26.  
  27. @RedisLock(プレフィックス = "ユーザー" )
  28. @GetMapping
  29. パブリック文字列queryRedis(@CacheParam( name = "token" ) @RequestParam 文字列token) {
  30. 戻る  「成功 - 」 + トークン;
  31. }
  32. }

メイン関数のスタートアップクラスに、キー生成戦略関数を挿入します。

  1. @ビーン
  2. パブリックCacheKeyGenerator キャッシュキージェネレーター() {
  3. 新しい LockKeyGenerator()を返します
  4. }

<<:  ブルーグリーン/カナリアリリース向けの Argo ロールアウト

>>:  クラウドネイティブシステムの弾力性モデル

推薦する

マーケティングウェブサイト運営のCRM管理に関する簡単な説明

CRM 管理システムの全体的な中核は、ユーザーデータの管理です。ユーザー データベースをデータ セン...

データセンターのエッジコンピューティング機能の拡大から誰が恩恵を受けるのでしょうか?

日常生活では、ティーンエイジャーが Xbox でゲームをプレイしながら、タブレットで YouTube...

テンセントクラウド:最後のチャンス、219元/3年、4Gメモリ/2コア/60g SSD/6M帯域幅、LinuxとWindowsをサポート

少し前に、Tencent Cloud は 180 元 / 3 年の軽量クラウド サーバー アクティビ...

ブランドマーケティングオペレーション戦略

成功したいなら、心、スキル、身体の3つの段階を経なければなりません。上から下へ心(認知)、スキル(専...

hosteons: VPS 20% 割引クーポン (流通)、中国語 Windows、無制限トラフィック

Hosteons は 12 月に新しい 20% 割引コードを開始しました。この VPS プロモーショ...

buyvm - メカニカルディスクはSSDに無料で交換可能、これも購入の理由

buyvm は 2010 年以来、低価格 VPS ランキングで常に上位 3 位にランクされています。...

Bステーションはサークル外、UPホストは左

Bilibiliはサークルから抜け、UPホストも去っていきます。 6月14日、「ウィザードファイナン...

Listia: ポイントオークションモデルを使用した未使用アイテムを扱うネットワーク

北京時間2月2日、外国メディアの報道によると、多くの人が次のような問題に直面している。処分しなければ...

企業がビジネスをクラウドに移行する新たな理由

ポストエピデミック時代において、クラウド コンピューティングが企業にもたらすメリットは、設備投資の節...

Baidu の検索ページでウェブサイトのキーワードを上位にランク付けする方法の簡単な分析

あなたのウェブサイトが Baidu でもっと上位にランクインしたい場合、あるいはウェブサイトのキーワ...

クラウドプラットフォームへのデータベース移行を慎重に計画する方法

今日、多くの組織がデータベースをクラウドに移行することを決定しています。これは正しいアプローチでしょ...

bandwagonhost/バンワゴンVPS-改訂版(大容量ハードディスク)プロモーション

Bandwagonhost の VPS の特別な機能: データ センターはフェニックスとオランダにあ...

初心者が検索エンジンのインクルージョンを改善する方法の簡単な分析

ウェブサイトを構築し始めてまだ間もないウェブマスターとして、最初はすべてが新鮮で興味深いと感じました...

Baidu は新サイトの詳細な最適化手順を、公開後 5 分以内に公開する予定です。

当社は広告ギフトのカスタマイズなどを手掛けています。夏が近づいてきたので、広告ファン向けの新しいコー...