nginx是什么?
nginx高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器,IMAP就是internet mail acceess protocol(邮件访问协议),和POP3的区别是它不需要用户把所有邮件下载,可以通过客户端直接操作服务器邮件。SMTP(简单邮件传输协议)。
什么叫反向代理服务器?
代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中。当一个代理服务器能够代理外部网络上的主机,访问内部网络时,这种代理服务的方式称为反向代理服务。其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。代理服务器就好象一个大的Cache,这样就能显著提高浏览速度和效率。更重要的是:Proxy Server(代理服务器)是Internet链路级网关所提供的一种重要的安全功能。
代理有哪些?
http代理:常见www.xxxxx采用的http协议,所以我们在浏览网页,下载数据(也可采用ftp协议)时就是用http代理。
socket代理:Socks 代理只是简单地传递数据包,而不必关心是何种应用协议(比如FTP、HTTP和NNTP请求),socket有如下三部分
① SOCKS服务器的IP地址
② SOCKS服务所在的端口
③ 这个SOCKS服务是否需要用户认证?如果需要,您要向您的网络管理员申请一个用户和口令
折叠VPN代理
指在共用网络上建立专用网络的技术。之所以称为虚拟网主要是因为整个VPN网络的任意两个结点之间的连接并没有传统专网建设所需的点到点的物理链路,而是架构在公用网络服务商ISP所提供的网络平台之上的逻辑网络。用户的数据是通过ISP在公共网络(Internet)中建立的逻辑隧道(Tunnel),即点到点的虚拟专线进行传输的。通过相应的加密和认证技术来保证用户内部网络数据在公网上安全传输,从而真正实现网络数据的专有性。
折叠反向代理
反向代理服务器架设在服务器端,通过缓冲经常被请求的页面来缓解服务器的工作量。 安装反向代理服务器有几个原因:
加密和SSL加速
负载平衡
缓存静态内容
压缩 减速上传
安全 外网发布
详细介绍:
因为负载均衡需要多台服务器,所以本地想要模拟只能是通过端口不同来配置
具体每个配置的含义和安装去这里http://www.runoob.com/linux/nginx-install-setup.html自行查看
upstream xxx中的xxx是代理组的名称可以随意,但是需要和server中proxy_pass中的xxx一致,因为本意就是从server中通过
proxy_pass转发到upstream组。
负载均衡服务器 8080
接收分发请求的服务器 8082 8083两台
负载均衡的是8080这台服务器,监听8080端口的请求拦截后通过proxy_pass转发到对应的upstream,通过weight权重配置转发到相应服务器,注意注释掉的注意哪里,因为意思Ip_hash不支持backup servers和权重,如果你配置了配置文件会报错无法启动。backup servers是指Nginx负载均衡的双机热备服务器(关于ip_hash和Nginx负载均衡的双机热备服务器介绍可以看)
主要环境搭建的配置如下
upstream xxxx {
ip_hash; #为了配置session共享
#weight权重 代表分配到接收多少请求量,这里分别占2/5,3/5,默认是1:1比例分配
#max_fails最大失败次数 fail_timeout超时时间 这里含义就是在超时时间内,
#请求失败次数达到2次后就不再分配请求到这台服务器
server localhost:8082 weight=2 max_fails=2 fail_timeout=10;
server localhost:8083 weight=3 ;
#注意 server localhost:8084 backup; Ip_hash balancer does not support backup servers and weight
server localhost:8083 down; #表示这台服务器不能使用
}
server {
listen 8080;
server_name localhost;
location /项目名{
proxy_pass http://xxx;
}
}
如果我的项目是Test,那么我就通过localhost:8080/Test/login.jsp请求进来,那么nginx监听后转发到分配请求的服务器组
通过权重来分配请求,分配方式默认是1:1的方式轮询分配。
上面的配置配置好后如果缺少ip_hash回出现强制退出的情况,为什么会这样呢?大家思考一下。
基础配置就是上面这个,具体很细的就需要大家各自去看,下面讲讲session问题
三种方式解决sesssion问题
1.配置ip_hash,对客户端ip进行hash计算,因为ip是唯一的,hash值唯一每次请求都是定到同一台服务器也就不存在session共享问题
优点:配置简单,只需要如上在upstream中配置ip_hash即可
缺点:就是因为每次请求都固定到一台服务器,所以无法好好利用好nginx负载均衡,提高服务器的利用率,而且如果nginx不是放在最前面一层,那么拿到的就可能不是用户的真实ip,那么转发到的服务器就会不一样
2.Session Sticky,向客户的浏览器写入 Cookie ,默认名为route ,保存的内容是一个 md5 码,
当有用户请求请求进来时就会查找该route,'然后分到对应的服务器,请求不会跑到其他服务器
3。使用springsession和redis实现session共享,把session存放在redis中,就会有两层屏障的效果来保证用户正常使用,提高用户体验。
(1)如果服务器挂了,登陆信息存放到redis中,如果redus挂了,那么session仍然还存放到session中
看到这里大家应该明白上面提出的那个问题是怎么回事了,因为解决session问题。
大家需要先了解一下session是什么?
session是服务端存会话信息的一个对象,如登陆信息,权限等。
什么是会话?什么是连接?先建立了连接才有会话,连接一般不需要建立多次,建立好连接后就开始进行交流了,也就是会话
Oracle数据库中,连接就指和数据库的连接,会话就是和数据库中的表的数据通讯。不说远了
服务器为了识别不同的请求是否来自于同一个浏览器,就需要一个sessionid来标识浏览器,当用户请求到服务器时,服务器
就会把一些信息和jsessionid存起来放入cookie返回给用户存放在浏览器中,当再次发起请求时就会把cookie中的jsessionid带到服务器端
验证服务器端是否存在该session对象,就登陆验证来说如果成功获得,则查找当前对象中是否有已登录标识、无则视为未登录状态,阻止某些请求。
cookie存在客户端,session存在服务器端,cookie中存放的jseesionid为了去客户端验证是否已经登陆。
上面那个问题是因为没有解决session问题,如果我们第一次登陆被负载均衡服务器分配到了8082服务器上,那么再次请求时
可能就会可能被分配到8083,但是登陆信息是存放到8082服务器上的,当在8083上面没有找到登陆标识后就会被视为没有
登陆验证,然后被拦截跳转到了登陆页面。问题分析完毕,那么上面提出的3点是怎么解决seesion问题的呢?
第一点和第二点不再解释,原理都一样,不同点就是第二种优于第一点,第二个原理类似于分析的需要待解决的session问题
,但是第一点因为每次的请求转发是依赖于客户端的请求ip,如果这个ip不是客户的真实ip就会出现问题,如果nginx不是第一个代理服务器
如前面的服务器是squid时,那么拿到的ip不是真是ip就会出现分配混乱的情况,如果需要用ip_hash,那么nginx必须是最前端
否则nginx得不到正确ip,就不能根据ip作hash。譬如使用 的是squid为最前端,那么nginx取ip时只能得到squid的服务器ip地址,用这个地址来作分流是肯定错乱的。
第二点因为他的匹配规则不是通过客户端ip,而是根据一个存放加密的唯一md5去分配。
上面两种方式我个人理解都不属于解决session共享方案,只能说解决session共享,因为接收分配请求的服务器之间本质
是没有session共享的,而是每次请求都找到你存放session的那个服务器,这里是我个人观点,虽然很多地方说是解决session共享
第三种才是真正意义上的session共享,因为session是存放在redis中的,接收分发请求的服务器之间共享了session,
这种方式对比第一种第二种优点在同个客户端不在单一的把所有请求定位到同一台服务器,不管分配到那台服务器,只要
redis中存放了session,那么就是已经登陆的,不需要重复验证,而是可以提供双层保障提高用户体验,在两种特殊情况下
仍然不需要重复验证
1.redis挂了,sesion在服务器端还有存放,不需要验证
2.服务器挂了,session存放在redis端,不需要验证,session会话不会失效
个人理解:如果是第一次请求登陆成功第二次redis就挂了,那么如果分配到没有存放session的那台服务器肯定是需要
再次验证的,排除这种特殊情况
所以session共享大大提高了nginx负载均衡的利用率,并且提高了用户体验。
session+redis共享流程大概如下,目前个人理解,如果有错后续更改,仅供参考,这个图有问题,应该是配置服务器1和2去redis获取session,而不是先获取session再去请求服务器1和2.
下面直接上关键代码,maven工程
applicationContext.xml配置
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
spring-servlet.xml配置
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
pom.xml配置 注意最好用我这个版本 免得麻烦 ,包之间的适配我也搞了好久
web.xml配置
Controller控制器
package com.nginx.test.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@RequestMapping("/setsession")
@ResponseBody
public void setSession(HttpServletRequest request, String name) {
System.out.println("设置key的value");
request.getSession().setAttribute("key", name);
}
@RequestMapping(value = "/getsession")
@ResponseBody
public String getSession(HttpServletRequest request, HttpServletRequest req) {
System.out.println("获取key的value");
if(request.getSession().getAttribute("key")!=null){
return request.getSession().getAttribute("key").toString() + "-----" + req.getSession().getServletContext().getRealPath("/");
}else{
return "session no value";
}
}
}
jsp页面:这里可以修改titile来标识不同的服务器
<%@ page contentType="text/html;charset=UTF-8" language="java"%>