1、基本配置
1.1、引入依赖
首先,建立Maven项目,在Maven项目中引入pom.xml文件:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
Jedis封装了Spring Boot与Redis的连接工具。
1.2、application.properties
# Redis数据库索引(默认为0)
spring.redis.database=0 # Redis服务器地址
spring.redis.host=127.0.0.1 # Redis服务器连接端口
spring.redis.port=6379 # Redis服务器连接密码(默认为空)
spring.redis.password=
2、RedisTemplate API详解
Redis有5种不同的数据类型,这5种数据类型分别为String、List、Set、Hash和Sorted Set。Spring封装了RedisTemplate对象来对5种数据类型操作,它支持所有的Redis原生的API:
- redisTemplate.opsForValue():操作String。
- redisTemplate.opsForHash():操作Hash。
- redisTemplate.opsForList():操作List。
- redisTemplate.opsForSet():操作Set。
- redisTemplate.opsForZSet():操作Sorted Set。
编写RedisService类,通过注解的方式调用RedisTemplate对象来操作Redis的5种数据类型。在RedisService类中使用了@Service注解来标注业务层,RedisService类将自动注入到Spring容器中。本实例使用“RedisService.java”,内容如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service; import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit; @Service
public class RedisService { @Autowired private RedisTemplate redisTemplate; }
2.1、写入和读取缓存
在RedisService类中封装写入Redis缓存的业务逻辑的具体内容如下,使用redisTemplate.opsForValue()操作String:
public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result;
}
其中封装写入Redis缓存设置过期时间的业务逻辑的具体内容如下,需要调用redisTemplate.expire(key,expireTime,TimeUnit.SECONDS):
public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { e.printStackTrace(); } return result;
}
其中封装批量删除对应的key的业务逻辑的具体内容如下:
public void remove(final String... keys) { for (String key : keys) { remove(key); }
}
其中封装删除对应的key的业务逻辑的具体内容如下,需要调用redisTemplate.delete(key):
public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); }
}
其中封装判断Redis缓存中是否有对应的key的业务逻辑的具体内容如下,需要调用redisTemplate.hasKey(key):
public boolean exists(final String key) { return redisTemplate.hasKey(key);
}
其中封装读取Redis缓存的业务逻辑的具体内容如下,使用redisTemplate.opsForValue()获得operations句柄,然后使用operations.get(key)获得Redis中对应的key的业务逻辑:
public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result;
}
2.2、添加和获取散列数据
在RedisService类中封装添加散列数据的业务逻辑的具体内容如下,使用redisTemplate.opsForHash()操作Hash:
public void hmSet(String key, Object hashKey, Object value) { HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); hash.put(key, hashKey, value);
}
封装获取散列数据的业务逻辑的具体内容如下,使用redisTemplate.opsForHash()获得hash句柄,然后使用hash.get(key,hashKey)获得key对应的Hash值:
public Object hmGet(String key, Object hashKey) { HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); return hash.get(key, hashKey);
}
2.3、添加和获取列表数据
在RedisService类中封装添加列表数据的业务逻辑的具体内容如下,使用redisTemplate.opsForList()操作List:
public void lPush(String k, Object v) { ListOperations<String, Object> list = redisTemplate.opsForList(); list.rightPush(k, v);
}
封装获取列表数据的业务逻辑的具体内容如下,使用redisTemplate.opsForList()获得list句柄,然后使用list.range(k,l,l1)获得Redis的列表数据:
public List<Object> lRange(String k, long l, long l1) { ListOperations<String, Object> list = redisTemplate.opsForList(); return list.range(k, l, l1);
}
2.4、添加和获取集合数据
在RedisService类中封装添加集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForSet()操作Set:
public void add(String key, Object value) { SetOperations<String, Object> set = redisTemplate.opsForSet(); set.add(key, value);
}
封装获取集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForSet()获得SetOperations<String,Object>对象,然后使用set.members(key)获得Redis的集合数据:
public Set<Object> setMembers(String key) { SetOperations<String, Object> set = redisTemplate.opsForSet(); return set.members(key);
}
2.5、添加和获取有序集合数据
在RedisService类中封装添加有序集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForZSet()操作Sorted Set:
public void zAdd(String key, Object value, double scoure) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();zset.add(key, value, scoure);
}
封装获取有序集合数据的业务逻辑的具体内容如下,使用redisTemplate.opsForZSet()获得ZSetOperations<String,Object>对象,然后使用zset.rangeByScore(key,scoure,scoure1)获得Redis的有序集合数据:
public Set<Object> rangeByScore(String key, double scoure, double scoure1) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); redisTemplate.opsForValue(); return zset.rangeByScore(key, scoure, scoure1);
}
3、Spring Boot集成Spring Session
传统Session的问题在于Session是由Web容器管理的,即一个Session只保存在一台服务器上,适合于单体应用。随着架构不断地向微服务分布式集群演进,传统的Session在集群环境下就不能正常工作了。例如,现在有3台Web服务器,客户端访问服务器通过负载均衡Nginx负载到某一台服务器上,用户此次的数据就保存到这台服务器的Web容器中了,如果用户下次请求被负载到其他服务器上,就获取不到之前保存的数据了。
这时候就需要整个服务器集群共享同一个Session。为了解决所有服务器共享一个Session的问题,Session就不能单独保存在自己的Web容器中,而是保存在一个公共的会话仓库(Session Repository)中,所有服务器都访问同一个会话仓库,这样所有服务器的状态都一致了。Spring Session支持的会话仓库有Redis、MongoDB、JDBC,本帖使用Redis作为Spring Session的会话仓库。
Spring Session有以下优点:
- Spring Session是基于Servlet规范实现的一套Session管理框架。Spring Session主要解决了分布式场景下Session的共享问题。Spring Session最核心的类是SessionRepositoryFilter,用于包装用户的请求和响应。
- 可在程序中直接替换HttpSession,而无须修改一行代码。
- 可以很方便地与Spring Security集成,增加诸如findSessionsByUserName、rememberMe,限制同一个账号可以同时在线的Session数(例如设置成1,即可达到把前一次登录替换的效果)等。
3.1、配置Spring Boot项目
在Maven项目的pom.xml文件里引入必要的依赖包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId>
</dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件application.properties,内容如下:
# Redis 配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
3.2、创建配置类和控制器类
创建配置类RedisHttpSessionConfiguration,本实例使用“Configuration.java”,内容如下:
import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; @Configuration
//maxInactiveIntervalInSeconds默认是1800s过期,这里测试修改为60s
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=60)
public class RedisHttpSessionConfiguration { }
配置类RedisHttpSessionConfiguration使用@Configuration注解表明这是一个配置类。在这个类中也添加了注解@EnableRedisHttpSession,表示开启Redis的Session管理。如果需要设置会话失效时间,可以使用@EnableRedisHttpSession(maxInactiveIntervalInSeconds=60)表示在60秒后会话失效。
创建用户类User,用户类只有两个属性username和password,保存用户登录的用户名和密码。本实例使用“User.java”,内容如下:
import java.io.Serializable; public class User implements Serializable{ private String name; private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; }
}
创建控制器类SessionController,在这个类中定义了2个方法。login方法用于登录验证,当输入username等于“xinping”,password等于“123”,判断用户登录成功,在Session中保存用户信息。get方法用于从Session中获取用户信息。本实例使用“SessionController.java”,内容如下:
package com.dxtd.SpringSessionDemo.controller; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map; @RestController
public class SessionController {/** * 登录系统 * * */ @RequestMapping(value="/session/login", method = RequestMethod.GET) @ResponseBody public Map login(@RequestParam("username") String username,@RequestParam
("password") String password,HttpServletRequest request, HttpSession session) { String message = "login failure"; Map<String, Object> result = new HashMap<String, Object>(); if(username != null && "xinping".equals(username) && "123".equals
(password)){ User user = new User(); user.setName(username); user.setPassword(password); request.getSession().setAttribute("admin", user); message = "login success"; result.put("message" , message); result.put("sessionId",session.getId()); }else{ result.put("message" , message); } return result; } /** * 查询用户 * * */ @RequestMapping(value="/session/get", method = RequestMethod.GET) @ResponseBody public Map get(@RequestParam("username") String username,HttpServletRequest
request, HttpSession session) { Object value = request.getSession().getAttribute("admin"); Map<String,Object> result = new HashMap<String,Object>(); result.put("message" ,value); result.put("sessionId",session.getId()); return result; } }
3.3、项目验证
启动项目,运行SpringSessionDemoApplication.java,进入SpringSessionDemo项目所在目录:
D:\quant\Redis\Chapter08\SpringSessionDemo>mvn clean package
在SpringSessionDemo\target目录下获得编译好的压缩包SpringSessionDemo-0.0.1-SNAPSHOT.jar,如下图所示:
使用如下命令分别以两个不同的端口(8081和8082)启动项目,用于模拟分布式应用中的两个服务:
java -jar SpringSessionDemo-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar SpringSessionDemo-0.0.1-SNAPSHOT.jar --server.port=8082
启动项目,如下图所示:
第1步:访问http://127.0.0.1:8081/session/login?username=xinping&password=123。第一次访问URL地址端口为8081的Spring Boot项目时模拟用户登录,在浏览器中访问这个请求后的结果如下图所示:
第2步:访问地址http://127.0.0.1:8082/session/get?username=xinping。第二次访问URL地址端口为8082的Spring Boot项目时模拟获取登录的用户信息,在浏览器中访问这个请求后的结果如下图所示:
可以看出成功获取了保存在Redis中的用户名为xinping的用户信息,并且两次请求的Session ID是相同的,实现了Session的共享。
通过Spring Boot使用Redis来实现Session的共享很方便,再配合Nginx进行负载均衡,便能实现分布式的应用了。