Nginx 笔记


  前段时间做 Laravel 项目一直用 Apache 做 web 服务,没什么特别原因,一是就因为是系统自带,懒得折腾其他的,二是刚接触编程没多久,并不太了解其他的 web 服务器。近来开始接触到 Nginx 服务器,相对于 Apache 的稳定,Nginx 以性能见长。作为 Web 服务器:Nginx 使用更少的资源,处理请求是异步非阻塞的,支持更多的并发连接;作为反向代理服务器完成负载均衡: Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务, 也可以支持作为 HTTP 代理服务器对外进行服务。

1、Nginx 功能

Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP / POP3 / SMTP 服务器。

1.1、HTTP 基础功能:

  • 处理静态文件,索引文件以及自动索引;
  • 反向代理加速(无缓存),简单的负载均衡和容错;
  • FastCGI,简单的负载均衡和容错;
  • 模块化的结构。过滤器包括 gzipping, byte ranges, chunked responses, 以及 SSI-filter ,在 SSI 过滤器中,到同一个 proxy 或者 FastCGI 的多个子请求并发处理;
  • SSL 和 TLS SNI 支持。

1.2、IMAP/POP3 代理服务功能:

  • 使用外部 HTTP 认证服务器重定向用户到 IMAP/POP3 后端;
  • 使用外部 HTTP 认证服务器认证用户后连接重定向到内部的 SMTP 后端;
  • 认证方法:
  • POP3: POP3 USER/PASS, APOP, AUTH LOGIN PLAIN CRAM-MD5 ;
  • IMAP: IMAP LOGIN ;
  • SMTP: AUTH LOGIN PLAIN CRAM-MD5 ;
  • SSL 支持 ;
  • 在 IMAP 和 POP3 模式下的 STARTTLS 和 STLS 支持。

1.3、支持的操作系统:

  • FreeBSD 3.x, 4.x, 5.x, 6.x i386; FreeBSD 5.x, 6.x amd64 ;
  • Linux 2.2, 2.4, 2.6 i386; Linux 2.6 amd64 ;
  • Solaris 8 i386; Solaris 9 i386 and sun4u; Solaris 10 i386 ;
  • MacOS X (10.4) PPC。

1.4、结构与扩展:

一个主进程和多个工作进程。工作进程是单线程的,且不需要特殊授权即可运行;

  • kqueue (FreeBSD 4.1+), epoll (Linux 2.6+), rt signals (Linux 2.2.19+), /dev/poll (Solaris 7 11/99+), select, 以及 poll 支持;
  • kqueue支持的不同功能包括 EV_CLEAR, EV_DISABLE (临时禁止事件), NOTE_LOWAT, EV_EOF, 有效数据的数目,错误代码;
  • sendfile (FreeBSD 3.1+), sendfile (Linux 2.2+), sendfile64 (Linux 2.4.21+), 和 sendfilev (Solaris 8 7/01+) 支持;
  • 输入过滤 (FreeBSD 4.1+) 以及 TCP_DEFER_ACCEPT (Linux 2.4+) 支持;
  • 10,000 非活动的 HTTP keep-alive 连接仅需要 2.5M 内存;
  • 最小化的数据拷贝操作。

1.5、其他HTTP功能:

  • 基于IP 和名称的虚拟主机服务;
  • Memcached 的 GET 接口;
  • 支持 keep-alive 和管道连接;
  • 灵活简单的配置;
  • 重新配置和在线升级而无须中断客户的工作进程;
  • 可定制的访问日志,日志写入缓存,以及快捷的日志回卷;
  • 4xx-5xx 错误代码重定向;
  • 基于 PCRE 的 rewrite 重写模块;
  • 基于客户端 IP 地址和 HTTP 基本认证的访问控制;
  • PUT, DELETE, 和 MKCOL 方法;
  • 支持 FLV (Flash 视频);
  • 带宽限制。

2、反向代理

  正向代理服务器,只用于代理内部网络对 Internet 的连接请求,客户机必须指定代理服务器,并将本来要直接发送到 Web 服务器上的 http 请求发送到代理服务器中。当一个代理服务器能够代理外部网络上的主机,访问内部网络时,这种代理服务的方式称为反向代理服务。

2.1、正向代理

  通常来说,对于人来说可以感知到,但服务器感知不到的服务器,我们叫他正向代理服务器。比如访问 google.com 时我们需要 vpn 翻墙才能访问。vpn 对于客户来说,是可以感知到的(客户连接 vpn),vpn 对于google 服务器来说,是不可感知的(google 只知道有 http 请求过来)。

