您的位置:首页 > 数据库 > Redis

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

  • 点赞
  • 收藏
  • 分享
  • 文章举报
iT_MaNongZYQ 发布了10 篇原创文章 · 获赞 2 · 访问量 1054 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: