最近在研究Web架构方面的知识,包括数据库读写分离,Redis缓存和队列,集群,以及负载均衡(LVS),今天就来先学习下我在负载均衡中遇到的问题,那就是session共享的问题。

一、负载均衡

负载均衡:把众多的访问量分担到其他的服务器上,让每个服务器的压力减少。

通俗的解释就是:把一项任务交由一个开发人员处理总会有上限处理能力,这时可以考虑增加开发人员来共同处理这项任务,多人处理同一项任务时就会涉及到调度问题,即任务分配,这和多线程理念是一致的。nginx在这里的角色相当于任务分配者。

如我们第一次访问 www.baidu.com 这个域名,可能会对应这个IP111.13.101.208的服务器,然后第二次访问,IP可能会变为 111.13.101.209的服务器,这就是百度采用了负载均衡,一个域名对应多个服务器,将访问量分担到其他的服务器,这样很大程度的减轻了每个服务器上访问量。

但是,这里有一个问题,如果我们登录了百度的一个账号,如网页的百度网盘,但是每次有可能请求的是不同的服务器,我们知道每个服务器都会有自己的会话session,所以会导致用户每次刷新网页又要重新登录,这是非常糟糕的体验,因此,根据以上问题,希望session可以共享,这样就可以解决负载均衡中同一个域名不同服务器对应不同session的问题。

二、Redis介绍

目前多服务器的共享session,用的最多的是redis。

关于Redis的基础知识,可以看我之前的博文Redis开发学习。

再简单的梳理下:

1.redis是key-value的存储系统,属于非关系型数据库

2.特点:支持数据持久化,可以让数据在内存中保存到磁盘里(memcached:数据存在内存里,如果服务重启,数据会丢失)

3.支持5种数据类型:string,hash,list,set,zset

4.两种文件格式(即数据持久化)

  1. RDB(全量数据):多长时间/频率,把内存中的数据刷到磁盘中,便于下次读取文件时进行加载。

  2. AOF(增量请求):类似mysql的二进制日志,不停地把对数据库的更改语句记录到日志中,下次重启服务,会根据二进制日志把数据重写一次,加载到内存里,实现数据持久化

5.存储

  1. 内存存储

  2. 磁盘存储(RDB)

  3. log文件(AOF)

三、实现的核心思想

首先要明确session和cookie的区别。浏览器端存的是cookie每次浏览器发请求到服务端是http 报文头是会自动加上你的cookie信息的。服务端拿着用户的cookie作为key去存储里找对应的value(session)。

同一域名下的网站的cookie都是一样的。所以无论几台服务器,无论请求分配到哪一台服务器上同一用户的cookie是不变的。也就是说cookie对应的session也是唯一的。

所以,这里只要保证多台业务服务器访问同一个redis服务器(或集群)就行了。

四、PHP会话session配置改为Redis

我们可以看到PHP默认的的session配置使用文件形式保存在服务器临时目录中,我们需要Redis作为保存session的驱动,所以,这里需要对配置文件进行修改,PHP的自定义会话机制改为Redis。

这里有三种修改方式:

1.修改配置文件php.ini

找到配置文件 php.ini,修改为下面内容,保存并重启服务

  1. session.save_handler = redis

  2. session.save_path = "tcp://127.0.0.1:6379"

2.代码中动态配置修改

直接在代码中加入以下内容:

  1. ini_set("session.save_handler", "redis");

  2. ini_set("session.save_path", "tcp://127.0.0.1:6379");

注:如果配置文件redis.conf里设置了连接密码requirepass,save_path需要这样写tcp://127.0.0.1:6379?auth=authpwd ,否则保存session的时候会报错。

测试:

  1. <?php

  2. //ini_set("session.save_handler", "redis");

  3. //ini_set("session.save_path", "tcp://127.0.0.1:6379");

  4. session_start();

  5. //存入session

  6. $_SESSION['class'] = array('name' => 'toefl', 'num' => 8);

  7. //连接redis

  8. $redis = new redis();

  9. $redis->connect('127.0.0.1', 6379);

  10. //检查session_id

  11. echo 'session_id:' . session_id() . '<br/>';

  12. //redis存入的session(redis用session_id作为key,以string的形式存储)

  13. echo 'redis_session:' . $redis->get('PHPREDIS_SESSION:' . session_id()) . '<br/>';

  14. //php获取session值

  15. echo 'php_session:' . json_encode($_SESSION['class']);

