Nginx是一款自由的、开源的、高性能的HTTP服务器和 反向代理 服务器;同时也是一个IMAP、POP3、SMTP代理服务器;Nginx可以作为一个HTTP服务器进行网站的发布处理,另外Nginx可以作为反向代理进行负载均衡的实现。
epoll能实现高并发原理
内存映射(mmap):内存映射文件,是由一个文件到一块内存的映射,将不必再对文件执行I/O操作
nginx相对于apache的优点
apache 相对于nginx 的优点
正向代理:
正向代理的用途:
正向代理配置:
环境介绍:
[root@localhost ~] vim /usr/local/nginx-1.12.1/conf/nginx.conf
server {
resolver 114.114.114.114; #指定DNS服务器IP地址
listen 80;
location / {
proxy_pass http://$host$request_uri; #设定代理服务器的协议和地址
proxy_set_header HOST $host;
proxy_buffers 256 4k;
proxy_max_temp_file_size 0k;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_next_upstream error timeout invalid_header http_502;
}
}
server {
resolver 114.114.114.114; #指定DNS服务器IP地址
listen 443;
location / {
proxy_pass https://$host$request_uri; #设定代理服务器的协议和地址
proxy_buffers 256 4k;
proxy_max_temp_file_size 0k;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_next_upstream error timeout invalid_header http_502;
}
}
[root@localhost ~] /usr/local/nginx-1.12.1/sbin/nginx -s reload
Linux客户端访问测试:
[root@localhost ~] curl -I --proxy 192.168.10.10:80 www.baidu.com
HTTP/1.1 200 OK
Server: nginx/1.12.1
Date: Mon, 11 Jun 2018 15:37:47 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Thu, 31 May 2018 09:28:16 GMT
Connection: keep-alive
ETag: "5b0fc030-264"
Accept-Ranges: bytes
https的访问测试
[root@localhost ~] curl -I --proxy 192.168.10.10:443 www.baidu.com
HTTP/1.1 200 OK
Server: nginx/1.12.1
Date: Mon, 11 Jun 2018 15:38:07 GMT
Content-Type: text/html
Content-Length: 277
Connection: keep-alive
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Etag: "575e1f5c-115"
Last-Modified: Mon, 13 Jun 2016 02:50:04 GMT
Pragma: no-cache
设置Linux客户端全局代理:
[root@localhost ~] vim /etc/profile
export http_proxy='192.168.10.10:80'
export http_proxy='192.168.10.10:443'
export ftp_proxy='192.168.10.10:80'
[root@localhost ~] source /etc/profile
[root@localhost ~] curl -I www.baidu.com:80
HTTP/1.1 200 OK
Server: nginx/1.12.1
Date: Mon, 11 Jun 2018 16:10:18 GMT
Content-Type: text/html
Content-Length: 277
Connection: keep-alive
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Etag: "575e1f5c-115"
Last-Modified: Mon, 13 Jun 2016 02:50:04 GMT
Pragma: no-cache
[root@localhost ~]# curl -I www.baidu.com:443
HTTP/1.1 200 OK
Server: nginx/1.12.1
Date: Mon, 11 Jun 2018 16:10:27 GMT
Content-Type: text/html
Content-Length: 277
Connection: keep-alive
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Etag: "575e1f59-115"
Last-Modified: Mon, 13 Jun 2016 02:50:01 GMT
Pragma: no-cache
上面结果就说明我们的服务端nginx正向代理和客户端使用nginx做为全局代理设置成功。
反向代理:
反向代理的作用:
反向代理配置:
虚拟机环境介绍:
测试搭建的nginx,tomcat是否正常访问: 先测试一下访问搭建好的nginx有没有问题。
Nginx访问正常。 测试一下搭建的tomcat,(自己编辑了一个用于测试的简单页面,端口是8070)
配置反向代理:
[root@localhost conf] vim Nginx.conf
在server段里面的location加上proxy_pass http://ip:端口
server{
listen 80;
server_name 192.168.161.189;
# charset koi8-r;
# access_log logs/host.access.log main;
location / {
proxy_pass http://192.168.161.189:8070; `注意这里`
root html;
idnex index.html index.htm;
}
}
Nginx配置完成后重启一下nginx:
# 命令大全
1、查找nginx路径:whereis nginx
2、启动 service nginx start
3、查看Nginx的版本号:nginx -V
4、停止 nginx -s stop
5、退出 nginx -s quit
6、重启加载配置 nginx -s reload
/etc/init.d/nginx -s reload 也可以
重启没报错说明配置文件没问题:
使用浏览器进行访问
简单的反向代理已经完成
配置代理多个网站及服务:
[root@localhost conf] vim Nginx.conf
配置多个反向代理实现方式,是通过不同的端口代理访问。这里复制一个server段,将两个server段nginx的端口更改,使用nginx的不同端口访问。
第一个server段配置tomcat1(192.168.161.189:8070)
第二个server段配置(192.168.161.189:8080)
配置完成后,重启nginx代理。
[root@localhost conf] /etc/init.d/nginx -s reload
先访问nginx代理的第一个tomcat1。(通过nginx的8081代理的tomcat1。)
访问nginx代理的第二个tomcat2。(通过nginx的8082代理的tomcat2。)
总结:
基于上文的一些简单配置,大概能知道Nginx的一个流程,我们继续往下看:
我们用一个最简单的例子,测试一下tornado的吞吐能力:
# -*- coding: utf-8 -*-
import os
import sys
import tornado.web
import tornado.ioloop
import tornado.httpserver
from tornado.options import parse_command_line
class Handler(tornado.web.RequestHandler):
def get(self):
self.write('我是tornado,我够快!')
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", Handler)
]
settings = dict(
title='压力测试',
debug=True,
)
tornado.web.Application.__init__(self, handlers, **settings)
parse_command_line()
http_server = tornado.httpserver.HTTPServer(Application(), xheaders=True, max_buffer_size=504857600)
http_server.listen(80)
print('Web server is started')
tornado.ioloop.IOLoop.instance().start()
启动该脚本后,使用浏览器访问127.0.0.1,页面显示“我是tornado,我够快!”。这个例子没有使用文件读写、数据库读写等耗时的操作,更能反应出tornado本身的吞吐能力。
压力测试通常使用Apache自带的ab.exe,ab的使用方法为:
ab -n 请求数 -c 并发数 URL
下面是并发10个请求共计100个请求的压力测试:
ab -n 100 -c 10 http://127.0.0.1/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient).....done
Server Software: TornadoServer/6.0.3
Server Hostname: 127.0.0.1
Server Port: 9001
Document Path: /
Document Length: 22 bytes
Concurrency Level: 10
Time taken for tests: 0.107 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 21700 bytes
HTML transferred: 2200 bytes
Requests per second: 937.09 [#/sec] (mean)
Time per request: 10.671 [ms] (mean)
Time per request: 1.067 [ms] (mean, across all concurrent requests)
Transfer rate: 198.58 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.6 0 2
Processing: 4 9 3.0 9 18
Waiting: 2 9 3.2 8 18
Total: 4 10 3.1 9 19
WARNING: The median and mean for the initial connection time are not within a normal deviation
These results are probably not that reliable.
Percentage of the requests served within a certain time (ms)
50% 9
66% 10
75% 13
80% 14
90% 15
95% 15
98% 18
99% 19
100% 19 (longest request)
它的输出中有以下关键信息:
我们发送10000次请求,用不同的并发数,多次进行测试,得到的结果如下表所示:
从数据中可以看出,随着并发数量的增加,服务器平均处理时间和用户平均请求等待时间都在增加;并发小于100时,服务器还没有饱和,吞吐量还在增加;并发大于100后,服务器的处理能力开始受到影响,吞吐量开始下降。
我使用windows平台,在我的测试条件下,tornado每秒最多响应1305次请求。Linux平台上,tornado的表现要比windows平台好得多。
首先,我们修改一下服务器的代码,使之可以同时启动多个进程:
# -*- coding: utf-8 -*-
import os
import sys
import multiprocessing
import tornado.web
import tornado.ioloop
import tornado.httpserver
from tornado.options import parse_command_line
# 页面句柄
class Handler(tornado.web.RequestHandler):
def get(self):
self.write('我是tornado,我够快!')
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", Handler),
]
settings = dict(
title='压力测试',
debug=True,
)
tornado.web.Application.__init__(self, handlers, **settings)
# 启动服务器
def start_web_server(port):
parse_command_line()
http_server = tornado.httpserver.HTTPServer(Application(), xheaders=True, max_buffer_size=504857600)
http_server.listen(port)
print('Web server is started on port %d.' % port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
if len(sys.argv) == 1:
start_web_server(80)
else:
try:
ports = [int(port) for port in sys.argv[1].split(',')]
except:
try:
a, b = sys.argv[1].split('-')
ports = range(int(a), int(b) + 1)
except:
ports = list()
print ('Parameter error.')
multiprocessing.freeze_support()
for port in ports:
p = multiprocessing.Process(target=start_web_server, args=(port,))
p.start()
在命令行中输入如下命令,启动两个服务器进程,每个进程使用不同的端口:
python server.py 9001-9002
接下来,配置ngnix。nginx的配置并不复杂,可以复制解压目录下的conf/ngnix.conf,进行修改即可。 在http部分中添加upstream,语法为:
http {
upstream 名称 {
负载均衡策略
server IP地址:端口 其它参数;
}
}
其中可选的负载均衡策略有:
我选择least_time,配置如下:
upstream serv {
least_conn;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
}
将原来的location /的内容修改为如下内容:
proxy_pass http://serv$request_uri;
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
其中proxy_pass后面的http://serv$request_uri中,serv为刚才配置的upstream的名称。 修改并删除了原来配置文件中的注释会,配置文件如下:
worker_processes 1;
events {
worker_connections 1024;
}
http {
sendfile on;
keepalive_timeout 65;
upstream serv {
least_conn;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://serv$request_uri;
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
启动tornado,并进入配置文件的目录,使用如下命令启动nginx:
nginx -c nginx.conf
OK,反向代理配置完成。再次用ab进行压力测试, 结果并不像我们期望的那样,吞吐量成倍增长。这是因为tornado的IO几乎已经做到了极致,几乎比肩nginx,wondows平台上单台PC的吞吐量大致也就如此了。当tornado需要进行文件读写、数据库读写等耗时的操作时,多进程的反向代理才能体现出优势。
除了反向代理,nginx还可以启用缓存技术,进一步提高服务能力。当客户端第一次请求某url时,nginx将请求转发给服务器,服务器返回后,nginx在本地创建缓存。在缓存未失效前,nginx不再转发请求,而是直接将缓存的内容返回给客户端,服务器的负载被转嫁到nginx上,而nginx的性能是非常出色的。 在nginx配置文件中设置缓存,语法为:
http {
proxy_cache_path 缓存路径 keys_zone=缓存名称:缓存大小 levels=一级缓存名长度:二级缓存名长度 inactive=失活时间 max_size=最大大小;
server {
location url {
proxy_cache 缓存名称;
proxy_cache_min_uses 访问次数(url被访问多少次后进行缓存);
proxy_cache_valid any 有效期;
}
}
}
修改后nginx的配置文件为:
worker_processes 1;
events {
worker_connections 1024;
}
http {
sendfile on;
keepalive_timeout 65;
upstream serv {
least_conn;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
server 127.0.0.1:9004;
}
# 设置缓存路径
proxy_cache_path cache keys_zone=CACHE:10m levels=1:4 inactive=1m max_size=1g;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://serv$request_uri;
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 缓存
proxy_cache CACHE;
proxy_cache_min_uses 1;
proxy_cache_valid any 1m;
}
}
}
重启nginx,此时使用浏览器访问127.0.0.1,第一次nginx没有缓存,服务器端打印出了访问日志,再以后的访问,服务器不再打印日志,说明nginx缓存起到了作用。
我们在tornado服务器的代码中加入100毫秒的sleep,来模拟访问数据库的操作,对不启用缓存和启用缓存进行压力测试:
可以看出,缓存技术对吞吐量的提升非常有效!
缓存,意味着不是最新的,如果某页面的内容的变化很快,使用缓存技术将导致客户端接收到错误的结果。如我增加一个url,输出服务器当前的时间:
# -*- coding: utf-8 -*-
import os
import sys
import time
import datetime
import multiprocessing
import tornado.web
import tornado.ioloop
import tornado.httpserver
from tornado.options import parse_command_line
# 页面句柄
class StaticHandler(tornado.web.RequestHandler):
def get(self):
time.sleep(0.1)
self.write('我是tornado,我够快!')
class VariableHandler(tornado.web.RequestHandler):
def get(self):
now = datetime.datetime.now()
self.write(now.strftime("%Y-%m-%d %H:%M:%S"))
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", StaticHandler), # 可以缓存的页面
(r"/variable", VariableHandler), # 禁止缓存的页面
]
settings = dict(
title='压力测试',
debug=True,
)
tornado.web.Application.__init__(self, handlers, **settings)
# 启动服务器
def start_web_server(port):
parse_command_line()
http_server = tornado.httpserver.HTTPServer(Application(), xheaders=True, max_buffer_size=504857600)
http_server.listen(port)
print('Web server is started on port %d.' % port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
if len(sys.argv) == 1:
start_web_server(80)
else:
try:
ports = [int(port) for port in sys.argv[1].split(',')]
except:
try:
a, b = sys.argv[1].split('-')
ports = range(int(a), int(b) + 1)
except:
ports = list()
print ('Parameter error.')
multiprocessing.freeze_support()
for port in ports:
p = multiprocessing.Process(target=start_web_server, args=(port,))
p.start()
此时浏览器访问127.0.0.1/variable,第一次出现了正确的时间,以后的1分钟以内,时间不再变化,等1分钟以后缓存过期,再访问出能得到新的时间。为了解决这个问题,可以在nginx配置中添加多个location,分别指定是否启用缓存即可:
worker_processes 1;
events {
worker_connections 1024;
}
http {
sendfile on;
keepalive_timeout 65;
upstream serv {
least_conn;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
server 127.0.0.1:9004;
}
proxy_cache_path cache keys_zone=CACHE:1m levels=1:2 inactive=1m max_size=1g;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://serv$request_uri;
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 缓存
proxy_cache CACHE;
proxy_cache_min_uses 1;
proxy_cache_valid any 1m;
}
# 只转发请求,不进行缓存
location /variable {
proxy_pass http://serv$request_uri;
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
重启nginx后,再访问127.0.0.1/variable,每次都可以得到最新的时间。
RR(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
简单配置:
upstream test {
server localhost:8080;
server localhost:8081;
}
server {
listen 81;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
负载均衡的核心代码为:
upstream test {
server localhost:8080;
server localhost:8081;
}
这里我配置了2台服务器,当然实际上是一台,只是端口不一样而已,而8081的服务器是不存在的,也就是说访问不到,但是我们访问http://localhost 的时候,也不会有问题,会默认跳转到http://localhost:8080
具体是因为Nginx会自动判断服务器的状态,如果服务器处于不能访问(服务器挂了),就不会跳转到这台服务器,所以也避免了一台服务器挂了影响使用的情况,由于Nginx默认是RR策略,所以我们不需要其他更多的设置。
权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。例如
upstream test {
server localhost:8080 weight=9;
server localhost:8081 weight=1;
}
那么10次一般只会有1次会访问到8081,而有9次会访问到8080
ip_hash
上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream test {
ip_hash;
server localhost:8080;
server localhost:8081;
}
fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backend {
fair;
server localhost:8080;
server localhost:8081;
}
url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
upstream backend {
hash $request_uri;
hash_method crc32;
server localhost:8080;
server localhost:8081;
}
以上5种负载均衡各自适用不同情况下使用,所以可以根据实际情况选择使用哪种策略模式,不过fair和url_hash需要安装第三方模块才能使用,由于本文主要介绍Nginx能做的事情,所以Nginx安装第三方模块不会再本文介绍
Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
root e:\wwwroot;
index index.html;
}
}
这样如果访问http://localhost 就会默认访问到E盘wwwroot目录下面的index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署。
动静分离
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路
upstream test{
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
location / {
root e:\wwwroot;
index index.html;
}
# 所有静态请求都由nginx处理,存放目录为html
location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
root e:\wwwroot;
}
# 所有动态请求都转发给tomcat处理
location ~ \.(jsp|do)$ {
proxy_pass http://test;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root e:\wwwroot;
}
}
这样我们就可以吧HTML以及图片和css以及js放到wwwroot目录下,而tomcat只负责处理jsp和请求,例如当我们后缀为gif的时候,Nginx默认会从wwwroot获取到当前请求的动态图文件返回,当然这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了,只要搞清楚了最基本的流程,很多配置就很简单了,另外localtion后面其实是一个正则表达式,所以非常灵活
Nginx是支持热启动的,也就是说当我们修改配置文件后,不用关闭Nginx,就可以实现让配置生效,当然我并不知道多少人知道这个,反正我一开始并不知道,导致经常杀死了Nginx线程再来启动…..Nginx重新读取配置的命令是
nginx -s reload
windows下面就是:
nginx.exe -s reload