入门Nginx

一、正向代理和反向代理

正向代理举例:翻越万里长城去游览墙外的景色
反向代理举例:负载均衡

正向代理和反向代理涉及三个主体:

  • 请求方
  • 代理
  • 被请求方

正向代理中,代理跟请求方是一家子,请求方说要啥,代理就给他啥。
反向代理中,代理跟被请求方是一家子,代理统筹规划让哪一个被请求方来处理请求,对于请求方来说,代理就是处理请求的人。大多数情况下,反向代理和被请求方在同一个服务器上。Nginx就是最常用的反向代理服务器。

这里也提一下:动态代理和静态代理
正向代理和反向代理是代理服务器的两种类型
动态代理和静态代理是Java中的设计模式:代理模式。
Spring的两大核心:

  • IOC控制反转依赖注入
  • AOP面向切面编程

面向切面编程中大量使用动态代理,在每一个方法调用前、调用后、抛异常时进行处理,跟装饰器模式很像。

二、nginx配置体系

nginx主要配置位于/etc/nginx目录下,nginx不仅仅可以用于负载均衡HTTP请求,也可以用于基于TCP的其它协议的负载均衡。/etc/nginx/nginx.conf是nginx的跟配置,一切配置都是这个配置的子孙。

/etc/nginx/nginx.conf

users www-data;定义当前用户
worker_prosesses 4;定义worker数
pid /run/nginx.pid;定义pid文件

