導航
環境:
redis:5.0.8
Springboot: 2.2.3.RELEASE
redis安裝參見:CentOS7下安裝Redis(單機版)
集羣方式及配置
基礎配置集羣配置:
#後臺運行(守護進程)
daemonize yes
#去除保護模式(允許遠程訪問)
protected-mode no
#去除綁定(遠程訪問)
#bind 127.0.0.1
# 設置密碼
requirepass redispwd
本次配置基於同一機器不通端口做集羣,當redis位於不同機器時,部分配置可以省去(以下配置過程中會說明/【單機非必須】:當前機器僅運行一個redis實例)
Cluster分區集羣
Cluster分區集羣表現形式爲數據均勻分佈在其中一個分區節點上(預設虛擬槽的範圍爲0到16383,每個節點間共享數據槽範圍)。
特別注意:cluster分區集羣要求至少三個節點
修改節點1配置 6379
拷貝一份配置文件命名爲redis-cluster6379.conf(來源安裝目錄:redis-5.0.8/redis.conf)
修改配置
# db寫入磁盤目錄位置(文件夾路徑如果不存在,需要手動創建):【單機非必須】
dir ./cluster6379
#日誌目錄,默認運行命令時的目錄【單機非必須】
logfile "./cluster6379/cluster6379.log"
#pid寫入位置,默認/var/run/redis_6379.pid【單機非必須】
pidfile "/var/run/redis_6379.pid"
# 開啓集羣
cluster-enabled yes
修改節點2配置 6380
拷貝redis-cluster6379.conf 命名爲redis-cluster6380.conf
# redis端口【單機非必須】
port 6380
# db寫入磁盤目錄位置(文件夾路徑如果不存在,需要手動創建):【單機非必須】
dir ./cluster6380
#日誌目錄,默認運行命令時的目錄【單機非必須】
logfile "./cluster6380/cluster6380.log"
#pid寫入位置,默認/var/run/redis_6379.pid【單機非必須】
pidfile /var/run/redis_6380.pid
修改節點3配置 6381
拷貝redis-cluster6379.conf 命名爲redis-cluster6381.conf
# redis端口【單機非必須】
port 6381
# db寫入磁盤目錄位置(文件夾路徑如果不存在,需要手動創建):【單機非必須】
dir ./cluster6381
#日誌目錄,默認運行命令時的目錄【單機非必須】
logfile "./cluster6381/cluster6381.log"
#pid寫入位置,默認/var/run/redis_6379.pid【單機非必須】
pidfile /var/run/redis_6381.pid
啓動節點1(6379)、節點2(6380)、節點3(6381)
./bin/redis-server redis-cluster6379.conf
./bin/redis-server redis-cluster6380.conf
./bin/redis-server redis-cluster6381.conf
查看6379狀態及cluster 節點
查看6380狀態及cluster 節點
對比發現,這是兩個不同的集羣(槽點分佈沒有顯示,如果不理解繼續向下看即可)。
配置集羣關係
這裏需要注意的是,redis5.0.8已經不支持redis-trib.rb配置集羣了
使用redis-cli配置集羣
測試一下少於三個的集羣會發生什麼?
./bin/redis-cli --cluster create 192.168.1.17:6379 192.168.1.17:6380 --cluster-replicas 0 -a redispwd
注: -a參數後面跟的是密碼,如果沒有密碼可以不設置此參數
執行結果:
尷尬不!!!
配置集羣
./bin/redis-cli --cluster create 192.168.1.17:6379 192.168.1.17:6380 192.168.1.17:6381 --cluster-replicas 0 -a redispwd
注意,中間有個詢問,輸入yes即可
分別查看三個基點的節點信息
可以看到槽點分佈情況如下(對比未配置集羣時的狀態-無槽點信息):
節點 | 槽點範圍 |
---|---|
6379 | 0-5460 |
6380 | 5461-10922 |
6381 | 10923-16381 |
數據寫入及讀取
分別在6397、6380、6381節點上set一個相同的key
可以看到key [testkey]只能在節點6379上插入,其他節點上插入會提示錯誤並告訴可供插入的節點信息。
使用redis-cli新增節點
修改節點4配置 6382
拷貝redis-cluster6379.conf 命名爲redis-cluster6382.conf
#redis端口【單機非必須】
port 6382
# db寫入磁盤目錄位置(文件夾路徑如果不存在,需要手動創建):【單機非必須】
dir ./cluster6382
#日誌目錄,默認運行命令時的目錄【單機非必須】
logfile "./cluster6382/cluster6382.log"
#pid寫入位置,默認/var/run/redis_6379.pid【單機非必須】
pidfile /var/run/redis_6382.pid
啓動節點4【6382】
./bin/redis-server redis-cluster6382.conf
此時6382是個獨立的節點
添加節點4【6382】到集羣
#-a 後面跟的是密碼,如果沒有密碼則不需要-a參數
#add-node 後面的參數形式 new_host:new_port existing_host:existing_port
./bin/redis-cli --cluster add-node 192.168.1.17:6382 192.168.1.17:6380 -a redispwd
給節點4【6382】分配槽點
查看節點信息:
節點添加成功後未分配槽點
./bin/redis-cli --cluster reshard 192.168.1.17:6379 -a redispwd
執行過程中有詢問
#準備挪出來多少個槽點
How many slots do you want to move (from 1 to 16384)? 800
#哪個節點準備接受新分配的槽點
What is the receiving node ID? 18936b87abadcb66135e243fa3fe49e88e8bbdc7
#準備從哪些節點上挪出這800(向上看這個數的來源)個槽點
Please enter all the source node IDs.
# 所有節點
Type 'all' to use all the nodes as source nodes for the hash slots.
#指定節點
Type 'done' once you entered all the source nodes IDs.
Source node #1: all
#是否執行reshare
Do you want to proceed with the proposed reshard plan (yes/no)? yes
查看節點信息:
節點擴容完成
使用redis-cli刪除節點
刪除節點需要經過以下兩步驟
1. 轉移槽點
這一步跟添加節點後的槽點一樣,大致過程爲,挪出來所需要的節點數 -> 選擇接收數據的節點 -> 選擇挪出的節點
#挪出多少槽點(5195),把6380節點挪出來,節點數在該詢問上面,不需要你計算
How many slots do you want to move (from 1 to 16384)? 5195
#哪個節點接收(18936b87abadcb66135e243fa3fe49e88e8bbdc7)6382節點接收
What is the receiving node ID? 18936b87abadcb66135e243fa3fe49e88e8bbdc7
#從哪挪
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
#6380上挪,如果需要刪除多個節點,繼續回車填寫下一個就行,填寫完畢最後一個節點鍵入‘done’即可完成
Source node #1: 50a78a3daad0e95ed04e10ac2e115fbc3ff1f9da
Source node #2: done
查看節點信息:
不再有槽點分配
2. 刪除節點
./bin/redis-cli --cluster del-node 192.168.1.17:6380 50a78a3daad0e95ed04e10ac2e115fbc3ff1f9da -a redispwd
節點已刪除
需要注意的是,當節點上分配的有槽點時是不允許刪除的
Cluster分區集羣在Springboot中的配置
1. pom引入
<!-- springboot整合redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.yml配置(properties配置文件類型自行轉換)
spring:
redis:
# 集羣(分片)模式
cluster:
max-redirects: 3
#節點
nodes:
- 192.168.1.17:6379
- 192.168.1.17:6381
- 192.168.1.17:6382
# Redis服務器連接密碼(默認爲空)
password: redispwd
#jedis連接池信息僅供參考
jedis:
pool:
#連接池最大連接數(使用負值表示沒有限制)
max-active: 8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
max-wait: -1
# 連接池中的最大空閒連接
pool.max-idle: 8
# 連接池中的最小空閒連接
pool.min-idle: 0
3.封裝RedisTemplate MyRedisConfig.java
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.HashSet;
@Configuration
@EnableCaching
public class MyRedisConfig {
@Bean
public RedisClusterConfiguration redisClusterConfiguration(RedisProperties redisProperties){
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(new HashSet<>(redisProperties.getCluster().getNodes()));
redisClusterConfiguration.setPassword(redisProperties.getPassword().toCharArray());
return redisClusterConfiguration;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替換默認的jdkSerializeable序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 設置value的序列化規則和 key的序列化規則
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 對redis字符串類型數據操作
*
* @param stringRedisTemplate
* @return
*/
@Bean({"valueoperations"})
public ValueOperations<String, String> valueOperations(StringRedisTemplate stringRedisTemplate) {
return stringRedisTemplate.opsForValue();
}
}
4.測試Controller SysController.java
import com.platform.test.common.exception.BusinessException;
import com.platform.test.service.SysService;
import com.platform.test.vo.BaseRespVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/sys")
public class SysController {
static final Logger logger = LoggerFactory.getLogger(SysController.class);
@Autowired
@Qualifier("valueoperations")
ValueOperations<String, String> valueOperations;
@Autowired
RedisClusterConfiguration redisClusterConfiguration;
@Autowired
RedisTemplate<Object,Object> redisTemplate;
@Autowired
SysService sysService;
final static String REDIS_TEST_KEY_VALUE = "__REDIS_TEST_KEY_VALUE";
@RequestMapping("/health")
public BaseRespVo health(HttpSession session) throws BusinessException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
HashMap<String, Object> data = new HashMap<>();
data.put("time-server",sf.format(new Date()));
data.put("status-redis",checkRedis());
return new BaseRespVo(data);
}
private boolean checkRedis() {
long time = System.currentTimeMillis();
if(valueOperations == null){
return false;
}
try {
valueOperations.set(REDIS_TEST_KEY_VALUE+time,REDIS_TEST_KEY_VALUE,30000, TimeUnit.MILLISECONDS);
logger.info("寫入redis key: "+ REDIS_TEST_KEY_VALUE+time +" value:"+REDIS_TEST_KEY_VALUE);
Thread.sleep(100);
String value = valueOperations.get(REDIS_TEST_KEY_VALUE+time);
if (value!=null && REDIS_TEST_KEY_VALUE.equals(value)) {
logger.info("讀取redis key: "+ REDIS_TEST_KEY_VALUE+time +" value:"+value);
return true;
}
}catch (Exception e){
logger.error("redis test exception!",e);
}
return false;
}
}
執行結果: