SpringBoot + redis cluster集群搭建

系统管理员 2023-07-05 14:57 77阅读 0赞

SpringBoot + redis cluster集群搭建

  前言:本文针对另一篇Redis集群策略及集群实例(集群节点新增、删除、重新分配slot实战)博文搭建的Java项目用于redis集群完整流程的学习,仅供参考;
本文代码参考与码云开源项目相关资料
1. 环境
(1).springboot 2.0
(2).redis 4.0.10
2.相关代码
一,pom.xml文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.2.4.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>spring.boot</groupId>
  12. <artifactId>demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <packaging>jar</packaging>
  15. <name>jpa</name>
  16. <description>Demo project for Spring Boot</description>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <start-class>org.tdcg.Application</start-class>
  20. <java.version>1.8</java.version>
  21. </properties>
  22. <dependencies>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-data-jpa</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-starter-web</artifactId>
  34. </dependency>
  35. <dependency>
  36. <groupId>com.alibaba</groupId>
  37. <artifactId>druid</artifactId>
  38. <version>1.1.6</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>mysql</groupId>
  42. <artifactId>mysql-connector-java</artifactId>
  43. <scope>runtime</scope>
  44. </dependency>
  45. <dependency>
  46. <groupId>org.springframework.boot</groupId>
  47. <artifactId>spring-boot-starter-test</artifactId>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.projectlombok</groupId>
  51. <artifactId>lombok</artifactId>
  52. <version>1.18.10</version>
  53. </dependency>
  54. <!--监控sql日志-->
  55. <dependency>
  56. <groupId>org.bgee.log4jdbc-log4j2</groupId>
  57. <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
  58. <version>1.16</version>
  59. </dependency>
  60. <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui ->
  61. <dependency>
  62. <groupId>io.springfox</groupId>
  63. <artifactId>springfox-swagger2</artifactId>
  64. <version>2.6.1</version>
  65. </dependency>
  66. <dependency>
  67. <groupId>io.springfox</groupId>
  68. <artifactId>springfox-swagger-ui</artifactId>
  69. <version>2.6.1</version>
  70. </dependency>
  71. <!--属性配置支持-->
  72. <dependency>
  73. <groupId>org.springframework.boot</groupId>
  74. <artifactId>spring-boot-configuration-processor</artifactId>
  75. <optional>true</optional>
  76. </dependency>
  77. <!--redis依赖-->
  78. <!--默认继承lettuce,切换成jedis需要排除依赖-->
  79. <dependency>
  80. <groupId>org.springframework.boot</groupId>
  81. <artifactId>spring-boot-starter-data-redis</artifactId>
  82. <exclusions>
  83. <exclusion>
  84. <groupId>io.lettuce</groupId>
  85. <artifactId>lettuce-core</artifactId>
  86. </exclusion>
  87. </exclusions>
  88. </dependency>
  89. <!--redis 客户端-->
  90. <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
  91. <dependency>
  92. <groupId>redis.clients</groupId>
  93. <artifactId>jedis</artifactId>
  94. </dependency>
  95. <dependency>
  96. <groupId>com.alibaba</groupId>
  97. <artifactId>fastjson</artifactId>
  98. <version>1.2.31</version>
  99. </dependency>
  100. <dependency>
  101. <groupId>org.springframework</groupId>
  102. <artifactId>spring-context</artifactId>
  103. <version>5.2.3.RELEASE</version>
  104. </dependency>
  105. <dependency>
  106. <groupId>log4j</groupId>
  107. <artifactId>log4j</artifactId>
  108. <version>1.2.17</version>
  109. </dependency>
  110. <dependency>
  111. <groupId>junit</groupId>
  112. <artifactId>junit</artifactId>
  113. <version>4.12</version>
  114. <scope>test</scope>
  115. </dependency>
  116. </dependencies>
  117. <build>
  118. <plugins>
  119. <plugin>
  120. <groupId>org.springframework.boot</groupId>
  121. <artifactId>spring-boot-maven-plugin</artifactId>
  122. </plugin>
  123. </plugins>
  124. <finalName>SpringBootJap</finalName>
  125. </build>
  126. </project>

二,配置文件 application.properties

  1. #datasource#mysql数据库#
  2. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  3. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/study-jpa?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
  4. spring.datasource.username = root
  5. spring.datasource.password = 123456
  6. spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
  7. spring.datasource.max-active=20
  8. spring.datasource.max-idle=8
  9. spring.datasource.min-idle=8
  10. spring.datasource.initial-size=10
  11. # server
  12. server.port: 8081