events{......}
http{...
    http协议的相关配置
    include xxx路径下的conf.d/*.conf;  可以使用文件通配符来描述文件,但是一定是文件而非文件夹
}
mail{
    ....mail相关的一些配置
    server{
        listen localhost:xx;
        protocol pop3;
        proxy on;
    }
    server{
        listen localhost:xxxx;
        protocol imap;
        proxy on;
    }
}

可以发现,最外层是协议,协议内部有若干个server组成,每个server监听一个端口,根据路径可以转发到本地的其它端口进行处理。include是简单的复制粘贴。

三、用Nginx反向代理Tomcat服务器和Gunicorn服务器

需求说明:Tomcat和Gunicorn分别运行在8080端口和8000端口,现在要让它们共用80端口。
当使用:localhost/tomcat/myapp时,相当于localhost:8080/myapp
当使用:localhost/gunicorn/myapp时,相当于localhost:8000/myapp

server {
	listen	80;
	server_name	www.haha.com;
	access_log	/var/log/nginx/reverse.log;
	location /tomcat {
		proxy_pass	http://127.0.0.1:8080/;
		proxy_set_header Host $host;  
        proxy_set_header X-Real-IP $remote_addr;  
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
	}
	location /gunicorn {
		proxy_pass	http://127.0.0.1:8000/;
		proxy_set_header Host $host;  
        proxy_set_header X-Real-IP $remote_addr;  
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
	}
}

server_name描述了服务器地址部分,既可以是域名,也可以是IP地址。这一项不可省略。
设置proxy_set_header部分是将请求者的真实信息告知后台服务器

需要注意的是:

  • 配置nginx时,每一句后面都带有一个分号,不带分号会出错,这是conf文件的语法;
  • 配置proxy_pass时,只能精确到端口号,不能转发到再下一级,比如proxy_pass http://127.0.0.1:8000/myweb/ 这样是错误的;
  • 配置proxy_pass时,端口号后面必须加上反斜杠/,否则路径拼接时会因为少一个反斜杠而无法解析,导致404错误。

四、了解负载均衡

负载均衡必然要用到反向代理,让Nginx作为大管家凌驾于处理请求的服务器之上,Nginx可以动态决定让哪个服务器来处理请求。负载均衡问题源远流长:两台相同的处理机处理若干任务,怎么处理能够使得任务尽早完成?这是“双塔”问题;两台不同的处理机处理若干任务,不同处理机处理不同任务花费时间不同,怎样分配任务才能使得任务尽早完成?

负载均衡的核心问题就是:建立用户请求和服务器之间的映射。有如下几种方式:

  • 轮询
    server=counter++%server_size
    用于后台服务器性能差不多的情况
  • 轮盘赌
    每个服务器有一个权重weight,表示选中这个服务器作为请求处理者的概率。
    用于后台服务器性能有差异的情况
  • IP-Hash
    建立用户IP地址和后台服务器之间的映射表。
    用于解决Session问题,避免了Session共享带来的IO压力。
  • URL-Hash
    建立用户请求URL和后台服务器之间的映射表。
    用于解决服务器缓存分布分散,让某个服务器专门负责某类请求,这样能够更专业地处理请求。
    这种方式其实就相当于新建了一个Web程序,这时Nginx的作用更像是正向代理而不是反向代理。
  • Fair:按照响应时间
    Nginx知道每个后端服务器处理请求的时间,谁处理时间短就让谁来处理请求。
    在轮盘赌方法中,需要明确知道各个后台服务器的性能好坏。使用Fair方法可以自动检测出后台服务器性能好坏,从而动态分配。

轮盘赌的方式进行如下配置,首先定义一个upstream,它是多个server的集合。然后在location中就可以直接使用这个upstream。

upstream your_host_or_ip {     
    server 127.0.0.3:8000 weight=5;     
    server 127.0.0.3:8001 weight=5;     
    server 192.168.0.1:8000;     
    server 192.168.0.1:8001;     
}     

server {     
    listen          80;     
    server_name     your_host_or_ip;     
    access_log      /var/log/nginx/my_access.log main;     

    location / {     
            proxy_pass      http://your_host_or_ip;     
    }     
}     

需要注意的是,一台服务器有两种访问方式:ip地址和域名方式。ip地址方式肯定只能有一种方式进行访问,因为ip地址是全网唯一的。而一台服务器可以绑定多个域名,这样就可以通过多个域名访问同一个服务器了。
当配置了upstream之后,upstream的名称肯定跟服务器的名称(IP或者域名)相同,所以一个域名只能进行一个负载均衡,一个IP也只能进行一个负载均衡,而一台服务器可以进行多个负载均衡。

五、地址匹配

location配置:
直接写一个字符串,常规字符串匹配
~ 表示执行一个正则匹配,区分大小写
~* 表示执行一个正则匹配,不区分大小写
^~ 表示普通字符匹配。使用前缀匹配。如果匹配成功,则不再匹配其他location。
= 进行普通字符精确匹配。也就是完全匹配。

location配置的优先级:
在Nginx的location和配置中location的顺序没有太大关系。正location表达式的类型有关。相同类型的表达式,字符串长的会优先匹配。
以下是按优先级排列说明:
第一优先级:等号类型(=)的优先级最高。一旦匹配成功,则不再查找其他匹配项。
第二优先级:^~类型表达式。一旦匹配成功,则不再查找其他匹配项。
第三优先级:正则表达式类型(~ ~*)的优先级次之。如果有多个location的正则能匹配的话,则使用正则表达式最长的那个。
第四优先级:常规字符串匹配类型。按前缀匹配。

下面示例一下四种地址类型:

location = / {
    # 仅仅匹配请求 /
    [ configuration A ]
}
location / {
    # 匹配所有以 / 开头的请求。但是如果有更长的同类型的表达式,则选择更长的表达式。如果有正则表达式可以匹配,则优先匹配正则表达式。
    [ configuration B ]
}
location /documents/ {
    # 匹配所有以 /documents/ 开头的请求。但是如果有更长的同类型的表达式,则选择更长的表达式。
    #如果有正则表达式可以匹配,则优先匹配正则表达式。
    [ configuration C ]
}
location ^~ /images/ {
    # 匹配所有以 /images/ 开头的表达式,如果匹配成功,则停止匹配查找。所以,即便有符合的正则表达式location,也
    # 不会被使用
    [ configuration D ]
}
location ~* .(gif|jpg|jpeg)$ {
    # 匹配所有以 gif jpg jpeg结尾的请求。但是 以 /images/开头的请求,将使用 Configuration D
    [ configuration E ]
}

请求匹配实例:

/ -> configuration A
/index.html -> configuration B
/documents/document.html -> configuration C
/images/1.gif -> configuration D
/documents/1.jpg -> configuration E

应用举例:隐藏内在的地址

server {
    # 用 xxoo_admin 来掩饰 admin
    location / {
        # 使用break拿一旦匹配成功则忽略后续location
        rewrite /xxoo_admin /admin break;
    }

    # 访问真实地址直接报没权限
    location /admin {
        return 403;
    }
}

六、重写

Nginx的反向代理像一个URL函数,输入是一个URL,输出也是一个URL。运行过程可能是这样的:

用户发起请求url0
url1=f(url0)
url2=f(url1)
...直到url不再被重写,被路由到合适的请求处理结点

语法

在配置文件的server块中写,如:

server {
    rewrite 规则 定向路径 重写类型;
}
  • 规则:可以是字符串或者正则来表示想匹配的目标url
  • 定向路径:表示匹配到规则后要定向的路径,如果规则里有正则,则可以使用$index来表示正则里的捕获分组
  • 重写类型:
  • last :浏览器地址栏URL地址不变,本条规则完成后,会对重写后的URL进行重新匹配。
  • break:浏览器地址栏URL地址不变。本条规则匹配完成后,终止匹配,不再匹配后面的规则,
  • redirect:返回302临时重定向,浏览器地址会显示跳转后的URL地址
  • permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址

需要注意:

  • 重写表达式只对相对路径有效。如果想配对主机名,应该使用if语句。
  • rewrite只是会改写路径部分的东东,不会改动用户的输入参数,因此这里的if规则里面,你无需关心用户在浏览器里输入的参数,rewrite后会自动添加的

下面举例说明4种重写类型

server {
    # 访问 /last.html 的时候,页面内容重写到 /index.html 中
    rewrite /last.html /index.html last;

    # 访问 /break.html 的时候,页面内容重写到 /index.html 中,并停止后续的匹配
    rewrite /break.html /index.html break;

    # 访问 /redirect.html 的时候,页面直接302定向到 /index.html中
    rewrite /redirect.html /index.html redirect;

    # 访问 /permanent.html 的时候,页面直接301定向到 /index.html中
    rewrite /permanent.html /index.html permanent;

    # 把 /html/*.html => /post/*.html ,301定向
    rewrite ^/html/(.+?).html$ /post/$1.html permanent;

    # 把 /search/key => /search.html?keyword=key
    rewrite ^/search/([^/]+?)(/|$) /search.html?keyword=$1 permanent;
}

七、Nginx内置的全局变量

在/etc/nginx/fastcgi.conf中可以看到全部定义。

$args :这个变量等于请求行中的参数,同$query_string
$content_length : 请求头中的Content-length字段。
$content_type : 请求头中的Content-Type字段。
$document_root : 当前请求在root指令中指定的值。
$host : 请求主机头字段,否则为服务器名称。
$http_user_agent : 客户端agent信息
$http_cookie : 客户端cookie信息
$limit_rate : 这个变量可以限制连接速率。
$request_method : 客户端请求的动作,通常为GET或POST。
$remote_addr : 客户端的IP地址。
$remote_port : 客户端的端口。
$remote_user : 已经经过Auth Basic Module验证的用户名。
$request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成。
$scheme : HTTP方法(如http,https)。
$server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
$server_name : 服务器名称。
$server_port : 请求到达服务器的端口号。
$request_uri : 包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
$uri : 不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。
$document_uri : 与$uri相同。

如:

访问链接是:http://localhost:88/test1/test2/test.php
网站路径是:/var/www/html

$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php

八、使用if语句

if (表达式) {
}

表达式的写法有很多:

  • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
  • 直接比较变量和内容时,使用=或!=
  • 正则表达式匹配,`~*`不区分大小写的匹配,!区分大小写的不匹配

表达式中可以使用shell常用运算符:

  • -f和!-f用来判断是否存在文件
  • -d和!-d用来判断是否存在目录
  • -e和!-e用来判断是否存在文件或目录
  • -x和!-x用来判断文件是否可执行

下面看几个if语句的例子:
例子一:简单的if语句

# 如果文件不存在则返回400
if (!-f $request_filename) {
    return 400;
}

# 如果host不是xuexb.com,则301到xuexb.com中
if ( $host != 'xuexb.com' ){
    rewrite ^/(.*)$ https://xuexb.com/$1 permanent;
}

# 如果请求类型不是POST则返回405
if ($request_method = POST) {
    return 405;
}

# 如果参数中有 a=1 则301到指定域名
if ($args ~ a=1) {
    rewrite ^ http://example.com/ permanent;
}

例子二:在重写中使用if语句

# 访问 /test.html 时
location = /test.html {
    # 默认值为xiaowu
    set $name xiaowu;

    # 如果参数中有 name=xx 则使用该值
    if ($args ~* name=(w+?)(&|$)) {
        set $name $1;
    }

    # 301
    rewrite ^ /$name.html permanent;
}

举例说明运行结果:

/test.html => /xiaowu.html
/test.html?name=ok => /ok.html?name=ok

九、常用命令

service nginx configtest 用来检测配置是否正确
service nginx reload 重新加载配置

使用sudo service nginx命令可以查看可用参数.

十、root和alias

root

location /request_path/image/ {
    root /local_path/image/;
}

这样配置的结果就是当客户端请求 /request_path/image/cat.png 的时候,
Nginx把请求映射为/local_path/image/request_path/image/cat.png

alias

location /request_path/image/ {
    alias /local_path/image/;
}

这时候,当客户端请求 /request_path/image/cat.png 的时候,
Nginx把请求映射为/local_path/image/cat.png

下面举一个例子,当访问myip/poem/authorImage/libai.jpg的时候会自动去文件夹/home/USER_NAME/authorImage/下去寻找libai.jpg。

location /poem/authorImage/ {
    alias /home/USER_NAME/authorImage/;
    autoindex on;
}

需要注意的是,必须保证authorImage这个文件夹权限足够,否则Nginx没有权力访问这个文件夹。最简单但是不够安全的方式就是把/etc/nginx.conf文件开头的user www-data改为user root,这样把管理员权限赋予Nginx,Nginx就能够访问任意文件了。这样做的缺点就是,一旦Nginx被攻破,整个系统的文件资源可能都会受到威胁。

十一、例子集合

1、访问http://IP:端口/myf_test/in_ftp/ftp/space_1/111.jpg

实际访问/var/ftp/in_ftp/ftp/space_1/111.jpg

location /myf_test/ {  
           root /;  
           rewrite ^/myf_test/(.*)$ /var/ftp/$1 break;  
        } 

2、把www.ikscher.com/index.PHP?route=product/product&product_id=123 重定向到

www.ikscher.com/product/product&product_id=123

if ($request_uri ~* "(.*)index.php?route=(.*)"){  
    set $host_ $1;  
    set $last_ $2;  
    rewrite (.*)  $host_$last_? permanent; #这里的.*代表的是url的原先地址,即要转向的url地址。  
}  

注意:

  • 这段规则直接下到server里面,if后面必须有空格,否则报语法错误。
    正则表达式的 点和问号都需要斜杠转义。

  • nginx的问号处理,假如现在我要重定向到www.ikscher.com/?route=product/product&product_id=123,nginx在进行rewrite的正则表达式中只会将url中?前面的部分拿出来匹配,匹配完成后,?后面的内容将自动追加到url中(包含?),如果不让后面的内容追加上去,请在最后加上?即可。如果想要?后面的内容时请使用$query_string

  • 在这里提醒一点,调试的时候在rewrite的最后一个配置项中不要使用break last这些,使用redirect可以看到转换后的地址。综合以上几点,使用的配置项为

    rewrite (.)index.php(.) $1$query_string? permanent;

3、多目录转成参数

要求:abc.domian.com/sort/2 => abc.domian.com/index.PHP?act=sort&name=abc&id=2

规则配置:

if ($host ~* (.*).domain.com) { 
    set $sub_name $1;
    rewrite ^/sort/(d+)/?$ /index.php?act=sort&cid=$sub_name&id=$1 last; 
} 

4、目录对换

要求:/123456/xxxx -> /xxxx?id=123456
规则配置:rewrite ^/(d+)/(.+)/ /$2?id=$1 last;

5、特殊浏览器特殊处理

设定nginx在用户使用ie的使用重定向到/nginx-ie目录

规则如下:

if ($http_user_agent ~ MSIE) {
     rewrite ^(.*)$ /nginx-ie/$1 break; 
} 

6、请求目录自动加/

目录自动加“/” ,这个功能一般浏览器自动完成

if (-d $request_filename){ 
	rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent; 
} 

7、禁止某些URL

禁止多个目录 
location ~ ^/(cron|templates)/ { 
    deny all; break; 
} 
禁止以/data开头的文件,可以禁止/data/下多级目录下.log.txt等请求
location ~ ^/data { 
    deny all; 
} 

禁止单个文件 
location ~ /data/sql/data.sql { 
    deny all; 
} 

8、让浏览器缓存静态数据,避免频繁请求静态数据

给favicon.ico和robots.txt设置过期时间; 这里为favicon.ico为99天,robots.txt为7天并不记录404错误日志

location ~(favicon.ico) { 
    log_not_found off; 
    expires 99d; 
    break; 
} 
location ~(robots.txt) { 
    log_not_found off; 
    expires 7d; 
    break; 
} 

设定某个文件的浏览器缓存过期时间;这里为600秒,并不记录访问日志

location ^~ /html/scripts/loadhead_1.js { 
    access_log off; 
    expires 600; 
    break; 
} 

Nginx还可以自定义某一类型的文件的保质期时间,具体写法看下文的代码:

location ~* .(js|css|jpg|jpeg|gif|png|swf)$ {
	if (-f $request_filename) {
	   expires    1h;
	   break;
	  }
}
//上段代码就将js|css|jpg|jpeg|gif|png|swf这类文件的保质期设置为一小时。

9、防盗链的设置

防盗链:如果你的网站是个下载网站,下载步骤应该是先经过你的主页找到下载地址,才能下载,为了防止某些网友直接访问下载地址完全不通过主页下载,我们就可以使用防盗链的方式,具体代码如下:

location ~* .(gif|jpg|swf)$ {
  valid_referers none blocked start.igrow.cn sta.igrow.cn;
  if ($invalid_referer) {
  rewrite ^/ http://$host/logo.png;
  }
}

文件反盗链并设置过期时间

--<盗链多次请求也会打开你的站点的图片啊,所以设置下缓存时间,不会每次盗链都请求并下载这张图片>
location ~* ^.+.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ { 

    valid_referers none blocked *.jjonline.cn *.jjonline.com.cn *.lanwei.org *.jjonline.org localhost  42.121.107.189; 
    if ($invalid_referer) { 
        rewrite ^/ http://img.jjonline.cn/forbid.gif; 
        return 417; 
        break; 
    } 
    access_log off; 
    break; 
} 

说明:
这里的return 417为自定义的http状态码,默认为403,方便通过nginx的log文件找出正确的盗链的请求地址
rewrite ^/ http://img.jjonline.cn/forbid.gif;显示一张防盗链图片
“access_log off;”不记录访问日志,减轻压力
“expires 3d”所有文件3天的浏览器缓存

10、只允许固定ip访问网站,并加上密码;这个对有权限认证的应用比较在行

location  { 
    allow 22.27.164.25; #允许的ipd
    deny all; 
    auth_basic “KEY”; #认证的一些设置
    auth_basic_user_file htpasswd; 
}

11、文件和目录不存在的时重定向

if (!-e $request_filename) { 
    #proxy_pass http://127.0.0.1; #这里是跳转到代理ip,这个代理ip上有一个监听的web服务器
    rewrite ^/ http://www.jjonline.cn/none.html;  #跳转到这个网页去
    #return 404; #直接返回404码,然后会寻找root指定的404.html文件
} 

12、域名跳转

域名跳转

server { 
    listen 80; 
    server_name jump.jjonline.cn ;#需要跳转的多级域名
    index index.html index.htm index.php; #入口索引文件的名字
    root /var/www/public_html/; #这个站点的根目录
    rewrite ^/ http://www.jjonline.cn/; 
    #rewrite到这个地址,功能表现:在浏览器上输入jump.jjonline.cn并回车,不会有任何提示直接变成www.jjonline.cn
    access_log off; 
} 

多域名转向

server { 
    listen 80; 
    server_name www.jjonline.cn www.jjonline.org;
    index index.html index.htm index.php; 
    root /var/www/public_html/; 
    if ($host ~ “jjonline.org”) { 
        rewrite ^(.*) http://www.jjonline.cn$1 permanent; 
    } 
}

三级域名跳转

if ($http_host ~* “^(.*).i.jjonline.cn$”) { 
    rewrite ^(.*) http://demo.jjonline.cn$1; 
    break; 
} 

13、域名镜像

server { 
    listen 80; 
    server_name mirror.jjonline.cn; 
    index index.html index.htm index.php; 
    root /var/www/public_html; 
    rewrite ^/(.*) http://www.jjonline.cn/$1 last; 
    access_log off; 
} 

十二、Nginx的其他功能

  • 压缩
    使用gzip压缩的方式来传递HTML文件能够减少传输量。
  • 缓存
    对于常用的请求,可以设置缓存时间。

最后、参考资料

java静态代理和动态代理
Nginx location在配置中的优先级
nginx配置url重写
nginx 重写 rewrite 基础及实例

原文地址:https://www.cnblogs.com/weiyinfu/p/6857425.html