集群环境(session多服务器共享的方案梳理)
目前业界解决session共享的几种思路,我总结如下:
第一种办法:把原来存储在服务器磁盘上的session数据存储到客户端的cookie中去。
这样子,就不需要涉及到数据共享了。a客户端请求的时候,原来生成在服务器的数据生成到浏览器的cookie中,根据cookie中的数据识别用户。php由原来的”从本地(也就是服务器)磁盘上读取session数据”转变为”浏览器的cookie中读取数据”,
这样子,在多台php服务器负载均衡的情况下,即便第一秒请求是a服务器,第二秒请求是b服务器,都不需要管哪台服务器了。反正都是读取客户端上的cookie数据。
一般是把session数据按照自己定义的加密规则,加密后后存在cookie中。
数据保存在cookie中这种做法有好处,也有坏处。
好处是服务器的压力减小了,因为session数据不存在服务器磁盘上。根本就不会出现session读取不到的问题。
带来的弊端是:
网络请求占用很多。每次请求时,客户端都要通过cookie发送session数据给服务器。
另外,浏览器对cookie的大小存在限制。每个浏览器限制是不同的。
Firefox和Safari允许cookie多达4097个字节,包括名(name)、值(value)和等号。
Opera允许cookie多达4096个字节,包括:名(name)、值(value)和等号。
Internet Explorer允许cookie多达4095个字节,包括:名(name)、值(value)和等号。
所以第一种方案不适合高访问量的情况下,因为高访问量的情况下,每次请求浏览器都要发送session数据给服务器。一般一个cookie大小2k的样子。
要占用很多带宽了(服务器购买带宽是一个很大费用),成本增高。归纳为带宽性能,速度问题。
存储到cookie中去,第二方面是安全问题:把session数据放到客户端,一般session中存的都是重要性数据(帐号、昵称、用户id等),会存在安全问题。
了解到,淘宝以前用过这种方式,把session数据存储到cookie中,根据cookie来识别用户。
归纳:(1)好处是服务器压力减少;
(2)坏处就是带宽成本提高,cookie在搞访问量的时候,会占用很多带宽;
(3)涉及安全问题,cookie一般是存放于浏览器中,要是session中的信息含有重要数据比如账号密码之类的,就会存在被破解盗号的问题。
第二种思路:用一种算法(简单理解为规则),什么机制下session是保存在哪台服务器下,那么读取的时候就按照这种规则去读取,就能定位到原来的服务器。叫做分发请求,分发到特定的服务器上去,我理解其原理是存session和读session数据保证都在一台服务器操作,就不会需要涉及到共享,具体实现方式是通过约定一种分发机制来实现。
也叫做sticky模式(粘性会话模式),同一个用户的访问请求都被派送到同一个服务器上。
假设是同一个用户user1,每次访问都路由到同一台服务器上,这样即便是在负载均衡的情况下,也能保证每次访问都能读取到session,不需要做session数据共享了。
关键多台server的原因是为负载均衡而做的,那么就得把原来负载均衡的规则假设是—a,现在改为按照session来均衡分发请求的规则—b。
如果这台机子挂掉了,那么后续的请求按照session的规则还是会分发到这台服务器上去,但是现在不可用了。
本来负载均衡有一个目的就是:当其中一台机子不可用的时候,会自动分发到可用的机子上去(自动判断现在要请求的机子是否可用)
因为某种规则的session都是保存在一台服务器上,比如用户编号是1-200涉及到的session数据保存到a服务器上去。所以只要一台出问题,1-200的用户就无法实现登录了。后面就不可用了(可能想到1-200用户的session服务器用多台进行复制,这感觉很蹩脚,仍然需要用到复制的话,还不如用其他简便的方法)
第三种思路:做一个中间层,专门来存储所有访问涉及到的session。也就是所有的session都存储在这里。
服务器端统一从这里读取session数据。
具体实现方式很多种。我的理解是,这里只是一种思想层面上的。我不知道淘宝的tbsession框架的具体实现。但是大致思想差不多,
由这个session框架来维护所有网站的session数据。我根据自己的理解,猜测淘宝的结构画图大致如下:

使用这种中间层的思想来实现共享,具体的技术方案,我归纳为以下几种:
1、 通过NFS文件共享的方式,多台php服务器共享保存session文件的磁盘。
通过nfs的方式,各个php服务器操作session数据的时候,是读取本地磁盘目录,但实际上是一个共享网络文件。各个php服务器实际上操作的都是同一个目录的文件。
具体的操作细节。到时候还需要详细写一下。我根据理解,画了下面的图:

