前段时间做 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。客户
感知不到反向代理server
,server1 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; } 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; } 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 -s quit nginx -s reload nginx -s reopen nginx -c filename nginx -t nginx -v nginx -V
6、http 反向代理配置 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 D:/Tools/nginx-1.10.1/logs/nginx.pid; events { worker_connections 1024; } http { 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 on; keepalive_timeout 120; tcp_nodelay on; upstream zp_server1{ server 127.0.0.1:8089; } server { listen 80; server_name www.helloworld.com; index index.html 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 ; location / { proxy_pass http://zp_server1; } location ~ ^/(images|javascript|js|css|flash|media|static)/ { root D:\01_Workspace\Project\github\zp\SpringNotes\spring-security\spring-shiro\src\main\webapp\views; expires 30d; } location /NginxStatus { stub_status on; access_log on; auth_basic "NginxStatus" ; auth_basic_user_file conf/htpasswd; } location ~ /\.ht { deny all; } } }
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 { include /etc/nginx/mime.types; default_type application/octet-stream; access_log /var/log /nginx/access.log; upstream load_balance_server { server 192.168.1.11:80 weight=5; server 192.168.1.12:80 weight=1; server 192.168.1.13:80 weight=6; } server { listen 80; server_name www.helloworld.com; location / { root /root; index index.html index.htm; proxy_pass http://load_balance_server ; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $remote_addr ; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; 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 { 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
server { listen 443 ssl; server_name www.helloworld.com; ssl_certificate cert.pem; ssl_certificate_key cert.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; 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; } } }
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; 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 :
set $ACAO '*' ;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 来引入跨域配置:
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; } }