如何处理Nginx负载均衡中的session会话信息丢失问题?
Nginx简介
Nginx是一个高性能的http和反向代理服务器,它看起来好像不太符合英文单词的拼写习惯,因为Nginx是由名为 伊戈尔·赛索耶夫 的俄罗斯人开发的。Nginx主要特点为占用内存小,处理并发能力强悍,在国内被广泛采用。
启动,停止,和重新加载配置文件命令
Nginx解压完毕之后,直接双击nginx.exe 即可启动,当Nginx启动之后,通过nginx -s 命令可以实现停止nginx和重新加载配置文件。
nginx -s stop # 立即停止
nginx -s quit # 停止,在Nginx停止前会等待当前正在进行的任务
nginx -s reload # 重新加载配置文件
Nginx功能
——反向代理
——正向代理
——负载均衡
——HTTP服务器(动静分离)
正向代理和反向代理的区别
1、正向代理:类似我们想要访问国外的Google服务器,但是由于访问限制,我们需要找一个代理去访问。换句话说,客户端明确知道要访问的服务器的地址,客户端把请求发送给代理,代理转发给服务器,服务器把响应传给代理,最后代理把响应传给客户端。我们可以看到客户端知道服务器是谁,但是服务器并不知道客户端是谁,这就是正向代理,隐藏了客户端的真实信息
2、反向代理:类似我们访问淘宝,由于访问量巨大,淘宝会使用许多台服务器(就是分布式服务器)来支持,但是每个客户端的请求到底由哪一台服务器来响应,我们需要一个代理来决定。换句话说,客户端并不知道要把请求发送给哪一台服务器,但是知道发送给哪一个代理,然后代理依据规则(响应时间,负载均衡等)决定把请求转发给哪一台服务器。可以看到,客户端并不知道他访问的服务器是谁,这就是反向代理,隐藏了服务器的真实信息。
反向代理
server{
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host:$server_port;
}
}
保存配置文件后启动Nginx,这样当我们访问localhost的时候,就相当于访问localhost:8080了
负载均衡
负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。而Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略。
1、RR(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
简单配置
upstream test{
server localhost:8080;
server localhost:8081;
}
server{
listen 81;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
负载均衡的核心代码为
upstream test{
server localhost:8080;
server localhost:8081;
}
当8081端口的服务器宕机,也就是说访问不到,但是我们访问http://localhost 的时候,也不会有问题,会默认跳转到http://localhost:8080 具体是因为Nginx会自动判断服务器的状态,如果服务器处于不能访问(服务器挂了),就不会跳转到这台服务器,所以也避免了一台服务器挂了影响使用的情况,由于Nginx默认是RR策略,所以我们不需要其他更多的设置。
2、权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
例如
upstream test{
server localhost:8080 weight=9;
server localhost:8081 weight=1;
}
那么10次一般只会有1次会访问到8081,而有9次会访问到8080
3、ip_hash
上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream test{
ip_hash;
server localhost:8080;
server localhost:8081;
}
4、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backend{
fair;
server localhost:8080;
server localhost:8081;
}
5、url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
upstream backend{
hash $request_uri;
hash_method crc32;
server localhost:8080;
server localhost:8081;
}
以上5种负载均衡各自适用不同情况下使用,所以可以根据实际情况选择使用哪种策略模式,不过fair和url_hash需要安装第三方模块才能使用
HTTP服务器
Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器
server{
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
root e:\wwwroot;
index index.html;
}
}
这样如果访问http://localhost 就会默认访问到E盘wwwroot目录下面的index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署。
动静分离
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路
upstream test{
server localhost:8080;
server localhost:8081;
}
server{
listen 80;
server_name localhost;
location / {
root e:\wwwroot;
index index.html;
}
#所有静态请求都由Nginx处理,存放目录为html
location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
root e:\wwwroot;
}
#所有动态请求都转发给Tomcat处理
location ~ \.(jsp|do)$ {
proxy_pass http://test;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root e:\wwwroot;
}
}
这样我们就可以吧HTML以及图片和css以及js放到wwwroot目录下,而tomcat只负责处理jsp和请求,例如当我们后缀为gif的时候,Nginx默认会从wwwroot获取到当前请求的动态图文件返回,当然这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了,只要搞清楚了最基本的流程,很多配置就很简单了,另外localtion后面其实是一个正则表达式,所以非常灵活
Nginx的匹配优先级为,正则,长路径,短路径。关于正则的配置必须以~开头,后面跟要匹配的文件名正则表达式
正向代理
正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。当你需要把你的服务器作为代理服务器的时候,可以用Nginx来实现正向代理
resolver 114.114.114.114 8.8.8.8
server{
resolver_timeout 5s;
listen 81;
access_log e:\wwwroot\proxy.access.log;
error_log e:\wwwroot\proxy.error.log;
location / {
proxy_pass http://$host$request_uri;
}
}
resolver是配置正向代理的DNS服务器,listen 是正向代理的端口,配置好了就可以在ie上面或者其他代理插件上面使用服务器ip+端口号进行代理了。
为什么要使用Nginx?
提及Nginx,我们就需要了解下web应用服务器。在此,我们暂且以tomcat这款应用服务器为例来介绍。
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。由于Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
一般来说,用单机tomcat搭建的网站,在比较理想的状态下能够承受的并发访问量在150到200左右。按照并发访问量占总用户数量的5%到10%这样计算,单点tomcat网站的用户人数在1500到4000左右。对于一个为全国范围提供服务的网站显然是不够用的。
在互联网飞速发展的今天,大用户量高并发已经成为互联网的主体.怎样能让一个网站能够承载几万个或几十万个用户的持续访问呢?这是一些中小网站急需解决的问题。
为了解决这个问题,我们引入了负载均衡方法。负载均衡就是一个web服务器解决不了的问题可以通过多个web服务器来平均分担压力来解决,并发过来的请求被平均分配到多个后台web服务器来处理,这样压力就被分解开来。
目前,负载均衡服务器主要分为两种,一种是通过硬件实现的负载均衡服务器,简称硬负载,例如f5;另一种是通过软件来实现的负载均衡,简称软负载,例如apache和nginx。硬负载和软负载相比,前者作用的网络层次比较多,可以作用到socket接口的数据链路层,并且对发出的请求进行分组转发,但是价格成本也随之较贵;而软负载作用的层次在http协议层之上,可以对http请求进行分组转发,并且由于是开源的,所以几乎是0成本,当前,阿里巴巴,京东等电商网站使用的也是Nginx服务器。
tomcat如何配置首个启动工程?
在讲述Nginx负载均衡之前,我们先熟悉tomcat的配置。
以win7系统为例,我们平时在电脑上开启tomcat应用后,通过浏览器访问,会访问到tomcat的系统默认首页。那么,如何修改tomcat的首个启动工程呢?
以tomcat7为例,我们将一个名称为Demo1的java web项目导出为一个war文件,然后放在tomcat的webapps文件夹下,接着打开tomcat的conf文件夹下的server.xml文件,在<Host></Host>中加入
<Context docBase="E:\nginx\tomcatA\webapps\Demo1" path="" reloadable="true" crossContext="true"/>
然后重启tomcat,并在浏览器地址栏里输入http://localhost:8080,这时就会发现tomcat的默认首个启动工程变为Demo1。
如何在同一个tomcat上配置多个虚拟主机?
接下来,我们再看如何在一台服务器上设置多个域名,从而使得用户访问多个域名时,可以分别指向同一个tomcat应用服务器中配置的不同web工程。
首先,我们按照C:\Windows\System32\drivers\etc路径,打开该路径下的hosts文件,在里面配置下域名映射信息,如下:
127.0.0.1 www.baidudu.com
127.0.0.1 www.taobaobao.com
接着,我们另建两个java web项目,分别是Demo1和Demo2,然后再打开tomcat中的server.xml文件,在里面配置两个服务器主机信息,如下所示:
<Host name="www.baidudu.com" appBase="webapps" autoDeploy="true" unpackWARs="true">
<Context docBase="E:\nginx\tomcatA\webapps\Demo2" path="" reloadable="true" crossContext="true"/>
</Host>
<Host name="www.taobaobao.com" appBase="webapps" autoDeploy="true" unpackWARs="true">
<Context docBase="E:\nginx\tomcatA\webapps\Demo3" path="" reloadable="true" crossContext="true"/>
</Host>
配置完毕后,我们再重启tomcat,分别访问新配置的两个域名,发现它们会各自指向相应的web工程,从而实现了用一个tomcat配置多个虚拟主机。
如何通过Nginx实现tomcat集群配置与负载均衡?
我们接下来看通过Nginx对多个tomcat进行集群配置。在这里,我暂且在一台电脑上用两个tomcat做下演示,同时将这两个tomcat分别命名为tomcatA和tomcatB。为了避免同一台电脑上开启多个tomcat引起端口冲突,我将tomcatA的http访问端口配置为8081,将tomcatB的http访问端口配置为8082,为了显示不同tomcat的访问区别,我暂且让tomcatA的默认首个启动工程为Demo1,让tomcatB的默认首个启动工程为Demo3。
接着,我们双击ningx中的nginx.exe文件,从而启动nginx服务。当然,我们这里还需要对nginx中的配置文件进行一番配置,所以,如果nginx处于开启状态,则暂且在DOS窗口中关闭nginx服务。
接下来,我们打开nginx中的conf文件夹下的nginx.conf文件,我们在里面加入要配置的tomcat集群,同时,考虑到不同的服务器性能差别,可以配置相应的权重,从而实现服务器的负载均衡。如下所示:
upstream server_tomcats{
server 127.0.0.1:8081 weight=5;
server 127.0.0.1:8082 weight=1;
}
server{
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
proxy_pass http://server_tomcats;
}
}
上图中,标注红框的部分是关键配置,然后,我们在浏览器的地址栏中输入localhost,会发现tomcatA和tomcatB的所承担的访问任务比重约为5:1,从而实现了nginx对tomcat的负载均衡配置。
如何处理Nginx负载均衡中的session会话信息丢失问题?
我们在通过nginx完成了对tomcat的集群与负载均衡配置后,还面临着一个关键问题,那就是如何解决session会话信息的共享问题。解决方法也很简单,需要两个步骤:
步骤一:在每个tomcat的server.xml中找到标签:
<Engine name="Catalina" defaultHost="localhost">
#....
#....
#....
</Engine>
在上面的<Engine></Engine>标签中加入下述代码,注意不要做任何更改:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
步骤二:在web项目的web.xml文件中,添加节点<distributable/>就可以了,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<distributable/>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
到此,我们就解决了Nginx在tomcat集群配置中的session共享问题。这是基于tomcat自身的广播机制完成,虽然在功能上实现了session共享,但是运行效率相对较低。
为此,我们再介绍一种企业级解决方案,那就是引入redis缓存完成session共享。
引入redis缓存完成session共享
那么,什么是redis呢?为此,我们要对NoSql有所了解。NoSql叫非关系型数据库,它的全名Not only sql,它不能替代关系型数据库,只能作为关系型数据库的一个良好补充。它是为了解决高并发、高可用、高可扩展,大数据存储等一系列问题而产生的数据库解决方案。
NoSql可以有多种分类,如根据键值对(Key-Value)存储数据的数据库(如Redis),列存储型数据库(如Hbase),文档型数据库(如MongoDB),图形(Graph)数据库等。我们在这里用到的Redis就是NoSql数据库中的一种。Redis可以将数据储存在缓存中,极大地提高了访问数据的效率,还可以根据需要对数据进行持久化存储。
Redis中可以储存5种类型的数据,分别是:
1 String
2 hash
3 list
4 set
5 zset
言归正传,我们继续介绍运用Redis实现session共享。
首先,我们可以根据自己所用电脑的操作系统下载Redis文件,如下图所示:
我们以Windows系统为例,上面分别对应32位和64位的win7操作系统,我们选择对应的压缩文件,解压到电脑中的一个位置,如下图所示:
在上图中,我们可以对redis的配置做下修改,如设置数据持久化策略,配置端口号等,为了方便启动redis服务器,我们还可以写个批处理文件,通过双击该批处理文件即可启动redis。上图中的“启动redis服务器.bat”文件内容如下:
C:
cd C:\Users\Administrator\Desktop\work\redis
redis-server redis.windows.conf
接下来,为了便于更好地模拟nginx集群配置tomcat时解决session共享的问题,我们先下载好nginx和两个tomcat,如下图所示:
然后,我们在每个tomcat中的server.xml里,删除原先通过tomcat广播机制配置的代码块,如下图所示:
然后,我们继续在server.xml中,找到<Context />标签,将其改成<Context></Context>,根据需要,在该标签中加入下面的信息:
<Context docBase="E:\nginx\tomcatB\webapps\Demo" path="" reloadable="true" crossContext="true">
<!--redis配置session共享-->
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="127.0.0.1"
port="6379"
maxInactiveInterval="600000"
<!--
database="0" #optional: defaults to "0"
sessionPersistPolicies="PERSIST_POLICY_1,PERSIST_POLICY_2,.." #optional
sentinelMaster="SentinelMasterName" #optional
sentinels="sentinel-host-1:port,sentinel-host-2:port,.." /> # optional
-->
/>
</Context>
我们要在做集群配置的tomcat的server.xml中均做如上配置,同时确保在web项目的web.xml中添加上<distributable/>标签。
接着,我们把三个jar包放入每个tomcat根目录下的lib文件中,三个jar包如下图所示
commons-pool2-2.2.jar
jedis-2.5.2.jar
tomcat-redis-session-manage-tomcat7.jar
至此,我们就运用redis技术做好了nginx集群配置tomcat中面临的session共享问题。
最后,我们依次开启tomcat,nginx,以及redis,就会发现session已经实现了共享。