前言:本文针对另一篇Redis集群策略及集群实例(集群节点新增、删除、重新分配slot实战)博文搭建的Java项目用于redis集群完整流程的学习,仅供参考;
本文代码参考与码云开源项目相关资料
1. 环境
(1).springboot 2.0
(2).redis 4.0.10
2.相关代码
一,pom.xml文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.4.RELEASE
spring.boot
demo
0.0.1-SNAPSHOT
jar
jpa
Demo project for Spring Boot
UTF-8
org.tdcg.Application
1.8
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
com.alibaba
druid
1.1.6
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
org.projectlombok
lombok
1.18.10
org.bgee.log4jdbc-log4j2
log4jdbc-log4j2-jdbc4.1
1.16
org.springframework.boot
spring-boot-configuration-processor
true
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
com.alibaba
fastjson
1.2.31
org.springframework
spring-context
5.2.3.RELEASE
log4j
log4j
1.2.17
junit
junit
4.12
test
org.springframework.boot
spring-boot-maven-plugin
SpringBootJap
二,配置文件 application.properties
#datasource#mysql数据库#
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/study-jpa?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = 123456
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10
# server
server.port: 8081
三,redis配置文件 redis.properties
#代表redis多个节点的ip与端口号,多个节点需要使用“,”隔开。
spring.redis.cluster.nodes=192.168.0.108:8001,192.168.0.108:8002,192.168.0.108:8003,192.168.0.108:8004,192.168.0.108:8005,192.168.0.108:8006
# 最大的要重定向的次数(由于集群中数据存储在多个节点所以,在访问数据时需要通过节点进行转发)
# 连接超时的时间
spring.redis.cluster.timeout=5000
#最大的连接重试次数
spring.redis.cluster.max-attempts=3
#读取数据超时
spring.redis.cluster.soTimeout=3000
spring.redis.cluster.max-redirects=3
三,创建文件夹redis配置,依此创建redis初始化配置RedisConfiguration ,JedisClusterFactory工厂类,自定义缓存接口ICacheManager 以及redis客户端实现类JedisCacheManager
(一)redis初始化配置RedisConfiguration
package spring.boot.jpa.redis;
import com.google.common.collect.Sets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.util.Collections;
import java.util.Set;
/**
* @Title: RedisConfiguration
* @Description: redis初始化配置
* @Author: zyq
* @date: 2020/02/12
* @Version: V1.0
*/
@Configuration
@PropertySource("classpath:redis.properties")
public class RedisConfiguration extends CachingConfigurerSupport {
@Bean(name = "jedisCluster")
public JedisClusterFactory jedisCluster(
@Value("${spring.redis.cluster.nodes}") String host,
@Value("${spring.redis.cluster.timeout}") int connectionTimeout,
@Value("${spring.redis.cluster.soTimeout}") int soTimeout) {
JedisClusterFactory jedisClusterFactory = new JedisClusterFactory();
jedisClusterFactory.setConnectionTimeout(connectionTimeout);
// jedisClusterFactory.setMaxRedirections(maxRedirections);
jedisClusterFactory.setSoTimeout(soTimeout);
String[] split = host.split(",");
Set<String> hosts = Sets.newHashSet();
Collections.addAll(hosts, split);
jedisClusterFactory.setJedisClusterNodes(hosts);
return jedisClusterFactory;
}
}
(二)JedisClusterFactory工厂类
package spring.boot.jpa.redis;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.text.ParseException;
import java.util.HashSet;
import java.util.Set;
/**
* @Title: JedisClusterFactory
* @Description: 工厂类
* @Author: zyq
* @date: 2020/02/12
* @Version: V1.0
*/
public class JedisClusterFactory implements FactoryBean<JedisCluster>, InitializingBean {
private GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
private JedisCluster jedisCluster;
private int connectionTimeout = 2000;
private int soTimeout = 3000;
private int maxRedirections = 5;
private Set<String> jedisClusterNodes;
@Override
public void afterPropertiesSet() throws Exception {
if (jedisClusterNodes == null || jedisClusterNodes.size() == 0) {
throw new NullPointerException("jedisClusterNodes is null.");
}
Set<HostAndPort> haps = new HashSet<HostAndPort>();
for (String node : jedisClusterNodes) {
String[] arr = node.split(":");
if (arr.length != 2) {
throw new ParseException("node address error !",node.length()-1);
}
haps.add(new HostAndPort(arr[0], Integer.valueOf(arr[1])));
}
jedisCluster = new JedisCluster(haps, connectionTimeout, soTimeout, maxRedirections, genericObjectPoolConfig);
}
@Override
public JedisCluster getObject() throws Exception {
return jedisCluster;
}
@Override
public Class<?> getObjectType() {
return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);
}
@Override
public boolean isSingleton() {
return true;
}
public GenericObjectPoolConfig getGenericObjectPoolConfig() {
return genericObjectPoolConfig;
}
public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) {
this.genericObjectPoolConfig = genericObjectPoolConfig;
}
public JedisCluster getJedisCluster() {
return jedisCluster;
}
public void setJedisCluster(JedisCluster jedisCluster) {
this.jedisCluster = jedisCluster;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public int getSoTimeout() {
return soTimeout;
}
public void setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
}
public int getMaxRedirections() {
return maxRedirections;
}
public void setMaxRedirections(int maxRedirections) {
this.maxRedirections = maxRedirections;
}
public Set<String> getJedisClusterNodes() {
return jedisClusterNodes;
}
public void setJedisClusterNodes(Set<String> jedisClusterNodes) {
this.jedisClusterNodes = jedisClusterNodes;
}
}
(三) 自定义缓存操作接口ICacheManager
package spring.boot.jpa.redis;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* @Title: ICacheManager
* @Description: 缓存接口,定义方法
* @Author: zyq
* @date: 2020/02/12
* @Version: V1.0
*/
public interface ICacheManager {
/**
* 根据缓存key获取值
*
* @param cacheKey
* @return
*/
public Object getCache(Serializable cacheKey);
/**
* 设置缓存数据的key-value,并设置失效时间,单位为秒
*
* @param cacheKey
* @param objValue
* @param expiration
* @return
*/
public boolean putCache(Serializable cacheKey, Object objValue, int expiration);
/**
* 清除缓存
*
* @param cacheKey
*/
public Long removeCache(Serializable cacheKey);
/**
* 向指定list集合中添加对象,在list尾部添加对象
*
* @param cacheKey
* @param objValue
* @return
*/
public boolean putListCache(Serializable cacheKey, Object objValue);
/**
* 向指定list集合中添加对象,并指定位置坐标
*
* @param cacheKey
* @param objValue
* @param index
* @return
*/
public boolean putListCache(Serializable cacheKey, Object objValue, int index);
/**
* 根据坐标,返回一段集合
*
* @param cacheKey
* @param start 起始坐标 头部为0
* @param end 结束坐标 尾部为-1
* @return
*/
public List<Object> getListCache(Serializable cacheKey, int start, int end);
/**
* 返回结合
*
* @param cacheKey
* @return
*/
public List<Object> getListCache(Serializable cacheKey);
/**
* 裁剪list集合
*
* @param cacheKey
* @param start 起始坐标
* @param end 结束坐标
* @return
*/
public boolean trimListCache(Serializable cacheKey, int start, int end);
/**
* 添加map集合
*
* @param cacheKey
* @param map
* @return
*/
public boolean putMapCache(Serializable cacheKey, Map<Object, Object> map);
/**
* 删除map中的键值
*
* @param cacheKey
* @param mapKey
* @return
*/
public boolean deleteMapCache(Serializable cacheKey, Serializable mapKey);
/**
* 获取map中的值
*
* @param cacheKey
* @param mapKey
* @return
*/
public Object getMapValueCache(Serializable cacheKey, Serializable mapKey);
}
(四)redis客户端实现类JedisCacheManager
package spring.boot.jpa.redis.impl;
import com.alibaba.druid.util.StringUtils;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;
import spring.boot.jpa.redis.ICacheManager;
import spring.boot.jpa.redis.SerializingUtil;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* @Title: JedisCacheManager
* @Description: 接口实现
* @Author: zyq
* @date: 2020/02/12
* @Version: V1.0
*/
@Service("iCacheManager")
public class JedisCacheManager implements ICacheManager {
private static final String JEDIS_SET_RETURN_OK = "OK";
@Resource
private JedisCluster jedisCluster;
@Override
public Object getCache(Serializable cacheKey) {
return SerializingUtil.deserialize((byte[]) jedisCluster.get(SerializingUtil.serialize(cacheKey)));
}
@Override
public boolean putCache(Serializable cacheKey, Object objValue, int expiration) {
String result = jedisCluster.setex(SerializingUtil.serialize(cacheKey), expiration, SerializingUtil.serialize(objValue));
if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
return true;
}
return false;
}
@Override
public Long removeCache(Serializable cacheKey) {
return jedisCluster.del(SerializingUtil.serialize(cacheKey));
}
@Override
public boolean putListCache(Serializable cacheKey, Object objValue) {
Long num = jedisCluster.rpush(SerializingUtil.serialize(cacheKey), SerializingUtil.serialize(objValue));
if (num > 0) {
return true;
}
return false;
}
@Override
public boolean putListCache(Serializable cacheKey, Object objValue, int index) {
String result = jedisCluster.lset(SerializingUtil.serialize(cacheKey), index, SerializingUtil.serialize(objValue));
if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
return true;
}
return false;
}
@Override
public List<Object> getListCache(Serializable cacheKey, int start, int end) {
List<byte[]> list = jedisCluster.lrange(SerializingUtil.serialize(cacheKey), start, end);
if (null != list && list.size() > 0) {
List<Object> objList = new ArrayList<Object>();
for (byte[] b : list) {
objList.add(SerializingUtil.deserialize(b));
}
return objList;
}
return null;
}
@Override
public List<Object> getListCache(Serializable cacheKey) {
return getListCache(cacheKey, 0, -1);
}
@Override
public boolean trimListCache(Serializable cacheKey, int start, int end) {
String result = jedisCluster.ltrim(SerializingUtil.serialize(cacheKey), start, end);
if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
return true;
}
return false;
}
@Override
public boolean putMapCache(Serializable cacheKey, Map<Object, Object> map) {
if (null != map && !map.isEmpty()) {
Map<byte[], byte[]> byteMap = new HashMap<byte[], byte[]>();
for (Entry<Object, Object> entry : map.entrySet()) {
byteMap.put(SerializingUtil.serialize(entry.getKey()), SerializingUtil.serialize(entry.getValue()));
}
String result = jedisCluster.hmset(SerializingUtil.serialize(cacheKey), byteMap);
if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
return true;
}
return true;
}
return false;
}
@Override
public boolean deleteMapCache(Serializable cacheKey, Serializable mapKey) {
Long result = jedisCluster.hdel(SerializingUtil.serialize(cacheKey), SerializingUtil.serialize(mapKey));
if (result > 0) {
return true;
}
return false;
}
@Override
public Object getMapValueCache(Serializable cacheKey, Serializable mapKey) {
List<byte[]> list = jedisCluster.hmget(SerializingUtil.serialize(cacheKey), SerializingUtil.serialize(mapKey));
if (null != list && list.size() > 0) {
return SerializingUtil.deserialize(list.get(0));
}
return null;
}
}
(五) 序列化工具类
package spring.boot.jpa.redis;
import org.apache.log4j.Logger;
import org.springframework.cache.CacheManager;
import java.io.*;
/**
* @Title: SerializingUtil
* @Package: org.tdcg.util
* @Description: 序列化工具类,负责byte[]和Object之间的相互转换.
* @Author: zyq
* @date: 2020/02/12
* @Version: V1.0
*/
public class SerializingUtil {
private static final Logger logger = Logger.getLogger(CacheManager.class);
/**
* 功能简述: 对实体Bean进行序列化操作.
*
* @param source 待转换的实体
* @return 转换之后的字节数组
* @throws Exception
*/
public static byte[] serialize(Object source) {
ByteArrayOutputStream byteOut = null;
ObjectOutputStream ObjOut = null;
try {
byteOut = new ByteArrayOutputStream();
ObjOut = new ObjectOutputStream(byteOut);
ObjOut.writeObject(source);
ObjOut.flush();
} catch (IOException e) {
logger.error(source.getClass().getName() + " serialized error !", e);
} finally {
try {
if (null != ObjOut) {
ObjOut.close();
}
} catch (IOException e) {
ObjOut = null;
}
}
return byteOut.toByteArray();
}
/**
* 功能简述: 将字节数组反序列化为实体Bean.
*
* @param source 需要进行反序列化的字节数组
* @return 反序列化后的实体Bean
* @throws Exception
*/
public static Object deserialize(byte[] source) {
ObjectInputStream ObjIn = null;
Object retVal = null;
try {
ByteArrayInputStream byteIn = new ByteArrayInputStream(source);
ObjIn = new ObjectInputStream(byteIn);
retVal = ObjIn.readObject();
} catch (Exception e) {
logger.error("deserialized error !", e);
} finally {
try {
if (null != ObjIn) {
ObjIn.close();
}
} catch (IOException e) {
ObjIn = null;
}
}
return retVal;
}
}
(五) 测试类
package spring.boot.jpa;
import com.google.common.collect.Maps;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import spring.boot.jpa.entity.User;
import spring.boot.jpa.redis.impl.JedisCacheManager;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* @Title: JedisCacheManagerTest
* @Description: 测试类,测试方法有顺序要求
* 添加@FixMethodOrder(MethodSorters.NAME_ASCENDING) 以使执行方法按名称顺序执行
* @Author: zyq
* @date: 2020/02/12
* @Version: V1.0
*/
//Junit4运行环境
@RunWith(SpringJUnit4ClassRunner.class)
//单元测试时需要执行的SpringBoot启动类(根据需要引入执行)
@SpringBootTest(classes={JpaApplication.class})// 指定启动类
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
//如果是Web项目,Junit需要模拟ServletContext获取web等配置
//@WebAppConfiguration
public class JedisCacheManagerTest {
private final int expiration = 3600;
@Resource
private JedisCacheManager jedisCacheManager;
@Test
public void testAPutCache() throws Exception {
boolean test = jedisCacheManager.putCache("test", "welocme redis cluster! created by tdcg!", expiration);
assert(test);
}
@Test
public void testBGetCache() throws Exception {
Object test = jedisCacheManager.getCache("test");
System.out.println(test);
assert(test.equals("welocme redis cluster! created by tdcg!"));
}
@Test
public void testCRemoveCache() throws Exception {
Long test = jedisCacheManager.removeCache("test");
assert(test == 1L);
}
}
至此redis集群代码层面基本实现,仅供参考,欢迎指教!!!!!!