Hey Isaac,
The short, unhelpful answer is that "it depends".
The longer answer is that there are different approaches that are going to trade-off up-front simplicity vs. compute vs. network. I'm going to assume N + 1 servers here — e.g. three (3) chat server instances for redundancy. These are also not the only options, and are simplified themselves, but should you thinking.
1. Single topic. A user connects to one of three servers. The server itself publishes all messages to a single topic, and listens to that same topic. The message payload itself defines which "channel" the message is bound for and sends it to all clients associated with that room (e.g. via a map). A channel could be a shared room with N users, or just one (1) user ("direct message").
Upsides: super simple. Likely (untested at scale!) good for a few thousand clients, hundreds of messages per second and tens of rooms on even a modest Redis instance. Don't need to track multiple topics, don't care what instance a client connects to (all are the same), if one instance fails clients can re-connect to the next and continue on.
Downsides: Because all servers can handle all clients, all servers must also receive and process all messages, parsing the payload to determine which clients to deliver the message to.
2. Topic per channel. Servers publish and listen to the topics they care about: when a user associated with that channel joins. Further, because a client could effectively have multiple WS connections (one per room), there's an ability to optimize by having clients routed to specific servers - e.g. a routing layer up front that deterministically routes clients to a specific server, or group of servers, such that not all servers have to listen to all topics and thus process all messages.
You can start with #1 and move to #2 over time. You may also find Redis' Pub Sub implementation limiting (see below), so encoding additional metadata into message payloads can be worthwhile, especially while you're figuring out the architecture. Has a network bytes + compute cost too, but I wouldn't prematurely optimize there.
Unaddressed: servers disconnecting from Redis will not be able to pick up from where they left off—those messages will be missed, as Redis' Pub Sub is not designed to guarantee delivery. Thus, clients would have a gap in their timelines or miss messages due to network events. This is different from something like Google Cloud Pub/Sub (
https://cloud.google.com/pubsub/docs/subscriber), which provides re-delivery functionality, or even Disque (also authored by Redis' author)
https://github.com/antirez/disque - which adheres to queue-like paradigms.