前言

大学三年多,也做个几个网站和APP后端,老是被人问到,如果用户多了服务器会不会挂,总是很尴尬的回答:“哈哈,我们的用户还少,到了服务器撑不住的时候,估计都上市了吧”。说是这么说,但是对于有强迫症的我,这个问题一直回响在我脑海里,久久不散啊。如今大四下了,终于有时间来深入了解一下这个问题了。

貌似解决大访问量的方案有硬件和软件两个大类的方法,硬件一般比较贵,学生党就不去考虑了。还是想想怎么用软件解决吧。于是乎,Google,Baidu,balabala… 搜到最多的词就是“均衡负载”,搭配的一般都是Nginx。找到了方向,那就撸起袖子干活吧。

集群搭建

首先在vmware12中安装3台debain,命名为debian1,debian2,debian3。一路默认就好(其实并不好,后面会说)。

vmware有个问题,一旦窗口获得焦点,就自动关闭了小键盘,导致我设置root密码的时候输入为空(它也没提示)。
后来我想用su命令才发现密码错误,输入空密码一样错误,就只有找回密码了。

对于debian来说,这样改:在grub界面光标指向待启动的系统,然后按 e 键进行编辑,如图:

在 quiet 后面加个1(注意要有空格),按F10,你就可以以root身份进入命令行界面的。

这时候就用passwd修改密码,然后reboot就可以了。

终于打开了,准备试试网络,发现无法访问外网,但是windows主机可以,如果一路默认的话不应该出现问题,最有可能就是杀毒软件把vmware的服务进程给关了(装了360…)。在windows中启动Vmware的DHCP服务

然后虚拟机要reboot一下来获取ip。好了,现在虚拟机可以访问外网了。

安装nginx,才发现根本连不上,一看才发现是老美的源,应该是一路默认惹的祸啊,修改为科大源(我为母校自豪,哈哈)。

vi /etc/apt/source.list

修改为:

deb http://mirrors.ustc.edu.cn/debian/ wheezy main non-free contrib
	deb http://mirrors.ustc.edu.cn/debian/ wheezy-proposed-updates main non-free contrib
	deb-src http://mirrors.ustc.edu.cn/debian/ wheezy main non-free contrib
	deb-src http://mirrors.ustc.edu.cn/debian/ wheezy-proposed-updates main non-free contrib

	deb http://mirrors.ustc.edu.cn/debian-security/ wheezy/updates main non-free contrib
	deb-src http://mirrors.ustc.edu.cn/debian-security/ wheezy/updates main non-free contrib

然后执行这个命令来更新: apt-get update

安装: apt-get install nginx

启动:/etc/init.d/nginx start

随便用一个虚拟机开启一个浏览器打开localhost,成功启动,如图:

vi用不惯 apt-get install vim 安装vim报错:

The following packages have unmet dependencies:
     vim : Depends: vim-common (= 2:7.3.547-7) but 2:7.4.488-7 is to be installed
    E: Unable to correct problems, you have held broken packages.

可见冲突了,解决:

先执行apt-get remove vim-common 卸载vim-common

再进行安装vim,执行 apt-get install vim

找找nginx的根目录,我们打开配置文件(和Apache一样,配置文件模块化的,不是一个单独的nginx.conf)看一看

vim /etc/nginx/sites-enabled/default

中间有一行

root /usr/share/nginx/www;

这就是根目录啦

修改index.html来区分三台主机

用ipconfig 分别获得 ip 地址,在windows中访问

debian1  http://192.168.182.128/

debian2  http://192.168.182.129/

debian3  http://192.168.182.130/

基础尝试

先来一个小例子,以便对均衡负载产生一个直观的感受吧。

我们把debian1作为主服务器承担请求分发的任务,即外部访问的是debian1,然后debain1把请求发送给debian2或者debain3,如下图:

在debian1中修改配置文件 :vim /etc/nginx/nginx.conf

在http配置项中加入如下

upstream site {
	      server  192.168.182.129:80;
	      server  192.168.182.130:80;
	} 

	server{
	    listen 80;
	    location / {
	        proxy_pass         http://site;
	    }
	}

这是选择的轮询的模式

保存重启nginx。

现在在windows中访问debian1,http://192.168.182.128/。多次刷新 可见如下两图依次出现:

说明发送给 debian1 的请求的确是均匀分配到 debian2和debian3了,亦即轮询。

session共享

