Redis发布/订阅(Pub/Sub)是一种消息传递模式,其中发送者(发布者)将消息发送到主题(频道),而接收者(订阅者)则从主题中接收这些消息。以下是Redis发布/订阅的实践和原理:
发布者使用PUBLISH命令向指定主题发送消息。
订阅者使用SUBSCRIBE命令订阅一个或多个主题。
当发布者向某个主题发送消息时,所有订阅该主题的订阅者都会接收到该消息。
除了基本的PUBLISH/SUBSCRIBE模式外,Redis还支持模式匹配(Pattern-Matching)模式,即订阅者可以使用通配符订阅多个主题。例如,使用SUBSCRIBE news.*可以订阅news.sports、news.weather等所有以news.为前缀的主题。
Redis使用发布/订阅频道来实现消息传递,每个频道都有一个名称。
订阅者通过向Redis服务器发送SUBSCRIBE命令来订阅一个或多个频道。
发布者使用PUBLISH命令将消息发送到指定频道。
当Redis服务器接收到一条新消息时,它将该消息发送给所有已订阅该频道的客户端。
消息传递过程是异步的,即当发布者发送消息后,不需要等待所有订阅者接收到该消息才能继续执行其他任务。
在Redis中,发布/订阅是通过使用内部通信机制实现的。当一个客户端订阅一个频道时,Redis会将该客户端添加到频道的订阅列表中。当其他客户端向该频道发送消息时,Redis会遍历订阅列表,并将消息发送给所有已订阅该频道的客户端。
以下是Java Redis客户端Jedis实现发布/订阅的具体代码:
import redis.clients.jedis.Jedis;
public class Publisher {
public static void main(String[] args) {
// 创建Jedis对象连接Redis服务器
Jedis jedis = new Jedis("localhost", 6379);
// 向指定频道发布消息
jedis.publish("news.sports", "Kobe Bryant has passed away.");
// 关闭Jedis连接
jedis.close();
}
}
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
public class Subscriber extends JedisPubSub {
@Override
public void onMessage(String channel, String message) {
System.out.println("Received message from channel " + channel + ": " + message);
}
public static void main(String[] args) {
// 创建Jedis对象连接Redis服务器
Jedis jedis = new Jedis("localhost", 6379);
// 创建Subscriber对象
Subscriber subscriber = new Subscriber();
// 订阅一个或多个频道
jedis.subscribe(subscriber, "news.sports", "news.weather");
// 进入订阅状态,等待接收消息
jedis.close();
}
}
在上面的代码中,发布者使用jedis.publish()向指定频道发布消息,而订阅者则创建一个继承自JedisPubSub类的子类,并重写其onMessage()方法用于处理接收到的消息。接着,订阅者使用jedis.subscribe()方法订阅一个或多个频道,并在订阅状态下等待接收消息。
需要注意的是,由于Redis的发布/订阅功能是异步的,因此jedis.subscribe()和jedis.publish()方法不会阻塞当前线程。因此,在生产环境中,应该将发布/订阅功能作为一个独立的进程进行部署,以充分利用其异步性能优势。
以下是使用Java Redisson客户端实现发布/订阅的具体代码:
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.api.RTopic;
public class Publisher {
public static void main(String[] args) {
// 创建RedissonClient连接Redis服务器
RedissonClient redisson = Redisson.create();
// 获取RTopic对象,指定频道名称
RTopic<String> topic = redisson.getTopic("news.sports");
// 向指定频道发布消息
topic.publish("Kobe Bryant has passed away.");
// 关闭Redisson连接
redisson.shutdown();
}
}
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.api.listener.MessageListener;
import org.redisson.api.listener.PatternMessageListener;
import org.redisson.api.listener.TopicPatternMessageListener;
import org.redisson.api.listener.TopicMessageListener;
public class Subscriber {
public static void main(String[] args) {
// 创建RedissonClient连接Redis服务器
RedissonClient redisson = Redisson.create();
// 获取RTopic对象,指定频道名称
RTopic<String> topic = redisson.getTopic("news.sports");
// 添加消息监听器
topic.addListener(new MessageListener<String>() {
@Override
public void onMessage(CharSequence channel, String msg) {
System.out.println("Received message from channel " + channel + ": " + msg);
}
});
// 关闭Redisson连接
redisson.shutdown();
}
}
在上面的代码中,发布者使用RTopic.publish()向指定频道发布消息,而订阅者则使用RTopic.addListener()方法添加一个消息监听器。Redisson支持多种类型的监听器接口,包括MessageListener、TopicMessageListener、PatternMessageListener和TopicPatternMessageListener,分别用于处理不同类型的消息。
需要注意的是,由于Redisson客户端使用了Netty框架,因此在应用程序退出时必须显式调用Redisson.shutdown()方法关闭连接以确保资源正确释放。
以下是使用Go Redis客户端库redigo实现发布/订阅的具体代码:
package main
import (
"github.com/gomodule/redigo/redis"
)
func main() {
// 创建Redis连接
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// 向指定频道发布消息
_, err = conn.Do("PUBLISH", "news.sports", "Kobe Bryant has passed away.")
if err != nil {
panic(err)
}
}
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
func main() {
// 创建Redis连接
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// 订阅一个或多个频道
psc := redis.PubSubConn{Conn: conn}
err = psc.Subscribe("news.sports")
if err != nil {
panic(err)
}
// 接收消息并处理
for {
switch v := psc.Receive().(type) {
case redis.Message:
fmt.Printf("Received message from channel %s: %s\n", v.Channel, v.Data)
case redis.Subscription:
fmt.Printf("Subscription message: %s: %d\n", v.Channel, v.Count)
case error:
panic(v)
}
}
}
在上面的代码中,发布者使用conn.Do()执行PUBLISH命令向指定频道发布消息,而订阅者则使用redis.PubSubConn类型创建一个PubSubConn对象,并通过Subscribe()方法订阅一个或多个频道。接着,订阅者使用Receive()方法从Redis服务器接收消息,并根据消息类型进行分类处理。
需要注意的是,由于Go Redis客户端库redigo不支持异步操作,因此订阅者在循环中调用Receive()方法时会被阻塞,直到有新消息到达。因此,在生产环境中,应该将订阅者作为一个独立的进程运行以充分利用其异步性能优势。