开篇词
该指南将引导你完成使用 Spring Data Redis 发布和订阅 Redis 所发的消息。
你将创建的应用
我们将构建一个应用,该应用借助 SpringRedisTemplate
发布字符串消息,并通过 MessageListenerAdapter
使 POJO 订阅该消息。
你将需要的工具
- 大概 15 分钟左右;
- 你最喜欢的文本编辑器或集成开发环境(IDE)
- JDK 1.8 或更高版本;
- Gradle 4+ 或 Maven 3.2+
- 你还可以将代码直接导入到 IDE 中:
- Spring Too Suite (STS)
- IntelliJ IDEA
- 一个 Redis 服务器(参看启动一个 Redis 服务器)
- 一个 Redis 服务器(参看启动一个 Redis 服务器)
如何完成这个指南
像大多数的 Spring 入门指南一样,你可以从头开始并完成每个步骤,也可以绕过你已经熟悉的基本设置步骤。如论哪种方式,你最终都有可以工作的代码。
- 要从头开始,移步至启动一个 Redis 服务器;
- 要跳过基础,执行以下操作:
- 下载并解压缩该指南将用到的源代码,或借助 Git 来对其进行克隆操作:
git clone https://github.com/spring-guides/gs-messaging-redis.git
- 切换至
gs-messaging-redis/initial
目录; - 或跳转至该指南的从 Spring Initializr 开始。
- 下载并解压缩该指南将用到的源代码,或借助 Git 来对其进行克隆操作:
待一切就绪后,可以检查一下 gs-messaging-redis/complete
目录中的代码。
启动一个 Redis 服务器
在构建消息传递应用之前,我们需要配置处理接收和发送消息的服务器。
Redis 是一款开源的,BSD 许可的键值数据存储,还附带了消息传递系统。该服务器可从 https://redis.io/download 免费获得。我们可以手动下载它,或如果使用 Mac,则可以通过 Homebrew 来进行下载,方法是在终端窗口中运行以下命令:
brew install redis
解包 Redis 之后,可以通过运行以下命令以默认配置来启动它:
redis-server
我们应该看到类似于以下内容的消息:
[35142] 01 May 14:36:28.939 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
[35142] 01 May 14:36:28.940 * Max number of open files set to 10032
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 2.6.12 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in stand alone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 35142
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
[35142] 01 May 14:36:28.941 # Server started, Redis version 2.6.12
[35142] 01 May 14:36:28.941 * The server is now ready to accept connections on port 6379
从 Spring Initializr 开始
对于所有的 Spring 应用来说,你应该从 Spring Initializr 开始。Initializr 提供了一种快速的方法来提取应用程序所需的依赖,并为你完成许多设置。该示例仅需要 Spring for Redis 依赖。下图显示了此示例项目的 Initializr 设置:
上图显示了选择 Maven 作为构建工具的 Initializr。你也可以使用 Gradle。它还将
com.example
和messaging-redis
的值分别显示为 Group 和 Artifact。在本示例的其余部分,将用到这些值。
以下清单显示了选择 Maven 时创建的 pom.xml
文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>messaging-redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>messaging-redis</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
以下清单显示了在选择 Gradle 时创建的 build.gradle
文件:
plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
创建一个 Redis 消息接收者
在任何基于消息的应用中,都有消息发布者和消息接收者。要创建消息接收者,请使用一种响应消息的方法来实现接收者,如以下示例(来自 src/main/java/com/example/messagingredis/Receiver.java
)所示:
package com.example.messagingredis;
import java.util.concurrent.CountDownLatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class Receiver {
private static final Logger LOGGER = LoggerFactory.getLogger(Receiver.class);
private CountDownLatch latch;
@Autowired
public Receiver(CountDownLatch latch) {
this.latch = latch;
}
public void receiveMessage(String message) {
LOGGER.info("Received <" + message + ">");
latch.countDown();
}
}
Receiver
是一个 POJO,它定义了一种接收消息的方法。在将 Receiver
注册为消息监听器时,可以根据需要来命名消息处理方法。
出于演示目的,接收者由其构造函数与倒计时闩锁自动连线。这样,它可以在收到消息时发出信号。
注册监听器并发送一个消息
Spring Data Redis 提供了使用 Redis 发送和接收消息所需的所有组件。具体来说,我们需要配置:
- 连接工厂
- 消息监听器容器
- Redis 模版
我们将使用 Redis 模版发送消息,并在消息监听器容器中注册 Receiver
,以便它能接收消息。连接工厂将同时驱动模版以及消息监听器容器,从而使它们连接到 Redis 服务器。
该示例使用 Spring Boot 的默认 RedisConnectionFactory
,这是一个基于 Jedis Redis 库的 JedisConnectionFactory
实例。连接工厂被注入到消息监听器容器以及 Redis 模版中,如以下示例(来自 src/main/java/com/example/messagingredis/MessagingRedisApplication.java
)所示:
package com.example.messagingredis;
import java.util.concurrent.CountDownLatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@SpringBootApplication
public class MessagingRedisApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(MessagingRedisApplication.class);
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
@Bean
Receiver receiver(CountDownLatch latch) {
return new Receiver(latch);
}
@Bean
CountDownLatch latch() {
return new CountDownLatch(1);
}
@Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
public static void main(String[] args) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(MessagingRedisApplication.class, args);
StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class);
CountDownLatch latch = ctx.getBean(CountDownLatch.class);
LOGGER.info("Sending message...");
template.convertAndSend("chat", "Hello from Redis!");
latch.await();
System.exit(0);
}
}
在 listenerAdapter
方法中定义的 Bean 在 container
中定义的消息监听器容器中注册为消息监听器,并将监听 chat
主题中的消息。由于 Receiver
类是 POJO,因此需要将其包装在实现 MessageListener
接口的消息监听器适配器中(addMessageListener()
所需的)。消息监听器适配器还配置了在消息到达时在 Receiver
上调用 receiveMessage()
方法。
我们只需要连接工厂和消息监听器容器 Bean 即可监听消息。要发送消息,我们还需要一个 Redis 模版。在这里,它是一个被配置为 StringRedisTemplate
的 bean,这是 RedisTemplate
的实现,重点是 Redis 的常用用法,其中键和值都是 String
实例。
main()
方法通过创建 Spring 应用上下文开始所有工作。然后,应用上下文启动消息监听器容器,并且消息监听器容器 Bean 开始监听消息。接下来,main()
方法从应用上下文中检索 StringRedisTemplate
的 bean,并使用它来发送有关 chat
主题的消息 Hello from Redis!
。最后,它关闭 Spring 应用上下文,然后应用结束。
运行应用
我们可以结合 Gradle 或 Maven 来从命令行运行该应用。我们还可以构建一个包含所有必须依赖项、类以及资源的可执行 JAR 文件,然后运行该文件。在整个开发生命周期中,跨环境等等情况下,构建可执行 JAR 可以轻松地将服务作为应用进行发布、版本化以及部署。
如果使用 Gradle,则可以借助 ./gradlew bootRun
来运行应用。或通过借助 ./gradlew build
来构建 JAR 文件,然后运行 JAR 文件,如下所示:
java -jar build/libs/gs-messaging-redis-0.1.0.jar
如果使用 Maven,则可以借助 ./mvnw spring-boot:run
来运行该用。或可以借助 ./mvnw clean package
来构建 JAR 文件,然后运行 JAR 文件,如下所示:
java -jar target/gs-messaging-redis-0.1.0.jar
我们还可以构建一个经典的 WAR 文件。
我们应该看到以下输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.8.RELEASE)
2019-09-23 12:57:11.578 INFO 35396 --- [ main] c.e.m.MessagingRedisApplication : Starting MessagingRedisApplication on Jays-MBP with PID 35396 (/Users/j/projects/guides/gs-messaging-redis/complete/target/classes started by j in /Users/j/projects/guides/gs-messaging-redis/complete)
2019-09-23 12:57:11.581 INFO 35396 --- [ main] c.e.m.MessagingRedisApplication : No active profile set, falling back to default profiles: default
2019-09-23 12:57:11.885 INFO 35396 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2019-09-23 12:57:11.887 INFO 35396 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2019-09-23 12:57:11.914 INFO 35396 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 13ms. Found 0 repository interfaces.
2019-09-23 12:57:12.685 INFO 35396 --- [ container-1] io.lettuce.core.EpollProvider : Starting without optional epoll library
2019-09-23 12:57:12.685 INFO 35396 --- [ container-1] io.lettuce.core.KqueueProvider : Starting without optional kqueue library
2019-09-23 12:57:12.848 INFO 35396 --- [ main] c.e.m.MessagingRedisApplication : Started MessagingRedisApplication in 1.511 seconds (JVM running for 3.685)
2019-09-23 12:57:12.849 INFO 35396 --- [ main] c.e.m.MessagingRedisApplication : Sending message...
2019-09-23 12:57:12.861 INFO 35396 --- [ container-2] com.example.messagingredis.Receiver : Received <Hello from Redis!>
概述
恭喜你!我们刚刚使用 Spring 结合 Redis 开发了一个发布订阅的应用。
Redis 支持可用
参见
以下指南也可能会有所帮助:
- 使用 RabbitMQ 进行消息传递(尽请期待~)
- 使用 JMS 进行消息传递(尽请期待~)
- 使用 SpringBoot 进行消息传递(尽请期待~)
想看指南的其他内容?请访问该指南的所属专栏:《Spring 官方指南》