# redis-client **Repository Path**: mayanyun1986/redis-client ## Basic Information - **Project Name**: redis-client - **Description**: 封装了lettuce客户端 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-07-20 - **Last Updated**: 2024-06-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # br-redis-client --- ## 简介 br-redis-client基于lettuce,实现了双写(代理及物理机)、重试(幂等命令)、多集群配置、 代理和物理机切换(如果配置物理机)、部分命令禁用(服务端和cluster相关命令)、支持redis单独配置文件; #### 双写 可以同时配置代理和物理机,在访问redis时会优先访问代理;如果代理在给定时间内没有返回(默认2ms),则会启动另一个线程同时请求物理机;程序会返回最先返回线程的结果; 对于非幂等命令,则不执行双写; 流程如下图: ![double-write](./doc/double-write.png) #### 重试 对于幂等命令,当命令执行失败时,会进行重试,尽量使得请求成功; #### 多集群配置 可以在配置文件中配置多套集群,通过name来区分;系统启动时会将每一套配置初始化为一个BrRedisClient,方便业务使用; #### 代理和物理机自动切换 客户端会定时检测代理是否可用;如果发现代理不可用,则会自动切换到物理机模式;切换到物理机模式后,在执行非幂等命令,将优先采用物理机模式; #### 禁用部分命令 部分命令客户端调用会有较大的风险,会导致其它业务系统不能使用,所以对这类命令在客户端进行禁用,避免带来风险; 比如:cluster相关命令、server相关命令、keys、swapdb等; #### redis单独配置文件 redis的相关配置比较多,需要将配置写入到单独的redis.yml配置文件中;br-redis-client在启动时,会在以下目录加载该文件: - classpath:./ - classpath:./config - file:./ - file:./config 该文件与application.yml同级; ## 开始使用 --- #### 前提条件 - JDK 8+ #### 1、在pom.xml文件中引入依赖 ``` com.brgroup br-redis-client 1.0.0 ``` #### 2、在redis.yml增加配置 - 单集群模式: ``` spring: redis: standalone: password: pwd1 timeout: 10000 max-attempts: 5 proxy-uri: redis://10.100.123.52:6380 #cluster-uris: redis://10.100.123.190:7310,redis://10.100.123.190:27310,redis://10.100.123.191:7310,redis://10.100.123.191:27310,redis://10.100.123.192:7310,redis://10.100.123.192:27310 ``` 其中cluster-uris可以不配置,如果没有配置,则不会进行代理和cluster之间的切换; - 混合模式: ``` spring: redis: multiple: - name: api-redis password: pwd1 timeout: 1000 max-attempts: 5 proxy-uri: redis://10.100.123.211:6400 cluster-uris: redis://10.100.123.190:7310,redis://10.100.123.190:27310,redis://10.100.123.191:7310,redis://10.100.123.191:27310,redis://10.100.123.192:7310,redis://10.100.123.192:27310 - name: zw-redis password: pwd1 timeout: 1000 max-attempts: 5 proxy-uri: redis://10.100.123.211:6400 cluster-uris: redis://10.100.123.190:7310,redis://10.100.123.190:27310,redis://10.100.123.191:7310,redis://10.100.123.191:27310,redis://10.100.123.192:7310,redis://10.100.123.192:27310 ``` 混合模式放到multiple里,其中的name代表集群名称(必须英文),可以自行定义,定以后通过该名称获取对应的BrRedisClient; #### 3、代码引入使用 standalone模式可以通过以下方式引入使用: ``` private BrRedisClient redisClient = BrRedisClients.getRedisClient(); Map map = new ConcurrentHashMap<>(); map.put("age", "30"); map.put("sex", "15"); redisClient.set("name", map); Map result = (Map)redisClient.get("name"); System.out.println(result); redisClient.set("name1", 100); Integer result1 = (Integer) redisClient.get("name1"); System.out.println(result1); ``` 注:BrRedisClient的泛型指的是key-value的编码解码器解析的类型,默认key是String,Value为Object;其他解码器清参考配置表。 **关于初始化** ``` 当调用BrRedisClients.getRedisClient()时,会进行初始化连接。如果需要提前初始化连接可用执行以下方法: BrRedisClients.init(); ``` 混合集群模式用以下方式引入使用: ``` //其中name为配置文件中集群name private BrRedisClient apiRedisClient = BrRedisClients.getRedisClient("api-redis"); private BrRedisClient zxRedisClient = BrRedisClients.getRedisClient("zx-redis"); apiRedisClient.set("name", "myy"); String name = zxRedisClient.get("name"); ``` ## 其它功能 #### 1、禁用cluster 在默认情况下,cluster的配置是不起作用的,也就不会执行双写、切换;如果要启用需要按照规则在环境变量设置对应的启用标识; 环境变量名称: 该环境变量的名称由cluster.[配置集群名称|default].enabled组成;集群名称及在配置文件中配置的名称;如果是standalone模式,则名称为default;如: ``` cluster.zw-redis.enabled=true cluster.default.enabled=true ``` 在环境变量添加对应启用标识后,重启服务即可启用;以后代理稳定后则不需要双写及切换,可以将该环境变量删除即可。 #### 2、使用pipeline 如果需要同时大量请求redis,可以使用pipeline技术,将请求的命令组装在一起,一次性发送给redis,可以减少请求次数,提高执行效率。 使用示例如下: ``` private BrPipelineConnection pipelineConnection = BrRedisClients.getPipelineConnection(); ... StatefulRedisConnection connection = pipelineConnection.getDefaultPipelineConnection().getProxyConnection(); //其它集群的物理机可以通过以下方式访问。其中的参数clusterName即配置中的redis-cluster集群name //StatefulRedisClusterConnection clusterConnection = pipelineConnection.getPipelineConnection("redis-api").getClusterConnection(); //此处泛型定义与配置的编码器对应 RedisAsyncCommands commands = connection.async(); List> futures = Lists.newArrayList(); futures.add(commands.get("sex")); futures.add(commands.get("age")); connection.flushCommands(); boolean result = LettuceFutures.awaitAll(5, TimeUnit.SECONDS, futures.toArray(new RedisFuture[futures.size()])); if (result) { futures.forEach(future -> { try { System.out.println(future.get()); } catch (Exception e) { e.printStackTrace(); } }); } ``` 注:pipeline主要在connection层控制,使用后不能关闭连接。 #### 3、使用发布订阅 可以通过获取当前连接的pubSubConnection来实现发布订阅功能,代码如下: ``` StatefulRedisPubSubConnection pubSubConnection = redisClient.getCurrentPubSubConnection(); pubSubConnection.addListener(new RedisPubSubListener() { @Override public void message(String channel, String message) { //处理通过channel订阅后,接收的消息 System.out.printf("channel:%s, message:%s\n", channel, message); } @Override public void message(String pattern, String channel, String message) { //处理通过pattern匹配的channel,接收的消息 System.out.printf("pattern: %s, channel:%s, message:%s\n", pattern, channel, message); } ...... }); RedisPubSubCommands pubSubCommands = pubSubConnection.sync(); //通过channel名称订阅,在message(String channel, String message)里接收消息 //pubSubCommands.subscribe("test-channel"); //通过通配符匹配channel名称,在message(String pattern, String channel,String message)里接收消息 pubSubCommands.psubscribe("test*"); ``` 通过以下代码发布消息(注:不能使用与订阅同一个redisClient,因为同一个会话执行subscribe命令后不能执行publish;发布订阅可以在混合模式里再配置一个redisClient): ``` StatefulRedisPubSubConnection pubSubConnection = apiRedisClient.getCurrentPubSubConnection(); RedisPubSubCommands pubSubCommands = pubSubConnection.sync(); pubSubCommands.publish("test-channel", "hello world."); pubSubCommands.publish("test-channel2", "a test message."); ``` #### 4、对支持事务 br-redis-client通过暴露当前Commands的方式以保持会话,以支持事务。 ``` RedisCommands commands = redisClient.getCurrentCommands(); commands.multi(); try { commands.set("name", "mayanyun"); commands.get("name"); TransactionResult result = commands.exec(); System.out.println(result.get(0) + "_" + result.get(1)); } catch (Exception e) { log.error("产生异常。", e); commands.discard(); } ``` 注: 1. 事务中的key必须要在一个slot里才能执行; 2. 需要用try...cache....,避免事务没有关闭。 3. 该Commands依然提供了禁用部分功能; 4. redis的事务本质上只是批量提交命令,不能保证事务的原子性; ## 所有配置项 所有的配置及说明: ``` spring: redis: computationThreadPoolSize: 8 ioThreadPoolSize: 8 autoReconnect: true pingBeforeActivateConnection: true publishOnScheduler: false requestQueueSize: 36655 suspendReconnectOnProtocolFailure: false maxRedirects: 5 validateClusterNodeMembership: true retryWaitTimeMillis: 2000 doubleWriteClusterWaitTime: 5 multiple: - name: api-redis database: 0 clientName: api-redis-xxxx username: br password: pwd1 timeout: 10000 max-attempts: 5 codec: string.utf8 proxy-uri: redis://10.100.123.211:6400 - name: zw-redis database: 0 clientName: api-redis-xxxx username: br password: pwd1 timeout: 10000 max-attempts: 5 codec: string.utf8 proxy-uri: redis://10.100.123.211:6400 cluster-uris: redis://10.100.123.190:7310,redis://10.100.123.190:27310,redis://10.100.123.191:7310,redis://10.100.123.191:27310,redis://10.100.123.192:7310,redis://10.100.123.192:27310 standalone: database: 0 clientName: api-redis-xxxx username: br password: pwd1 timeout: 10000 max-attempts: 5 codec: string.utf8 proxy-uri: redis://10.100.123.211:6400 cluster-uris: redis://10.100.123.190:7310,redis://10.100.123.190:27310,redis://10.100.123.191:7310,redis://10.100.123.191:27310,redis://10.100.123.192:7310,redis://10.100.123.192:27310 metric: histogram: true localDistinction: false maxLatency: 5000 minLatency: 100 targetPercentiles: 0.50, 0.90, 0.95, 0.99, 0.999 ``` 配置说明如下: - spring.redis.computationThreadPoolSize netty计算线程池大小;可不配置该项,默认为系统可用核数 - spring.redis.ioThreadPoolSize netty io线程池大小;可不配置该项,默认为系统可用核数 - spring.redis.retryWaitTimeMillis 重试等待时间 ms;默认100ms - spring.redis.doubleWriteClusterWaitTime 双写集群等待时间 ms;默认2ms - spring.redis.standalone|multiple.database 数据库;可不配置该项,默认为0 - spring.redis.standalone|multiple.clientName 客户端名称;可不配置该项 - spring.redis.standalone|multiple.username - spring.redis.standalone|multiple.password 按照代理端标准配置 - spring.redis.standalone|multiple.timeout 超时时间;需要配置,建议为10秒 - spring.redis.standalone|multiple.max-attempts 最大重试次数,默认3次;需要配置,建议为3-5次 - spring.redis.standalone|multiple.codec 编码器,目前支持的方式如下: - jdk.serializer:当前默认方式;key使用String类型,Value使用Object类型,示例:BrRedisClient redisClient; - string.utf8:字符串类型,utf8编码(使用时泛型为String,示例:BrRedisClient redisClient) - string.ascii:字符串类型,ascii编码(使用时泛型为String,示例:BrRedisClient redisClient) - bytes:byte数组类型(使用时泛型为byte[],示例:BrRedisClient redisClient) - string-bytes:key用String序列化,value用byte[]序列化(示例:BrRedisClient redisClient) - bytes-string:key用byte[]序列化,value用String序列化(示例:BrRedisClient redisClient) - bytes-object:key用byte[]序列化,value用Object序列化(示例:BrRedisClient redisClient) - object-string:key用Object序列化,value用String序列化(示例:BrRedisClient redisClient) - object-bytes:key用Object序列化,value用byte[]序列化(示例:BrRedisClient redisClient) - object-object:key用Object序列化,value用Object序列化(示例:BrRedisClient redisClient) 注:默认为jdk.serializer - spring.redis.standalone|multiple.proxy-uri redis代理的连接 - spring.redis.standalone|multiple.cluster-uris redis cluster的连接 - redis cluster,可以不配置;不配置则代理发生错误不会进行切换;建议前期配置,后续代理稳定后不配置 - spring.redis.metric.* 配置prometheus暴露指标 一般不需要配置