2、保存在数据库中,这种方式的扩展性很强,可以随意增加WEB而不受影响。放在数据库里面安全方面好。
其实我理解本质是:自己写程序(php,java都可以实现,反正是保存在数据库中)模拟实现session的机制。
具体为,把以前存储在文件中的session数据存储到数据库中去,那么这样做,其实就不用到php内置的session机制了(像session_start()之类的函数都不需要去用了)。
写程序要模拟的是,从数据库拿session数据,约定什么情况下数据过期了然后自动清理,这里是指删除数据库中的行。保存在文件中的时候,php有垃圾回收机制会去自动清理过期的session文件。
====================================弊端
放在数据库里面,访问量小没有问题。大流量网站这么做,只会拖慢速度。因为得查询数据库,造成数据库压力大。
高并发访问的情况下,会出现很大的性能问题。
有些做法跟这种思想是类似的:比如ecshop、phpcms是把session数据都存储在数据库中去。服务端就是从数据库中拿session的数据。
放到数据库存储后,就可以实现:多台web服务器统一操作数据库,因为数据都在数据库,web服务器都能从数据库进行读取,那么session数据就能实现共享。
存储在数据库的做法,在线人数决定了其瓶颈,主要问题是影响性能。在线人数,因为登录的session数据存储在数据库中,只要是登录的用户就会涉及到频繁操作数据库。
我觉得小网站,同时1-2万个人在线情况下。应该没什么问题。
看网上丢出一个问题:对于大访问量的网站,数据库存储session方法可行性有待商榷。
我搜寻了一些资料,理解如下:
访问量大的话,一个用户访问了n多个页面,哪怕是刷新页面,都需要去数据库取session数据。数据库的承受压力,确实很恐怖。pv是多少,就要请求多少次数据库服务器。
访问每个页面都会去数据库查询是否登录,或者添加数据进数据库的sessions表
保存在文件中的时候,则交给了操作系统去控制。一个用户怎么刷新页面,查看其他页面,都只需要读取单个session文件(sess_74dd7807n2mfml49a1i12hkc45)。
我觉得,ecshop,discuz之类的系统之所以把session存储在数据库中去,跟网站的应用级别有关。他们设计的系统本身就是给中小站长用的,这些中小站长一般由于规模小,经济成本考虑,使用的是虚拟主机之类的。不具备对服务器的完全控制权限,比如还要安个memcache之类的,修改php.ini之类的都需要自己拥有独立服务器才能操控的(vps也算,只是虚拟出来的硬件而已)。
其实真正要做到网站大了,系统承受不住了。也会自己有独立的技术人员可以进行二次开发。
discuz这些做通用的软件要考虑思路有个特点:得考虑大部分用户的服务器环境。比如经常看到源代码里面要做php版本判断的代码,判断是5.0之前的要如何处理,以求尽量适应大部分环境。而我们公司自己运营的内部系统,环境我们完全可控。做这些确实是多余的工作量。
另外一个点是,这些通用软件不会为了高级用户的特殊需求,做一些改变,结果另外一部分用户就无法使用了。没法两全。所以我的理解是,他们一般不会随便去响应站长的需求,比如你明明是一个很大用户的站点,你用了我的系统,还要说数据量大了承受不住,表容易损坏。你都达到某种级别的应用了,还不自己进行开发。来这里抱怨。找我按照你们方式定制,愿意给钱就好。
从这里我看到,不是说这些软件技术含量就多好,是多么成熟的解决方案。他们针对的用户群不同。
由于http是短连接,每次过程是:建立连接(握手)》》数据通信》》通信结束后结束连接。如果频繁的这样子连接后再断开,性能会非常差。
session存储在数据库中,有多少pv,就要多少次这样的数据库连接操作(得去数据库拿session才能知道有没有登录,登录是否过时)。
3、可以将session数据保存在memcached,redis之类内存数据库中,memcached是基于内存存储数据的,性能很高,用户并发量很大的时候尤其合适。
主要是利用内存的数据读取速度是很快的,与磁盘读取的速度不是一个数量级的。
使用内存存储:方便统计在线人数,内存的速度比磁盘访问快、内存数据库系统能够控制内存中的过期数据自动失效(刚好符合session过期需要)。
存储在redis比较理想的选择,存储在数据库中方便存储统计在线人数,那么存储在redis中也实现了这个要求。
也可以存储在memcache中。但redis支持的数据类型多。所以用它好点。
关于使用技术工具复制session数据同步到多台服务器的方案权衡:
这种方案是,使用一些文件同步工具(linux下的rsync),当a服务器中的session数据有更改的时候,就会把这些更改也同步到b,c服务器上去。通过复制的方式,最终a,b,c各个服务器上都拷贝了一份session数据。
这种方式的弊端是,速度慢。复制数据会出现延迟。比如第一秒访问是a服务器,修改了session数据,负载均衡,可能下一秒访问是b服务器,session数据如果没有被复制到b服务器,则是读取不到session数据的,出现时间上的延迟。这种复制数据要消耗很多网络带宽的。在实际中业界用得比较少。机器的数量越多,复制数据的性能损耗越大。不具备高度扩展性。
复制session的方式,无论是网络带宽成本还是硬件开销上都很大的。
/** 集群:把所有的服务进行负载均衡;
分布式:集群服务程序之间的调用;
分布式:一个业务分拆多个子业务,部署在不同的服务器上
集群:同一个业务,部署在多个服务器上
**/
自我总结:
在集群环境中,多个服务器共同处理用户请求。但由于 HTTP 协议是无状态的,为了跟踪用户的会话状态,通常会使用 Session。然而,当用户的请求可能被分发到不同的服务器时,就会出现 Session 无法共享的问题。以下是几种常见的 Session 多服务器共享方案:
1. 基于客户端的 Session 共享方案:Cookie 存储
- 原理:将 Session 的数据存储在客户端的 Cookie 中。当用户发送请求时,会携带包含 Session 数据的 Cookie 到服务器,服务器从中获取所需的 Session 信息。
- 举例:一个电商网站采用集群部署,当用户登录后,服务器将用户的登录状态、购物车信息等 Session 数据加密后存储在 Cookie 中返回给客户端。后续用户的每次请求都会携带该 Cookie,服务器通过解析 Cookie 来获取 Session 数据。
- 优点:实现简单,不需要服务器端额外的存储和管理。
- 缺点:安全性较低,因为 Cookie 存储在客户端,可能被篡改或窃取;且 Cookie 有大小限制,无法存储大量的 Session 数据。
2. 基于服务器的 Session 共享方案
2.1 粘性会话(Sticky Session)
- 原理:通过负载均衡器(如 Nginx、HAProxy)将同一个用户的所有请求都分发到同一台服务器上。这样,用户的 Session 数据就可以在该服务器上正常使用。
- 举例:在一个在线教育平台的集群环境中,负载均衡器根据用户的 IP 地址或 Session ID 等信息,将用户的请求始终转发到某一台应用服务器。比如用户 A 的请求第一次被分发到服务器 Server1,后续的请求也都会被分发到 Server1,从而保证 Session 的一致性。
- 优点:实现简单,不需要对应用程序进行修改。
- 缺点:如果某台服务器出现故障,该服务器上的 Session 数据将丢失;并且服务器之间的负载可能不均衡,因为某些用户的请求会一直集中在某一台服务器上。
2.2 Session 复制(Session Replication)
- 原理:在集群中的每台服务器之间复制 Session 数据。当一台服务器上的 Session 数据发生变化时,会将这些变化同步到其他服务器上。
- 举例:一个社交网站采用 Session 复制方案,当用户在服务器 Server1 上更新了个人资料(如修改头像),Server1 会将更新后的 Session 数据复制到集群中的其他服务器(如 Server2、Server3 等)。这样,当用户的下一个请求被分发到其他服务器时,也能获取到最新的 Session 数据。
- 优点:对应用程序透明,不需要修改应用代码;用户请求可以被分发到任意一台服务器。
- 缺点:会占用大量的网络带宽,尤其是在 Session 数据较大或集群规模较大时;并且同步过程可能会有延迟,导致数据不一致。
2.3 集中式 Session 存储
- 原理:使用一个独立的存储系统(如 Redis、Memcached 等)来集中存储 Session 数据。所有服务器都从该存储系统中读取和写入 Session 数据。
- 举例:一个企业级的办公系统,采用 Redis 作为集中式 Session 存储。当用户登录后,服务器将用户的 Session 数据存储到 Redis 中。后续用户的请求无论被分发到哪台服务器,服务器都会从 Redis 中获取 Session 数据。
// Java 代码示例,使用 Jedis 操作 Redis 存储 Session
import redis.clients.jedis.Jedis;
public class SessionManager {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
private Jedis jedis;
public SessionManager() {
jedis = new Jedis(REDIS_HOST, REDIS_PORT);
}
public void saveSession(String sessionId, String sessionData) {
jedis.set(sessionId, sessionData);
}
public String getSession(String sessionId) {
return jedis.get(sessionId);
}
public void close() {
jedis.close();
}
}
- 优点:扩展性好,可以方便地增加或减少服务器;数据一致性高,避免了 Session 复制带来的网络开销和延迟问题。
- 缺点:需要额外维护一个存储系统,增加了系统的复杂度和运维成本。
总结
不同的 Session 共享方案有各自的优缺点,在实际应用中,需要根据系统的需求、性能要求、成本等因素来选择合适的方案。对于小型系统,可以考虑使用粘性会话或 Cookie 存储;对于对数据一致性要求较高、流量较大的系统,集中式 Session 存储是比较好的选择。

浙公网安备 33010602011771号