2.2、反向代理

  通常来说,对于人来说不可感知,但对于服务器来说是可以感知的,我们叫他反向代理服务器,通过反向代理实现负载均衡。比如访问 baidu.com 的时候,baidu 有一个代理服务器,通过这个代理服务器,可以做负载均衡,路由到不同的 server。客户感知不到反向代理serverserver1 server2 server3则可以感知到反向代理server

3、PHP-FPM

PHP-FPM(FastCGI Process Manager:FastCGI进程管理器)是一个PHPFastCGI管理器

3.1、cgi、fast-cgi 协议

  • cgi:为了解决不同的语言解释器(如 php、python 解释器)与 webserver 的通信,于是出现了 cgi 协议。只要你按照 cgi 协议去编写程序,就能实现语言解释器与 webwerver 的通信。如 php-cgi 程序。有了 cgi 协议,解决了 php 解释器与 webserver 通信的问题,webserver 终于可以处理动态语言了。但是,webserver每收到一个请求,都会去 fork 一个 cgi 进程,请求结束再 kill 掉这个进程。这样有 10000 个请求,就需要 fork、kill php-cgi 进程 10000 次。
  • fast-cgi:cgi 资源浪费严重,于是出现了 cgi 的改良版本,fast-cgi 。fast-cgi 每次处理完请求后,不会 kill 掉这个进程,而是保留这个进程,使这个进程可以一次处理多个请求。这样每次就不用重新 fork 一个进程了,大大提高了效率。

3.2、php-fpm 即 php-Fastcgi Process Manager

  • php-fpm 是 FastCGI 的实现,并提供了进程管理的功能。进程包含 master 进程和 worker 进程两种进程。
  • master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,
  • worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

4、Nginx 安装与配置

4.1、nginx 安装

brew install nginx

