Nginx 部分 (二) - 基础概念-基本结构、反向代理配置

基础概念

上一章单纯是为了跑起来,其中的配置是生产环境的配置,如果简单也是可以的,现在结构概念部分;

 

指令和上下文:

nginx的配置文件参照上文,/opt/nginx/conf/nginx.conf 

 

 

①指令 – 可选项,包含名称和参数,以分号结尾

gzip on;  //对响应数据进行Gzip压缩

 

上下文 – 分块,你可以声明指令 – 类似于编程语言中的作用域

worker_processes 2; # 全局上下文指令     
 
http {              # http 上下文
    gzip on;        # http 上下文中的指令
 
  server {          # server 上下文
    listen 80;      # server 上下文中的指令
  }
}

 

指令类型

 

 

 普通指令在每个上下文仅有唯一值。而且,它只能在当前上下文中定义一次。子级上下文可以覆盖父级中的值,并且这个

 

覆盖值只在当前的子级上下文中有效。

 

gzip on;
 gzip off; # 非法,不能在同一个上下文中指定同一普通指令2次
 
 server {
   location /downloads {
     gzip off;
   }
 
   location /assets {
     # gzip is on here
   }
 }

 


 数组指令

 

在同一上下文中添加多条指令,将添加多个值,而不是完全覆盖。在子级上下文中定义指令将覆盖给父级上下文中的值。

 

error_log /var/log/nginx/error.log;
error_log /var/log/nginx/error_notive.log notice;
error_log /var/log/nginx/error_debug.log debug;
 
server {
  location /downloads {
    # 下面的配置会覆盖父级上下文中的指令
    error_log /var/log/nginx/error_downloads.log;
  }
}

 

 行动指令

 

行动是改变事情的指令。根据模块的需要,它继承的行为可能会有所不同。

例如rewrite指令,只要是匹配的都会执行

 

server {
  rewrite ^ /foobar;
 
  location /foobar {
    rewrite ^ /foo;
    rewrite ^ /bar;
  }
}

 

如果用户想尝试获取 /sample

 

 

server的rewrite将会执行,从 /sample rewrite 到 /foobar
location /foobar 会被匹配
location的第一个rewrite执行,从/foobar rewrite到/foo
location的第二个rewrite执行,从/foo rewrite到/bar

 

 

return指令提供的是不同的行为:

 

 

 

server {
  location / {
    return 200;
    return 404;
  }
}

上面操作返回200

 

处理请求

在 Nginx 内部,你可以指定多个虚拟服务器,每个虚拟服务器用 server{} 上下文描述,但http只能有一个。

 

server {
  listen      *:80 default_server;
  server_name netguru.co;
 
  return 200 "Hello from netguru.co";
}
 
server {
  listen      *:80;
  server_name foo.co;
 
  return 200 "Hello from foo.co";
}
 
server {
  listen      *:81;
  server_name bar.co;
 
  return 200 "Hello from bar.co";
}

 

 这将告诉 Nginx 如何处理到来的请求。Nginx 将会首先通过检查 listen 指令来测试哪一个虚拟主机在监听给定的 IP 端口组合。然后,server_name指令的值将检测 Host 头(存储着主机域名)。

 

 

Nginx 将会按照下列顺序选择虚拟主机

 

  1. 匹配sever_name指令的IP-端口主机

  2. 拥有default_server标记的IP-端口主机

  3. 首先定义的IP-端口主机

  4. 如果没有匹配,拒绝连接。

 

 

 

 例如:

 

Request to foo.co:80     => "Hello from foo.co"
 Request to www.foo.co:80 => "Hello from netguru.co"
 Request to bar.co:80     => "Hello from netguru.co"
 Request to bar.co:81     => "Hello from bar.co"
 Request to foo.co:81     => "Hello from bar.co"

 

 

 

 

server_name 指令

 

server_name指令接受多个值。它还处理通配符匹配和正则表达式。

 

server_name netguru.co www.netguru.co; # exact match
  server_name *.netguru.co;              # wildcard matching
  server_name netguru.*;                 # wildcard matching
  server_name  ~^[0-9]*\.netguru\.co$;   # regexp matching

 

当有歧义时,nginx 将使用下面的命令:

 

 

  1. 确切的名字

  2. 最长的通配符名称以星号开始,例如“* .example.org”。

  3. 最长的通配符名称以星号结尾,例如“mail.**”

  4. 首先匹配正则表达式(按照配置文件中的顺序)

 

 

 

 

Nginx 会存储 3 个哈希表:①确切的名字,②以星号开始的通配符,和③以星号结尾的通配符。如果结果不在任何表中,则将按顺序进行正则表达式测试。

 值得谨记的是

 

server_name .netguru.co;


 是一个来自下面的缩写

server_name  netguru.co  www.netguru.co  *.netguru.co;

 

 有一点不同,.netguru.co 存储在第二张表,这意味着它比显式声明的慢一点

 

 

 listen 指令

 在很多情况下,能够找到 listen 指令,接受IP:端口值

 

listen 127.0.0.1:80;
listen 127.0.0.1;    # by default port :80 is used
 
listen *:81;
listen 81;           # by default all ips are used
 
listen [::]:80;      # IPv6 addresses
listen [::1];        # IPv6 addresses

 

 然而,还可以指定 UNIX-domain 套接字。

 

 

listen unix:/var/run/nginx.sock;

 

 

 

 

最小化配置:


 

 

# /opt/nginx/nginx.conf
 
events {}                   # events context needs to be defined to consider config valid
 
http {
 server {
    listen 80;
    server_name  netguru.co  www.netguru.co  *.netguru.co;
 
    return 200 "Hello";
  }
}


页面访问后显示: Hello

 

 

root, location, 和 try_files 指令

root 指令

root 指令设置请求的根目录,允许 nginx 将传入请求映射到文件系统。

 

server {
  listen 80;
  server_name netguru.co;
  root /var/www/netguru.co;
}

根据给定的请求,指定 nginx 服务器允许的内容

netguru.co:80/index.html     # returns /var/www/netguru.co/index.html
netguru.co:80/foo/index.html # returns /var/www/netguru.co/foo/index.html

 

 

 

location 指令

      location指令根据请求的 URI 来设置配置。

 

location [modifier] path

location /foo/ {
  # ...
}

如果没有指定修饰符,则路径被视为前缀,其后可以跟随任何东西。

 

 

以上例子将匹配

/foo
/fooo
/foo123
/foo/bar/index.html
...


此外,在给定的上下文中可以使用多个 location 指令

server {
  listen 80;
  server_name netguru.co;
  root /var/www/netguru.co;
 
  location / {
    return 200 "root";
  }
 
  location /foo/ {
    return 200 "foo";
  }
}

netguru.co:80   /       # => "root"
netguru.co:80   /foo    # => "foo"
netguru.co:80   /foo123 # => "foo"
netguru.co:80   /bar    # => "root"


Nginx 也提供了一些修饰符,可用于连接 location。这些修饰符将影响 location 模块使用的地方,因为每个修饰符都分配了优先级(上至下)。

 

=         	 - Exact match  准确匹配 		①
^~         	 - Preferential match  优先匹配		②
~ && ~*    	 - Regex match  正则匹配		③
no modifier	 - Prefix match	前缀匹配		④

 

 

(Nginx 会先检查精确匹配。如果找不到,我们会找优先级最高的。如果这个匹配依然失败,正则表达式匹配将按照出现的顺序进行测试。至少,最后一个前缀匹配将被使用.)

 

 

 

 

location /match {
  return 200 'Prefix match: matches everything that starting with /match';
}
 
location ~* /match[0-9] {
  return 200 'Case insensitive regex match';
}
 
location ~ /MATCH[0-9] {
  return 200 'Case sensitive regex match';
}
 
location ^~ /match0 {
  return 200 'Preferential match';
}
 
location = /match {
  return 200 'Exact match';
}

/match/    # => 'Exact match'
/match0    # => 'Preferential match'
/match1    # => 'Case insensitive regex match'
/MATCH1    # => 'Case sensitive regex match'
/match-abc # => 'Prefix match: matches everything that starting with /match'

 

try_files 指令

尝试不同的路径,找到一个路径就返回。

 

try_files $uri index.html =404;

所以对于 /foo.html 请求,它将尝试按以下顺序返回文件:

 

$uri ( /foo.html )
index.html
如果什么都没找到则返回 404

 

有趣的是,如果我们在服务器上下文中定义 try_files,然后定义匹配的所有请求的 location —— try_files 将不会执行

这是因为在服务器上下文中定义的 try_files 是它的 pseudo-location(假地址),这是最不可能的位置。因此,定义 location/ 将比 pseudo-location 更具体。

 

server {
  try_files $uri /index.html =404;
 
  location / {
  }
}