三,redis配置文件 redis.properties

  1. #代表redis多个节点的ip与端口号,多个节点需要使用“,”隔开。
  2. 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
  3. # 最大的要重定向的次数(由于集群中数据存储在多个节点所以,在访问数据时需要通过节点进行转发)
  4. # 连接超时的时间
  5. spring.redis.cluster.timeout=5000
  6. #最大的连接重试次数
  7. spring.redis.cluster.max-attempts=3
  8. #读取数据超时
  9. spring.redis.cluster.soTimeout=3000
  10. spring.redis.cluster.max-redirects=3

三,创建文件夹redis配置,依此创建redis初始化配置RedisConfiguration ,JedisClusterFactory工厂类,自定义缓存接口ICacheManager 以及redis客户端实现类JedisCacheManager

(一)redis初始化配置RedisConfiguration

  1. package spring.boot.jpa.redis;
  2. import com.google.common.collect.Sets;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.cache.annotation.CachingConfigurerSupport;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.context.annotation.PropertySource;
  8. import java.util.Collections;
  9. import java.util.Set;
  10. /** * @Title: RedisConfiguration * @Description: redis初始化配置 * @Author: zyq * @date: 2020/02/12 * @Version: V1.0 */
  11. @Configuration
  12. @PropertySource("classpath:redis.properties")
  13. public class RedisConfiguration extends CachingConfigurerSupport {
  14. @Bean(name = "jedisCluster")
  15. public JedisClusterFactory jedisCluster(
  16. @Value("${spring.redis.cluster.nodes}") String host,
  17. @Value("${spring.redis.cluster.timeout}") int connectionTimeout,
  18. @Value("${spring.redis.cluster.soTimeout}") int soTimeout) {
  19. JedisClusterFactory jedisClusterFactory = new JedisClusterFactory();
  20. jedisClusterFactory.setConnectionTimeout(connectionTimeout);
  21. // jedisClusterFactory.setMaxRedirections(maxRedirections);
  22. jedisClusterFactory.setSoTimeout(soTimeout);
  23. String[] split = host.split(",");
  24. Set<String> hosts = Sets.newHashSet();
  25. Collections.addAll(hosts, split);
  26. jedisClusterFactory.setJedisClusterNodes(hosts);
  27. return jedisClusterFactory;
  28. }
  29. }

(二)JedisClusterFactory工厂类

  1. package spring.boot.jpa.redis;
  2. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
  3. import org.springframework.beans.factory.FactoryBean;
  4. import org.springframework.beans.factory.InitializingBean;
  5. import redis.clients.jedis.HostAndPort;
  6. import redis.clients.jedis.JedisCluster;
  7. import java.text.ParseException;
  8. import java.util.HashSet;
  9. import java.util.Set;
  10. /** * @Title: JedisClusterFactory * @Description: 工厂类 * @Author: zyq * @date: 2020/02/12 * @Version: V1.0 */
  11. public class JedisClusterFactory implements FactoryBean<JedisCluster>, InitializingBean {
  12. private GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
  13. private JedisCluster jedisCluster;
  14. private int connectionTimeout = 2000;
  15. private int soTimeout = 3000;
  16. private int maxRedirections = 5;
  17. private Set<String> jedisClusterNodes;
  18. @Override
  19. public void afterPropertiesSet() throws Exception {
  20. if (jedisClusterNodes == null || jedisClusterNodes.size() == 0) {
  21. throw new NullPointerException("jedisClusterNodes is null.");
  22. }
  23. Set<HostAndPort> haps = new HashSet<HostAndPort>();
  24. for (String node : jedisClusterNodes) {
  25. String[] arr = node.split(":");
  26. if (arr.length != 2) {
  27. throw new ParseException("node address error !",node.length()-1);
  28. }
  29. haps.add(new HostAndPort(arr[0], Integer.valueOf(arr[1])));
  30. }
  31. jedisCluster = new JedisCluster(haps, connectionTimeout, soTimeout, maxRedirections, genericObjectPoolConfig);
  32. }
  33. @Override
  34. public JedisCluster getObject() throws Exception {
  35. return jedisCluster;
  36. }
  37. @Override
  38. public Class<?> getObjectType() {
  39. return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);
  40. }
  41. @Override
  42. public boolean isSingleton() {
  43. return true;
  44. }
  45. public GenericObjectPoolConfig getGenericObjectPoolConfig() {
  46. return genericObjectPoolConfig;
  47. }
  48. public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) {
  49. this.genericObjectPoolConfig = genericObjectPoolConfig;
  50. }
  51. public JedisCluster getJedisCluster() {
  52. return jedisCluster;
  53. }
  54. public void setJedisCluster(JedisCluster jedisCluster) {
  55. this.jedisCluster = jedisCluster;
  56. }
  57. public int getConnectionTimeout() {
  58. return connectionTimeout;
  59. }
  60. public void setConnectionTimeout(int connectionTimeout) {
  61. this.connectionTimeout = connectionTimeout;
  62. }
  63. public int getSoTimeout() {
  64. return soTimeout;
  65. }
  66. public void setSoTimeout(int soTimeout) {
  67. this.soTimeout = soTimeout;
  68. }
  69. public int getMaxRedirections() {
  70. return maxRedirections;
  71. }
  72. public void setMaxRedirections(int maxRedirections) {
  73. this.maxRedirections = maxRedirections;
  74. }
  75. public Set<String> getJedisClusterNodes() {
  76. return jedisClusterNodes;
  77. }
  78. public void setJedisClusterNodes(Set<String> jedisClusterNodes) {
  79. this.jedisClusterNodes = jedisClusterNodes;
  80. }
  81. }