4.2、nginx 配置 nginx.conf

  默认位置/usr/local/etc/nginx/nginx.conf,作为初级配置 nginx.conf 不需要更改。配置文件末尾语句include servers/*,包含 servers 文件下的子配置文件,可以对不同的项目建立不同的子配置文件。

  • 例如 larave.conf ,访问 http://localhost/ 即可访问项目。
server {
listen 80;
server_name localhost;
root /对应项目目录;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

index index.html index.htm index.php;

charset utf-8;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }

#error_page 404 /index.php;

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_intercept_errors on;
include fastcgi_params;
include fastcgi.conf;
}

location ~ /\.(?!well-known).* {
deny all;
}

}
  • 再如 phpMyAdmin.conf ,host 文件添加 127.0.0.0 phpmyadmin,访问 http://phpmyadmin/ 即可访问项目。
server {
listen 80;
server_name phpmyadmin;
root /目录路径/phpMyAdmin;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

index index.php;

charset utf-8;

location / {
try_files $uri $uri/ /index.php?$query_string;
}

location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }

#error_page 404 /index.php;

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_intercept_errors on;
include fastcgi_params;
include fastcgi.conf;
}

location ~ /\.(?!well-known).* {
deny all;
}
}

4.3、php-fpm 配置 php-fpm.conf

  • 默认位置/private/etc/php-fpm.conf,默认没有配置文件,需要将 php-fpm.conf. 重命名为 php-fpm.conf
;去掉 pid 和 erro 的注释,修改文件位置到 local 目录,避免权限问题。
[global]
; Pid file
; Note: the default prefix is /usr/var
; Default Value: none
pid = /usr/local/var/run/php-fpm.pid

; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; into a local file.
; Note: the default prefix is /usr/var
; Default Value: log/php-fpm.log
error_log = /usr/local/var/run/php-fpm.log
  • /private/etc/php-fpm.d/www.conf.default 重命名为 www.conf

  至此配置完成,访问 http://localhost:8098 即可访问数据库。nginx 的优势是处理静态请求,cpu内存使用率低,可以反向代理隐藏主机 IP,apache 适合处理动态请求,所以现在一般前端用 nginx 作为反向代理抗住压力,apache 作为后端处理动态请求。

5、常用命令

nginx -s stop 
#快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit
#平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload
#因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen
#重新打开日志文件。
nginx -c filename
#为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t
#不运行,而仅仅测试配置文件。
#nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v
#显示 nginx 的版本。
nginx -V
#显示 nginx 的版本,编译器版本和配置参数。

6、http 反向代理配置

#运行用户
#user somebody;

#启动进程,通常设置成和cpu的数量相等
worker_processes 1;

#全局错误日志
error_log D:/Tools/nginx-1.10.1/logs/error.log;
error_log D:/Tools/nginx-1.10.1/logs/notice.log notice;
error_log D:/Tools/nginx-1.10.1/logs/info.log info;

#PID文件,记录当前启动的nginx的进程ID
pid D:/Tools/nginx-1.10.1/logs/nginx.pid;

#工作模式及连接数上限
events {
worker_connections 1024; #单个后台worker process进程的最大并发链接数
}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
#设定mime类型(邮件支持类型),类型由mime.types文件定义
include D:/Tools/nginx-1.10.1/conf/mime.types;
default_type application/octet-stream;

#设定日志
log_format main '[$remote_addr] - [$remote_user] [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log D:/Tools/nginx-1.10.1/logs/access.log main;
rewrite_log on;

#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,
#对于普通应用,必须设为 on,如果用来进行下载等应用磁盘IO重负载应用,
#可设置为 off,以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;

#连接超时时间
keepalive_timeout 120;
tcp_nodelay on;

#gzip压缩开关
#gzip on;

#设定实际的服务器列表
upstream zp_server1{
server 127.0.0.1:8089;
}

#HTTP服务器
server {
#监听80端口,80端口是知名端口号,用于HTTP协议
listen 80;

#定义使用www.xx.com访问
server_name www.helloworld.com;

#首页
index index.html

#指向webapp的目录
root D:\01_Workspace\Project\github\zp\SpringNotes\spring-security\spring-shiro\src\main\webapp;

#编码格式
charset utf-8;

#代理配置参数
proxy_connect_timeout 180;
proxy_send_timeout 180;
proxy_read_timeout 180;
proxy_set_header Host $host;
proxy_set_header X-Forwarder-For $remote_addr;

#反向代理的路径(和upstream绑定),location 后面设置映射的路径
location / {
proxy_pass http://zp_server1;
}

#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root D:\01_Workspace\Project\github\zp\SpringNotes\spring-security\spring-shiro\src\main\webapp\views;
#过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
expires 30d;
}

#设定查看Nginx状态的地址
location /NginxStatus {
stub_status on;
access_log on;
auth_basic "NginxStatus";
auth_basic_user_file conf/htpasswd;
}

#禁止访问 .htxxx 文件
location ~ /\.ht {
deny all;
}

#错误处理页面(可选择性配置)
#error_page 404 /404.html;
#error_page 500 502 503 504 /50x.html;
#location = /50x.html {
# root html;
#}
}
}

7、负载均衡配置

  假设这样一个应用场景:将应用部署在 192.168.1.11:80、192.168.1.12:80、192.168.1.13:80 三台 linux 环境的服务器上。网站域名叫 www.helloworld.com,公网 IP 为 192.168.1.11。在公网 IP 所在的服务器上部署 nginx,对所有请求做负载均衡处理。

http {
#设定mime类型,类型由mime.type文件定义
include /etc/nginx/mime.types;
default_type application/octet-stream;
#设定日志格式
access_log /var/log/nginx/access.log;

#设定负载均衡的服务器列表
upstream load_balance_server {
#weigth参数表示权值,权值越高被分配到的几率越大
server 192.168.1.11:80 weight=5;
server 192.168.1.12:80 weight=1;
server 192.168.1.13:80 weight=6;
}

#HTTP服务器
server {
#侦听80端口
listen 80;

#定义使用www.xx.com访问
server_name www.helloworld.com;

#对所有请求进行负载均衡请求
location / {
root /root;
#定义服务器的默认网站根目录位置
index index.html index.htm;
#定义首页索引文件的名称
proxy_pass http://load_balance_server ;
#请求转向load_balance_server 定义的服务器列表

#以下是一些反向代理的配置(可选择性配置)
#proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Forwarded-For $remote_addr;
proxy_connect_timeout 90;
#nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90;
#后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90;
#连接成功后,后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k;
#设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k;
#proxy_buffers缓冲区,网页平均在32k以下的话,这样设置
proxy_busy_buffers_size 64k;
#高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k;
#设定缓存文件夹大小,大于这个值,将从upstream服务器传

client_max_body_size 10m;
#允许客户端请求的最大单文件字节数
client_body_buffer_size 128k;
#缓冲区代理缓冲用户端请求的最大字节数
}
}
}

8、网站有多个 webapp 的配置

  举个例子:假如 www.helloworld.com 站点有好几个 webapp,finance(金融)、product(产品)、admin(用户中心)。访问这些应用的方式通过上下文(context)来进行区分:

  • www.helloworld.com/finance/
  • www.helloworld.com/product/
  • www.helloworld.com/admin/

  我们知道,http 的默认端口号是 80,如果在一台服务器上同时启动这 3 个 webapp 应用,都用 80 端口,肯定是不成的。所以,这三个应用需要分别绑定不同的端口号。那么,问题来了,用户在实际访问 www.helloworld.com 站点时,访问不同 webapp,总不会还带着对应的端口号去访问吧。所以,你再次需要用到反向代理来做处理。

http {
#此处省略一些基本配置

upstream product_server{
server www.helloworld.com:8081;
}

upstream admin_server{
server www.helloworld.com:8082;
}

upstream finance_server{
server www.helloworld.com:8083;
}

server {
#此处省略一些基本配置
#默认指向product的server
location / {
proxy_pass http://product_server;
}

location /product/{
proxy_pass http://product_server;
}

location /admin/ {
proxy_pass http://admin_server;
}

location /finance/ {
proxy_pass http://finance_server;
}
}
}

9、https 反向代理配置

  • HTTPS 的固定端口号是 443,不同于 HTTP 的 80 端口
  • SSL 标准需要引入安全证书,所以在 nginx.conf 中你需要指定证书和它对应的 key
#其他和 http 反向代理基本一样,只是在 Server 部分配置有些不同。
#HTTP服务器
server {
#监听443端口。443为知名端口号,主要用于HTTPS协议
listen 443 ssl;

#定义使用www.xx.com访问
server_name www.helloworld.com;

#ssl证书文件位置(常见证书文件格式为:crt/pem)
ssl_certificate cert.pem;
#ssl证书key位置
ssl_certificate_key cert.key;

#ssl配置参数(选择性配置)
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#数字签名,此处使用MD5
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
root /root;
index index.html index.htm;
}
}

10、静态站点配置

  如果所有的静态资源都放在了 /app/dist 目录下,我们只需要在 nginx.conf 中指定首页以及这个站点的 host 即可,配置如下:

worker_processes  1;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

gzip on;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png;
gzip_vary on;

server {
listen 80;
server_name static.zp.cn;

location / {
root /app/dist;
index index.html;
#转发任何请求到 index.html
}
}
}

11、搭建文件服务器

  • 将 autoindex 开启可以显示目录,默认不开启。
  • 将 autoindex_exact_size 开启可以显示文件的大小。
  • 将 autoindex_localtime 开启可以显示文件的修改时间。
  • root 用来设置开放为文件服务的根路径。
  • charset 设置为 charset utf-8,gbk;,可以避免中文乱码问题(windows 服务器下设置后,依然乱码,本人暂时没有找到解决方法)。
autoindex on;# 显示目录
autoindex_exact_size on;# 显示文件大小
autoindex_localtime on;# 显示文件时间

server {
charset utf-8,gbk; # windows 服务器下设置后,依然乱码,暂时无解
listen 9050 default_server;
listen [::]:9050 default_server;
server_name _;
root /share/fs;
}

12、跨域解决方案

  web 领域开发中,经常采用前后端分离模式。这种模式下,前端和后端分别是独立的 web 应用程序,例如:后端是 Java 程序,前端是 React 或 Vue 应用。各自独立的 web app 在互相访问时,势必存在跨域问题。解决跨域问题一般有两种思路:

  • CORS 在后端服务器设置 HTTP 响应头,把你需要运行访问的域名加入加入 Access-Control-Allow-Origin 中。
  • jsonp 把后端根据请求,构造 json 数据,并返回,前端用 jsonp 跨域。

  nginx 根据第一种思路,也提供了一种解决跨域的解决方案。举例:www.helloworld.com 网站是由一个前端 app ,一个后端 app 组成的。前端端口号为 9000, 后端端口号为 8080。前端和后端如果使用 http 进行交互时,请求会被拒绝,因为存在跨域问题。

  • 首先,在 enable-cors.conf 文件中设置 cors :
# allow origin list
set $ACAO '*';

# set single origin
if ($http_origin ~* (www.helloworld.com)$) {
set $ACAO $http_origin;
}

if ($cors = "trueget") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}

if ($request_method = 'OPTIONS') {
set $cors "${cors}options";
}

if ($request_method = 'GET') {
set $cors "${cors}get";
}

if ($request_method = 'POST') {
set $cors "${cors}post";
}
  • 接下来,在你的服务器中 include enable-cors.conf 来引入跨域配置:
# ----------------------------------------------------
# 此文件为项目 nginx 配置片段
# 可以直接在 nginx config 中 include(推荐)
# 或者 copy 到现有 nginx 中,自行配置
# www.helloworld.com 域名需配合 dns hosts 进行配置
# 其中,api 开启了 cors,需配合本目录下另一份配置文件
# ----------------------------------------------------
upstream front_server{
server www.helloworld.com:9000;
}
upstream api_server{
server www.helloworld.com:8080;
}

server {
listen 80;
server_name www.helloworld.com;

location ~ ^/api/ {
include enable-cors.conf;
proxy_pass http://api_server;
rewrite "^/api/(.*)$" /$1 break;
}

location ~ ^/ {
proxy_pass http://front_server;
}
}


下一篇 Apace 虚拟主机配置
上一篇 RESTful API
查看评论