Nginx location规则详解

由于团队在进行前后端分离,前端接管了Nginx和node层,在日常的工作中,跟Nginx打交道的时候挺多的。之前对location的匹配规则是一知半解的,为了搞明白location是如何匹配的,查了些资料总结此文。希望能给大家带来帮助。

语法规则

location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }

语法规则很简单,一个location关键字,后面跟着可选的修饰符,后面是要匹配的字符,花括号中是要执行的操作。

修饰符
= 表示精确匹配。只有请求的url路径与后面的字符串完全相等时,才会命中。
~ 表示该规则是使用正则定义的,区分大小写。
~* 表示该规则是使用正则定义的,不区分大小写。
^~ 表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找。
匹配过程
对请求的url序列化。例如,对%xx等字符进行解码,去除url中多个相连的/,解析url中的.,..等。这一步是匹配的前置工作。

location有两种表示形式,一种是使用前缀字符,一种是使用正则。如果是正则的话,前面有~或~*修饰符。

具体的匹配过程如下:

首先先检查使用前缀字符定义的location,选择最长匹配的项并记录下来。

如果找到了精确匹配的location,也就是使用了=修饰符的location,结束查找,使用它的配置。

然后按顺序查找使用正则定义的location,如果匹配则停止查找,使用它定义的配置。

如果没有匹配的正则location,则使用前面记录的最长匹配前缀字符location。

基于以上的匹配过程,我们可以得到以下两点启示:

使用正则定义的location在配置文件中出现的顺序很重要。因为找到第一个匹配的正则后,查找就停止了,后面定义的正则就是再匹配也没有机会了。
使用精确匹配可以提高查找的速度。例如经常请求/的话,可以使用=来定义location。
示例
接下来我们以一个例子来具体说明一下匹配过程。

假如我们有下面的一段配置文件:

location = / {
    [ configuration A ]
}

location / {
    [ configuration B ]
}

location /user/ {
    [ configuration C ]
}

