SpringBoot + redis cluster集群搭建

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集群代码层面基本实现,仅供参考,欢迎指教!!!!!!

发布了3 篇原创文章 · 获赞 1 · 访问量 79

猜你喜欢

转载自blog.csdn.net/iT_MaNongking/article/details/104337107