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 ロールアウト

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

推薦する

hncloud (ワーナークラウド) 香港 (独立) サーバー (10M 帯域幅 cn2) の簡単な評価

HNCloud Limited.は、APNICおよびARINのメンバーである香港聯合国際通信有限公司...

杭州愛北が企業ウェブサイト最適化の基本的な考え方と戦略について語る

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っていますインターネ...

ウェブマスターは、製品管理にユーザーの概念をさらに適用する必要がある

ウェブサイトを構築する目的は何ですか? また、どのような種類のビジネスをサポートする必要がありますか...

MiniDates: 見知らぬ人のための出会い系サイト

世の中が「独身男性」で溢れかえっている現状を受けて、さまざまな出会い系アプリが登場し始めている。 L...

「中学高」が打ち砕いたのは、ネットセレブ経済のバブルだった

暑い夏には、アイスクリームやアイスキャンディーの売り上げが急増しました。ネットセレブの商品が複数、安...

中国のモバイルゲーム市場に関する洞察

コンテンツタグ:モバイルゲーム、年次分析、業界予測、デジタルユーザーの洞察Analysys Inte...

K12が方向転換、ガオトゥらは岐路に立つ

これまでの夏休みでは、オンライン教育企業はマーケティングに多額の資金を費やしました。この夏、オンライ...

ウェブマスターネットワークニュース:Kuaiboのボスである王欣は1か月近く行方不明になっており、彼の妻はトイレに隠れて現れていない

1. Qvodの社長である王欣は1か月近く行方不明になっており、妻は浴室に隠れたまま姿を現していない...

zipvps-1g メモリ kvm/50g ハードディスク/1T トラフィック/G ポート/ニューヨーク/月額 6.5 ドル

zipvps はいくつかのプロモーションを行ってきました。ワンマン商人ホスト猫として、これまでここで...

クラウドネイティブアーキテクチャ、DevOps入門

クラウド ネイティブ アーキテクチャは、クラウド コンピューティング プラットフォームの利点を最大限...

VPS 50% オフ: vps.net - 2.5 USD/Xen/512 MB RAM/15 GB SSD/1 TB 帯域幅/10 データセンター

UK2 グループの子会社である VPS.NET は、XEN ベースの仮想 VPS を 50% 割引で...

zenhost、シンガポールの DDoS 保護の高い VPS、KVM 仮想化、512M メモリが 3.6 ドルから

zenhost.net はインドネシアの事業者であり、非常に新しい事業者です。現在の主な事業は、仮想...

Google 特許におけるドメイン名の重みから見るドメイン名の SEO への影響

2003 年 12 月 31 日、Google は、図に示すように、「履歴データに基づく情報検索」と...

2023 年の主要なクラウド コンピューティングとセキュリティのトレンド

研究機関は2023年に強い経済的逆風が吹くと予測しており、企業はより少ないリソースでより多くの成果を...