上面的例子可以说简单到没有什么实用价值,大型网站一般不可能是纯静态的,一般都涉及到用户登录的问题,那么就涉及到session的问题了。你想用户在A登陆了,A记住了用户的登录状态,可是下一次用户请求被分配到B去了怎么办?显然不可能让用户再登陆一次。所以要实现session共享。一般有几个解决办法:

  1. iphash,把特定ip发送给特定主机,就不存在session这个问题了,因为1个用户对应1台主机。但是某时刻当来自某个IP地址的请求特别多,那么将导致某台负载服务器的压力可能非常大,而其他负载服务器却空闲的不均衡情况,这就违背了我们负载均衡的初衷。
  2. 搭建redis集群或者memcached集群,用集群自带的同步方法来帮我们在不同的主机中同步session,这样就相当于把原来的一份session变成了N分session(有点浪费,哈哈),session的同步就依赖于NoSql集群的同步了。
  3. 不使用session,换作cookie。但是秉承着防御性编程的原则,我们不能相信用户输入,因为cookie可能被禁用,甚至篡改。
  4. 单独设置一个session服务器,负载服务器得到一个sessionid过后,去session服务器获得会话状态,然后根据状态来响应用户请求,如果会话状态为空,则在session服务器中设置一个会话状态,然后返回给用户一个sessionid。

我准备采用方案4,即用debian1作为分发服务器,同时作为session服务器(用redis实现),负载服务器每次都要向分发服务器请求用户的session对应的会话状态,以此决定响应方式。

php 环境搭建

在debain2,3中搭建php环境

先在2中修改

命令 :

apt-get update    #更新源
apt-get install php5		#安装php5
apt-get install php5-cli		#安装php5 命令行工具
apt-get install php5-fpm

最后一句就报错了:

The following packages have unmet dependencies:
	 gnupg : Depends: libreadline6 (>= 6.0) but it is not going to be installed
	   Recommends: gnupg-curl but it is not going to be installed
	 	php5-fpm : Depends: libssl1.0.0 (>= 1.0.0) but it is not going to be installed
         Depends: php5-common (= 5.4.45-0+deb7u2) but it is not going to be installed
         Depends: ucf but it is not going to be installed
         Depends: tzdata but it is not going to be installed
          PreDepends: dpkg (>= 1.16.1~) but it is not going to be installed

搞了好久也没解决,还是换个fastcgi管理工具吧(回头再来啃一啃):

apt-get install spawn-fcgi

启动spawn-fcgi:

/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -C 5 -u www-data -g www-data -f /usr/bin/php-cgi

说明:

-a : PHP FastCGI 绑定IP地址

-p : PHP FastCGI 指定端口

-u : PHP FastCGI 用户名

-g : PHP FastCGI 用户组

-f : 指向 PHP5 fastcgi

另外

vim /etc/rc.local

加入上述命令使得它开机自启

配置nginx的php选项(还是看官网比较好,不要到处乱搜):

location ~ [^/]\.php(/|$) {
	fastcgi_split_path_info ^(.+?\.php)(/.*)$;
	if (!-f $document_root$fastcgi_script_name) {
		return 404;
	}

	fastcgi_pass 127.0.0.1:9000;
	fastcgi_index index.php;
	include fastcgi_params;
}

重启:/etc/init.d/nginx restart

在根目录中加入1.php

<?php
	phpinfo();
	?>

访问,终于成功了,泪奔

接下来对debain3 如法炮制。总算是完成这一步了。

redis 环境搭建

在debain1中搭建redis服务器

命令:

wget http://download.redis.io/releases/redis-2.8.12.tar.gz
    	tar xzf redis-2.8.12.tar.gz
    	cd redis-2.8.12
    	make

编译成功,运行:./src/redis-server redis.conf

修改配置 打开 redis.conf

  • 把 bind 127.0.0.1 修改为 bind 0.0.0.0 即任意主机可以访问
  • 找到“requirepass”字段,在后面加上密码 password

重启redis服务器:

./src/redis-cli -h 127.0.0.1 -p 6379 shutdown #关闭
./src/redis-server redis.conf 开启

这时你会发现如果用redis客户端直接访问会报错

要输入密码后在能正常使用,如图:

在debain2,3中配置phpredis

命令:

apt-get install php5-dev #php开发者工具,后面编译需要
	    wget https://github.com/nicolasff/phpredis/archive/master.tar.gz
	    tar xvf master.tar.gz
	    cd phpredis-master/
	    phpize
	    ./configure --enable-redis
	    make && make install

然后修改配置:

vim /etc/php5/apache2/php.ini

在Dynamic Extensions 后面添加extension=redis.so