3.自定义会话机制

使用 session_set_save_handle 方法自定义会话机制,网上发现了一个封装非常好的类,我们可以直接使用这个类来实现我们的共享session操作。

  1. <?php

  2. class redisSession{

  3.    /**

  4.     * 保存session的数据库表的信息

  5.     */

  6.    private $_options = array(

  7.        'handler' => null, //数据库连接句柄

  8.        'host' => null,

  9.        'port' => null,

  10.        'lifeTime' => null,

  11.        'prefix'   => 'PHPREDIS_SESSION:'

  12.    );

  13.    /**

  14.     * 构造函数

  15.     * @param $options 设置信息数组

  16.     */

  17.    public function __construct($options=array()){

  18.        if(!class_exists("redis", false)){

  19.            die("必须安装redis扩展");

  20.        }

  21.        if(!isset($options['lifeTime']) || $options['lifeTime'] <= 0){

  22.            $options['lifeTime'] = ini_get('session.gc_maxlifetime');

  23.        }

  24.        $this->_options = array_merge($this->_options, $options);

  25.    }

  26.    /**

  27.     * 开始使用该驱动的session

  28.     */

  29.    public function begin(){

  30.        if($this->_options['host'] === null ||

  31.           $this->_options['port'] === null ||

  32.           $this->_options['lifeTime'] === null

  33.        ){

  34.            return false;

  35.        }

  36.        //设置session处理函数

  37.        session_set_save_handler(

  38.            array($this, 'open'),

  39.            array($this, 'close'),

  40.            array($this, 'read'),

  41.            array($this, 'write'),

  42.            array($this, 'destory'),

  43.            array($this, 'gc')

  44.        );

  45.    }

  46.    /**

  47.     * 自动开始回话或者session_start()开始回话后第一个调用的函数

  48.     * 类似于构造函数的作用

  49.     * @param $savePath 默认的保存路径

  50.     * @param $sessionName 默认的参数名,PHPSESSID

  51.     */

  52.    public function open($savePath, $sessionName){

  53.        if(is_resource($this->_options['handler'])) return true;

  54.        //连接redis

  55.        $redisHandle = new Redis();

  56.        $redisHandle->connect($this->_options['host'], $this->_options['port']);

  57.        if(!$redisHandle){

  58.            return false;

  59.        }

  60.        $this->_options['handler'] = $redisHandle;

  61. //        $this->gc(null);

  62.        return true;

  63.    }

  64.    /**

  65.     * 类似于析构函数,在write之后调用或者session_write_close()函数之后调用

  66.     */

  67.    public function close(){

  68.        return $this->_options['handler']->close();

  69.    }

  70.    /**

  71.     * 读取session信息

  72.     * @param $sessionId 通过该Id唯一确定对应的session数据

  73.     * @return session信息/空串

  74.     */

  75.    public function read($sessionId){

  76.        $sessionId = $this->_options['prefix'].$sessionId;

  77.        return $this->_options['handler']->get($sessionId);

  78.    }

  79.    /**

  80.     * 写入或者修改session数据

  81.     * @param $sessionId 要写入数据的session对应的id

  82.     * @param $sessionData 要写入的数据,已经序列化过了

  83.     */

  84.    public function write($sessionId, $sessionData){

  85.        $sessionId = $this->_options['prefix'].$sessionId;

  86.        return $this->_options['handler']->setex($sessionId, $this->_options['lifeTime'], $sessionData);

  87.    }

  88.    /**

  89.     * 主动销毁session会话

  90.     * @param $sessionId 要销毁的会话的唯一id

  91.     */

  92.    public function destory($sessionId){

  93.        $sessionId = $this->_options['prefix'].$sessionId;

  94. //        $array = $this->print_stack_trace();

  95. //        log::write($array);

  96.        return $this->_options['handler']->delete($sessionId) >= 1 ? true : false;

  97.    }

  98.    /**

  99.     * 清理绘画中的过期数据

  100.     * @param 有效期

  101.     */

  102.    public function gc($lifeTime){

  103.        //获取所有sessionid,让过期的释放掉

  104.        //$this->_options['handler']->keys("*");

  105.        return true;

  106.    }

  107.    //打印堆栈信息

  108.    public function print_stack_trace()

  109.    {

  110.        $array = debug_backtrace ();

  111.        //截取用户信息

  112.        $var = $this->read(session_id());

  113.        $s = strpos($var, "index_dk_user|");

  114.        $e = strpos($var, "}authId|");

  115.        $user = substr($var,$s+14,$e-13);

  116.        $user = unserialize($user);

  117.        //print_r($array);//信息很齐全

  118.        unset ( $array [0] );

  119.        if(!empty($user)){

  120.          $traceInfo = $user['id'].'|'.$user['user_name'].'|'.$user['user_phone'].'|'.$user['presona_name'].'++++++++++++++++\n';

  121.        }else{

  122.          $traceInfo = '++++++++++++++++\n';

  123.        }

  124.        $time = date ( "y-m-d H:i:m" );

  125.        foreach ( $array as $t ) {

  126.            $traceInfo .= '[' . $time . '] ' . $t ['file'] . ' (' . $t ['line'] . ') ';

  127.            $traceInfo .= $t ['class'] . $t ['type'] . $t ['function'] . '(';

  128.            $traceInfo .= implode ( ', ', $t ['args'] );

  129.            $traceInfo .= ")\n";

  130.        }

  131.        $traceInfo .= '++++++++++++++++';

  132.        return $traceInfo;

  133.    }

  134. }