(三) 自定义缓存操作接口ICacheManager

  1. package spring.boot.jpa.redis;
  2. import java.io.Serializable;
  3. import java.util.List;
  4. import java.util.Map;
  5. /** * @Title: ICacheManager * @Description: 缓存接口,定义方法 * @Author: zyq * @date: 2020/02/12 * @Version: V1.0 */
  6. public interface ICacheManager {
  7. /** * 根据缓存key获取值 * * @param cacheKey * @return */
  8. public Object getCache(Serializable cacheKey);
  9. /** * 设置缓存数据的key-value,并设置失效时间,单位为秒 * * @param cacheKey * @param objValue * @param expiration * @return */
  10. public boolean putCache(Serializable cacheKey, Object objValue, int expiration);
  11. /** * 清除缓存 * * @param cacheKey */
  12. public Long removeCache(Serializable cacheKey);
  13. /** * 向指定list集合中添加对象,在list尾部添加对象 * * @param cacheKey * @param objValue * @return */
  14. public boolean putListCache(Serializable cacheKey, Object objValue);
  15. /** * 向指定list集合中添加对象,并指定位置坐标 * * @param cacheKey * @param objValue * @param index * @return */
  16. public boolean putListCache(Serializable cacheKey, Object objValue, int index);
  17. /** * 根据坐标,返回一段集合 * * @param cacheKey * @param start 起始坐标 头部为0 * @param end 结束坐标 尾部为-1 * @return */
  18. public List<Object> getListCache(Serializable cacheKey, int start, int end);
  19. /** * 返回结合 * * @param cacheKey * @return */
  20. public List<Object> getListCache(Serializable cacheKey);
  21. /** * 裁剪list集合 * * @param cacheKey * @param start 起始坐标 * @param end 结束坐标 * @return */
  22. public boolean trimListCache(Serializable cacheKey, int start, int end);
  23. /** * 添加map集合 * * @param cacheKey * @param map * @return */
  24. public boolean putMapCache(Serializable cacheKey, Map<Object, Object> map);
  25. /** * 删除map中的键值 * * @param cacheKey * @param mapKey * @return */
  26. public boolean deleteMapCache(Serializable cacheKey, Serializable mapKey);
  27. /** * 获取map中的值 * * @param cacheKey * @param mapKey * @return */
  28. public Object getMapValueCache(Serializable cacheKey, Serializable mapKey);
  29. }

