SpringBoot + redis cluster集群搭建
2020-03-05 09:40
691 查看
SpringBoot + redis cluster集群搭建
前言:本文针对另一篇Redis集群策略及集群实例(集群节点新增、删除、重新分配slot实战)博文搭建的Java项目用于redis集群完整流程的学习,仅供参考;
本文代码参考与码云开源项目相关资料
1. 环境
(1).springboot 2.0
(2).redis 4.0.10
2.相关代码
一,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.2.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>spring.boot</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>jpa</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <start-class>org.tdcg.Application</start-class> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <!--监控sql日志--> <dependency> <groupId>org.bgee.log4jdbc-log4j2</groupId> <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId> <version>1.16</version> </dependency> <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.6.1</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.6.1</version> </dependency> <!--属性配置支持--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!--redis依赖--> <!--默认继承lettuce,切换成jedis需要排除依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <!--redis 客户端--> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <finalName>SpringBootJap</finalName> </build> </project>
二,配置文件 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集群代码层面基本实现,仅供参考,欢迎指教!!!!!!
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- 在windows上搭建redis集群,结合springboot 2.x配置lettuce及redisson
- 非关系型数据库Redis(三):搭建三主三从的两种方式与SpringBoot整合Redis集群
- CentOS7下Redis5.0集群搭建、整合springBoot
- 在阿里云上搭建redis高可用集群,并写一个简单的springboot小demo测试
- redis5.0集群搭建实战,spring-boot整合redis集群测试
- springboot2.0整合redis-cluster集群
- 在阿里云搭建redis高可用集群加SpringBoot 小demo
- SpringBoot注解缓存配置浅析(Redis集群)
- 阿里云服务器搭建SpringBoot-MySQL-Redis-Nginx项目搭建
- 二:redis3.x-cluster集群于spring整合
- Nginx+Tomcat搭建集群,Spring Session+Redis实现Session共享
- 利用IDEA 搭建springboot+mybatis+redis+lombok
- springboot redis(单机/集群)
- Redis3缓存集群(cluster)搭建
- linux +redis cluster 集群搭建
- redis 分片集群(cluster)搭建
- spring boot之Session实现集群-redis
- Redis-3.2.4集群配置(RedisCluster+SpringBoot+Jedis)
- 基于springboot+bootstrap+mysql+redis搭建一套完整的权限架构【一】【构建工程】
- 使用Spring boot基于Redis快速搭建分布式Session缓存方案