因此,应该避免在server 上下文中出现 try_files:

 

server {
  location / {
    try_files $uri /index.html =404;
  }
}

 

 

 

 

 

 

 

tcp_nodelay, tcp_nopush 和 sendfile

 

tcp_nodelay (为了尽可能快地推送数据包)

  起初用于针对Tcp流量冲突和堵塞的问题,Nagle 的算法为了防止通讯被大量的小包淹没,然后采取了限制,只针对比MSS(最大报文长度)小的包,只有当接收方成功将ACK返回,才能进行下次发送,等待期间,发送方可以在此期间缓冲更多的数据之后再进行发送。

在 TCP 通讯中,在发送数据后,需要接收回应包(ACK)来确认数据被成功传达(延时 ACK),延时 ACK 旨在解决线路被大量的 ACK 包拥堵的状况。为了减少 ACK 包的

数量,接收者等待需要回传的数据加上 ACK 包回传给发送方,如果没有数据需要回传,必须在至少每 2 个 MSS,或每 200 至 500 毫秒内发送 ACK(以防我们不再收到包)。

(在这个数据交换过程中,由于 Nagel 和延迟 ACK 之间的死锁,因此引入了 200ms 的延迟)

 

在大多数情况下,我们不会在我们的网站上使用它,因此可以通过添加 TCP_NODELAY 标志来安全地关闭它。(TCP中就会获得200ms的提速)
tcp_nodely on

 

 

 

tcp_nopush(一次性优化数据的发送量)

在发送给客户端之前,它将强制等待包达到最大长度(MSS)。而且这个指令只有在 sendfile 开启时才起作用。

sendfile on;
tcp_nopush on;

看起来 tcp_nopush 和 tcp_nodelay 是互斥的。但是,如果所有 3 个指令都开启了,nginx 会:

 

 

1、确保数据包在发送给客户之前是已满的
2、对于最后一个数据包,tcp_nopush 将被删除 —— 允许 TCP 立即发送,没有 200ms 的延迟

 


sendfile

 

 

 

 

 

正常来说,当要发送一个文件时需要下面的步骤:

 

malloc(3) – 分配一个本地缓冲区,储存对象数据。
read(2) – 检索和复制对象到本地缓冲区。
write(2) – 从本地缓冲区复制对象到 socket 缓冲区。

 

这涉及到两个上下文切换(读,写),并使相同对象的第二个副本成为不必要的。正如所看到的,这不是最佳的方式。值得庆幸的是还有另一个系统调用,提升了发送文件(的效率),它被称为:sendfile(2)(想不到吧!居然是这名字)。这个调用在文件 cache 中检索一个对象,并传递指针(不需要复制整个对象),直接传递到 socket 描述符

 

sendfile(2) 有一些注意事项:

 

1、不可用于 UNIX sockets(例如:当通过你的上游服务器发送静态文件时)
2、能否执行不同的操作,取决于操作系统

 

#打开方式:
sendfile on;

 

 sendfile_max_chunk 512k; # 较大的文件不要一次全读取了,浪费内存,意思是超过512k的文件不用缓存

 

 

 

 

 

反向代理配置:

 

server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
          proxy_redirect              off;
          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 /{
            root   html;
            index  index.html index.htm;
            proxy_pass  http://baidu.com; //指定请求转发到
    	    proxy_connect_timeout 600;//设置连接超时
	    proxy_read_timeout 600;//设置读响应超时

        }	    proxy_connect_timeout 600;//设置连接超时
	    proxy_read_timeout 600;//设置读响应超时

        }

 

 

 

 

 

 

1、proxy_set_header Host       $host;

$host 时它的值在请求包含“Host”请求头时为“Host”字段的值,在请求未携带“Host”请求头时为虚拟主机的主域名:

Host参阅地址

 

2、proxy_set_header    X-real-ip $remote_addr;

当你使用了nginx反向服务器后,在web端使用request.getRemoteAddr()(本质上就是获取$remote_addr),取得的是nginx的地址,即$remote_addr变量中封装的是nginx的地址,当然是没法获得用户的真实ip的,但是,nginx是可以获得用户的真实ip的,也就是说nginx使用$remote_addr变量时获得的是用户的真实ip,如果我们想要在web端获得用户的真实ip,就必须在nginx这里作一个赋值操作,如下:
proxy_set_header            X-real-ip $remote_addr;