在你的项目入口处调用上边的类:上边的方法等于是重写了session写入文件的方法,将数据写入到了Redis中。

初始化文件 init.php

  1. <?php

  2. require_once("redisSession.php");

  3. $handler = new redisSession(array(

  4.                'host' => "127.0.0.1",

  5.                'port' => "6379"

  6.        ));

  7. $handler->begin();

  8. // 这也是必须的,打开session,必须在session_set_save_handler后面执行

  9. session_start();

测试 test.php

  1. <?php

  2. // 引入初始化文件

  3. include("init.php");

  4. $_SESSION['isex'] = "Hello";  

  5. $_SESSION['sex']  = "Corwien";

  6. // 打印文件

  7. print_r($_SESSION);

  8. // ( [sex] => Corwien [isex] => Hello )

在Redis客户端使用命令查看我们的这条数据是否存在:

  1. 27.0.0.1:6379> keys *

  2. 1) "first_key"

  3. 2) "mylist"

  4. 3) "language"

  5. 4) "mytest"

  6. 5) "pragmmer"

  7. 6) "good"

  8. 7) "PHPREDIS_SESSION:29a111bcs120sv48ibmmjqdag4"

  9. 8) "user:1"

  10. 9) "counter:__rand_int__"

  11. 10) "key:__rand_int__"

  12. 11) "tutorial-list"

  13. 12) "id:1"

  14. 13) "name"

  15. 127.0.0.1:6379> get PHPREDIS_SESSION:29a111bcs120sv48ibmmjqdag4

  16. "sex|s:7:\"Corwien\";isex|s:5:\"Hello\";"

  17. 127.0.0.1:6379>

我们可以看到,我们的数据被保存在了Redis端了,键为: PHPREDIS_SESSION:29a111bcs120sv48ibmmjqdag4.

转载于:https://www.cnblogs.com/itrena/p/9070282.html

