当前位置:网站首页>如何正确使用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。
边栏推荐
- Love business in Little Red Book
- Lumiprobe 细胞成像研究丨PKH26细胞膜标记试剂盒
- 精耕渠道共謀發展 福昕攜手偉仕佳傑開展新產品培訓大會
- Lean thinking: source, pillar, landing. I understand it after reading this article
- 案例分享:QinQ基本组网配置
- Lake Shore—CRX-EM-HF 型低温探针台
- Chinese and English instructions human soluble advanced glycation end products receptor (sRAGE) ELISA Kit
- English grammar_ Adjective / adverb Level 3 - precautions
- Dlib+opencv library for fatigue detection
- Solidity - truncated and checked modes of arithmetic operations - new features of 0.8.0
猜你喜欢
Lake shore M91 fast hall measuring instrument
nacos配置文件发布失败,请检查参数是否正确的解决方案
数字化转型企业成功的关键,用数据创造价值
宝,运维100+服务器很头疼怎么办?用行云管家!
More information about M91 fast hall measuring instrument
机械设备行业数字化供应链集采平台解决方案:优化资源配置,实现降本增效
Bao, que se passe - t - il si le serveur 100 + O & M a mal à la tête? Utilisez le majordome xingyun!
Dlib+Opencv库实现疲劳检测
Lumiprobe 细胞成像研究丨PKH26细胞膜标记试剂盒
案例分享:QinQ基本组网配置
随机推荐
混沌工程平台 ChaosBlade-Box 新版重磅发布
【森城市】GIS数据漫谈(一)
【Go ~ 0到1 】 第五天 7月1 类型别名,自定义类型,接口,包与初始化函数
241. Different Ways to Add Parentheses
[English grammar] Unit1 articles, nouns, pronouns and numerals
torch.nn.functional.interpolate函数
【pytorch记录】模型的分布式训练DataParallel、DistributedDataParallel
The former 4A executives engaged in agent operation and won an IPO
[info() method in org.slf4j.logger]
CDGA|从事通信行业,那你应该考个数据管理证书
使用环信提供的uni-app Demo,快速实现一对一单聊
生鲜行业B2B电商平台解决方案,提高企业交易流程标准化和透明度
Technical secrets of ByteDance data platform: implementation and optimization of complex query based on Clickhouse
Dlib+Opencv库实现疲劳检测
Introduction to relevant processes and functions of wechat official account development
Lumiprobe phosphide hexaethylene phosphide specification
求各种极限的方法
Solidity - 合约结构 - 错误(error)- ^0.8.4版本新增
241. Different Ways to Add Parentheses
制造业SRM管理系统供应商全方位闭环管理,实现采购寻源与流程高效协同