(四)redis客户端实现类JedisCacheManager

  1. package spring.boot.jpa.redis.impl;
  2. import com.alibaba.druid.util.StringUtils;
  3. import org.springframework.stereotype.Service;
  4. import redis.clients.jedis.JedisCluster;
  5. import spring.boot.jpa.redis.ICacheManager;
  6. import spring.boot.jpa.redis.SerializingUtil;
  7. import javax.annotation.Resource;
  8. import java.io.Serializable;
  9. import java.util.ArrayList;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.Map.Entry;
  14. /** * @Title: JedisCacheManager * @Description: 接口实现 * @Author: zyq * @date: 2020/02/12 * @Version: V1.0 */
  15. @Service("iCacheManager")
  16. public class JedisCacheManager implements ICacheManager {
  17. private static final String JEDIS_SET_RETURN_OK = "OK";
  18. @Resource
  19. private JedisCluster jedisCluster;
  20. @Override
  21. public Object getCache(Serializable cacheKey) {
  22. return SerializingUtil.deserialize((byte[]) jedisCluster.get(SerializingUtil.serialize(cacheKey)));
  23. }
  24. @Override
  25. public boolean putCache(Serializable cacheKey, Object objValue, int expiration) {
  26. String result = jedisCluster.setex(SerializingUtil.serialize(cacheKey), expiration, SerializingUtil.serialize(objValue));
  27. if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
  28. return true;
  29. }
  30. return false;
  31. }
  32. @Override
  33. public Long removeCache(Serializable cacheKey) {
  34. return jedisCluster.del(SerializingUtil.serialize(cacheKey));
  35. }
  36. @Override
  37. public boolean putListCache(Serializable cacheKey, Object objValue) {
  38. Long num = jedisCluster.rpush(SerializingUtil.serialize(cacheKey), SerializingUtil.serialize(objValue));
  39. if (num > 0) {
  40. return true;
  41. }
  42. return false;
  43. }
  44. @Override
  45. public boolean putListCache(Serializable cacheKey, Object objValue, int index) {
  46. String result = jedisCluster.lset(SerializingUtil.serialize(cacheKey), index, SerializingUtil.serialize(objValue));
  47. if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
  48. return true;
  49. }
  50. return false;
  51. }
  52. @Override
  53. public List<Object> getListCache(Serializable cacheKey, int start, int end) {
  54. List<byte[]> list = jedisCluster.lrange(SerializingUtil.serialize(cacheKey), start, end);
  55. if (null != list && list.size() > 0) {
  56. List<Object> objList = new ArrayList<Object>();
  57. for (byte[] b : list) {
  58. objList.add(SerializingUtil.deserialize(b));
  59. }
  60. return objList;
  61. }
  62. return null;
  63. }
  64. @Override
  65. public List<Object> getListCache(Serializable cacheKey) {
  66. return getListCache(cacheKey, 0, -1);
  67. }
  68. @Override
  69. public boolean trimListCache(Serializable cacheKey, int start, int end) {
  70. String result = jedisCluster.ltrim(SerializingUtil.serialize(cacheKey), start, end);
  71. if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
  72. return true;
  73. }
  74. return false;
  75. }
  76. @Override
  77. public boolean putMapCache(Serializable cacheKey, Map<Object, Object> map) {
  78. if (null != map && !map.isEmpty()) {
  79. Map<byte[], byte[]> byteMap = new HashMap<byte[], byte[]>();
  80. for (Entry<Object, Object> entry : map.entrySet()) {
  81. byteMap.put(SerializingUtil.serialize(entry.getKey()), SerializingUtil.serialize(entry.getValue()));
  82. }
  83. String result = jedisCluster.hmset(SerializingUtil.serialize(cacheKey), byteMap);
  84. if (StringUtils.equals(JEDIS_SET_RETURN_OK, result)) {
  85. return true;
  86. }
  87. return true;
  88. }
  89. return false;
  90. }
  91. @Override
  92. public boolean deleteMapCache(Serializable cacheKey, Serializable mapKey) {
  93. Long result = jedisCluster.hdel(SerializingUtil.serialize(cacheKey), SerializingUtil.serialize(mapKey));
  94. if (result > 0) {
  95. return true;
  96. }
  97. return false;
  98. }
  99. @Override
  100. public Object getMapValueCache(Serializable cacheKey, Serializable mapKey) {
  101. List<byte[]> list = jedisCluster.hmget(SerializingUtil.serialize(cacheKey), SerializingUtil.serialize(mapKey));
  102. if (null != list && list.size() > 0) {
  103. return SerializingUtil.deserialize(list.get(0));
  104. }
  105. return null;
  106. }
  107. }

