精华内容
下载资源
问答
  • Session共享问题

    2021-06-29 19:55:22
    Session共享问题 Session共享及Session保持或者叫做Session⼀致性 Session问题原因分析 出现这个问题的原因,从根本上来说是因为Http协议是⽆状态的协议。客户端和服务端在某次会话中产⽣的数据不会被保留下来,...

    Session共享问题

    Session共享及Session保持或者叫做Session⼀致性

    在这里插入图片描述

    Session问题原因分析

    出现这个问题的原因,从根本上来说是因为Http协议是⽆状态的协议。客户端和服务端在某次会话中产⽣的数据不会被保留下来,所以第⼆次请求服务端⽆法认识到你曾经来过, Http为什么要设计为⽆状态协议?早期都是静态⻚⾯⽆所谓有⽆状态,后来有动态的内容更丰富,就需要有状态,出现了两种⽤于保持Http状态的技术,那就是Cookie和Session。⽽出现上述不停让登录的问题,分析如下图:

    场景:nginx默认轮询策略

    在这里插入图片描述

    解决Session⼀致性的方案

    • Nginx的 IP_Hash 策略(可以使⽤)
      同⼀个客户端IP的请求都会被路由到同⼀个⽬标服务器,也叫做会话粘滞
      优点:配置简单,不⼊侵应⽤,不需要额外修改代码
      缺点:
      • 服务器重启Session丢失
      • 存在单点负载⾼的⻛险
      • 单点故障问题
    • Session复制(不推荐)
      也即,多个tomcat之间通过修改配置⽂件,达到Session之间的复制

    在这里插入图片描述
    优点:
    - 不⼊侵应⽤
    - 便于服务器⽔平扩展
    - 能适应各种负载均衡策略
    - 服务器重启或者宕机不会造成Session丢失
    缺点:
    - 性能低
    - 内存消耗
    - 不能存储太多数据,否则数据越多越影响性能
    - 延迟性

    • Session共享,Session集中存储(推荐)
      Session的本质就是缓存,那Session数据为什么不交给专业的缓存中间件呢?⽐如Redis
      在这里插入图片描述
      优点:
      • 能适应各种负载均衡策略
      • 服务器重启或者宕机不会造成Session丢失
      • 扩展能⼒强
      • 适合⼤集群数量使⽤

    缺点:
    对应⽤有⼊侵,引⼊了和Redis的交互代码

    Spring Session使得基于Redis的Session共享应⽤起来⾮常之简单

    引⼊Jar

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
     <groupId>org.springframework.session</groupId>
     <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    

    配置redis

    spring.redis.database=0
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    

    添加注解
    在这里插入图片描述

    源码示意(了解)

    在这里插入图片描述
    观察其⽗类,⽗类中有Filter
    在这里插入图片描述
    这个Filter就是SpringSession最核⼼的地⽅
    在这里插入图片描述
    在过滤器中将HttpServletRequest包装

    在这里插入图片描述
    本质就是⼀个HtppRequest,拥有同样的⽅法,找getSession
    在这里插入图片描述
    回到SessionRepositoryFilter的doFilterInternal⽅法
    在这里插入图片描述
    在这里插入图片描述
    原理示意

    在这里插入图片描述

    展开全文
  •   在单体应用进行跨页面共享数据,一般常用的方法是使用HttpSession,在没有关闭页面之前,整个session的数据都是实时共享存在的,其原理大致可以参考下图   在分布式中,每个服务都有自己的域名,所以每个服务...

      在单体应用进行跨页面共享数据,一般常用的方法是使用HttpSession,在没有关闭页面之前,整个session的数据都是实时共享存在的,其原理大致可以参考下图
    在这里插入图片描述
      在分布式中,每个服务都有自己的域名,所以每个服务都有自己的cookie作用域,而cookie只在它自己的作用域中存在,也就是说session不能跨作用域共享;即使在同一个服务中,如果这个服务部署在多个服务器上,在一个服务器存的session,但是在其他服务器是没有这个session的,当负载均衡转发到其他服务器当然是找不到session的,也就是说同一服务复制多份存在session不同步问题
      
      当然有问题就得解决,接下来就聊聊如果解决这些问题

    session复制

      例如我们访问同一个服务,当负载均衡到以一个服务器产生了session,使用使用session复制方法将这个服务器的session同步复制到其他的服务器上,这样当负载均衡到其他服务器上依旧可以使用到这个session
    优点
      web-server(tomcat)原生支持,只需要修改配置文件即可
    缺点
      1. session同步需要数据传输,占用大量网络带宽,降低了服务器集群的业务处理能力
      2. 任意一台web-server都保存了所有web-server的session总和,受到内存限制无法水平扩展更多的web-server
      3. 大型分布式集群下,所有web-server都保存全量数据,这种方案不可取,当然如果只有几个服务还是可以考虑此方案的
    在这里插入图片描述

    客户端存储

      所谓客户端存储,就是将服务返回的数据存在浏览器的cookie中,浏览器请求服务时将cookie带上,服务直接读取浏览器带来的cookie就行了
    优点
      服务器不用保存session,用户保存自己的session信息到cookie中,节省服务器资源
    缺点
      1. 每次http请求,都需要携带用户cookie中的完整信息,浪费网络带宽
      2. session数据存在cookie中,而cookie的长度只有4K,不能保存大量信息
      3. session保存在cookie中,存在泄露、篡改、窃取等安全隐患
    在这里插入图片描述

    hash一致性

      利用负载均衡,将用户的请求全部转发到同一个服务器上,例如识别用户的ip地址,用户ID等唯一的身份识别,利用负载均衡机制,将他所有的请求固定的转发到同一个服务器上,在同一个服务器上就不用担心session不存在的问题了
    优点
      1. 只需要修改NGINX的配置,不需要修改应用代码
      2. 负载均衡,只要hash属性的值分布均匀,多态web-server的负载就是均衡的
      3. 可以支持web-server的水平扩展
    缺点
      1. session依旧存在在web-server中,如果服务重启,可能导致session丢失,影响业务
      2. 如果web-server水平扩展,rehash后session重新分布,也会有部分用户路由不到正确的session
    在这里插入图片描述

    统一存储

      上面的方法,由于负载均衡机制会跳转到不同的服务,而session是每个服务储存各自的内存空间的,所以导致跳转到不同的服务时,上一个服务的session不存在当前服务中,所以可以让session统一存储,无论是哪个服务,都不用将session存在它自己的内存中,而是将session存在数据库中如Redis或者中间件等
    优点
      1. 没有安全隐患
      2. 可以水平扩展,数据库/缓存水平切分即可
      3. web-server重启或扩容都不会有session丢失
    缺点
      1. 增加一次网络调用,并且需要修改应用代码
    在这里插入图片描述

    展开全文
  • 当浏览器登录会员服务后,用户信息存在Session中,Session存在Redis中,会员服务返回SessionId给浏览器,再扩大SessionId的作用域为父域(会员服务,订单服务,商品服务均为子域);当浏览器再访问其他子域时,根据...
    1. 思路:
      当浏览器登录会员服务后,用户信息存在Session中,Session存在Redis中,会员服务返回SessionId给浏览器,再扩大SessionId的作用域为父域(会员服务,订单服务,商品服务均为子域);当浏览器再访问其他子域时,根据SessionId到Redis中查
      Spring Session框架利用装饰者模式,实现了扩大SessionId的作用域为父域,开发中整合Spring Session即可
      在这里插入图片描述
    2. SpringBoot整合Spring Session

    1.依赖

    <dependency>
    		<groupId>org.springframework.session</groupId>
    		<artifactId>spring-session-data-redis</artifactId>
    </dependency>
    

    2.配置(如下省略了整合redis原本需要的配置)

    spring.session.store-type=redis   #session保存到redis
    

    3.开启spring session

    @EnableRedisHttpSession
    

    4.配置类 设置SessionId的作用域

    @Bean
    	public CookieSerializer cookieSerializer() {
    		DefaultCookieSerializer serializer = new DefaultCookieSerializer();
    		serializer.setCookieName("JSESSIONID"); 
    		serializer.setDomainName("父域"); 
    		return serializer;
    	}
    

    5.序列化
    为了保证对象在内存与redis间传递,配置序列化器

    @Bean
    	public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
    		return new GenericJackson2JsonRedisSerializer();
    	}
    
    展开全文
  • redis解决websocket在分布式场景下session共享问题 在显示项目中遇到了一个问题,需要使用到websocket与小程序建立长链接。由于项目是负载均衡的,存在项目部署在多台机器上。这样就会存在一个问题,当一次请求负载...

    redis解决websocket在分布式场景下session共享问题

    在显示项目中遇到了一个问题,需要使用到websocket与小程序建立长链接。由于项目是负载均衡的,存在项目部署在多台机器上。这样就会存在一个问题,当一次请求负载到第一台服务器时,socketsession在第一台服务器线程上,第二次请求,负载到第二台服务器上,需要通过id查找当前用户的session时,是查找不到的。
    在这里插入图片描述

    • 可以看到,由于websocket的session并没有实现序列化接口。所以无法将session序列化到redis中。

    • web的中的httpsession 主要是通过下面的两个管理器实现序列化的。

      org.apache.catalina.session.StandardManager
    
      org.apache.catalina.session.PersistentManager
    

    StandardManager是Tomcat默认使用的,在web应用程序关闭时,对内存中的所有HttpSession对象进行持久化,把他们保存到文件系统中。默认的存储文件为

    <tomcat安装目录>/work/Catalina/<主机名>/<应用程序名>/sessions.ser

    PersistentManager比StandardManager更为灵活,只要某个设备提供了实现org.apache.catalina.Store接口的驱动类,PersistentManager就可以将HttpSession对象保存到该设备。
    在这里插入图片描述
    所以spring-session-redis 解决分布场景下的session共享就是将session序列化到redis中间件中,使用filter 加装饰器模式解决分布式场景httpsession 共享问题。

    解决方案

    1. 使用消息中间件解决websocket session共享问题。
    2. 使用redis的发布订阅模式解决

    本文使用方式二

    • 使用StringRedisTemplate的convertAndSend方法向指定频道发送指定消息:
    		this.execute((connection) -> {
                connection.publish(rawChannel, rawMessage);
                return null;
            }, true);
    

    redis的命令publish channel message

    • 添加一个监听的容器以及一个监听器适配器
    	@Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter)
        {
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 可以添加多个 messageListener,配置不同的交换机
            container.addMessageListener(listenerAdapter, new PatternTopic(Constants.REDIS_CHANNEL));// 订阅最新消息频道
            return container;
        }
    
        @Bean
        MessageListenerAdapter listenerAdapter(RedisReceiver receiver)
        {
            // 消息监听适配器
            return new MessageListenerAdapter(receiver, "onMessage");
        }
    
    • 添加消息接收器
    /**
     * 消息监听对象,接收订阅消息
     */
    @Component
    public class RedisReceiver implements MessageListener {
        Logger log = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        private WebSocketServer webSocketServer;
    
    
        /**
         * 处理接收到的订阅消息
         */
        @Override
        public void onMessage(Message message, byte[] pattern)
        {
            String channel = new String(message.getChannel());// 订阅的频道名称
            String msg = "";
            try
            {
                msg = new String(message.getBody(), Constants.UTF8);//注意与发布消息编码一致,否则会乱码
                if (!StringUtils.isEmpty(msg)){
                    if (Constants.REDIS_CHANNEL.endsWith(channel))// 最新消息
                    {
                        JSONObject jsonObject = JSON.parseObject(msg);
                        webSocketServer.sendMessageByWayBillId(
                                Long.parseLong(jsonObject.get(Constants.REDIS_MESSAGE_KEY).toString())
                                ,jsonObject.get(Constants.REDIS_MESSAGE_VALUE).toString());
                    }else{
                        //TODO 其他订阅的消息处理
                    }
    
                }else{
                    log.info("消息内容为空,不处理。");
                }
            }
            catch (Exception e)
            {
                log.error("处理消息异常:"+e.toString());
                e.printStackTrace();
            }
        }
    }
    
    • websocket的配置类
    /**
     * @description: websocket的配置类
     * @dateTime: 2021/6/16 15:43
     */
    @Configuration
    @EnableWebSocket
    public class WebSocketConfiguration {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    
    
    • 添加websocket的服务组件
    @ServerEndpoint("/websocket/{id}")
    @Component
    public class WebSocketServer {
    
        private static final long sessionTimeout = 600000;
    
        private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    
        /**
         * 当前在线连接数
         */
        private static AtomicInteger onlineCount = new AtomicInteger(0);
    
        /**
         * 用来存放每个客户端对应的 WebSocketServer 对象
         */
        private static ConcurrentHashMap<Long, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    
        /**
         * 与某个客户端的连接会话,需要通过它来给客户端发送数据
         */
        private Session session;
    
        /**
         * 接收 id
         */
        private Long id;
    
    
        @Autowired
        private StringRedisTemplate template;
        /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session, @PathParam("id") Long id) {
            session.setMaxIdleTimeout(sessionTimeout);
            this.session = session;
            this.id = id;
            if (webSocketMap.containsKey(id)) {
                webSocketMap.remove(id);
                webSocketMap.put(id, this);
            } else {
                webSocketMap.put(id, this);
                addOnlineCount();
            }
            log.info("编号id:" + id + "连接,当前在线数为:" + getOnlineCount());
            try {
                sendMessage("连接成功!");
            } catch (IOException e) {
                log.error("编号id:" + id + ",网络异常!!!!!!");
            }
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            if (webSocketMap.containsKey(id)) {
                webSocketMap.remove(id);
                subOnlineCount();
            }
            log.info("编号id:" + id + "退出,当前在线数为:" + getOnlineCount());
        }
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息
         */
        @OnMessage
        public void onMessage(String message, Session session) {
    
            log.info("编号id消息:" + id + ",报文:" + message);
        }
    
        /**
         * 发生错误时调用
         *
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("编号id错误:" + this.id + ",原因:" + error.getMessage());
            error.printStackTrace();
        }
        
    
        /**
         * @description:  分布式  使用redis 去发布消息
         * @dateTime: 2021/6/17 10:31
         */
        public void sendMessage(@NotNull String key,String message) {
            String newMessge= null;
            try {
                newMessge = new String(message.getBytes(Constants.UTF8), Constants.UTF8);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            Map<String,String> map = new HashMap<String, String>();
            map.put(Constants.REDIS_MESSAGE_KEY, key);
            map.put(Constants.REDIS_MESSAGE_VALUE, newMessge);
            template.convertAndSend(Constants.REDIS_CHANNEL, JSON.toJSONString(map));
        }
    
        /**
         * @description: 单机使用  外部接口通过指定的客户id向该客户推送消息。
         * @dateTime: 2021/6/16 17:49
         */
        public void sendMessageByWayBillId(@NotNull Long key, String message) {
            WebSocketServer webSocketServer = webSocketMap.get(key);
            if (!StringUtils.isEmpty(webSocketServer)) {
                try {
                    webSocketServer.sendMessage(message);
                    log.info("编号id为:"+key+"发送消息:"+message);
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("编号id为:"+key+"发送消息失败");
                }
            }
            log.error("编号id号为:"+key+"未连接");
        }
        /**
         * 实现服务器主动推送
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
    
        public static synchronized AtomicInteger getOnlineCount() {
            return onlineCount;
        }
    
        public static synchronized void addOnlineCount() {
            WebSocketServer.onlineCount.getAndIncrement();
        }
    
        public static synchronized void subOnlineCount() {
            WebSocketServer.onlineCount.getAndDecrement();
        }
    }
    
    
    • 项目结构

    在这里插入图片描述

    • 将该项目使用三个端口号启动三个服务
      在这里插入图片描述

    • 使用下面的这个网站进行演示。
      http://www.easyswoole.com/wstool.html
      在这里插入图片描述
      启动两个页面网址分别是:

    • ws://127.0.0.1:8081/websocket/456

    • ws://127.0.0.1:8082/websocket/456

    使用postman给http://localhost:8080/socket/456 发送请求

    在这里插入图片描述
    可以看到,我们给8080服务发送的消息,我们订阅的8081和8082 服务可以也可以使用该编号进行消息的推送。

    • 使用8082服务发送这个消息格式{"KEY":456,"VALUE":"aaaa"} 的消息。其他的服务也会收到这个信息。

    在这里插入图片描述

    • 以上就是使用redis的发布订阅解决websocket 的分布式session 问题。

    码云地址是: https://gitee.com/jack_whh/dcs-websocket-session

    展开全文
  • phpsession共享问题

    2021-05-08 15:28:13
    centos 7 下安装memcache 和 memcached扩展经测试通过PHP可以写入memcached但是在php.ini 中配置save_handler = memcachedsave_path = 127.0.0.1:11211发现session并没有写入到memcached中而且在/var/lib/...
  • 而要解决这些问题,Spring给我们提供了一个很强大的工具—SpringSession,通过将session存在数据库中,让每个服务在调用时去数据库获取来解决session共享问题,Spring在官方文档提供了几个数据库的整合例如redis,...
  • session在分布式环境下存在的问题 ...浏览器下次访问,cookie会自动携带上次...该机制在单体应用中是没有问题的,但是如果在分布式环境下,会产生session共享问题,即session的数据在服务1中存在,但是在服务2中不存在。
  • 在微服务中,需要我们在各个微服务中共享Session,使用Redis来共享Session是一个很好的解决方法,Redis是运行在内存中,查取速度很快。1.pom文件中添加依赖org.springframework.bootspring-boot-starter-redisorg....
  • 单机情况下,不存在Session共享的情况,分布式情况下,如果不进行Session共享会出现请求落到不同机器要重复登录的情况,一般来说解决Session共享有以下几种方案。 1、session复制 session复制是早期的企业级的使用...
  • 在进行集群部署,分布式部署,并发高的时候,遇见Session共享问题。 解决方案: 使用Nginx反向代理,用户通过反向代理服务器Nginx访问应用程序A时,打开了一个session,输入了用户名和密码,我们引用Redis缓存机制...
  • php session共享问题

    2021-04-26 10:54:54
    centos 7 下安装memcache 和 memcached扩展经测试通过PHP可以写入memcached但是在php.ini 中配置save_handler = memcachedsave_path = 127.0.0.1:11211发现session并没有写入到memcached中而且在/var/lib/...
  • 一、分布式环境下的Session问题 在单体应用时代,我们不需要考虑Session不同步的问题,但在分布式系统中,因为Http协议是⽆状态的协议,客户端和服务端在某次会话中产⽣的数据不会被保留下来,所以第⼆次请求...
  • docker linux centos tomcat session 共享问题37.6 安装一个 session 共享的 tomcat37.6.0 准备工作需要一个 curiousby/centos-ssh-root-java-tomcat需要 commons-pool2-2.2.jar jedis-2.5.2.jar tomcat-redis-...
  • 基于redis解决session共享问题

    千次阅读 2021-08-09 20:51:02
    网关模块 3.1application.properties server.port=8083 spring.session.store-type=redis spring.redis.host=localhost spring.redis.port=6379 #spring.redis.password= #为了解决session共享问题,客户端请求不...
  • Session共享问题-session原理

    千次阅读 2021-12-04 18:09:35
    session共享 问题2:分布式系统下。同一个域名下,多个服务。session会出现不同步问题(例如第一次访问的是A服务,保存了。第二次访问了B,查询到的还是没信息) 解决:1)、session复制(同步) web-server...
  • 一 简介如题所示,在分布式系统架构中需要解决的一个很重要的问题就是——如何保证各个应用节点之间的Session共享。其实有一个很好的解决办法就是在redis、memcached等组件中独立存储所有应用节点的Session,以达到...
  • session共享问题 1.不能跨不同域名进行共享。(不同服务, 假设是会员服务和订单服务部署在了不同域名。不同域名下jsessionid不能共享) 2.同域名下也会无法共享。(会员服务1,会员服务2。。。。多台服务器同时会有...
  • Redis 处理Spring Boot项目Session 共享问题背景一、问题再现二、原因分析 背景 Web 开发中,通过 Session 在服务端记录用户状态是很常见的操作。但是 Session 的机制对于单机应用是没问题的,但是对于集群环境,...
  • 目录http请求是无状态的问题session 原理分布式环境下,session丢失问题(无法共享问题session会话共享解决方案1 nginx方案2. tomcat方案3 Spring Session + Redis (推荐) http请求是无状态的问题 http请求是无...
  • 1、session复制(效率低,不使用) 2、客户端存储(不安全,不使用) 3、hash一致性(网络变,ip变,不使用) 4、统一存储(好用易配置,使用)
  • Session共享问题 首先在进入主题之前,我们先复习以下 Cookie 与 Session 何为 Cookie 1、Cookie是服务器通知客户端保存键值对的一种技术。同时客户端保存了Cookie之后,每次请求都会发送给服务器。每个Cookie的...
  • 集群服务器之间的Session共享问题 引入一张图 这是一张传统型的多服务器部署的场景(假设已经实现负载均衡,ribbon或者网关或者nginx等负载均衡技术) 客户端第一次登录从服务器1拿到session,此时服务器1当然也会...
  • <% request.setCharacterEncoding("utf-8"); String name = request.getParameter("uname"); String pwd = request.... System.out.println("sessionID"+session.getId()); if(name.equals("lg")&&...
  • Websocket Session ...所以我的解决方案采用订阅式队列去触发Websocket 反推的方式来实现看上去是Session共享 的,思路如下图: Websocket Session共享 思路图解释: 模块一:这里没有什么好说明的,因为每一...
  • 前言如果你正在使用Java开发Web应用,想必你对HttpSession非常熟悉,但我们知道HpptSession默认使用内存来管理Session,如果将应用横向扩展将会出现Session共享问题。Spring Session提供了一套创建和管理Servlet ...
  • 文章目录前言一、nginx的ip_hash策略二、Tomcat Session复制(不推荐)三、Session共享(推荐)1.简介2.Spring Session 前言 由于HTTP协议是无状态的,客户端和服务器的会话产生的数据不会被保留,所以第二次请求时...
  • 解决nginx负载均衡的session共享问题

    千次阅读 2021-12-11 22:48:48
    工作 一般 要不 借助redis 要不就是 nginx 使用ip hash 配置做调整 upstream nginx.gaoxin.com { server 192.168.74.235:80; server 192.168.74.236:80; ip_hash; } server { listen 80; location / ...}
  • 文章目录前言一、概述1.用户 IP 识别2.cookie 识别二、配置 Web 监控平台1.配置部分及解释2.测试3.页面详细参数解释 前言 本文基于前篇博客配置下完成...所以需要在实施负载均衡时考虑 session 共享问题 Haproxy 使用
  • ip_hash方式是指:每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session共享问题 nginx.conf添加如下配置 upstream myfz { ip_hash; server 192.168.153.140:80; server 192....
  • 先了解一下为什么会出现这种session共享的解决方案? 随着互联网公司的项目在微服务和分布式的环境下进行的搭建,导致一个项目可能分别部署在几个甚至很多的服务器集群下,此时就会出现一个问题: 当用户进行一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 257,267
精华内容 102,906
关键字:

session共享问题