当前位置:网站首页>如何正确使用Vertx操作Redis(3.9.4带源码分析)
如何正确使用Vertx操作Redis(3.9.4带源码分析)
2022-07-01 18:44:00 【天然玩家】
1 缘起
真的是一段特别的经历。
增加了知识储备:Vert.x异步连接Redis并操作Redis。
事情经过:
团队需要开发离线算法中间件,集成到业务侧后台服务中,
原有的中间件中使用哨兵方式操作Redis,现在需要增加新的逻辑,
使用集群方式操作Redis,中间件中基于Vert.x 3.5.1修改出了一个支持集群连接的版本,
满足集群连接和操作。一切都很美好,测试通过。
于是,自己测试原生Vertx高版本(>3.8.x)的集群连接,这才是噩梦的开始,
由于我自建的Redis集群有密码认证,
所以,使用了Vert.x不同版本测试都无法连接,出于无奈,只能,去掉Redis集群的密码认证,
但是,3.8.x仍旧无法连接,
最后,使用了3.9.4,连接成功,并成功完成Redis操作,
特分享如下,帮助未来有需要的开发者。
特别声明:
Vert.x版本:3.9.4
本文实测:单体Redis和集群Redis,并给出测试结果;
由于没有搭建哨兵环境,所以,哨兵模式没有测试,虽然给出连接样例,但是没有测试结果。
2 Vert.x依赖
<!-- https://mvnrepository.com/artifact/io.vertx/vertx-core -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.vertx/vertx-redis-client -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-redis-client</artifactId>
<version>3.9.4</version>
</dependency>
3 Redis地址库
为统一管理Redis地址,使用枚举管理,
后续添加Redis连接配置时,从这个枚举库中提取,
代码如下所示:
package com.monkey.java_study.common.enums;
/** * Redis地址库. * * @author xindaqi * @since 2022-07-01 11:10 */
public enum RedisAddressEnum {
// Redis集群地址
CLUSTER_NODE_1("redis://192.168.211.129:9001"),
CLUSTER_NODE_2("redis://192.168.211.129:9002"),
CLUSTER_NODE_3("redis://192.168.211.129:9003"),
CLUSTER_NODE_4("redis://192.168.211.129:9004"),
CLUSTER_NODE_5("redis://192.168.211.129:9005"),
CLUSTER_NODE_6("redis://192.168.211.129:9006"),
// 单机Redis地址
STANDALONE_NODE("redis://:[email protected]/0"),
;
private String address;
RedisAddressEnum(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
}
4 Redis集群
4.1 配置地址和相关参数:RedisOptions
Vert.x 3.9.4提供了两种方式配置Redis地址,
分别为:addConnectionString和setEndpoints,
同时,可以指定连接类型,集群为:RedisClientType.CLUSTER
特别注意:集群方式连接时,集群千万不可配置密码认证,否则无法连接到Redis集群
- addConnectionString方式
此种方式,地址一条一条地添加。地址格式:redis://ip:port
/** * Redis集群连接配置:addConnectionString方式 * * @return Redis集群配置 */
private static RedisOptions setClusterRedisAddressOneByOne() {
return new RedisOptions()
.setType(RedisClientType.CLUSTER)
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_1.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_2.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_3.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_4.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_5.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_6.getAddress());
}
- setEndpoints方式
批量添加Redis地址,endpoints的类型为List<String>,因此可以批量添加。
/** * Redis集群连接配置:setEndpoints方式 * * @return Redis集群配置 */
private static RedisOptions setClusterRedisAddressBatch() {
List<String> redisAddressList = Stream.of(
RedisAddressEnum.CLUSTER_NODE_1.getAddress(),
RedisAddressEnum.CLUSTER_NODE_2.getAddress(),
RedisAddressEnum.CLUSTER_NODE_3.getAddress(),
RedisAddressEnum.CLUSTER_NODE_4.getAddress(),
RedisAddressEnum.CLUSTER_NODE_5.getAddress(),
RedisAddressEnum.CLUSTER_NODE_6.getAddress()).collect(Collectors.toList());
return new RedisOptions()
.setType(RedisClientType.CLUSTER)
.setEndpoints(redisAddressList);
}
4.2 源码:连接类型
由Vert.x3.9.4源码可知,Redis客户端连接类型有三种:STANDALONE(单体)、SENTINEL(哨兵)和CLUSTER(集群),
源码如下图所示。
位置:io.vertx.redis.client.RedisClientType
4.3 其他默认参数
配置Redis集群连接地址后,就可以直接连接Redis集群,
因为,Vert.x3.9.4在初始化时,自动配置了默认值,如:
maxWaitingHandler、maxPoolSize和maxPoolWaiting等,源码如下图所示,
默认情况,连接Redis的类型为单体(RedisClientType.STANDALONE),
哨兵模式下的默认不使用从节点:RedisSlaves.NEVER。
位置:io.vertx.redis.client.RedisOptions#init
为什么会加载这些参数,因为,RedisOptions的构造函数:
位置:io.vertx.redis.client.RedisOptions#RedisOptions()
4.4 创建Redis客户端
创建样例如下,通过传入Vertx.vertx()和redisOptions(上文配置的参数对象),
即可获取Redis客户端,获取客户端即与Redis建立起连接,
接下来即可完成CURD操作。
Redis.createClient(Vertx.vertx(), redisOptions);
4.5 源码:Redis.createClient
Vert.x 3.9.4创建Redis客户端源码如下图所示,
由源码可知,创建过程中会根据type类型选择不同的客户端(单体、哨兵和集群)。
位置:io.vertx.redis.client.Redis#createClient(io.vertx.core.Vertx, io.vertx.redis.client.RedisOptions)
4.6 Redis操作:RedisAPI
Vert.x 3.9.4通过RedisAPI操作Redis数据,
测试样例如下,使用Vert.x可知,异步操作,
因此,读取数据使用Vetx.x线程处理:vert.x-eventloop-thread-0
/** * 读取数据从Redis * * @param redisClient Redis客户端 * @param key Redis中查询的键 */
public void readDataFromCluster(Redis redisClient, String key) {
RedisAPI redisAPI = RedisAPI.api(redisClient);
redisAPI.get(key, res -> {
if (res.succeeded()) {
String value = String.valueOf(res.result());
logger.info(">>>>>>>>Read data from redis cluster (key, value)->:({},{})", key, value);
} else {
logger.error(">>>>>>>>Redis cluster read data error:", res.cause());
}
});
}
4.7 完整样例
package com.monkey.java_study.thirdparty.vertx_test;
import com.monkey.java_study.common.enums.RedisAddressEnum;
import io.vertx.core.Vertx;
import io.vertx.redis.client.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** * Vert.x连接Redis集群服务和读数据测试. * * @author xindaqi * @since 2022-06-30 12:16 */
public class VertxClusterOpRedisTest {
private static final Logger logger = LoggerFactory.getLogger(VertxClusterOpRedisTest.class);
/** * Redis集群客户端 */
private static final Redis redisClient;
/** * Redis连接配置 */
private static final RedisOptions redisOptions;
static {
redisOptions = setClusterRedisAddressBatch();
redisClient = Redis.createClient(Vertx.vertx(), redisOptions);
}
/** * Redis集群连接配置:addConnectionString方式 * * @return Redis集群配置 */
private static RedisOptions setClusterRedisAddressOneByOne() {
return new RedisOptions()
.setType(RedisClientType.CLUSTER)
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_1.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_2.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_3.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_4.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_5.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_6.getAddress());
}
/** * Redis集群连接配置:setEndpoints方式 * * @return Redis集群配置 */
private static RedisOptions setClusterRedisAddressBatch() {
List<String> redisAddressList = Stream.of(
RedisAddressEnum.CLUSTER_NODE_1.getAddress(),
RedisAddressEnum.CLUSTER_NODE_2.getAddress(),
RedisAddressEnum.CLUSTER_NODE_3.getAddress(),
RedisAddressEnum.CLUSTER_NODE_4.getAddress(),
RedisAddressEnum.CLUSTER_NODE_5.getAddress(),
RedisAddressEnum.CLUSTER_NODE_6.getAddress()).collect(Collectors.toList());
return new RedisOptions()
.setType(RedisClientType.CLUSTER)
.setEndpoints(redisAddressList);
}
public Redis getRedisClient() {
return redisClient;
}
/** * 读取数据从Redis * * @param redisClient Redis客户端 * @param key Redis中查询的键 */
public void readDataFromCluster(Redis redisClient, String key) {
RedisAPI redisAPI = RedisAPI.api(redisClient);
redisAPI.get(key, res -> {
if (res.succeeded()) {
String value = String.valueOf(res.result());
logger.info(">>>>>>>>Read data from redis cluster (key, value)->:({},{})", key, value);
} else {
logger.error(">>>>>>>>Redis cluster read data error:", res.cause());
}
});
}
public static void main(String[] args) {
VertxClusterOpRedisTest vertxOpRedisTest = new VertxClusterOpRedisTest();
vertxOpRedisTest.readDataFromCluster(vertxOpRedisTest.getRedisClient(), "name");
}
}
4.8 测试结果
由测试结果可知,Vert.x 3.9.4操作Redis使用异步线程:vert.x-enventloop-thread-0,
并完成结果读取。
5 Redis单体
有了上面集群配置的基础,
配置单体连接和哨兵连接就相对好理解一些,
直接给出完整样例,代码如下:
需要注意的是:配置Redis时需要按照需要配置数据库0-15,
连接类型选择:RedisClientType.STANDALONE,
如果是单体可以不选,因为RedisOptions默认值即为:RedisClientType.STANDALONE。
地址格式:redis://username:[email protected]:port/db
参数说明:
| 序号 | 参数 | 描述 |
|---|---|---|
| 1 | username | 没有可以不填 |
| 2 | password | Redis连接密码,没有可以不填 |
| 3 | ip | Redis运行主机IP |
| 4 | port | Redis端口 |
| 5 | db | Redis数据库编号,0-15中的一个 |
特别地:
如果只有密码没有用户名,应该这样配置:redis://:[email protected]:port/db
5.1 完整样例
package com.monkey.java_study.thirdparty.vertx_test;
import com.monkey.java_study.common.enums.RedisAddressEnum;
import io.vertx.core.Vertx;
import io.vertx.redis.client.Redis;
import io.vertx.redis.client.RedisAPI;
import io.vertx.redis.client.RedisClientType;
import io.vertx.redis.client.RedisOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** * Vert.x连接Redis单体服务和读数据测试. * * @author xindaqi * @since 2022-06-30 12:16 */
public class VertxStandaloneOpRedisTest {
private static final Logger logger = LoggerFactory.getLogger(VertxStandaloneOpRedisTest.class);
/** * Redis单体客户端 */
private static final Redis redisClient;
/** * Redis连接配置 */
private static final RedisOptions redisOptions;
static {
redisOptions = setStandaloneRedisAddress();
redisClient = Redis.createClient(Vertx.vertx(), redisOptions);
}
/** * 配置Redis单体连接地址 * * @return Redis单体配置 */
private static RedisOptions setStandaloneRedisAddress() {
return new RedisOptions()
.setType(RedisClientType.STANDALONE)
.addConnectionString(RedisAddressEnum.STANDALONE_NODE.getAddress());
}
public Redis getRedisClient() {
return redisClient;
}
/** * 读取数据从Redis * * @param redisClient Redis客户端 * @param key Redis中查询的键 */
public void readDataFromStandalone(Redis redisClient, String key) {
RedisAPI redisAPI = RedisAPI.api(redisClient);
redisAPI.get(key, res -> {
if (res.succeeded()) {
String value = String.valueOf(res.result());
logger.info(">>>>>>>>Read data from redis standalone (key, value)->:({},{})", key, value);
} else {
logger.error(">>>>>>>>Redis standalone read data error:", res.cause());
}
});
}
public static void main(String[] args) {
VertxStandaloneOpRedisTest vertxOpRedisTest = new VertxStandaloneOpRedisTest();
vertxOpRedisTest.readDataFromStandalone(vertxOpRedisTest.getRedisClient(), "name");
}
}
5.2 测试结果

