一、Nginx中虚拟主机配置
1、基于域名的虚拟主机配置
1、修改宿主机的hosts文件(系统盘/windows/system32/driver/etc/HOSTS)
linux : vim /etc/hosts
格式: ip地址 域名
eg: 192.168.3.172 www.gerry.com
2、在nginx.conf文件中配置server段
代码语言:javascript复制server {
listen 80;
server_name www.gerry.com; # 域名区分
location / {
root html/gerry;
index index.html;
}
}
server {
listen 80;
server_name www.rm.com; # 域名区分
location / {
root html/rm;
index index.html;
}
}
2、基于端口号的虚拟主机配置
1、在nginx.conf文件中配置server段
代码语言:javascript复制server {
listen 80; # 端口区分
server_name www.gerry.com;
location / {
root html/gerry;
index index.html;
}
}
server {
listen 8080; # 端口区分
server_name www.gerry.com;
location / {
root html/gerry;
index index.html;
}
}
3、基于IP的虚拟主机配置
1、添加网卡的IP别名
代码语言:javascript复制ifconfig ens33:1 192.168.3.202 broadcast 192.168.3.255 netmask 255.255.255.0 up
route add -host 192.168.3.202 dev ens33:1
ifconfig ens33:2 192.168.3.203 broadcast 192.168.3.255 netmask 255.255.255.0 up
route add -host 192.168.3.203 dev ens33:2
从另外一台服务器Ping 192.168.3.202和192.168.3.203两个IP,如果能够Ping通,则证明配置无误。但是,通过ifconfig和route配置的IP别名在服 务器重启后会消失,不过可以将这两条ifconng和route命令添加到/etc/rc.local文件中,让系统开机时自动运行,以下是相关命令:vi /etc/rc.local 在文件末尾增加以下内容,然后保存即可
ifconfig ens33:1 192.168.3.202 broadcast 192.168.3.255 netmask 255.255.255.0 up route add -host 192.168.3.202 dev ens33:1 ifconfig ens33:2 192.168.3.203 broadcast 192.168.3.255 netmask 255.255.255.0 up route add -host 192.168.3.203 dev ens33:2
2、修改配置文件做如下的Server段配置
代码语言:javascript复制server {
listen 80;
server_name 192.168.3.202;
location / {
root html/host1;
index index.html;
}
}
server {
listen 80;
server_name 192.168.3.203;
location / {
root html/host2;
index index.html;
}
}
二、nginx中server_name的匹配顺序
前提:安装echo-nginx-module
1、下载https://github.com/openresty/echo-nginx-module并解压到服务器上
2、进入nginx的安装目录,用下列命令进行配置和安装 ./configure --prefix=/usr/local/nginx --add-module=/usr/local/nginx_modules/echo-nginx-module-master make -j4 && make install
修改hosts配置文件
在Hosts文件中做如下配置:
vim /etc/hosts
127.0.0.1 ddd.cn
127.0.0.1 aaa.cn
1、最高优先级:完全匹配
首先,在nginx.conf中创建2个server,1个完全匹配,1个通配符匹配
通配符server放在最前,以证明完全匹配的优先级与配置顺序无关
代码语言:javascript复制server {
listen 80;
server_name *.cn;
location / {
default_type text/html;
echo "通配符在前";
}
}
server {
listen 80;
server_name ddd.cn;
location / {
default_type text/html;
echo "完全匹配";
}
}
2、第二优先级:通配符在前
代码语言:javascript复制server {
listen 80;
server_name ddd.*;
location / {
default_type text/html;
echo "通配符在后";
}
}
server {
listen 80;
server_name *.cn;
location / {
default_type text/html;
echo "通配符在前";
}
}
3、第三优先级:通配符在后
代码语言:javascript复制server {
listen 80;
server_name ~^w .cn$;
location / {
default_type text/html;
echo "正则匹配";
}
}
server {
listen 80;
server_name ddd.*;
location / {
default_type text/html;
echo "通配符在后";
}
}
4、listen配置项中default的影响
代码语言:javascript复制server {
listen 80;
server_name ~^w .cn$;
location / {
default_type text/html;
echo "正则匹配";
}
}
server {
listen 80;
server_name dddd.xxx;
location / {
default_type text/html;
echo "不匹配";
}
}
server {
listen 80 default;
server_name haha.xxx;
location / {
default_type text/html;
echo "不匹配找Listen Default";
}
}
5、验证default的作用域
代码语言:javascript复制server {
listen 80 default;
server_name *.cn;
location / {
default_type text/html;
echo "通配符在前";
}
}
server {
listen 80;
server_name ddd.cn;
location / {
default_type text/html;
echo "完全匹配";
}
}
结论: default关键字在所有server段下面的server_name都匹配不上的时候才有效果
6、验证没有匹配成功也没有default的情况
代码语言:javascript复制server {
listen 80;
server_name haha.cn;
location / {
default_type text/html;
echo "不匹配,通配符在前";
}
}
server {
listen 80;
server_name heihei.cn;
location / {
default_type text/html;
echo "不匹配,通配符在后";
}
}
结论: 在请求的server_name 都不匹配也没有default关键字情况下,会根据配置文件的顺序选择配置文件中第一虚拟主机。
三、日志的配置
代码语言:javascript复制http {
include mime.types;
default_type application/octet-stream;
# 定义日志的格式
log_format main '$remote_addr - [$time_iso8601] - $msec - $status - $request_time - $body_bytes_sent - "$http_host" - "$request" - "$http_referer" - "$http_user_agent" - "$http_x_forwarded_for"';
#设置日志默认存储目录
access_log logs/access.log main;
error_log logs/error.log;
}
====== 内置的变量 ===============
$remote_addr, $http_x_forwarded_for(反向) 记录客户端IP地址
$remote_user 记录客户端用户名称
$request 记录请求的URL和HTTP协议
$status 记录请求状态
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小; 该变量与Apache模块mod_log_config里的“%B”参数兼容。
$bytes_sent 发送给客户端的总字节数。
$connection 连接的序列号。
$connection_requests 当前通过一个连接获得的请求数量。
$msec 日志写入时间。单位为秒,精度是毫秒。
$pipe 如果请求是通过HTTP流水线(pipelined)发送,pipe值为“p”,否则为“.”。
$http_referer 记录从哪个页面链接访问过来的
$http_user_agent 记录客户端浏览器相关信息
$request_length 请求的长度(包括请求行,请求头和请求正文)。
$request_time 请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。
$time_iso8601 ISO8601标准格式下的本地时间。
$time_local 通用日志格式下的本地时间。
四、nginx配置location(html location重定向)
匹配指定的请求uri(请求uri不包含查询字符串,如http://localhost:8080/test?id=10,请求uri是/test)
语法形式
location [ = | ~ | ~* | ^~ | @] /uri/ { configuration }
location = /uri =开头表示精确前缀匹配,只有完全匹配才能生效。
location ^~ /uri ^~开头表示普通字符串匹配上以后不再进行正则匹配。
location ~ pattern ~开头表示区分大小写的正则匹配。
location ~* pattern ~*开头表示不区分大小写的正则匹配。
location /uri 不带任何修饰符,表示前缀匹配。(一般匹配,最长命中匹配)
location /uri/aaa/bbb 最长命中匹配
location / 通用匹配,任何未匹配到其他location的请求都会匹配到。
注意:正则匹配会根据匹配顺序,找到第一个匹配的正则表达式后将停止搜索。普通字符串匹配则无视顺序,只会选择最精确的匹配。
1、location常用匹配
- = :精准匹配
- ~ :正则匹配
- ~* :正则匹配,不区分大小写
- ^~ : 普通字符匹配, ^~ 的含义是如果命中,则不会再进行任何的正则匹配(关闭正则匹配)
- 前面没有任何修饰: 普通字符匹配
2、location匹配顺序
- 精确匹配
- 普通匹配
- 正则匹配
精准匹配
代码语言:javascript复制location =/test/index.html {
root gerry/host2;
index index.html;
}
普通匹配
代码语言:javascript复制location /test {
root gerry/host1;
index index.html;
}
# 最长命中匹配
location /test/aaa {
root gerry/host1;
index index.html;
}
# root 和 alias 区别
location /test {
root gerry/host1;
index index.html;
}
location /kkk/ {
alias /usr/local/nginx/gerry/host1/;
index index.html;
}
正则匹配
代码语言:javascript复制##正则匹配
location ~ /test/. .html$ {
root gerry/host1;
}
location ~ /test/. .js$ {
root gerry/host1;
}
## 关闭正则匹配
location ^~ /test {
root gerry/host2;
index index.html;
}
## 忽略大小写匹配
location ~* /. .HTML$ {
root gerry/html;
index index.html;
}
3、location匹配规则流程
A、判断是否精准匹配,如果匹配,直接返回结果并结束搜索匹配过程。 B、判断是否普通匹配,如果匹配,看是否包含^~前缀,包含则返回,否则记录匹配结果,(如果匹配到多个location时返回或记录最长匹配的那个) C、判断是否正则匹配,按配置文件里的正则表达式的顺序,由上到下开始匹配,一旦匹配成功,直接返回结果,并结束搜索匹配过程。 D、如果正则匹配没有匹配到结果,则返回步骤B记录的匹配结果。
注意:
A、多个普通匹配的location时,和location的顺序无关,总是匹配所有的location,然后取匹配最长的location作为结果
B、多个正则匹配的location时,和顺序有关,从上到下依次匹配,一旦匹配成功便结束,直接返回结果。
4、常用配置指令alias、root
1、alias——别名配置,用于访问文件系统,在匹配到location配置的URL路径后,指向alias配置的路径,如:
location /test/ { alias /usr/local/; ## 文件路径必须为绝对路径 }
请求/test/1.jpg(省略了协议和域名),将会返回文件/usr/local/1.jpg。
如果alias配置在正则匹配的location内,则正则表达式中必须包含捕获语句(也就是括号()),而且alias配置中也要引用这些捕获值。如:
location ~* /img/(. .(gif|png|jpeg) { alias /usr/local/images/$1; }
请求中只要能匹配到正则,比如/img/flower.png 或者 /resource/img/flower.png,都会转换为请求/usr/local/images/flower.png。
2、root——根路径配置,用于访问文件系统,在匹配到location配置的URL路径后,指向root配置的路径,并把请求路径附加到其后,如:
location /test/ { root /usr/local/; }
请求/test/1.jpg,将会返回文件/usr/local/test/1.jpg。
五、Nginx的Rewrite
1、rewrite的介绍
1、Rewrite通过ngx_http_rewrite_module模块支持url重写、支持if判断,但不支持else
2、rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向
3、rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用
http://www.gerry.com/test/v1/10
http://www.gerry.com/test?version=1&id=10
2、常用指令
If 空格 (条件) {设定条件进行重写}
条件的语法:
- “=” 来判断相等,用于字符比较
- “~” 用正则来匹配(表示区分大小写),“~*” 不区分大小写
- “-f -d -e” 来判断是否为文件、目录、是否存在
return 指令
语法:return code;
停止处理并返回指定状态码给客户端。
if ($request_uri ~ *.sh ){
return 403
}
set指令
set variable value;
定义一个变量并赋值,可以是文本、变量或者文本变量混合体
rewrite指令
语法:rewrite regex replacement [flag]{last / break/ redirect 返回临时301/ permanent 返回永久302}
last: 停止处理后续的rewrite指令集、 然后对当前重写的uri在rewrite指令集上重新查找
break: 停止处理后续的rewrite指令集 ,并不会重新查找
3、实例讲解
执门搜索
这个规则的目的是为了执行搜索,搜索URL中包含的关键字。
代码语言:javascript复制加了问号和不加问号区别?
URI:/search/some-search-keywords/zhangsan?name=zhangsan
/search.do?p=some-search-keywords&name=zhangsan&name=zhangsan 不加问号。
/search.do?p=some-search-keywords&name=zhangsan 加问号。
热门搜索
代码语言:javascript复制请求的URL //hqidi.com/search/some-search-keywords
重写后URL //hqidi.com/search.do?p=some-search-keywords
重写规则 rewrite ^/search/(.*)$ /search.do?p=$1?;
用户个人资料页面
大多数运行访问者注册的动态网站都提供一个可以查看个人资料的页面,这个页面的URL包含用户的UID和用户名
代码语言:javascript复制请求的URL //hqidi.com/user/47/dige
重写后URL //hqidi.com/user.do?id=47&name=dige
重写规则 rewrite ^/user/([0-9] )/(. )$ /user.do?id=$1&name=$2?;
多个参数
有些网站对字符串参数使用不同的语法,例如 通过斜线“/”来分隔非命名参数
代码语言:javascript复制请求的URL //hqidi.com/index.php/param1/param2/param3
重写后URL //hqidi.com/index.php?p1=param1&p2=param2&p3=param3
重写规则 rewrite ^/index.do/(.*)/(.*)/(.*)$ /index.do?p1=$1&p2=$2&p3=$3?;
类似百科的格式
这种格式特点,一个前缀目录,后跟文章名称
代码语言:javascript复制请求的URL //hqidi.com/wiki/some-keywords
重写后URL //hqidi.com/wiki/index.do?title=some-keywords
重写规则 rewrite ^/wiki/(.*)$ /wiki/index.do?title=$1?;
论坛
论坛一般用到两个参数,一个话题标识(topic)一个出发点(starting post)
代码语言:javascript复制请求的URL //hqidi.com/topic-1234-50-some-keywords.html
重写后URL //hqidi.com/viewtopic.do?topic=1234&start=50
重写规则 rewrite ^/topic-([0-9] )-([0-9] )-(.*).html$ viewtopic.do?topic=$1&start=$2?;
新网站的文章
这种URL结构的特点,由一个文章标识符,后跟一个斜线,和一个关键字列表组成。
代码语言:javascript复制请求的URL //hqidi.com/88/future
重写后URL //hqidi.com/atricle.php?id=88
重写规则 rewrite ^/([0-9] )/.*$ /aticle.do?id=$1?;
最后一个问号
若被替换的URI中含有参数(类似/app/test.php?id=5之类的URI),默认情况下参数会被自动附加到替换串上,可以通过在替换串的末尾加上?标记来解决这一问题。
代码语言:javascript复制rewrite ^/users/(.*)$ /show?user=$1? last;
比较一个加上?标记和不加?标记的URL跳转区别:
代码语言:javascript复制rewrite ^/test(.*)$ //hqidi.com/home premanent;
访问//hqidi.com/test?id=5 经过301跳转后的URL地址为 //hqidi.com/home?id=5
代码语言:javascript复制rewrite ^/test(.*)$ //hqidi.com/home? premanent;
访问//hqidi.com/test?id=5 经过301跳转后的URL地址为 //hqidi.com/home
六、浏览器本地缓存配置
语法:expires 60 s|m|h|d
代码语言:javascript复制动静分离效果:
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html;
}
location ~ .(png|jpg|js|css|gif)$ {
root html/images;
expires 5m;
}
}
七、Gzip压缩策略
浏览器请求 -> 告诉服务端当前浏览器可以支持压缩类型->服务端会把内容根据浏览器所支持的压缩策略去进行压缩返回
->浏览器拿到数据以后解码; 常见的压缩方式:gzip、deflate 、sdch
Gzip on|off 是否开启gzip压缩 Gzip_buffers 4 16k
设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。4 16k代表以16k为单位,安装原始数据大小以16k为单位的4倍申请内存。
Gzip_comp_level[1-9] 压缩级别, 级别越高,压缩越小,但是会占用CPU资源 Gzip_disable #正则匹配UA 表示什么样的浏览器不进行gzip Gzip_min_length 开始压缩的最小长度(小于多少就不做压缩)
Gzip_http_version 1.0|1.1 表示开始压缩的http协议版本 Gzip_proxied (nginx 做前端代理时启用该选项,表示无论后端服务器的headers头返回什么信息,都无条件启用压缩) Gzip_type text/pliain,application/xml 对那些类型的文件做压缩 (conf/mime.conf) Gzip_vary on|off 是否传输gzip压缩标识
代码语言:javascript复制server {
listen 80;
server_name localhost;
gzip on;
gzip_buffers 4 16K;
gzip_comp_level 7;
gzip_min_length 500;
gzip_types text/css text/xml text/javascript;
location / {
root html;
index index.html;
}
location ~ .(png|jpg|js|css|gif)$ {
root html/images;
expires 5m;
}
}
注意点
- 图片、mp3这样的二进制文件,没必要做压缩处理,因为这类文件压缩比很小,压缩过程会耗费CPU资源
- 太小的文件没必要压缩,因为压缩以后会增加一些头信息,反而导致文件变大
- Nginx默认只对text/html进行压缩 ,如果要对html之外的内容进行压缩传输,我们需要手动来配置
八、Nginx反向代理
location {proxy_pass}
代码语言:javascript复制server {
listen 80;
server_name www.gerry.com;
location =/s {
proxy_pass http://www.baidu.com;
}
}
Proxy_pass 既可以是ip地址,也可以是域名,同时还可以指定端口
Proxy_pass指定的地址携带了URI,看我们前面的配置【/web】,那么这里的URI将会替换请求URI中匹配location参数部分;如上代码将会访问到http://www.baidu.com/web
九、负载均衡
upstream是Nginx的HTTP Upstream模块,这个模块通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡
1、Upstream常用参数介绍
语法:server address [parameters]
其中关键字server必选。 address也必选,可以是主机名、域名、ip或unix socket,也可以指定端口号。 parameters是可选参数,可以是如下参数:
down:表示当前server已停用
backup:表示当前server是备用服务器,只有其它非backup后端服务器都挂掉了或者很忙才会分配到请求
weight:表示当前server负载权重,权重越大被请求几率越大。默认是1
max_fails和fail_timeout一般会关联使用,如果某台server在fail_timeout时间内出现了max_fails次连接失败,那么Nginx会认为其已经挂掉了,从而在fail_timeout时间内不再去请求它,fail_timeout默认是10s,max_fails默认是1,即默认情况是只要发生错误就认为服务器挂掉了,如果将max_fails设置为0,则表示取消这项检查。
代码语言:javascript复制upstream tomcatserver {
server 192.168.3.220:8080;
server 192.168.3.222:8080 weigth=4;
}
2、ups支持的调度算法
在做负载均衡前,我们首先需要定义一个 Server 组用来表示所有存在的后台服务:
代码语言:javascript复制http {
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com;
server 192.0.0.1 backup;
}
}
然后,我们需要把流量重定向到上一步定义的 backend 上去, 我们可以通过指定 proxy_pass 的值来完成这一操作:
代码语言:javascript复制 upstream backend {
server backend1.example.com;
server backend2.example.com;
server 192.0.0.1 backup;
}
server {
location / {
proxy_pass http://backend;
}
}
}
这里我们将所有的流量重定向到 http://backend , 这将这个 NGINX 实例上的所有流量重定向到之前定义的 backend 上去。
负载均衡算法
当没有指定任何信息时, NGINX 默认使用了 Round Robin(轮询)算法来重定向流量。其实 NGINX 提供了多种算法来做负载均衡,下面我们来介绍一下:
1、Round Robin (轮询)
在没有指定 weight(权重) 的情况下,Round Robin 会将所有请求均匀地分发给所有后台服务实例:
代码语言:javascript复制upstream backend {
server backend1.example.com;
server backend2.example.com;
}
这里我们没有指定权重,所以两个后台服务会收到等量的请求。但是,当指定了权重之后,NGINX 就会将权重考虑在内:
代码语言:javascript复制upstream backend {
server backend1.example.com weight=5;
server backend2.example.com;
}
在 NGINX 中,weight 默认被设置为 1。这里我们用一开始的配置举例, backend1.example.com 的权重被设置为 5,另一个的权重没设置,所以是默认值 1。我们也没有设置轮询算法,所以这时候 NGINX 会以 5:1 的比例转发请求,即 6 个请求中, 5 个被放到了 backend1.example.com 上, 有一个被发到了 backend2.example.com 上。
2、Least Connections(最少连接算法)
在这个模式下,一个请求会被 NGINX 转发到当前活跃请求数量最少的服务实例上:
代码语言:javascript复制upstream backend {
least_conn;
server backend1.example.com;
server backend2.example.com;
}
我们用 least_conn 来指定最少连接优先算法, NGINX 会优先转发请求到现有连接数少的那一个服务实例上。
3、IP Hash (IP 哈希)
在 IP Hash 模式下,NGINX 会根据发送请求的 IP 地址的 hash 值来决定将这个请求转发给哪个后端服务实例。被 hash 的 IP 地址要么是 IPv4 地址的前三个 16 进制数或者是整个 IPv6 地址。使用这个模式的负载均衡模式可以保证来自同一个 IP 的请求被转发到同一个服务实例上。当然,这种方法在某一个后端实例发生故障时候会导致一些节点的访问出现问题。
代码语言:javascript复制upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
}
如果某一台服务器出现故障或者无法进行服务,我们可以给它标记上 down,这样之前被转发到这台服务器上的请求就会重新进行 hash 计算并转发到新的服务实例上:
代码语言:javascript复制upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com down;
}
4、Generic Hash(通用哈希)
这个模式允许管理员自定义 hash 函数的输入,比如:
代码语言:javascript复制upstream backend {
hash $reqeust_uri consistent;
server backend1.example.com;
server backend2.example.com;
}
在这个例子中,我们以请求中所带的 url 为 hash 的输入。 注意到这里在 hash 那一行的最后加入了 consistent 这个关键词。这个关键词会使用一种新的 hash 算法 ketama, 该算法会让管理员添加或删除某个服务实例的时候,只有一小部分的请求会被转发到与之前不同的服务实例上去,其他请求仍然会被转发到原有的服务实例上去。
5、Random (随机算法)
顾名思义,每个请求都被随机发送到某个服务实例上去:
代码语言:javascript复制upstream backend {
random;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
server backend4.example.com;
}
Random 模式还提供了一个参数 two,当这个参数被指定时,NGINX 会先随机地选择两个服务器(考虑 weight),然后用以下几种方法选择其中的一个服务器:
代码语言:javascript复制1. least_conn: 最少连接数
2. least_time=header(NGINX PLUS only): 接收到 response header 的最短平均时间
3. least_time=last_byte(NGINX PLUS only): 接收到完整请求的最短平均时间
我们可以参考下面的一个例子:
代码语言:javascript复制upstream backend {
random two least_time=last_byte;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
server backend4.example.com;
}
当环境中有多个负载均衡服务器在向后端服务转发请求时,我们可以考虑使用 Random 模式,在只有单个负载均衡服务器时,一般不建议使用 Random 模式。
6、Least Time (NGINX PLUS only)
这是一个 NGINX PLUS (NGINX 的付费版) 才有的模式,可以将请求优先转发给平均响应时间较短的服务实例,它也有三个模式:
代码语言:javascript复制1. header: 从服务器接收到第一个字节的时间
2. last_byte: 从服务器接收到完整的 response 的时间
3. last_byte inflight: 从服务器接收到完整地 response 的时间(考虑不完整的请求)
例子如下:
代码语言:javascript复制upstream backend {
least_time header;
server backend1.example.com;
server backend2.example.com;
}
总结
NGINX 提供了多种负载均衡模式,在实际使用中,需要根据实际业务需求去做尝试,分析日志来找到最适合当前场景的复杂均衡模式。