springboot+redis主从复制、哨兵、读写分离
一、redis安裝
# 安裝包存放目錄 cd /opt/software/ # 下載最新穩定版 wget https://download.redis.io/releases/redis-6.2.6.tar.gz # 解壓 tar -zxvf redis-6.2.6.tar.gz # 進入解壓后的目錄 cd /opt/software/redis-6.2.6/ # 編譯 make # 執行 "make install" 默認會安裝到 /usr/local/bin,可通過PREFIX指定安裝路徑 make install PREFIX=/usr/local/redis # 測試是否安裝成功,執行下面命令 /usr/local/redis/bin/redis-server二、主從及哨兵配置
三個redis實例都運行在192.168.162.10服務器上,端口分別是7001、7002、7003,默認啟動時已7001作為主節點,7002與7003作為從節點,下面是主從與哨兵的配置文件需要修改的地方。
1、redis配置
原始配置文件可在解壓后的源碼文件根目錄中找到,這里以從節點 7002 配置文件為例,其余兩個配置文件幾乎一致。首先將配置文件拷貝到/opt/software/redis-cluster/redis-7002.conf,然后進行下面的修改。
# (1)設置允許外部ip訪問,需要注釋掉bind配置,并關掉保護模式 # bind 127.0.0.1 -::1 protected-mode no# (2)修改端口號 port 7002# (3)修改為以守護進程模式后臺運行 daemonize yes# (4)修改pid文件名,以守護進程運行的時候,會產生pid文件,默認位置為 /run/redis.pid # 因為這里在同一臺機器上運行多個實例,所以需要指定 pidfile /opt/software/redis-cluster/redis_7002.pid# (5)修改日志文件位置 logfile /opt/software/redis-cluster/redis_7002.log# (6)修改rdb快照文件位置 dir /opt/software/redis-cluster dbfilename dump_7002.rdb# (7)修改主節點地址,在部分舊版本中是slaveof命令,主節點7001配置文件中不要加這一行 replicaof 192.168.162.10 7001# (8)aof可按需要開啟 appendonly yes appendfilename appendonly_7002.aof在上面的配置中,7001文件與7003一致,改一下其中的端口及地址就可以了,其中,7001作為主節點,沒有第(7)點。建議三個實例運行在不同的文件夾下,我為了省去切換文件目錄的時間,都放在一個文件夾下了。
配置完成后,按三個端口號的順序啟動分別啟動三個實例。
/usr/local/redis/bin/redis-server /opt/software/redis-cluster/redis-7001.conf /usr/local/redis/bin/redis-server /opt/software/redis-cluster/redis-7002.conf /usr/local/redis/bin/redis-server /opt/software/redis-cluster/redis-7003.conf啟動后,產生的文件如下所示
進入主節點,查看實例主從狀況
進入從節點,查看實例主從狀況
測試主從復制,在主節點中添加一個緩存,然后從節點中查詢
至此,主從復制基本上差不多了,接下來就是哨兵的配置了。
2、sentinel配置
共計啟動三個實例,分別運行于27001、27002、27003三個端口,以sentinel-27001.conf為例,配置信息如下,其余兩個配置文件基本上一致,改一下端口以及pidfile、logfile即可。
port 27001 daemonize yes pidfile /opt/software/redis-cluster/sentinel-27001.pid logfile /opt/software/redis-cluster/sentinel-27001.log# 監控192.168.162.10:7001實例,實例取名為mymaster,當有兩個哨兵認為實例下線后,自動進行故障轉移 sentinel monitor mymaster 192.168.162.10 7001 2 # 服務不可達時間,心跳超過這個時間,sentinel將認為節點掛了 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1分別啟動三個哨兵實例
隨意連接一個哨兵,查看哨兵監控信息
查看哨兵日志
關閉主節點,再看哨兵日志
從上面的日志文件中,我們可以看到哨兵投票選舉leader以及切換主節點的大概過程,這時候,主節點已經切換到7003節點了。
這時候,再重新啟動7001節點,也就是之前的主節點,這個節點會被哨兵自動加入到集群中作為從節點,sentinel會打印如下日志
+convert-to-slave slave 192.168.162.10:7001 192.168.162.10 7001 @ mymaster 192.168.162.10 7003至此,哨兵集群也OK了。接下來就是springboot中配置哨兵集群了。
三、springboot配置哨兵集群及讀寫分離
創建springboot測試項目,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.6.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>sentinel-cluster</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-boot-sentinel-cluster</name><description>spring-boot-sentinel-cluster</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.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>配置文件application.yml如下
spring:redis:sentinel:master: mymasternodes:- 192.168.162.10:27001- 192.168.162.10:27002- 192.168.162.10:27003 logging:pattern:console: '%date{yyyy-MM-dd HH:mm:ss.SSS} | %highlight(%5level) [%green(%16.16thread)] %clr(%-50.50logger{49}){cyan} %4line -| %highlight(%msg%n)'level:root: info創建配置類,配置RedisTemplate
package com.example.config;import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;import java.text.SimpleDateFormat; import java.util.TimeZone;/*** @author ygr* @date 2022-02-15 16:30*/ @Slf4j @Configuration public class RedisConfig {public ObjectMapper objectMapper() {ObjectMapper objectMapper = new ObjectMapper();objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);return objectMapper;}@Bean@ConditionalOnMissingBeanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {// 創建RedisTemplate<String, Object>對象RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 定義Jackson2JsonRedisSerializer序列化對象Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);jackson2JsonRedisSerializer.setObjectMapper(objectMapper());StringRedisSerializer stringSerial = new StringRedisSerializer();// redis key 序列化方式使用stringSerialtemplate.setKeySerializer(stringSerial);// redis value 序列化方式使用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// redis hash key 序列化方式使用stringSerialtemplate.setHashKeySerializer(stringSerial);// redis hash value 序列化方式使用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;} }新建一個RedisInit類來進行測試,該類實現了ApplicationRunner接口,在應用啟動后自動運行
package com.example.init;import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** @author ygr* @date 2022-02-15 16:32*/ @Slf4j @RequiredArgsConstructor @Component public class RedisInit implements ApplicationRunner {private final RedisTemplate<String, Object> redisTemplate;@Overridepublic void run(ApplicationArguments args) throws Exception {for (int i = 0; i < 300; i++) {try {redisTemplate.opsForValue().set("k" + i, "v" + i);log.info("set value success: {}", i);Object val = redisTemplate.opsForValue().get("k" + i);log.info("get value success: {}", val);TimeUnit.SECONDS.sleep(1);} catch (Exception e) {log.error("error: {}", e.getMessage());}}log.info("finished...");} }項目結構特別簡單
啟動項目,查看日志,可以看到讀寫一切正常。
途中嘗試將主節點干掉,接著看日志,從日志中可以看到,主從切換過來后,一切ok
但從info級別日志中,我們是看不出具體的讀寫連接信息的。將剛剛干掉的主節點重新啟動起來,保持一主二從的模式,并修改一下部分包的日志級別為debug,然后再次啟動看日志
logging:pattern:console: '%date{yyyy-MM-dd HH:mm:ss.SSS} | %highlight(%5level) [%green(%16.16thread)] %clr(%-50.50logger{49}){cyan} %4line -| %highlight(%msg%n)'level:root: infoio.lettuce.core: debugorg.springframework.data.redis: debug這部分日志有點長,我直接截取了一次讀寫的日志,如下
2022-02-28 15:43:04.962 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 143 -| Fetching Redis Connection from RedisConnectionFactory 2022-02-28 15:43:04.962 | DEBUG [ main] io.lettuce.core.RedisChannelHandler 175 -| dispatching command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.962 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 430 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1] write() writeAndFlush command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.962 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 207 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1] write() done 2022-02-28 15:43:04.963 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 383 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] write(ctx, AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise) 2022-02-28 15:43:04.963 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandEncoder 101 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001] writing command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.964 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 577 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] Received: 5 bytes, 1 commands in the stack 2022-02-28 15:43:04.964 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 651 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] Stack contains: 1 commands 2022-02-28 15:43:04.964 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.RedisStateMachine 298 -| Decode done, empty stack: true 2022-02-28 15:43:04.964 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 679 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] Completing command AsyncCommand [type=SET, output=StatusOutput [output=OK, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.964 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 389 -| Closing Redis Connection. 2022-02-28 15:43:04.965 | INFO [ main] com.example.init.RedisInit 28 -| set value success: 4 2022-02-28 15:43:04.965 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 143 -| Fetching Redis Connection from RedisConnectionFactory 2022-02-28 15:43:04.965 | DEBUG [ main] io.lettuce.core.RedisChannelHandler 175 -| dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.965 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 430 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1] write() writeAndFlush command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.965 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 207 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1] write() done 2022-02-28 15:43:04.965 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 383 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] write(ctx, AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise) 2022-02-28 15:43:04.966 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandEncoder 101 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001] writing command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.966 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 577 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] Received: 10 bytes, 1 commands in the stack 2022-02-28 15:43:04.966 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 651 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] Stack contains: 1 commands 2022-02-28 15:43:04.966 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.RedisStateMachine 298 -| Decode done, empty stack: true 2022-02-28 15:43:04.966 | DEBUG [nioEventLoop-6-2] io.lettuce.core.protocol.CommandHandler 679 -| [channel=0x72e65475, /192.168.162.1:61674 -> /192.168.162.10:7001, epid=0x1, chid=0x2] Completing command AsyncCommand [type=GET, output=ValueOutput [output=[B@393ff2f4, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:43:04.967 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 389 -| Closing Redis Connection. 2022-02-28 15:43:04.967 | INFO [ main] com.example.init.RedisInit 31 -| get value success: v4從日志中可以看到,讀與寫都是走的主節點(目前7001是主)。
如果想做讀寫分離,也很簡單,修改RedisConfig類,加入如下Bean的配置代碼
@Bean public RedisConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties) {RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(redisProperties.getSentinel().getMaster(), new HashSet<>(redisProperties.getSentinel().getNodes()));LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()// 讀寫分離,若主節點能抗住讀寫并發,則不需要設置,全都走主節點即可.readFrom(ReadFrom.ANY_REPLICA).build();return new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration); }ReadFrom的取值及讀取方式的對應關系如下,其中,REPLICA會一直讀取的同一個從節點,ANY_REPLICA則會隨機選擇
| MASTER / UPSTREAM | 僅讀取主節點 |
| MASTER_PREFERRED / UPSTREAM_PREFERRED | 優先讀取主節點,如果主節點不可用,則讀取從節點 |
| REPLICA/ SLAVE(已廢棄) | 僅讀取從節點 |
| REPLICA_PREFERRED / SLAVE_PREFERRED(已廢棄) | 優先讀取從節點,如果從節點不可用,則讀取主節點 |
| NEAREST | 從最近節點讀取 |
| ANY | 從任何節點讀取 |
| ANY_REPLICA | 從任意一個從節點讀取 |
再重啟查看日志
2022-02-28 15:40:58.971 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 143 -| Fetching Redis Connection from RedisConnectionFactory 2022-02-28 15:40:58.971 | DEBUG [ main] io.lettuce.core.RedisChannelHandler 175 -| dispatching command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.971 | DEBUG [ main] i.l.c.m.MasterReplicaConnectionProvider 112 -| getConnectionAsync(WRITE) 2022-02-28 15:40:58.971 | DEBUG [ main] io.lettuce.core.RedisChannelHandler 175 -| dispatching command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.971 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 430 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001, epid=0x7] write() writeAndFlush command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.972 | DEBUG [nioEventLoop-6-7] io.lettuce.core.protocol.CommandHandler 383 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001, epid=0x7, chid=0x7] write(ctx, AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise) 2022-02-28 15:40:58.972 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 207 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001, epid=0x7] write() done 2022-02-28 15:40:58.973 | DEBUG [nioEventLoop-6-7] io.lettuce.core.protocol.CommandEncoder 101 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001] writing command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.974 | DEBUG [nioEventLoop-6-7] io.lettuce.core.protocol.CommandHandler 577 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001, epid=0x7, chid=0x7] Received: 5 bytes, 1 commands in the stack 2022-02-28 15:40:58.974 | DEBUG [nioEventLoop-6-7] io.lettuce.core.protocol.CommandHandler 651 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001, epid=0x7, chid=0x7] Stack contains: 1 commands 2022-02-28 15:40:58.974 | DEBUG [nioEventLoop-6-7] io.lettuce.core.protocol.RedisStateMachine 298 -| Decode done, empty stack: true 2022-02-28 15:40:58.974 | DEBUG [nioEventLoop-6-7] io.lettuce.core.protocol.CommandHandler 679 -| [channel=0x4c2f55eb, /192.168.162.1:61317 -> /192.168.162.10:7001, epid=0x7, chid=0x7] Completing command AsyncCommand [type=SET, output=StatusOutput [output=OK, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.974 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 389 -| Closing Redis Connection. 2022-02-28 15:40:58.974 | INFO [ main] com.example.init.RedisInit 28 -| set value success: 4 2022-02-28 15:40:58.974 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 143 -| Fetching Redis Connection from RedisConnectionFactory 2022-02-28 15:40:58.975 | DEBUG [ main] io.lettuce.core.RedisChannelHandler 175 -| dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.975 | DEBUG [ main] i.l.c.m.MasterReplicaConnectionProvider 112 -| getConnectionAsync(READ) 2022-02-28 15:40:58.975 | DEBUG [ main] io.lettuce.core.RedisChannelHandler 175 -| dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.975 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 430 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002, epid=0x8] write() writeAndFlush command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.975 | DEBUG [nioEventLoop-6-8] io.lettuce.core.protocol.CommandHandler 383 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002, epid=0x8, chid=0x8] write(ctx, AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise) 2022-02-28 15:40:58.975 | DEBUG [ main] io.lettuce.core.protocol.DefaultEndpoint 207 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002, epid=0x8] write() done 2022-02-28 15:40:58.976 | DEBUG [nioEventLoop-6-8] io.lettuce.core.protocol.CommandEncoder 101 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002] writing command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.976 | DEBUG [nioEventLoop-6-8] io.lettuce.core.protocol.CommandHandler 577 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002, epid=0x8, chid=0x8] Received: 10 bytes, 1 commands in the stack 2022-02-28 15:40:58.976 | DEBUG [nioEventLoop-6-8] io.lettuce.core.protocol.CommandHandler 651 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002, epid=0x8, chid=0x8] Stack contains: 1 commands 2022-02-28 15:40:58.977 | DEBUG [nioEventLoop-6-8] io.lettuce.core.protocol.RedisStateMachine 298 -| Decode done, empty stack: true 2022-02-28 15:40:58.977 | DEBUG [nioEventLoop-6-8] io.lettuce.core.protocol.CommandHandler 679 -| [channel=0x83e97184, /192.168.162.1:61318 -> /192.168.162.10:7002, epid=0x8, chid=0x8] Completing command AsyncCommand [type=GET, output=ValueOutput [output=[B@75c09ac4, error='null'], commandType=io.lettuce.core.protocol.Command] 2022-02-28 15:40:58.977 | DEBUG [ main] o.s.data.redis.core.RedisConnectionUtils 389 -| Closing Redis Connection. 2022-02-28 15:40:58.977 | INFO [ main] com.example.init.RedisInit 31 -| get value success: v4從日志中可以看到,寫操作走的是主節點(7001),讀操作走的是從節點(7002),日志太長,沒有粘貼其他的,從完整的日志中可以看到,從節點其實是一直隨機選擇的。
總結
以上是生活随笔為你收集整理的springboot+redis主从复制、哨兵、读写分离的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据分析|爬取14455个基金,千万别被
- 下一篇: java broken pipe_jav