介绍
Redis是一个内存数据存储,可以用作NoSQL数据库,缓存或典型的消息代理。它使用ANSI C编写,可编译为效率极高的机器代码,并且能够将数据存储为键值对,从而使内存中缓存成为Redis有吸引力的用例,此外还可以将数据持久保存到磁盘中。
在本文中,我们将使用管道技术允许Spring Boot应用程序以非阻塞方式将多个请求发送到Redis服务器。
Redis中流水线的用例
Redis基于客户端/服务器(请求/响应)架构。在这些体系结构中,客户端通常向服务器发送查询或请求,然后等待响应。这通常以阻塞的方式完成,这样在发送最后一个请求的响应之前,无法发送新请求:
Client: <command 1>
Server: Response for <command 1>
Client: <command 2>
Server: Response for <command 2>
Client: <command 3>
Server: Response for <command 3>
这会导致效率低下,在接收命令和处理命令之间会出现高延迟。
流水线化允许我们发送多个命令,作为客户端的一项操作,而不必等待服务器之间每个命令之间的响应。然后,将所有响应一起阅读:
Client: <command 1>
Client: <command 2>
Client: <command 3>
Server: Response for <command 1>
Server: Response for <command 2>
Server: Response for <command 3>
由于客户端在发出另一个命令之前不等待服务器的响应,因此减少了等待时间,从而提高了应用程序的性能。
注意:此处的命令放置在队列中。该队列应保持合理的大小。如果要处理成千上万的命令,最好分批发送和处理它们,以免流水线化的好处变得多余。
实操
让我们继续前进,制作一个小型的Spring Boot应用程序,该应用程序可与Redis一起使用并通过管道传递多个命令。在Spring Data Redis项目的帮助下,这变得更加容易。
Spring启动设置
从空白的Spring Boot应用程序开始的最简单方法是使用Spring Initializr:
另外,您也可以使用Spring Boot CLI引导应用程序:
$ spring init --dependencies=spring-boot-starter-data-redis redis-spring-boot-demo
我们从spring-boot-starter-data-redis
包含spring-data-redis
,spring-boot-starter
和的依赖项开始lettuce-core
。
如果您已经拥有Maven / Spring应用程序,则将依赖项添加到pom.xml
文件中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${version}</version>
</dependency>
或者,如果您使用的是Gradle:
compile group: 'org.springframework.data', name: 'spring-data-redis', version: '${version}'
我们还将使用Jedis作为连接客户端,而不是Lettuce:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${version}</version>
</dependency>
Redis配置
我们将在Scalegrid上托管Redis ,该Scalegrid提供免费试用帐户,用于托管Redis服务器实例。或者,您可以下载服务器并将其托管在Linux和MacOS上的自己的计算机上。Windows需要一点技巧,并且设置起来很棘手。
让我们进行设置,JedisConnectionFactory
以便我们的应用程序可以连接到Redis服务器实例。在您的@Configuration
课程中,注释适当的内容@Bean
:
@Configuration
public class Config {
@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("<server-hostname-here>");
jedisConnectionFactory.setPort(6379);
jedisConnectionFactory.setPassword("<server-password-here>");
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
}
RedisTemplate
是Spring Data提供的入口类,通过它我们与Redis服务器进行交互。
我们将配置的连接工厂传递给它:
@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setDefaultSerializer(RedisSerializer.string());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
我们已将默认序列化程序设置为将键和值存储为String
。另外,您可以定义自己的自定义序列化程序。
使用RedisTemplate进行流水线化
让我们从列表中向Redis服务器添加一些项目。我们将使用进行流水线操作RedisTemplate
。具体来说,我们将使用ListOperations
从获取的接口opsForList()
:
List<String> values = Arrays.asList("value-1", "value-2", "value-3", "value-4", "value-5");
redisTemplate.opsForList().leftPushAll("pipeline-list", values);
运行此代码将导致:
现在,让我们删除这些。想象这可能是一项昂贵的操作,我们将对每个rPop()
命令进行流水线处理,以便将它们一起发送,并使结果在删除的所有元素上同步。然后,我们将收到这些结果。为了流水线命令,我们使用该executedPipeline()
方法。
它接受一个RedisCallback
或SessionCallback
我们提供的。该executedPipeline()
方法返回结果,然后我们可以捕获并查看该结果。如果不需要这样做,而您只想执行命令,则可以使用execute()
方法,并将其true
作为pipeline
参数传递:
List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
for(int i = 0; i < 5; i++) {
connection.rPop("pipeline-list".getBytes());
}
return null;
}
});
return results;
该executePipelined()
方法接受了new RedisCallback()
,其中我们使用doInRedis()
方法来指定我们要执行的操作。
具体来说,我们已经运行了rPop()
5次该方法,删除了我们预先插入的5个列表元素。
一旦执行了所有这五个命令,就将元素从列表中删除并发送回-结果打包在results
列表中:
结论
Redis最受欢迎的用例是作为缓存存储。但是,它也可以用作数据库或消息代理。
Redis使我们通过最小化对数据库层的调用来提高应用程序的性能。它对流水线的支持允许在单个写入操作中将多个命令发送到服务器,从而减少往返服务器的往返时间。
在本文中,我们使用RedisTemplate
API对多个命令进行了流水线处理,并验证了结果。