Spring-Session+Redis实现session共享实现统计在线⼈数和踢除⽤户下线。
⾸先添加pom相关依赖
<!--spring session 依赖 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>${spring-session.version}</version>
</dependency>
<!--spring redis依赖-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${dis.version}</version>
</dependency>
<!-- redis共享session -->
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
创建l并于l中引⼊。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance" xmlns:p="/schema/p"
xmlns:c="/schema/c" xmlns:util="/schema/util"springframework事务
xsi:schemaLocation="/schema/beans /schema/beans/spring-beans.xsd /schema/util /schema/util/spring-util.xsd" <!-- redis config start -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxTotal" value="${redis.maxActive}"/>
<property name="maxWaitMillis" value="${redis.maxWait}"/>
<property name="testOnBorrow" value="${stOnBorrow}"/>
</bean>
<bean id="jedisConnFactory"
class="org.tion.jedis.JedisConnectionFactory"
p:use-pool="true"
p:hostName="${redis.host}"
p:port="${redis.port}"
p:password="${redis.pass}"
p:database="${redis.dbIndex}"
p:poolConfig-ref="poolConfig"/>
<!-- stringRedisTemplate-->
<bean id="stringRedisTemplate" class="org.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory"/>
</bean>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.RedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory"/>
<!--如果不配置Serializer,那么存储的时候缺省使⽤String,如果⽤User类型存储,那么会提⽰错误User can't cast to String!!  -->
<property name="keySerializer">
<bean class="org.dis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.dis.serializer.StringRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.dis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.dis.serializer.JdkSerializationRedisSerializer"/>
</property>
<!--开启事务  -->
<property name="enableTransactionSupport" value="true"></property>
</bean>
<bean id="JdkSerializationRedisSerializer"
class="org.dis.serializer.JdkSerializationRedisSerializer"/>
<bean id="GenericToStringSerializer" class="org.dis.serializer.GenericToStringSerializer"
c:type="java.lang.String"/>
<bean id="redisCacheManager" class="org.dis.cache.RedisCacheManager">
<constructor-arg name="redisOperations" ref="redisTemplate"/>
<property name="defaultExpiration" value="${piration}"/>
</bean>
<bean id="redisCacheConfig" class="com.onfig.RedisCacheConfig">
<constructor-arg ref="jedisConnFactory"/>
<constructor-arg ref="redisTemplate"/>
<constructor-arg ref="redisCacheManager"/>
</bean>
<bean id="SessionListener" class="com.listener.SessionListener"/>
<!-- redis共享程序session -->
<util:constant static-field="org.springframework.fig.ConfigureRedisAction.NO_OP"/>
<bean id="redisHttpSessionConfiguration"
class="org.springframework.fig.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="1800"/>
<property name="redisNamespace" value="ianbase"/>
<property name="configureRedisAction" value="NO_OP"/>
<property name="httpSessionListeners">
<list>
<ref bean="SessionListener"/>
</list>
</property>
</bean><!-- redis config end -->
<!--⾃定义redis⼯具类,在需要缓存的地⽅注⼊此类  -->
<bean id="redisUtil" class="com.utils.RedisUtil">
<property name="redisTemplate" ref="redisTemplate"/>
</bean>
</beans>
在l中引⼊redis配置⽂件redis.properties 并引⼊l (l在此就不赘⾔了,⽼⽣常谈。)
<!-- 引⼊properties属性⽂件 -->
<context:property-placeholder location="classpath:jdbc/jdbc.properties
, classpath:redis/redis.properties"/>
<!-- 引⼊spring redis整合⽂件 -->
<import resource="classpath:l"/>
redis.properties 配置
# Redis settings
redis.host=127.0.0.1
redis.pass=foobared
redis.dbIndex=0
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
如果按照如上配置,则需要配置redis的密码为 foobared (可⾃⾏定义)
因为笔者使⽤的是windows版本,可以通过两种⽅式来配置redis的密码
1.修改redis⽬录下 f 中的 requirepass 配置项
配置完成后通过auth 命令登录
配置springSession监听
sessionListener的实现。
package com.listener;
import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.http.*;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aloha.stant.AuthorityConstant;
import com.aloha.ity.SysUser;
import com.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import t.ApplicationEventPublisher;
import org.tion.DataType;
import org.RedisTemplate;
import org.SetOperations;
import org.StringRedisTemplate;
import org.ValueOperations;
import org.dis.serializer.StringRedisSerializer;
import java.util.*;
/**
* com.listener
*
* @author zgl
* @name SessionListener
* @description
* @date 2018-04-11 14:10
* <p>
* <p>
* Copyright (c) 2018⼭东安合信达电⼦科技有限公司版权所有
* shandong aloha CO.,LTD. All Rights Reserved.
*/
public class SessionListener implements HttpSessionListener {
/**
* 当前⽤户
*/
public static final String CURRENT_USER = "CURRENT_USER";
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* session创建
*
* @param event
*/
@Override
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = Session();
// System.out.Attribute(AuthorityConstant.VERIFICATION)); null 获取不到
//去redis中根据sessionId获取
Set set = redisTemplate.keys("*" + Id() + "*");
ArrayList list = new ArrayList(set);
for (int i = 0; i < list.size(); i++) {
String key = (i).toString();
//判断是否hash类型并且判断是否是有效的⽤户session
if (pe(key) == DataType.HASH) {
Map<String, Object> map = redisTemplate.opsForHash().entries(key);
System.out.println(redisTemplate.opsForHash().entries(key));
List<String> mapKeys = new ArrayList<String>(map.keySet());
for (int j = 0; j < mapKeys.size(); j++) {
System.out.(j) + ":    " + ((j)));
if ((j).indexOf(AuthorityConstant.VERIFICATION) != -1) {
System.out.println("验证码产⽣的session不加⽤户数");
break;
} else if ((j).indexOf(AuthorityConstant.SESSION_USER_CODE) != -1  && (j).indexOf("userId") ==-1 ) {                        System.out.(j)+"--------------------------------");
System.out.println("⽤户登录后产⽣的session增加⽤户数");
//存储⽤户hash
Id().toString(),(SysUser) ((j)));
break;
}
}
}
//        logger.info("创建了⼀个Session连接:[" + Id() + "]");
System.out.println("创建了⼀个Session连接:[" + Id() + "]");
}
/**
* session销毁
*
* @param event
*/
@Override
public void sessionDestroyed(HttpSessionEvent event) {
boolean flag = true;
HttpSession session = Session();
Set<String> keys = redisTemplate.keys("*" + Id() + "*");
ArrayList removelist = new ArrayList(keys);
for (int i = 0; i < removelist.size(); i++) {
String key = (i).toString();
/
/判断是否hash类型并且判断是否是有效的⽤户session
if (pe(key) == DataType.HASH) {
Map<String, Object> map = redisTemplate.opsForHash().entries(key);
//System.out.println(redisTemplate.opsForHash().entries(key));
List<String> mapKeys = new ArrayList<String>(map.keySet());
for (int j = 0; j < mapKeys.size(); j++) {
//System.out.(j) + ":    " + ((j)));
if ((j).indexOf(AuthorityConstant.VERIFICATION) != -1) {
System.out.println("验证码产⽣的session不减⽤户数");
flag = false;
break;
}
}
}
}
redisTemplate.delete(keys);
Set set = redisTemplate.keys("spring:session:ianbase:expirations*");
ArrayList list = new ArrayList(set);
for (int i = 0; i < list.size(); i++) {
String key = String.(i));
//删除set
if (pe(key) == DataType.SET) {
SetOperations<String, String> vo = redisTemplate.opsForSet();
Iterator<String> it = vo.members(key).iterator();
while (it.hasNext()) {
if (it.next().Id().toString()) != -1) {
System.out.println(key+"................");
redisTemplate.delete(key);
//System.out.Id().toString());
System.out.println("删除成功了");
}
}
}
}
System.out.println("销毁了⼀个Session连接:[" + Id() + "]");
if (getAllUserNumber() > 0 && flag) {
Id().toString());
}
}
/**
* 保存⽤户数
*
* @param n
*/
private void setAllUserNumber(int n) {
Long number = getAllUserNumber() + n;
if (number >= 0) {
//            logger.info("⽤户数:" + number);
System.out.println("⽤户数:" + number);
stringRedisTemplate.opsForValue().set(AuthorityConstant.ALLUSER_NUMBER, String.valueOf(number));
}
}
/**
* 获取登录⽤户数⽬
*
* @return
*/
public Long getAllUserNumber() {
Object v = stringRedisTemplate.opsForValue().get(AuthorityConstant.ALLUSER_NUMBER);
if (v != null) {
return Long.String());
}
return 0L;
}
public void saveUserOnlineHash(String sessionId,SysUser sysUser){
Map<String,SysUser> onlineUsers = null;
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
if(redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS)!=null){
onlineUsers = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);
if(!onlineUsers.keySet().contains(sessionId)){
setAllUserNumber(+1);
}
onlineUsers.put(sessionId,sysUser);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS,onlineUsers);
}else{
onlineUsers = new HashMap<>();
if(!onlineUsers.keySet().contains(sessionId)){
setAllUserNumber(+1);
}
onlineUsers.put(sessionId,sysUser);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS,onlineUsers);
}
}
public void removeOnlineHashBySessionId(String sesssionId){
if(redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS)!=null){
Map<String,SysUser> onlineUsers = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);            if(onlineUsers.keySet().contains(sesssionId)){
setAllUserNumber(-1);
}
redisTemplate.delete(AuthorityConstant.ONLINE_USERS);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS,onlineUsers);
}
}
}
/**
* session销毁
*
* @param event
*/
@Override
public void sessionDestroyed(HttpSessionEvent event) {
boolean flag = true;
HttpSession session = Session();
Set<String> keys = redisTemplate.keys("*" + Id() + "*");
ArrayList removelist = new ArrayList(keys);
for (int i = 0; i < removelist.size(); i++) {
String key = (i).toString();
//判断是否hash类型并且判断是否是有效的⽤户session
if (pe(key) == DataType.HASH) {
Map<String, Object> map = redisTemplate.opsForHash().entries(key);
//System.out.println(redisTemplate.opsForHash().entries(key));
List<String> mapKeys = new ArrayList<String>(map.keySet());
for (int j = 0; j < mapKeys.size(); j++) {
//System.out.(j) + ":    " + ((j)));
if ((j).indexOf(AuthorityConstant.VERIFICATION) != -1) {
System.out.println("验证码产⽣的session不减⽤户数");
flag = false;
break;
}
}
}
}
redisTemplate.delete(keys);
Set set = redisTemplate.keys("spring:session:ianbase:expirations*");
ArrayList list = new ArrayList(set);
for (int i = 0; i < list.size(); i++) {
String key = String.(i));
//删除set
if (pe(key) == DataType.SET) {
SetOperations<String, String> vo = redisTemplate.opsForSet();
Iterator<String> it = vo.members(key).iterator();
while (it.hasNext()) {
if (it.next().Id().toString()) != -1) {
System.out.println(key+"................");
redisTemplate.delete(key);
//System.out.Id().toString());
System.out.println("删除成功了");
}
}
}
}
System.out.println("销毁了⼀个Session连接:[" + Id() + "]");
if (getAllUserNumber() > 0 && flag) {
Id().toString());
setAllUserNumber(-1);
}
}
/**
* 保存⽤户数
*
* @param n
*/
private void setAllUserNumber(int n) {
Long number = getAllUserNumber() + n;
if (number >= 0) {
//            logger.info("⽤户数:" + number);
System.out.println("⽤户数:" + number);
stringRedisTemplate.opsForValue().set(AuthorityConstant.ALLUSER_NUMBER, String.valueOf(number));
}
}
/**
* 获取登录⽤户数⽬
*
* @return
*/
public Long getAllUserNumber() {
Object v = stringRedisTemplate.opsForValue().get(AuthorityConstant.ALLUSER_NUMBER);
if (v != null) {
return Long.String());
}
return 0L;
}
public void saveUserOnlineHash(String sessionId,SysUser sysUser){
Map<String,SysUser> onlineUsers = null;
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
if(redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS)!=null){
onlineUsers = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);
onlineUsers.put(sessionId,sysUser);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS,onlineUsers);
}else{
onlineUsers = new HashMap<>();
onlineUsers.put(sessionId,sysUser);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS,onlineUsers);
}
}
public void removeOnlineHashBySessionId(String sesssionId){
if(redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS)!=null){
Map<String,SysUser> onlineUsers = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);            ve(sesssionId);
redisTemplate.delete(AuthorityConstant.ONLINE_USERS);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS,onlineUsers);
}
}
}
具体的逻辑就不多说了相信⼤家都能看懂,⽐较基础的代码。
重点说⼀下这⾥的坑!
程序跑起来以后发现 session的销毁⽅法没有监听到!!!!
解决⽅法:
查阅许多博客后发现 redis的 notify-keyspace-events  配置没有打开貌似是空间事件监听之类的具体的没有深究。然后配置 notify-keyspace-events 为KEA
详细的请参考
blog.csdn/gqtcgq/article/details/50808729
就此完成了登录⽤户信息的保存,和在线⼈数的统计。
查看在线⼈数和踢除⽤户下线的接⼝:
@GetMapping("onlineUser")
public Result onlineUserCount(HttpSession session) {
List<JSONObject> users = new ArrayList<>();
String count = stringRedisTemplate.opsForValue().get(AuthorityConstant.ALLUSER_NUMBER);
Map<String, Object> map = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);
List<String> mapKeys = new ArrayList<String>(map.keySet());
for (int j = 0; j < mapKeys.size(); j++) {
// System.out.(j) + ":    " + Id().toString());
JSONObject jsonObject = JSONUtil.((j)));
if ((j).Id().toString())) {
jsonObject.put("iscurr", true);
}
jsonObject.put("sessionId", (j));
users.add(jsonObject);
}
return Result.ok().put("data", users).put("count", StringUtils.isEmpty(count) ? "0" : count);
}
@PostMapping("downline")
public Result downline(@RequestBody String sid) {
sid= placeAll("=","");
Map<String, SysUser> map = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);
Set<String> keys = redisTemplate.keys("*" + sid + "*");
redisTemplate.delete(keys);
Set set = redisTemplate.keys("spring:session:ianbase:expirations*");
ArrayList list = new ArrayList(set);
for (int i = 0; i < list.size(); i++) {
String key = String.(i));
//删除set
if (pe(key) == DataType.SET) {
SetOperations<String, String> vo = redisTemplate.opsForSet();
Iterator<String> it = vo.members(key).iterator();
while (it.hasNext()) {
if (it.next().String()) != -1) {
redisTemplate.delete(key);
removeOnlineHashBySessionId(sid);
//减⽤户数
long count = Long.parseLong(stringRedisTemplate.opsForValue().get(AuthorityConstant.ALLUSER_NUMBER)) - 1;                        stringRedisTemplate.opsForValue().set(AuthorityConstant.ALLUSER_NUMBER, Strin
g.valueOf(count));
System.out.println("强制下线成功");
}
}
}
}
return Result.ok("已强制该⽤户下线。");
}
public void removeOnlineHashBySessionId(String sesssionId) {
if (redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS) != null) {
Map<String, SysUser> onlineUsers = redisTemplate.opsForHash().entries(AuthorityConstant.ONLINE_USERS);
redisTemplate.delete(AuthorityConstant.ONLINE_USERS);
redisTemplate.opsForHash().putAll(AuthorityConstant.ONLINE_USERS, onlineUsers);
}
}
sid 为前台传过来的对应⽤户的sessionid
然后这⾥扩展⼀点。负载均衡宕机情况下在线⽤户⼈数没有对应改变该如何处理呢?
这⾥简单说⼀下,笔者通过两种⽅式去实现了⼀下。
1.监听spring的⽣命周期实现redis数据
2.监听tomcat的开启关闭时间。
⼆者选其⼀即可但缺点是只有调⽤tomcat 的shutdown脚本时才会触发销毁。
因为此时笔者所做项⽬对这块没有什么要求因此选⽤了第⼀种实现⽅式。
见代码如下:
@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// 防⽌⼦容器启动完成后再次调⽤
ApplicationContext().getParent() == null){
//判断当前会否有tomcat在运⾏如果有则说明tomcat数的key已经创建否则重新创建
if(stringRedisTemplate.opsForValue().get(AuthorityConstant.TOMCAT_NUM)!=null){
long count =Long.parseLong(stringRedisTemplate.opsForValue().get(AuthorityConstant.TOMCAT_NUM));
stringRedisTemplate.opsForValue().set(AuthorityConstant.TOMCAT_NUM,String.valueOf(count+1));
}else{
//初始化
stringRedisTemplate.opsForValue().set(AuthorityConstant.TOMCAT_NUM,"1");
}
System.out.println("spring容器初始化");
}
}
@Component
public class StopAddDataListener implements ApplicationListener<ContextClosedEvent> {
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
ApplicationContext().getParent() == null) {
if(stringRedisTemplate.opsForValue().get(AuthorityConstant.TOMCAT_NUM)!=null){
long count =Long.parseLong(stringRedisTemplate.opsForValue().get(AuthorityConstant.TOMCAT_NUM));
if(count-1==0){
//清理redis
flushDB();
}else{
stringRedisTemplate.opsForValue().set(AuthorityConstant.TOMCAT_NUM,String.valueOf(count-1));
}
}
System.out.println("spring容器关闭");
}
}
}
public void flushDB() {
}
}
好吧,就写到这⾥吧,希望对有需要的⼈提供⼀定的帮助。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。