6 哨兵模式
同理,完整样例如下,
需要注意的是,类型配置为:RedisClientType.SENTINEL,
同时需要配置主服务的名称和角色,即
MasterName(“my_test”)和Role(RedisRole.MASTER),
由于没有测试用的哨兵Redis,因此,这里只给出配置样例,
没有实际测试。
package com.monkey.java_study.thirdparty.vertx_test;
import com.monkey.java_study.common.enums.RedisAddressEnum;
import io.vertx.core.Vertx;
import io.vertx.redis.client.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** * Vert.x连接Redis哨兵服务和读数据测试. * * @author xindaqi * @since 2022-06-30 12:16 */
public class VertxSentinelOpRedisTest {
private static final Logger logger = LoggerFactory.getLogger(VertxSentinelOpRedisTest.class);
/** * Redis哨兵客户端 */
private static final Redis redisClient;
/** * Redis连接配置 */
private static final RedisOptions redisOptions;
static {
redisOptions = setSentinelRedisAddressOneByOne();
redisClient = Redis.createClient(Vertx.vertx(), redisOptions);
}
/** * Redis哨兵连接配置:addConnectionString方式 * * @return Redis集群配置 */
private static RedisOptions setSentinelRedisAddressOneByOne() {
return new RedisOptions()
.setType(RedisClientType.SENTINEL)
.setMasterName("my_test")
.setRole(RedisRole.MASTER)
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_1.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_2.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_3.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_4.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_5.getAddress())
.addConnectionString(RedisAddressEnum.CLUSTER_NODE_6.getAddress());
}
/** * Redis哨兵连接配置:setEndpoints方式 * * @return Redis集群配置 */
private static RedisOptions setSentinelRedisAddressBatch() {
List<String> redisAddressList = Stream.of(
RedisAddressEnum.CLUSTER_NODE_1.getAddress(),
RedisAddressEnum.CLUSTER_NODE_2.getAddress(),
RedisAddressEnum.CLUSTER_NODE_3.getAddress(),
RedisAddressEnum.CLUSTER_NODE_4.getAddress(),
RedisAddressEnum.CLUSTER_NODE_5.getAddress(),
RedisAddressEnum.CLUSTER_NODE_6.getAddress()).collect(Collectors.toList());
return new RedisOptions()
.setType(RedisClientType.SENTINEL)
.setMasterName("my_test")
.setRole(RedisRole.MASTER)
.setEndpoints(redisAddressList);
}
public Redis getRedisClient() {
return redisClient;
}
/** * 读取数据从Redis * * @param redisClient Redis客户端 * @param key Redis中查询的键 */
public void readDataFromSentinel(Redis redisClient, String key) {
RedisAPI redisAPI = RedisAPI.api(redisClient);
redisAPI.get(key, res -> {
if (res.succeeded()) {
String value = String.valueOf(res.result());
logger.info(">>>>>>>>Read data from redis sentinel (key, value)->:({},{})", key, value);
} else {
logger.error(">>>>>>>>Redis sentinel read data error:", res.cause());
}
});
}
public static void main(String[] args) {
VertxSentinelOpRedisTest vertxOpRedisTest = new VertxSentinelOpRedisTest();
vertxOpRedisTest.readDataFromSentinel(vertxOpRedisTest.getRedisClient(), "name");
}
}
7 小结
Vert.x 3.9.4连接和操作Redis核心:
(1)配置Redis连接地址,通过RedisOptions,该类实例化时,有默认的参数;
(2)Vert.x3.9.4提供的Redis客户端无法连接需要密码认证的Redis集群,但是,可以连接单体需要密码认证的Redis;
(3)创建连接使用Redis.createClient,该方法根据type选择建立对应的Redis客户端:集群(cluster)、哨兵(sentinel)和单体(standalone);
(4)操作Redis使用RedisAPI完成;
(5)Vert.x操作Redis是通过异步线程完成:Vert.x的线程,如vert.x-eventloop-thread-0。
边栏推荐
- Lake Shore M91快速霍尔测量仪
- [pytorch record] distributed training dataparallel and distributeddataparallel of the model
- Junit单元测试框架详解
- 【pytorch记录】自动混合精度训练 torch.cuda.amp
- PostgreSQL varchar[] array type operation
- M91 fast hall measuring instrument - better measurement in a shorter time
- 线程的并行、并发、生命周期
- The difference between indexof and includes
- C-end dream is difficult to achieve. What does iFLYTEK rely on to support the goal of 1billion users?
- Lean thinking: source, pillar, landing. I understand it after reading this article
猜你喜欢