重启服务:还真没找到成熟的解决办法,只有采取笨办法了

lsof -i :9000  #列出该端口相关信息,包含PID
	kill -9 pid  # 把上一步显示出来的pid挨个杀死
	/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -C 5 -u www-data -g www-data -f /usr/bin/php-cgi  #启动

测试:在debian3的nginx根目录添加1.php 代码如下:

<?php
	 $redis_host = '192.168.182.128';
	 $redis_port = 6379;
	 $redis_psw = 'password';

	 $redis = new Redis ();
	 $redis->connect ( $redis_host, $redis_port );
	 $redis->auth ( $redis_psw );
	 $redis->set('a',1);
	 echo $redis->get('a');

	?>

结果如下:

可见是成功了,对debian2如法炮制,效果一样

逻辑实现

负载服务器查看客户端是否带有sessionid这个参数,如果有,则去session服务器获取会话状态并返回结果,否则产生一个session和会话状态存入session服务器并返回sessionid给客户端。这是一个大概的逻辑轮廓,细节就不讨论了,实现如下:

  1. 修改debian2,3的nginx配置文件使得默认路径是index.php 而非 index.html,同时删掉原有的index.html,加入index.php。重启。
  2. index.php 代码如下:
    <?php
    //初始化连接
    $redis_host = '192.168.182.128';
    $redis_port = 6379;
    $redis_psw = 'password';
    $redis = new Redis ();
    $redis->connect ( $redis_host, $redis_port );
    $redis->auth ( $redis_psw );
    $sessionid = ceil($_GET['sessionid']);
    $hostname = 'debian2';//debian3就要改成debian3
    
    if(empty($sessionid)){//没有sessionid
    	$sessionid = rand(10000000,99999999);//简便起见产生8位数字作为有效id
    	$status = '已经登陆,由 '.$hostname.' 设置session';
    	$redis->set($sessionid,$status);
    	$data = array('当前站点'=>$hostname,'sessionid'=>$sessionid,'info'=>'这是您第一次登陆');
    echo json_encode($data,JSON_UNESCAPED_UNICODE);
    	exit();
    }
    
    $status = $redis->get($sessionid);
    
    if(empty($status)){//sessionid无效
    	$sessionid = rand(10000000,99999999);//简便起见产生8位数字作为有效id
    	$status = '已经登陆,由 '.$hostname.' 设置session';
    	$redis->set($sessionid,$status);
    	$data = array('当前站点'=>$hostname,'sessionid'=>$sessionid,'info'=>'这是您第一次登陆');
    echo json_encode($data,JSON_UNESCAPED_UNICODE);
    	exit();
    }
    
    $data = array('当前站点'=>$hostname,'sessionid'=>$sessionid,'info'=>$status);
    echo json_encode($data,JSON_UNESCAPED_UNICODE);
    exit();
    
    ?>
  3. 测试

首次访问debian1

再次访问debain1,这里就出了点问题,不知道为什么,一直发送到到debian2,连续尝试很多次,没有一次请求到debian3,根本就没有轮询啊。但是过了几分钟再次访问debian1

请求就发送到debian3了,我估计是用了php过后,nginx把一小段时间内的请求发送到同一主机了,但是一大段时间上还是轮询的。

但是我换了个浏览器过后,又变成每次轮询了。一头汗…… 所以这还是和浏览器有关的?(暂时搞不定。回头再看看,先换个浏览器)

首次访问debian1

再次访问debian1

带上sessionid首次访问debian1

带上sessionid再次次访问debian1

可见的确是达到了均衡负载同时session共享的目的。

总结

这篇文章写下来可真是费了些力气,中间出了好多错,不过一个一个有耐心的解决掉,最后出来的结果还是令人挺有成就感的。毕竟心里的一块大石算是落了。以后有空再尝试一下其他几种方法。

PS : 修改配置文件的时候,一定要先备份再修改,不然出了问题都不能恢复。

更新

啃了好久,终于找到上面包依赖(冲突)的问题了(得感谢我的一位同学)。都是源惹的祸,我当时是直接找了一段代码放进source.list, 实际上把wheezy全部改成jessie,就行了,因为我的debian是8.3 而wheezy代表debian7。用了错误的源会导致很多的兼容问题,赶紧换回来。换回来过后安装php相关的组件就没有任何问题了。

