不积跬步,无以至千里

Nginx学习总结


最近要做一个服务反向代理,涉及运维层面的东西之前了解的不多,先了解下基本概念。

基本概念

代理服务器:一般指通过局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端,通过客户端的软件,将HTTP请求转发到其他不同的服务器端,实现请求的分发。

反向代理服务器:反向代理服务器作用在服务器端,在服务器端接收客户端的请求,然后将请求分发给具体的服务器进行处理,然后再将服务器的相应结果反馈给客户端。

Web服务器:提供HTTP的访问,有Nginx,Apache,IIS等。Web服务器的工作原理包括四个步骤:连接过程,请求过程,应答过程和关闭连接。

CDN缓存服务器:属于缓存服务器的角色,而且属于反向代理,将这些服务器分布在用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术把用户的访问指向距离最近的工作征程的缓存服务器上,由缓存服务器直接响应用户请求,从而使得用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。

Nginx

Nginx的特性我理解有两个:高并发,负载均衡

Nginx是如何实现高并发的

Nginx在处理连接请求时,采用了异步非阻塞的方式。为什么Nginx的性能要比Apache高得多?这得益于Nginx使用了最新的epoll(Linux 2.6内核)和kqueue(freebsd)网络I/O模型

Nginx负载均衡

Nginx的负载均衡策略可以划分为两大类,即内置策略和扩展策略.其中内置策略主要包含轮询,加权轮询和IPHash三种.使用的配置主要是proxy_pass指令和upstream指令.

以下为一个示例配置:

upstream backend_collection {
        server 167.140.3.107:80 max_fails=3 fail_timeout=10s;
        server 167.140.3.111:80 max_fails=3 fail_timeout=10s;
        server 167.140.3.112:80 max_fails=3 fail_timeout=10s;
    }

server {
        listen       80;
        server_name  host.com;

        location / {
                             proxy_pass http://backend_collection;  #配置被代理服务器的地址,可以是主机名称,也可以是IP地址加端口号的形式.如果被代理服务器是一组服务器的话,可以使用upstream指令.
                             #proxy_read_timeout      90s;
                             proxy_connect_timeout   1800s;
                             proxy_read_timeout      1800s;
                             proxy_send_timeout 1800s;
                             proxy_buffer_size  16k;
                             proxy_buffers      16 128k;
                             proxy_busy_buffers_size 256k;
                             proxy_temp_file_write_size 256k;
                             proxy_next_upstream     error;
                             proxy_set_header  X-Real-IP  $remote_addr;
                             proxy_set_header        Host $http_host;
                             client_max_body_size       30m;
                             client_body_buffer_size    256k;
                         }
        }

nginx默认使用轮询策略,加权轮询的概念很好理解,就是对配置的server地址增加权重的配置

upstream backend_collection {
        server 167.140.3.107:80 max_fails=3 fail_timeout=10s weight=3;
        server 167.140.3.111:80 max_fails=3 fail_timeout=10s weight=2;
        server 167.140.3.112:80 max_fails=3 fail_timeout=10s;
    }

默认情况下,权重为1,第一个server地址权重最高,为1/2

upstream backend_collection {
        server 167.140.3.107:80 max_fails=3 fail_timeout=10s;
        server 167.140.3.111:80 max_fails=3 fail_timeout=10s;
        server 167.140.3.112:80 max_fails=3 fail_timeout=10s;
        ip_hash;
    }

根据客户端的IP,将请求分发到不同的服务器上,实现按照ip做负载均衡

sticky模块

除了以上负载均衡策略之外,nginx还提供了一个模块,使得可以基于cookie的负载均衡解决方案:sticky模块.安装配置好之后,请求到达nginx上后,从cookie中取出指定的cookie,并根据cookie中的值,按照一定的策略请求服务器.如果到来的请求中不包含指定的cookie,nginx会在请求中添加一个cookie值.

当然,这种方式要求浏览器不能禁用cookie.

wget https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/master.tar.gz   #下载nginx-sticky模块
wget http://nginx.org/download/nginx-1.12.2.tar.gz  #下载nginx
tar -xzvf nginx-1.12.2.tar.gz
tar -xzvf master.tar.gz
groupadd nginx
useradd -g nginx -s /sbin/nologin nginx
mkdir -p /usr/local/nginx
cd nginx-1.12.2
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module  --with-http_realip_module --add-module=/root/nginx-goodies-nginx-sticky-module-ng-08a395c66e42  --user=nginx --group=nginx
make
sudo make install
/usr/local/nginx/sbin/nginx  #启动nginx

安装sticky模块需要openssl支持,安装步骤如下:

  • 1.OpenSSL安装
wget https://www.openssl.org/source/openssl-1.1.0g.tar.gz
tar -xzvf openssl-1.1.0g.tar.gz
cd openssl-1.1.0g
./config  shared zlib-dynamic enable-camellia
make depend
make
make test
sudo make install
  • 2.配置nginx sticky
upstream backend_collection {
        sticky;
        server 167.140.3.107:80 max_fails=3 fail_timeout=10s weight=3;
        server 167.140.3.111:80 max_fails=3 fail_timeout=10s weight=2;
        server 167.140.3.112:80 max_fails=3 fail_timeout=10s;
    }

