記事ディレクトリ
この記事では主に、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 はカスタム トピックもサポートしており、ユーザーは自分のニーズに応じて関連トピックを定義してサブスクライブできます。
上記の概念を理解したら、コードに直接アクセスして、監視メソッドを書き直す方法を見てみましょう。
リスナーを書き換える
- Redis キー変更イベントを有効にする
Redis 構成ファイルの構成notify-keyspace-events AKEx
、デフォルトは关闭的
- 依存関係を導入します (他の通常の依存関係は省略されます。忘れずに追加してください)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 設定ファイルを作成します
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;
}
}
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などのファイルタイプの相互変換をサポートしています。
引き続きコンテナの登録方法を見てみましょう
コンテナの登録
上記の設定は書き換えられません。
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));
}
}
- 次にコンテナ構成を変更します
@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 Ex
。ex は有効期限イベントのみをリッスンできますが、削除イベントはリッスンできません。
謝辞
- この記事を最初から最後まで読んでいただき、誠にありがとうございます。この内容があなたのお役に立てば幸いです。他にご質問がある場合、またはさらに詳しい情報が必要な場合は、お気軽に私の最新情報をフォローし、メッセージを残してください。
- 最後に、皆さんが作者に注目し、サポートしてくれることを願っています。
- 集める価値があると思われる場合は、集めることもできます。
- 最後に、いくつかのヒントを紹介します。サードパーティ呼び出し用のインターフェイスをエレガントにカプセル化します