DTD建模

原生js打造日程表-支持鼠标滚轮滚动选择月份-可以移植到任何框架中

Bao, que se passe - t - il si le serveur 100 + O & M a mal à la tête? Utilisez le majordome xingyun!

智慧防疫系统为建筑工地复工复产提供安全保障

MATLAB中subplot函数的使用

pickle.load报错【AttributeError: Can‘t get attribute ‘Vocabulary‘ on <module ‘__main__‘】

Parallelism, concurrency and life cycle of threads

EasyGBS主子码流都为H.265时,切换出现花屏如何解决?

AAAI2020: Real-time Scene Text Detection with Differentiable Binarization

Digital business cloud: from planning to implementation, how does Minmetals Group quickly build a new pattern of digital development?
随机推荐
Dom4J解析XML、Xpath检索XML
Download (export) PDF template file (such as approval form), and report error: invalid nested tag * * * found, expected closing tag***
EasyGBS主子码流都为H.265时,切换出现花屏如何解决?
3. "Create your own NFT collections and publish a Web3 application to show them" cast NFT locally
Three ways for redis to realize current limiting
nacos启动失败问题解决与总结
Lumiprobe 活性染料丨吲哚菁绿说明书
Team up to learn! 14 days of Hongmeng equipment development "learning, practicing and testing" practical camp, free of charge!
Solidity - truncated and checked modes of arithmetic operations - new features of 0.8.0
Detailed explanation of JUnit unit test framework
学习笔记-JDBC连接数据库操作的步骤
Learning notes - steps of JDBC connection database operation
More information about M91 fast hall measuring instrument
Go Language Advanced
微信公众号开发相关流程及功能介绍
Intensive cultivation of channels for joint development Fuxin and Weishi Jiajie held a new product training conference
EasyGBS网络不稳定情况下重复请求视频拉流问题的优化
见证时代!“人玑协同 未来已来”2022弘玑生态伙伴大会开启直播预约
Lumiprobe phosphide hexaethylene phosphide specification
白盒加密技术浅理解