Windows10系统Springboot2.x集成Redis集群(一:集群构建)
在上一篇博客中详细描述了windows系统下redis集群的构建,本篇继续为大家演示springboot2.x与redis集群的整合。
从工程构建开始,现在idea中构建一个springboot工程,如下:
下面需要引入redis相关依赖:
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.session
spring-session-data-redis
org.apache.commons
commons-pool2
修改application.yml:
spring:
# redis配置
redis:
cluster:
nodes: 127.0.0.1:6379, 127.0.0.1:6380, 127.0.0.1:6381
max-redirects: 3
lettuce:
pool:
max-active: 1000 # 连接池最大连接数
max-idle: 10 # 连接池最大空闲连接
min-idle: 5 # 连接池最小空闲连接
max-wait: -1ms # 连接池最大阻塞时间
timeout: 3000ms
session:
store-type: redis
timeout: 30m
redis:
namespace: redis:session
重写redis的key/value序列化策略:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author :duc
* @Description :重写redis序列化策略
* @date :Created in 2019/3/4 上午 11:12
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
封装redisUtil类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @author :duc
* @Description :
* @date :Created in 2019/3/4 上午 11:14
*/
@Component
public class RedisUtil {
private static final Logger log = LoggerFactory.getLogger(RedisUtil.class);
@Autowired
private RedisTemplate redisTemplate;
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
log.info("指定缓存失效时间成功,key:{}, time:{}", key, time);
return true;
} catch (Exception e) {
log.info("指定缓存失效时间错误,key:{}, time:{}", key, time);
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
log.info("获取过期时间,key:{}", key);
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
log.info("判断key是否存在,key:{}", key);
return redisTemplate.hasKey(key);
} catch (Exception e) {
log.info("判断key是否存在error,key:{}", key);
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
log.info("保存缓存, key:{}, value:{}", key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
log.info("保存缓存并设置时间, key:{}, value:{}, time:{}", key, value, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map
建立测试类:
import com.springdataredis.demo.util.RedisUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author :duc
* @Description :
* @date :Created in 2019/3/4 上午 11:15
*/
@RestController
public class Test {
@Resource
private RedisUtil redisUtil;
@RequestMapping("/test")
public void test(){
redisUtil.set("test111", 111);
redisUtil.set("test222", 111);
redisUtil.set("test333", 111);
redisUtil.set("test444", 111);
redisUtil.set("test555", 111);
redisUtil.set("test666", 111);
redisUtil.set("test777", 111);
redisUtil.set("test888", 111);
}
@RequestMapping("/test2")
public void test2(){
System.out.println(redisUtil.get("test111"));
System.out.println(redisUtil.get("test333"));
System.out.println(redisUtil.get("test555"));
System.out.println(redisUtil.get("test888"));
}
}
分别在浏览器访问localhost:8080/test,localhost:8080/test2, 结果如下:
可以看到数据已被分别保存至不同的节点,获取的时候也是从对应节点取值
到这里springboot与redis集群整合已经完成,细心的同学可能已经发现,上面的两个配置似乎没有用到:
org.springframework.session
spring-session-data-redis
session:
store-type: redis
timeout: 30m
redis:
namespace: redis:session
按名称可以看出这两个配置都和session有关,那这里继续为大家演示使用redis来保存session:
我们先来新建一个LoginInterceptor
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* @author :duc
* @Description :
* @date :Created in 2019/3/4 上午 11:59
*/
@Controller
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {
private static final Logger log = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("----------------进入拦截器----------------");
//这里的session将会直接从redis中获得
HttpSession session = request.getSession();
System.out.println(session.getId());
String users = session.getAttribute("user").toString();
if (users == null || "".equals(users)) {
String requestType = request.getHeader("X-Requested-With");
if (requestType != null && requestType.equals("XMLHttpRequest")) {
response.setHeader("sessionstatus", "timeout");
response.getWriter().print("LoginTimeout");
return false;
} else {
log.info("尚未登录,跳转到登录界面");
response.sendRedirect("/");
}
return false;
}
log.info("已登陆,不进行拦截");
return true;
}
}
新建SessionConfiguration
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class SessionConfiguration implements WebMvcConfigurer {
@Resource
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration interceptorRegistration = registry.addInterceptor(loginInterceptor);
// 这里添加拦截器例外,除了“/”之外的所有请求都会被拦截
interceptorRegistration.excludePathPatterns("/");
}
}
创建测试类
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @author :duc
* @Description :
* @date :Created in 2019/3/4 上午 11:15
*/
@RestController
public class Test {
@RequestMapping("/")
public void index(HttpServletRequest request){
HttpSession s = request.getSession();
// 这里随便往session中放入一个值,模拟用户登陆
s.setAttribute("user", "user");
System.out.println(s.getId());
}
}
在浏览器中访问localhost:8080, 然后查看数据库,可以看到session已经保存至redis中了
然后继续访问localhost:8080/test,可以从控制台看到两次sessionID与redis中保存的sessionID一致,说明我们用redis管理session成功。
至此,springboot2.x集成redis集群就结束了,感兴趣的同学可以使用多个工程来测试,这里就不再进行演示了。
源码地址
相关源码已上传至github,除了redis集群,源码中也有如mongodb,es等整合,相关博客会在后续发布。