location ^~ /images/ {
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

请求/精准匹配A,不再往下查找。

请求/index.html匹配B。首先查找匹配的前缀字符,找到最长匹配是配置B,接着又按照顺序查找匹配的正则。结果没有找到,因此使用先前标记的最长匹配,即配置B。

请求/user/index.html匹配C。首先找到最长匹配C,由于后面没有匹配的正则,所以使用最长匹配C。
请求/user/1.jpg匹配E。首先进行前缀字符的查找,找到最长匹配项C,继续进行正则查找,找到匹配项E。因此使用E。

请求/images/1.jpg匹配D。首先进行前缀字符的查找,找到最长匹配D。但是,特殊的是它使用了^~修饰符,不再进行接下来的正则的匹配查找,因此使用D。这里,如果没有前面的修饰符,其实最终的匹配是E。大家可以想一想为什么。

请求/documents/about.html匹配B。因为B表示任何以/开头的URL都匹配。在上面的配置中,只有B能满足,所以匹配B。

location @name的用法
@用来定义一个命名location。主要用于内部重定向,不能用来处理正常的请求。其用法如下:

location / {
    try_files $uri $uri/ @custom
}
location @custom {
    # ...do something
}

上例中,当尝试访问url找不到对应的文件就重定向到我们自定义的命名location(此处为custom)。

值得注意的是,命名location中不能再嵌套其它的命名location。

URL尾部的/需不需要
关于URL尾部的/有三点也需要说明一下。第一点与location配置有关,其他两点无关。

location中的字符有没有/都没有影响。也就是说/user/和/user是一样的。
如果URL结构是https://domain.com/的形式,尾部有没有/都不会造成重定向。因为浏览器在发起请求的时候,默认加上了/。虽然很多浏览器在地址栏里也不会显示/。这一点,可以访问baidu验证一下。
如果URL的结构是https://domain.com/some-dir/。尾部如果缺少/将导致重定向。因为根据约定,URL尾部的/表示目录,没有/表示文件。所以访问/some-dir/时,服务器会自动去该目录下找对应的默认文件。如果访问/some-dir的话,服务器会先去找some-dir文件,找不到的话会将some-dir当成目录,重定向到/some-dir/,去该目录下找默认文件。可以去测试一下你的网站是不是这样的。
总结
location的配置有两种形式,前缀字符和正则。查找匹配的时候,先查找前缀字符,选择最长匹配项,再查找正则。正则的优先级高于前缀字符。

正则的查找是按照在配置文件中的顺序进行的。因此正则的顺序很重要,建议越精细的放的越靠前。

使用=精准匹配可以加快查找的顺序,如果根域名经常被访问的话建议使用=。

转载自:https://segmentfault.com/a/1190000013267839

Nginx 使用$hostname做为access_log名中的坑

在多节点部署的情况下,需要对nginx日志文件按节点名[hostname]做区分,但实际使用中发现日志文件夹中并没有输出这种文件,本以为access_log中不支持这个变量,其实看到error_log中的日志发现了问题所在。

        access_log  /logs/nginx/prd-$hostname-access.log main;

继续阅读Nginx 使用$hostname做为access_log名中的坑

MySQL复制报错(Slave failed to initialize relay log info structure from the repository)

机器重启以后,主从出现了问题,具体报错信息:

Slave failed to initialize relay log info structure from the repository

解决方案:
继续阅读MySQL复制报错(Slave failed to initialize relay log info structure from the repository)

logrotate参数说明

logrotate 是 Linux 下的一个日志管理工具。通过 logrotate 可以很轻松地管理(拆分、压缩等)系统的所有日志文件。 通过配置,我们可以按日志大小或者固定周期来管理日志文件。

安装 logrotate

yum install logrotate

打开配置文件

vim /etc/logrotate.conf

在文件最后增加以下配置

/PATH/TO/YOUR/LOG/*.log {
    daily          # 按日,也可以 weekly 按周,monthly 按月
    dateext        # 增加日期作为后缀
    missingok      # 如果文件不存在,忽略错误信息
    rotate 30      # 保留 30 份
    compress       # 压缩
    delaycompress  # 延迟压缩,也就是本次拆分的文件在下次执行时再压缩
    notifempty     # 忽略空白文件
    copytruncate   # 拆分文件后,清空原有文件,而不是创建一个新文件
    su root root   # 解决没权限的问题
}

马上执行 logrotate

sudo /usr/sbin/logrotate -f /etc/logrotate.conf

查看你的 log 文件所在目录,应该可以得到类似这个的结果

-rw-r--r-- 1 deploy deploy  0K 2012-06-27 23:39 production.log
-rw-r--r-- 1 deploy deploy  27M 2012-06-27 23:15 production.log-20181217

小程序JS Date.parse返回null

前端同事在做微信小程序时发现IOS获取的时间戳为空的问题,后来通过跟踪发现,原来是因为IOS系统不支持2018-08-08格式的时间导致的,

var mydata = ‘2018-08-08 11:00:00’;
console.log(“返回时间:” + mydata);
var time = Date.parse(new Date(mydata)) / 1000;

这样的代码安卓手机开发手机测试都没有问题,唯独IOS获取的日期为null,若干次百度调试后发现,原来IOS只识别2018/08/08这样的日期格式,
下面用正则替换2018-08-08日期格式为2018/08/08后问题解决(代码如下)

var mydata = ‘2018-08-08 11:00:00’;
mydata=data.replace(/-/g, ‘/’);
console.log(“返回时间:” + mydata);
var time = Date.parse(new Date(mydata)) / 1000;

转自:https://blog.csdn.net/zqtsx/article/details/74331076

基于动态发现的ETCD集群的节点迁移

基于docker-compose编排了一组etcd集群,如果想把其中一个节点替换掉应该怎么操作呢,网上有一些质量非常高的技术文章,但是有一些细节地方没有说清楚。

先把我的docker-compose.yml帖上来。

version: '3'
services:
  etcd1:
    image: "quay.io/coreos/etcd:latest"
    network_mode: "host"
    volumes:
      - /data0/etcd/etcd1/:/etcd_data/:rw
    command:
      - etcd
      - --name
      - etcd1
      - --data-dir
      - /etcd_data
      - --listen-peer-urls
      - http://${DOCKER_IIP}:2380
      - --listen-client-urls
      - http://${DOCKER_IIP}:2379,http://127.0.0.1:2379
      - --advertise-client-urls
      - http://${DOCKER_IIP}:2379
      - --initial-advertise-peer-urls
      - http://${DOCKER_IIP}:2380
      - --discovery
      - http://discovery.etcd.io/TTTTTTTTTTTTTTTTTTTTTTT

下面来进行替换节点的操作,先查看一下节点列表

/ # etcdctl member list
1ca4fc0d4b2c9fa2: name=etcd1 peerURLs=http://192.168.56.42:2380 clientURLs=http://192.168.56.42:2379 isLeader=true
4cf04322e0117c69: name=etcd4 peerURLs=http://192.168.56.33:2380 clientURLs=http://192.168.56.33:2379 isLeader=false
6dc5120e6c9ba574: name=etcd3 peerURLs=http://192.168.56.42:2382 clientURLs=http://192.168.56.42:2381 isLeader=false

先拿掉etcd4。

/ # etcdctl member remove 4cf04322e0117c69
Removed member 4cf04322e0117c69 from cluster

再进行添加操作。

/ # etcdctl member add etcd2 http://192.168.56.33:2380
Added member named etcd2 with ID d401a8b3607279fc to cluster

ETCD_NAME="etcd2"
ETCD_INITIAL_CLUSTER="etcd1=http://192.168.56.42:2380,etcd3=http://192.168.56.42:2382,etcd2=http://192.168.56.33:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"

接下来是重点了,把这几个环境变量记录一下,修改一下原来的docker-compose.yml,进行新节点启动。

version: '3'
services:
  etcd2:
    image: "quay.io/coreos/etcd:latest"
    network_mode: "host"
    volumes:
      - /data0/etcd/etcd2/:/etcd_data/:rw
    command:
      - etcd
      - --name
      - etcd2
      - --data-dir
      - /etcd_data
      - --listen-peer-urls
      - http://${DOCKER_IIP}:2380
      - --listen-client-urls
      - http://${DOCKER_IIP}:2379,http://127.0.0.1:2379
      - --advertise-client-urls
      - http://${DOCKER_IIP}:2379
      - --initial-advertise-peer-urls
      - http://${DOCKER_IIP}:2380
      #- --discovery
      #- http://discovery.etcd.io/df65ece6e8eef0db7548c7a51adb894e
      - --initial-cluster-state
      - existing
      - --initial-cluster
      - etcd1=http://192.168.56.42:2380,etcd2=http://192.168.56.33:2380,etcd3=http://192.168.56.42:2382

将原来的–discovery临时注释,添加下面几行,然后进行节点启动,节点启动成功之后,把上面这一次临时修改还原就可以。