(五) 序列化工具类

  1. package spring.boot.jpa.redis;
  2. import org.apache.log4j.Logger;
  3. import org.springframework.cache.CacheManager;
  4. import java.io.*;
  5. /** * @Title: SerializingUtil * @Package: org.tdcg.util * @Description: 序列化工具类,负责byte[]和Object之间的相互转换. * @Author: zyq * @date: 2020/02/12 * @Version: V1.0 */
  6. public class SerializingUtil {
  7. private static final Logger logger = Logger.getLogger(CacheManager.class);
  8. /** * 功能简述: 对实体Bean进行序列化操作. * * @param source 待转换的实体 * @return 转换之后的字节数组 * @throws Exception */
  9. public static byte[] serialize(Object source) {
  10. ByteArrayOutputStream byteOut = null;
  11. ObjectOutputStream ObjOut = null;
  12. try {
  13. byteOut = new ByteArrayOutputStream();
  14. ObjOut = new ObjectOutputStream(byteOut);
  15. ObjOut.writeObject(source);
  16. ObjOut.flush();
  17. } catch (IOException e) {
  18. logger.error(source.getClass().getName() + " serialized error !", e);
  19. } finally {
  20. try {
  21. if (null != ObjOut) {
  22. ObjOut.close();
  23. }
  24. } catch (IOException e) {
  25. ObjOut = null;
  26. }
  27. }
  28. return byteOut.toByteArray();
  29. }
  30. /** * 功能简述: 将字节数组反序列化为实体Bean. * * @param source 需要进行反序列化的字节数组 * @return 反序列化后的实体Bean * @throws Exception */
  31. public static Object deserialize(byte[] source) {
  32. ObjectInputStream ObjIn = null;
  33. Object retVal = null;
  34. try {
  35. ByteArrayInputStream byteIn = new ByteArrayInputStream(source);
  36. ObjIn = new ObjectInputStream(byteIn);
  37. retVal = ObjIn.readObject();
  38. } catch (Exception e) {
  39. logger.error("deserialized error !", e);
  40. } finally {
  41. try {
  42. if (null != ObjIn) {
  43. ObjIn.close();
  44. }
  45. } catch (IOException e) {
  46. ObjIn = null;
  47. }
  48. }
  49. return retVal;
  50. }
  51. }

(五) 测试类

  1. package spring.boot.jpa;
  2. import com.google.common.collect.Maps;
  3. import org.junit.FixMethodOrder;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.junit.runners.MethodSorters;
  7. import org.springframework.boot.test.context.SpringBootTest;
  8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  9. import spring.boot.jpa.entity.User;
  10. import spring.boot.jpa.redis.impl.JedisCacheManager;
  11. import javax.annotation.Resource;
  12. import java.util.List;
  13. import java.util.Map;
  14. /** * @Title: JedisCacheManagerTest * @Description: 测试类,测试方法有顺序要求 * 添加@FixMethodOrder(MethodSorters.NAME_ASCENDING) 以使执行方法按名称顺序执行 * @Author: zyq * @date: 2020/02/12 * @Version: V1.0 */
  15. //Junit4运行环境
  16. @RunWith(SpringJUnit4ClassRunner.class)
  17. //单元测试时需要执行的SpringBoot启动类(根据需要引入执行)
  18. @SpringBootTest(classes={ JpaApplication.class})// 指定启动类
  19. @FixMethodOrder(MethodSorters.NAME_ASCENDING)
  20. //如果是Web项目,Junit需要模拟ServletContext获取web等配置
  21. //@WebAppConfiguration
  22. public class JedisCacheManagerTest {
  23. private final int expiration = 3600;
  24. @Resource
  25. private JedisCacheManager jedisCacheManager;
  26. @Test
  27. public void testAPutCache() throws Exception {
  28. boolean test = jedisCacheManager.putCache("test", "welocme redis cluster! created by tdcg!", expiration);
  29. assert(test);
  30. }
  31. @Test
  32. public void testBGetCache() throws Exception {
  33. Object test = jedisCacheManager.getCache("test");
  34. System.out.println(test);
  35. assert(test.equals("welocme redis cluster! created by tdcg!"));
  36. }
  37. @Test
  38. public void testCRemoveCache() throws Exception {
  39. Long test = jedisCacheManager.removeCache("test");
  40. assert(test == 1L);
  41. }
  42. }

至此redis集群代码层面基本实现,仅供参考,欢迎指教!!!!!!

发表评论

表情:
评论列表 (有 0 条评论,77人围观)

还没有评论,来说两句吧...

相关阅读

    相关 redis-cluster

    今天简单介绍一下redis-cluster集群搭建。 redis 最开始的时候,为了实现高可用,使用的主从模式,主从的模式的缺点是,一旦主挂了,没有办法自动的将“从 ”切换为“