用Nginx实现Session共享的均衡负载的更多相关文章

  1. .Net Core Web Api实践(二).net core+Redis+IIS+nginx实现Session共享

    前言:虽说公司app后端使用的是.net core+Redis+docker+k8s部署的,但是微信公众号后端使用的是IIS部署的,虽说公众号并发量不大,但领导还是使用了负载均衡,所以在介绍docke ...

  2. net core+Redis+IIS+nginx实现Session共享

    .Net Core Web Api实践(二).net core+Redis+IIS+nginx实现Session共享   前言:虽说公司app后端使用的是.net core+Redis+docker+ ...

  3. springboot+spring session+redis+nginx实现session共享和负载均衡

    环境 centos7. jdk1.8.nginx.redis.springboot 1.5.8.RELEASE session共享 添加spring session和redis依赖 <depen ...

  4. SpringBoot2.x+Redis+nginx实现session共享和负载均衡

    1.创建SpringBoot项目添加依赖 <dependency> <groupId>org.springframework.session</groupId> & ...

  5. Nginx 分布式session共享问题

    在集群的时候每次访问,都会被代理转到不同的服务器,那么在这些服务器之间如何共享session? 解决方式1:session复制 只能在window下好使,web服务器解决(广播机制,将一台机器上的se ...

  6. memcache+tomcat7.0.37+nginx实现session共享

    一.session工作原理 由于http是无状态的协议,当我们访问了页面A,然后访问页面B,http无法确定这2个页面的访问是来自同一个人.因此,我们要用cookie或session来跟踪用户,根据授 ...

  7. nginx+tomcat+session共享(转)

    1 起因   最近对新开发的web系统进行了压力测试,发现tomcat默认配置下压到600人的并发登录首页响应速度就有比较严重的影响,一轮出现2000多个的 500和502错误.我把登录的时间统计做了 ...

  8. 【nginx】nginx tomcat session 共享配置

    tomcat,redis下载忽略. 一.从github上下载源码,https://github.com/jcoleman/tomcat-redis-session-manager, 将源码复制到开发工 ...

  9. Tomcat+Redis+Nginx实现session共享(Windows版)

    redis安装:xx nginx安装:xx 步骤: 1.下载tomcat-redis-session-manager相应的jar包,主要有三个: wget https://github.com/dow ...

随机推荐

  1. SQL Server error "Xml data type is not supported in distributed queries" and workaround for it

    Recently while working with data migration,got an error while running a following query where Server ...

  2. java总结文章

    java总结文章 原创地址: http://www.cnblogs.com/Alandre/ (泥沙砖瓦浆木匠),须要转载的,保留下! Thanks Talk is cheap. Show me th ...

  3. 用户与 Oracle DB 交互具体过程

    与 Oracle DB 交互 以下的演示样例从最主要的层面描写叙述 Oracle DB 操作.该演示样例说明了一种 Oracle DB 配置,在该配置中,用户和关联server进程执行于通过网络连接的 ...

  4. Linux菜鸟学习笔记--Linux系统结构

      什么是Linux? Linux是一种自由和开放源码的类Unix操作系统,存在着许多不同的Linux版本,但它们都使用了Linux内核.严格来讲,Linux这个词本身只表示Linux内核,但实际上人 ...

  5. 32位PLSQL_Developer连接oracle11g_64位

    1. 请将你下载的instantclient-basic-win32-10.2.0.5 文件解压.然后复制到你的数据库安装的文件夹下的producti文件夹下,我的是: E:\app\Administ ...

  6. UVa 10098: Generating Fast

    这道题要求按字典序生成字符串的全排列,不可重复(但字符可以重复,且区分大小写). 基本思路是先对输入的字符串按字典序排序,然后从第一位开始递归,从所有输入的字符中选出一个填充,然后再选第二位..... ...

  7. 如何在webstrom中配置eslint和less

    webstrom 帮助文档(英文版) 1.在webstrom中使用ESLint规范代码格式: JSHint 可以帮助检测你的 JavaScript 代码中的错误和潜在的问题,而 eslint是一个 J ...

  8. 使用HTML5中的Canves标签制作时钟特效

    <!DOCTYPE html > <html> <head> </head> <body> <canvas id="cloc ...

  9. CSS3 @font-face (webfont)

    先大概介绍下计算机领域常见的字体类型与格式. 点阵字体(Bitmap Font)点阵字体也叫位图字体,其中每个字形都以一组二维像素信息表示.这种文字显示方式于较早前的电脑系统(例如未有图形接口时的 D ...

  10. 恢复root用户目录,及~目录

    普通帐号登su;mkdir /root;chown root:root /root cp -R /etc/skel/.[!.]* ./