其中这个X-real-ip是一个自定义的变量名,名字可以随意取,这样做完之后,用户的真实ip就被放在X-real-ip这个变量里了,然后,在web端可以这样获取:
request.getAttribute("X-real-ip")
 

3、proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;

 我们先看看这里有个X-Forwarded-For变量,这是一个squid开发的,用于识别通过HTTP代理或负载平衡器原始IP一个连接到Web服务器的客户机地址的非rfc标准,如果有做X-Forwarded-For设置的话,每次经过proxy转发都会有记录,格式就是client1, proxy1, proxy2,以逗号隔开各个地址,由于他是非rfc标准,所以默认是没有的,需要强制添加,在默认情况下经过proxy转发的请求,在后端看来远程地址都是proxy端的ip 。也就是说在默认情况下我们使用request.getAttribute("X-Forwarded-For")获取不到用户的ip,如果我们想要通过这个变量获得用户的ip,我们需要自己在nginx添加如下配置:

proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;

意思是增加一个$proxy_add_x_forwarded_for到X-Forwarded-For里去,注意是增加,而不是覆盖,当然由于默认的X-Forwarded-For值是空的,所以我们总感觉X-Forwarded-For的值就等于$proxy_add_x_forwarded_for的值,实际上当你搭建两台nginx在不同的ip上,并且都使用了这段配置,那你会发现在web服务器端通过request.getAttribute("X-Forwarded-For")获得的将会是客户端ip和第一台nginx的ip。

那么$proxy_add_x_forwarded_for又是什么?

  $proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr两部分,他们之间用逗号分开。

举个例子,有一个web应用,在它之前通过了两个nginx转发,www.linuxidc.com 即用户访问该web通过两台nginx。
在第一台nginx中,使用

proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;

现在的$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,所以只有$remote_addr,而$remote_addr的值是用户的ip,于是赋值以赋值以后,

X-Forwarded-For变量的值就是用户的真实的ip地址了。

到了第二台nginx,使用

 

proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;

现在的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含的是用户的真实ip,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值

以后,现在的X-Forwarded-For的值就变成了“用户的真实ip,第一台nginx的ip”,这样就清楚了吧。

 

$http_x_forwarded_for变量

这个变量就是X-Forwarded-For,由于之前我们说了,默认的这个X-Forwarded-For是为空的,所以当我们直接使用proxy_set_header    X-Forwarded-For $http_x_forwarded_for

 

时会发现,web服务器端使用request.getAttribute("X-Forwarded-For")获得的值是null。如果想要通过request.getAttribute("X-Forwarded-For")获得用户ip,就必须先使用

proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for ;这样就可以获得用户真实ip。

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Docker Compose 中安装配置 Nginx 来实现 HTTPS 和反向代理,你可以按照以下步骤进行操作: 1. 首先,在你的项目目录下创建一个名为 `docker-compose.yml` 的文件,并添加以下内容: ```yaml version: '3' services: nginx: image: nginx ports: - 80:80 - 443:443 volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./certs:/etc/nginx/certs ``` 这将创建一个名为 `nginx` 的服务,并将容器的 80 和 443 端口映射到主机的相应端口。同,我们将挂载一个自定义的 `nginx.conf` 配置文件和一个名为 `certs` 的文件夹用于存放 SSL/TLS 证书。 2. 在项目目录下创建一个名为 `nginx.conf` 的配置文件,并添加以下内容作为示例: ```nginx events {} http { server { listen 80; server_name example.com; location / { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/certs/cert.pem; ssl_certificate_key /etc/nginx/certs/key.pem; location / { proxy_pass http://backend:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } } ``` 这是一个简单的 Nginx 配置文件示例,其中设置了一个 HTTP 的反向代理和一个 HTTPS 的反向代理。你需要将 `example.com` 替换为你的域名,并将 SSL/TLS 证书文件 `cert.pem` 和 `key.pem` 放入 `certs` 文件夹中。 3. 在你的项目目录下创建一个名为 `certs` 的文件夹,并将正确的 SSL/TLS 证书文件放入其中。 4. 现在,你可以运行 `docker-compose up -d` 命令启动容器。 这样,Nginx 将通过反向代理将所有对于 `example.com` 的请求转发到名为 `backend` 的服务的 8000 端口上。同,通过 HTTPS 访问Nginx 使用了提供的 SSL/TLS 证书来进行加密通信。 请注意,上述步骤只是一个示例,你可能需要根据你的实际需求进行修改和调整。希望这能帮到你!如果有任何进一步的问题,请随提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值