默认情况下,添加的cookie的key为route,当然,也可以通过sticky name=${key}的方式指定key.

另外nginx plus提供了三种会话保持方法,需要注意的是nginx plus是收费版本.

  • 1.sticky cookie方法
upstream backend {  
    sticky cookie srv_id expires=1h domain=.example.com path=/;  
    server backend1.example.com;  
    server backend2.example.com;    
}

本例中,srv_id 参数设置用来设置或者检测的cookie名称。可选参数expires指定浏览器保存这个cookie的时长。可选参数domain定义cookie设置到的域名。可选参数path定义cookie的保存路径。这是最简单的会话持久方法。

  • 2.sticky route方法
    upstream backend {  
        sticky route $route_cookie $route_uri;
        server backend1.example.com route=a;  
        server backend2.example.com route=b;  

    }  

通过这个方法,Nginx在第一次接收到请求的时候给这个客户端分配一个“路由”。所有接下来的请求都会和server指令的route参数对比找出请求将被代理到的服务器。路由信息可以从cookie或URI中获取。

  • 3.cookie learn 方法
upstream backend {  
  server backend1.example.com;  
  server backend2.example.com;  

  sticky learn create=$upstream_cookie_sessionid  
      lookup=$cookie_sessionid  
      zone=client_sessions:1m  
      timeout=1h;  
}

Nginx首先通过检查请求和响应来查找会话ID。接着Nginx就知道哪个上游服务器对应哪个会话ID。通常,这些ID是通过HTTP cookie传递的。如果一个请求包含一个已经识别过的会话ID,Nginx直接发送这个请求给相应的服务器. 本例中,其中一个上游服务器通过在响应中设置cookie “SESSIONID” 来创建一个会话。必填参数create指定一个变量表示一个新的会话是如何创建的。

在我们的示例中,新的会话是通过上游服务器发送的cookie “SESSIONID”创建的。必填参数lookup指定如何搜索存在的会话。示例中,存在的会话在客户端发送的cookie “SESSIONID”中搜索。必填参数zone指定所有会话信息保存的共享内存空间。我们这个例子中,空间名为client_sessions,大小为1M。

这是一个更复杂的会话持久方法,因为他不需要在客户端保存任何cookie:所有信息保存在服务端的共享内存空间里。

nginx 安装

  1. 安装wget:yum -y install wget
  2. 安装依赖库:yum -y install zlib zlib-devel openssl openssl–devel pcre pcre-devel
  3. 下载安装包:wget http://nginx.org/download/nginx-1.12.2.tar.gz
  4. 解压tar -xzvf nginx-1.12.2.tar.gz
  5. groupadd nginx
  6. useradd -g nginx -s /sbin/nologin nginx
  7. mkdir -p /usr/local/nginx
  8. 编译安装:cd nginx-1.12.2 && ./configure –prefix=/usr/local/nginx –user=nginx –group=nginx && make && make install
  9. 启动: /usr/local/nginx/sbin/nginx

实际遇到的问题

实际系统部署如下:

用户在成都访问位于北京的机房.机房入口统一由F5接收web请求,F5通过cookie中的sessionid做负载均衡.当用户第一次访问时,cookie中不包含sessionid,此时重定向到登录页面,用户登录后再次访问后cookie中讲携带sessionid,此时,F5根据sessionid做负载均衡,从而保证同一个会话只请求同一个服务器. nginx4使用nginx的nginx-sticky-module做负载均衡,配置为:

upstream backend_server {
        sticky expires=1h domain=...com path=/;
        server 144.144.144.1:80 max_fails=3 fail_timeout=10s;  #ng1
        server 144.144.144.2:80 max_fails=3 fail_timeout=10s;  #ng2
        server 144.144.144.3:80 max_fails=3 fail_timeout=10s;  #ng3
    }

server {
        listen       80;
        server_name  server.com;

        location / {
                             proxy_pass              http://backend_server;
                             #proxy_read_timeout      90s;
                             proxy_connect_timeout   1800s;
                             proxy_read_timeout      1800s;
                             proxy_send_timeout 1800s;
                             proxy_buffer_size  16k;
                             proxy_buffers      16 128k;
                             proxy_busy_buffers_size 256k;
                             proxy_temp_file_write_size 256k;
                             proxy_next_upstream     error;
                             proxy_set_header  X-Real-IP  $remote_addr;
                             proxy_set_header        Host $http_host;
                         #    proxy_set_header X-Forwarded-Host $host;
                         #    proxy_set_header X-Forwarded-Server $host;
                         #    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                             client_max_body_size       30m;
                             client_body_buffer_size    256k;
                         }

        }

当用户访问时,ng4从请求头中查找route的key,如果找不到的话自动生成一个,并根据这个生成的值做负载均衡,然后将请求转发到配置的其中一台服务器.

造成问题的原因是有两个因素:

  1. sessionid存储在服务器上,而不是集中存储.
  2. 测试阶段,cdn按照流量切换.也就是一个用户可能第一次通过F5访问,第二次通过ng4访问北京机房 由此造成用户重复登录.

参考:

官方文档

Nginx 负载均衡

Nginx负载均衡配置