负载均衡中使用 Redis 实现共享 Session相关推荐

  1. nginx Win下实现简单的负载均衡(2)站点共享Session

    快速目录: 一.nginx Win下实现简单的负载均衡(1)nginx搭建部署 二.nginx Win下实现简单的负载均衡(2)站点共享Session 三.nginx Win下实现简单的负载均衡(3) ...

  2. tomcat使用redis存储共享session

    tomcat使用redis存储共享session 在tomcat集群环境下实现session共享有几种解决方式,这里介绍一种简单的方案. 使用redis对session进行存储,配置比較简单.webs ...

  3. 一文搞懂负载均衡中的一致性哈希算法

    一致性哈希算法在很多领域有应用,例如分布式缓存领域的 MemCache,Redis,负载均衡领域的 Nginx,各类 RPC 框架.不同领域场景不同,需要顾及的因素也有所差异,本文主要讨论在负载均衡中 ...

  4. 一致性 Hash 在负载均衡中的应用

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:干掉 Navicat:这个 IDEA 的兄弟真香!个人原创100W+访问量博客:点击前往,查看更多 转自:Mar ...

  5. hash地址_一致性Hash在负载均衡中的应用

    作者:marklux 原文:http://marklux.cn/blog/90 简介 一致性Hash是一种特殊的Hash算法,由于其均衡性.持久性的映射特点,被广泛的应用于负载均衡领域,如nginx和 ...

  6. 聊聊一致性Hash在负载均衡中的应用

    作者:marklux http://marklux.cn/blog/90 简介 一致性Hash是一种特殊的Hash算法,由于其均衡性.持久性的映射特点,被广泛的应用于负载均衡领域,如nginx和mem ...

  7. 一致性Hash在负载均衡中的应用

    简介 一致性Hash是一种特殊的Hash算法,由于其均衡性.持久性的映射特点,被广泛的应用于负载均衡领域,如nginx和memcached都采用了一致性Hash来作为集群负载均衡的方案.本文将介绍一致 ...

  8. 一致性 Hash 是什么?在负载均衡中的应用

    点击上方"服务端思维",选择"设为星标" 回复"669"获取独家整理的精选资料集 回复"加群"加入全国服务端高端社群「后 ...

  9. 负载均衡中SNA的ip-can(iscs使用示例i)

    负载均衡中SNA的ip-can(iscs使用示例i) 虽然上篇博文的方法已经提高了冗余及安全性,但是可以看出两个网站是存放在不同的服务器上的,在实际的工作中,两个网站的内容是完全一样的,并且,它们要有 ...

最新文章

  1. 'yii\base\InvalidRouteException' with message 'Unable to resolve the request site/error.'
  2. 从熵到交叉熵损失的直观通俗的解释
  3. 第六章:面向对象(下)
  4. 汇编语言随笔(5)-and、or指令,串传送指令movsb、访问内存单元的方式及实验4,5(向内存传送数据)
  5. 基于ssm的用户管理系统_基于SSM的高校学生实习管理系统
  6. 神策与CDA,一次不平凡的约会
  7. 5.intent_activity
  8. 使用wlan接收器经常重新登录怎么办
  9. DMG Canvas for mac(DMG打包工具)
  10. Spring Aop中@Pointcut用法
  11. caxa明细表导入零件材质_caxa中如何使明细表风格应用到任一个图纸中
  12. 图灵科普数学宝藏书单|购书狂欢618倒计时,这份书单闭眼入
  13. Android 恢复出厂设置(recovery)
  14. wordpress插件选择_如何选择2020年最好的WordPress托管(已比较)
  15. Binder 机制详解—Binder Java框架(转自Cloud Chou's Tech Blog)
  16. 为金融而生的区块链Injective(INJ)
  17. Kubernetes K8S之存储Volume详解
  18. [转] R 解读逻辑回归结果:回归系数,OR
  19. 静态时序分析-时序违例解决方法
  20. 计算机大一学生笔记本电脑选择

热门文章

  1. 【Kaggle Learn】Python 1-4
  2. 2021-2027年中国玩具行业市场研究及前瞻分析报告
  3. 2022-2028年中国铝工业投资分析及前景预测报告(全卷)
  4. 2022-2028年中国领带行业投资分析及前景预测报告
  5. Python 笔试面试合集
  6. LeetCode简单题之按既定顺序创建目标数组
  7. PyTorch 神经网络
  8. 在cuDNN中简化Tensor Ops
  9. 自动驾驶的分级和行业现状
  10. python format 格式化字符串