SpringBoot が Redis キー (期限切れ、新規、変更) を統合するには 3 つの方法があります。この記事を読んでください。

この記事では主に、Springboot が主要な変更を統合するための 3 つの方法について説明し、統合の落とし穴と概念もいくつかリストします。

原理

SpringBoot による Redis キーの変更の統合の原則は、変更されないということです。
spring-boot-starter-data-redis + notify-keyspace-events

通知キースペースイベントについて

  • notify-keyspace-events AKExサーバーによって送信される通知の種類を構成するために使用される Redis のコマンドです。ここでのパラメータは、AKExキー空間通知とキーイベント通知を表します。

  • 具体的には、Aすべてのタイプの通知の受信を示し、Kキー空間通知の受信を示し、Exキーイベント通知の受信を示します。キースペース通知はデータベース全体の変更に関するものですが、キー イベント通知は特定のキーの変更に関するものです。

  • たとえば、SET mykey "Hello"Redis でコマンドを実行すると、その構成を使用しているクライアントは、キーのキー イベント通知をnotify-keyspace-events AKEx受け取ります。mykey

  • notify-keyspace-eventsこのコマンドを使用するには、関連する通知メカニズムが Redis サーバーで有効になっている必要があることに注意してください。有効になっていないと、クライアントは通知を受信できません。同時に、このコマンドは Redis の値がstring 类型键変更された場合にのみ通知を送信できます。他のタイプのキー (ハッシュ、リスト、セット、ソートされたセットなど) の変更は通知をトリガーしません。

Redis に関するメッセージトピック (トピック)

Redis メッセージ トピック (Topic) は、メッセージのパブリッシュとサブスクライブに使用されるキーワードです。Redis の設計によれば、パブリッシュ/サブスクライブ モデルのメッセージのみがサポートされ、リクエスト/レスポンス モデルのメッセージはサポートされません。

Redis では、PSUBSCRIBEコマンドを使用して 1 つ以上のトピックをサブスクライブし、関連するメッセージを聞くことができます。一般的な Redis メッセージのトピックをいくつか示します。

  • __keyevent@*__:expired: 有効期限イベント トピック。すべてのライブラリの有効期限メッセージを公開し、*すべてを示します。
  • __keyevent@0__:expired: 有効期限イベント トピック。db0 データベースでの有効期限メッセージ0の公開を表しますdb0
  • __keyevent@1__:del: コマンドを使用してDELキーが削除されたときにトリガーされるイベント。
  • __keyevent@4__:rename: コマンドを使用してRENAMEキーの名前が変更されたときにトリガーされるイベント。
  • __keyevent@5__:set: コマンドを使用してSETキーの値が設定されたときにトリガーされるイベント。

これらのトピックは Redis 4.0 で導入され、キー固有のイベントを表すために使用されます。対応するトピックをサブスクライブすると、関連するイベントをリッスンし、それに応じて処理できます。
上記のトピックに加えて、Redis はカスタム トピックもサポートしており、ユーザーは自分のニーズに応じて関連トピックを定義してサブスクライブできます。

上記の概念を理解したら、コードに直接アクセスして、監視メソッドを書き直す方法を見てみましょう。

リスナーを書き換える

  1. Redis キー変更イベントを有効にする
    Redis 構成ファイルの構成notify-keyspace-events AKEx、デフォルトは关闭的
  2. 依存関係を導入します (他の通常の依存関係は省略されます。忘れずに追加してください)
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
  1. 設定ファイルを作成しますRedisListenerConfig(ファイル名は任意で、ブロガーと同じである必要はありません)。


