Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
67 lines
3.0 KiB
Markdown
67 lines
3.0 KiB
Markdown
[Home](../README.md) | Prev: [Hash TTL and keyspace events](06-hash-ttl-and-keyspace-events.md) | Next: [TTL](08-ttl.md)
|
|
|
|
# 7. Pub/Sub
|
|
|
|
Redis Pub/Sub is a broadcast: `PUBLISH channel message` sends the message to whoever is subscribed
|
|
to the channel *at that moment*, and Redis keeps nothing. The reply to `PUBLISH` is the number of
|
|
subscribers that received it.
|
|
|
|
## Boot 4.1 sets up the receiving side
|
|
|
|
Older articles declare a `RedisMessageListenerContainer` bean by hand. Boot 4.1 registers one
|
|
(`redisMessageListenerContainer`) and enables `@RedisListener`, so a listener is a method
|
|
([NewsListener](../src/main/java/com/ankurm/redis/messaging/NewsListener.java)):
|
|
|
|
```java
|
|
@RedisListener(topic = "news")
|
|
void onNews(String body) { ... }
|
|
```
|
|
|
|
[11-pub-sub.txt](output/11-pub-sub.txt) starts with what Boot did:
|
|
|
|
```
|
|
beans of type RedisMessageListenerContainer: [redisMessageListenerContainer]
|
|
running=true listening=true
|
|
```
|
|
|
|
A topic that contains a glob (`alerts.*`) subscribes with `PSUBSCRIBE`; the channel the message came
|
|
in on is available as a header (`@Header(PubSubHeaders.CHANNEL) String channel`). The
|
|
`@RedisListener` attributes, from the jar, are `id`, `container`, `value`, `topic` and `consumes`.
|
|
A `String` parameter receives the raw body; a `org.springframework.data.redis.connection.Message`
|
|
parameter did not work in this test, because the adapter tried to parse the payload as JSON
|
|
(a `StreamReadException` on the text `90% full`). This repository does not investigate why.
|
|
|
|
## Sending
|
|
|
|
`stringRedisTemplate.convertAndSend(channel, message)` returns the number of receivers:
|
|
|
|
- to a channel nobody listens to: `0`, and the message is gone;
|
|
- to `news` with the annotation listener: `1`;
|
|
- after a second listener is added to the same channel through the container, still `1`, because the container subscribes once to the channel and fans out to its listeners in-process. Both listeners received the message. The number is receivers on the Redis side, not listeners on yours.
|
|
|
|
`PUBSUB NUMSUB alerts.disk` is `0` while a pattern subscriber is receiving on `alerts.*`: `NUMSUB`
|
|
counts channel subscribers only, and `PUBSUB NUMPAT` counts patterns. `PUBLISH` counts both.
|
|
|
|
## At-most-once
|
|
|
|
[12-pub-sub-lost-message.txt](output/12-pub-sub-lost-message.txt): stop the container, publish, start
|
|
it again.
|
|
|
|
```
|
|
template.convertAndSend("news", "while you were away") = 0 receivers
|
|
...
|
|
messages the listener has: []
|
|
```
|
|
|
|
Nothing is queued. That is the design, and it is why Pub/Sub is right for cache invalidation hints and
|
|
live dashboards, and wrong for anything that must not be lost. For that, look at Redis Streams
|
|
(not covered in this repository).
|
|
|
|
## Going deeper
|
|
|
|
- [PubSubTest](../src/test/java/com/ankurm/redis/PubSubTest.java)
|
|
- [Redis Pub/Sub](https://redis.io/docs/latest/develop/pubsub/)
|
|
- [Spring Data Redis: Pub/Sub messaging](https://docs.spring.io/spring-data/redis/reference/redis/pubsub.html)
|
|
|
|
[Home](../README.md) | Prev: [Hash TTL and keyspace events](06-hash-ttl-and-keyspace-events.md) | Next: [TTL](08-ttl.md)
|