我們現在的系統對高可用高併發的需求越來越高了。通常爲降低應用內存壓力,減少IO消耗我們會選用redis來做緩存。
springboot 應用接入redis
我這裏簡單提一下 也可以參考之前的redis入門
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!--<version>${redis.version}</version>-->
</dependency>
- 並在yml 中配置redis 信息
當然springboot 1.X 與springboot2.x 配置參數命名會稍許不同,這裏使用的1.5
spring.redis.host=***
spring.redis.port=6379
spring.redis.password=123
- 應用
這時候你就可以在代碼中直接注入redisTemplate 了
public class IndexController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping({ "/test" })
@ResponseBody
public String rediesRest(ServletRequest request) {
stringRedisTemplate.opsForValue().set("test", "laowang");
return stringRedisTemplate.opsForValue().get("test");
}
redis的高可用
當然有了docker之後部署redis應用也就幾秒鐘的事了。
但是redis也會有性能瓶頸,這個時候redis的高可用就有必要了。
redis 的集羣方案:
參考這個文章redis集羣方案 和redis 集羣詳解
- 主從複製
當slave啓動後,主動向master發送SYNC命令。master接收到SYNC命令後在後臺保存快照(RDB持久化)和緩存保存快照這段時間的命令,然後將保存的快照文件和緩存的命令發送給slave。slave接收到快照文件和命令後加載快照文件和緩存的執行命令。
複製初始化後,master每次接收到的寫命令都會同步發送給slave,保證主從數據一致性。
- 哨兵模式
在主從的機制上引入哨兵進程:
1 監控主從數據庫是否正常運行
2 master出現故障時,自動將slave轉化爲master
3 多哨兵配置的時候,哨兵之間也會自動監控
4 多個哨兵可以監控同一個redis
- Redis-Cluster集羣
在redis的每一個節點上,都有這麼兩個東西,一個是插槽(slot),它的的取值範圍是:0-16383。還有一個就是cluster,可以理解爲是一個集羣管理的插件。當我們的存取的key到達的時候,redis會根據crc16的算法得出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,通過這個值,去找到對應的插槽所對應的節點,然後直接自動跳轉到這個對應的節點上進行存取操作。
爲了保證高可用,redis-cluster集羣引入了主從模式,一個主節點對應一個或者多個從節點,當主節點宕機的時候,就會啓用從節點。當其它主節點ping一個主節點A時,如果半數以上的主節點與A通信超時,那麼認爲主節點A宕機了。如果主節點A和它的從節點A1都宕機了,那麼該集羣就無法再提供服務了。
springboot 接入 redis cluster
yml 中配置 集羣信息
spring:
redis:
password: fuyun_redis
cluster:
nodes: 127.0.0.1:8001,127.0.0.1:8002,127.0.0.1:8003
在controller 使用就可以了
/**
* demo
*
* @author huiliuliu
* @date 2019-09-30
*/
@RestController
public class RedisTestController {
@Autowired
RedisTemplate redisTemplate;
@RequestMapping("redisSet")
public void test() {
redisTemplate.opsForValue().set("huiliuliu",131231);
}
@RequestMapping("redisGet")
public Object testGet() {
Object a = redisTemplate.opsForValue().get("huiliuliu");
return a;
}
}
如果你需要自己配置連接信息,則需要寫config
package com.fuyun.da.config.spring;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
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.RedisNode;
//import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
/**
* game-analytics
*
* @author: huiliuliu
* @date:2019-08-19
*/
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=18000)
public class SessionConfig {
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes;
@Value("${spring.redis.cluster.max-redirects:0}")
private int maxRedirects;
@Value("${spring.redis.password: }")
private String password;
@Value("${spring.redis.cluster.password: }")
private String clusterPassword;
@Value("${spring.redis.host: }")
private String host;
@Value("${spring.redis.port:6379}")
private int port;
@Value("${spring.redis.jedis.pool.max-idle:0}")
private int maxIdle;
// @Value("${spring.redis.maxTotal}")
// private int maxTotal;
// @Value("${spring.redis.maxWaitMillis}")
// private int maxWaitMillis;
// @Value("${spring.redis.minEvictableIdleTimeMillis}")
// private int minEvictableIdleTimeMillis;
// @Value("${redis.numTestsPerEvictionRun}")
// private int numTestsPerEvictionRun;
// @Value("${redis.timeBetweenEvictionRunsMillis}")
// private int timeBetweenEvictionRunsMillis;
// @Value("${spring.redis.testOnBorrow}")
// private boolean testOnBorrow;
// @Value("${spring.redis.testWhileIdle}")
// private boolean testWhileIdle;
@Bean
public JedisPoolConfig getJedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大空閒數
jedisPoolConfig.setMaxIdle(5);
// 連接池的最大數據庫連接數
jedisPoolConfig.setMaxTotal(5);
// 最大建立連接等待時間
jedisPoolConfig.setMaxWaitMillis(500);
// 逐出連接的最小空閒時間 默認1800000毫秒(30分鐘)
jedisPoolConfig.setMinEvictableIdleTimeMillis(1800000);
// 每次逐出檢查時 逐出的最大數目 如果爲負數就是 : 1/abs(n), 默認3
jedisPoolConfig.setNumTestsPerEvictionRun(1);
// 逐出掃描的時間間隔(毫秒) 如果爲負數,則不運行逐出線程, 默認-1
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(-1);
// 是否在從池中取出連接前進行檢驗,如果檢驗失敗,則從池中去除連接並嘗試取出另一個
jedisPoolConfig.setTestOnBorrow(true);
// 在空閒時檢查有效性, 默認false
jedisPoolConfig.setTestWhileIdle(false);
return jedisPoolConfig;
}
/**
* Redis集羣的配置
*
* @return RedisClusterConfiguration
* @throws
*/
@Bean
public RedisClusterConfiguration redisClusterConfiguration() {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
//Set<RedisNode> clusterNodes
String[] serverArray = clusterNodes.split(",");
Set<RedisNode> nodes = new HashSet<RedisNode>();
for (String ipPort : serverArray) {
String[] ipAndPort = ipPort.split(":");
nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1])));
}
redisClusterConfiguration.setClusterNodes(nodes);
redisClusterConfiguration.setMaxRedirects(maxRedirects);
// redisClusterConfiguration.setPassword(RedisPassword.of(password));
return redisClusterConfiguration;
}
/**
* @param
* @return
* @Description:redis連接工廠類
* @date 2018/10/25 19:45
*/
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
if(StringUtils.isNoneBlank(host)){
JedisConnectionFactory connection = new JedisConnectionFactory();
connection.setHostName(host);
connection.setDatabase(1);
if (StringUtils.isNoneBlank(password)){
connection.setPassword(password);
}
connection.setPort(port);
return connection;
}
//集羣模式
JedisConnectionFactory factory = new JedisConnectionFactory(redisClusterConfiguration(), getJedisPoolConfig());
factory.setDatabase(0);
factory.setUsePool(true);
factory.setPassword(clusterPassword);
return factory;
}
/**
* 實例化 RedisTemplate 對象
*
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
initDomainRedisTemplate(redisTemplate);
return redisTemplate;
}
/**
* 設置數據存入 redis 的序列化方式,並開啓事務
* 使用默認的序列化會導致key亂碼
*/
private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
//如果不配置Serializer,那麼存儲的時候缺省使用String,如果用User類型存儲,那麼會提示錯誤User can't cast to String!
redisTemplate.setKeySerializer(new StringRedisSerializer());
//這個地方有一個問題,這種序列化器會將value序列化成對象存儲進redis中,如果
//你想取出value,然後進行自增的話,這種序列化器是不可以的,因爲對象不能自增;
//需要改成StringRedisSerializer序列化器。
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setEnableTransactionSupport(false);
redisTemplate.setConnectionFactory(jedisConnectionFactory());
}
@Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
}
成功了,試一試吧