@Configuration
public class RedisListenerConfig {
    
    
	//配置redis监听容器
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
    //配置redis的序列化策略
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    
    
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//所有属性均可见
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);//为null不参加序列化
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//在Redis中存储对象类信息
        jackson2JsonRedisSerializer.setObjectMapper(mapper);
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.setConnectionFactory(factory);
        return template;
    }
}
  1. RedisKeyExpirationListenerリスナーを書く
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    
    

    public RedisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) {
    
    
        super(redisMessageListenerContainer);
    }

    /**
     * 针对redis数据失效事件,进行数据处理
     * @param message message must not be {@literal null}. 过期的key
     * @param pattern pattern matching the channel (if specified) - can be {@literal null}. 队列名称
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        // 拿到key
        String expiredKey = message.toString();
        log.info("监听Redis key过期,key:{},channel:{}", expiredKey, new String(pattern));
    }
}

この時点でキーの有効期限の監視は完了しましたが、このようにキーを更新および削除するにはどうすればよいでしょうか?

KeyExpirationEventMessageListener このクラスは Springboot によってカプセル化された有効期限リスニング クラスです。ソース コードを見てみましょう。

ここに画像の説明を挿入します

ただし、Springboot では更新と削除がカプセル化されません。したがって、私たちは 1 つの例から推論を導き出すことを学ばなければなりません。

  • 最初のステップはKeyExpirationEventMessageListener、名前を次のように仮定してをコピーすることです。KeyUpdateEventMessageListener
  • これを更新されたトピック__keyevent@0__:expired変更します__keyevent@0__:set
  • を書いてメソッドRedisKeyUpdateListener extent KeyUpdateEventMessageListenerを書き直すonMessage

さて、書き終えたので、いくつかのヒントを紹介します。Pythonで開発されたファイルタイプ変換Windowsツールで、png、jpeg、icoなどのファイルタイプの相互変換をサポートしています。

引き続きコンテナの登録方法を見てみましょう

コンテナの登録

上記の設定は書き換えられません。

  1. RedisKeyUpdatedListener実装を書くことに注意してくださいMessageListener
@Component
@Slf4j
public class RedisKeyUpdatedListener implements MessageListener {
    
    

    /**
     * key 更新监听
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        // 拿到key
        String expiredKey = message.toString();
        log.info("监听Redis keyg更新,key:{},channel:{}", expiredKey, new String(pattern));
    }
}
  1. 次にコンテナ構成を変更します
@Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //监听指定db0 的key set事件 *表示监听所有的db
        container.addMessageListener(redisKeyUpdatedListener, new PatternTopic("__keyevent@*__:set"));
        return container;
    }

ここで注意すべきは、redisKeyUpdatedListener新しい場合は新しいものではないため、注射によって注入されることです。リスナーにビジネス ロジックがある場合、複数のビジネス サービス コンポーネントが導入されます。new では、構築を通じてのみ渡すことができ、サービス コンポーネントは構成クラスから注入されます。

複数ある場合は次のようになります。

container.addMessageListener(redisKeyUpdatedListener, new PatternTopic("__keyevent@*__:set"));
container.addMessageListener(redisKeyExpiredListener, new PatternTopic("__keyevent@*__:expired"));
container.addMessageListener(redisKeyDelListener, new PatternTopic("__keyevent@*__:del"));

カスタム分析

こちらの方が便利です。コードを直接見てみましょうが、結合度が少し高く、デザインパターンの仕様を満たしていません。

@Component
public class CustomRedisMessageListener implements MessageListener {
    
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        String ops = new String(message.getBody());     //操作
        String channel = new String(message.getChannel());
        String key = channel.split(":")[1];
		//对redis的新增或删除事件进行监听
        if ("set".equals(ops)) {
    
    
            String value = redisTemplate.opsForValue().get(key);
            handleSet(key, value);
        } else if ("del".equals(ops)) {
    
    
            handleDel(key);
        }
    }

    /**
     * 监听新增 处理逻辑
     */
    private void handleSet(String key, String value) {
    
    
			//将数据同步刷新到内存中
            gatewayCache.refreshApiWhitelistsCache(id, JsonUtil.toObject(value, Set.class));
    }

    /**
     * 监听删除 处理逻辑
     * @param key 被删除的key
     */
    private void handleDel(String key) {
    
     
            gatewayCache.deleteApiWhitelists(id);
    }
}

よくある統合の問題

パッケージを導入したのに、コードは非常に正しいのはなぜですか? キーの有効期限が切れた後、キーを監視できないのはなぜですか?

  • プロジェクトが開始できることを確認する
  • 競合する依存関係がないことを確認してください
  • Redis 構成が有効になっていることを確認してくださいnotify-keyspace-events AKEx

なぜnotify-keyspace-eventsを設定したのか。キー更新イベントが検出されないためでしょうか?

  • notify-keyspace-events AKEx設定されているものが であるかどうかを確認してくださいnotify-keyspace-events Exex は有効期限イベントのみをリッスンできますが、削除イベントはリッスンできません。

謝辞

  • この記事を最初から最後まで読んでいただき、誠にありがとうございます。この内容があなたのお役に立てば幸いです。他にご質問がある場合、またはさらに詳しい情報が必要な場合は、お気軽に私の最新情報をフォローし、メッセージを残してください。
  • 最後に、皆さんが作者に注目し、サポートしてくれることを願っています。
  • 集める価値があると思われる場合は、集めることもできます。
  • 最後に、いくつかのヒントを紹介します。サードパーティ呼び出し用のインターフェイスをエレガントにカプセル化します

おすすめ

転載: blog.csdn.net/qq_40673786/article/details/132551197