公司开发定的数据库MYSQL规范

我们公司相当多的项目用的是mysql数据库,但是大家在开发过程中对mysql的认识问题,往往在数据库设计时对字段的定义不一致,在开发时对sql语句的执行出现问题,特地把一些通用性的、值得注意的问题做一下总结
一、数据库的设计规范
1、必须使用InnoDB存储引擎
原因:支持事务安全、行级锁、并发性能更好(查询不加锁,完全不影响查询),内存缓存页优化使得资源利用率更高,mysql5.6版本开始支持 全文索引
2、必须使用utf-8的字符编码
原因:这个无需过多解释,和网站以及其他系统完全统一,避免转码带来不必要的麻烦,而且系统数据接口都是使用json格式。
3、数据库、表、字段名必须有意义并且必须加入中文注释
原因:避免自己遗忘,方便他人进行开发,要不然一段时间之后谁还知道这是用来干什么的
4、禁止使用存储过程、视图、触发器
原因:高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引,计算还是使用程序来实现。使用存储过程等非常难于进行调试和测试。
5、禁止存储文件和图片
原因:存储路径在速度和空间方面会有更好的提升
6、数据库中表的数量不能高于500
原因:做好前期设计,尽量把一些相关度低的表进行分库处理
7、库名、表名、字段名的命名规则
所有的名字都使用小写并且间隔使用下划线风格,不超过32个字符,必须要见名知意,尽量使用英文,但是绝对禁止拼音英文混用命名。
 二、表的设计规则
8、表中的字段数不能超过30
原因:如果字段过多,就要把一些不常用的字段进行分表处理
9、表明和索引名统一
例如:表名table_xxx,非唯一索引名index_xxx,唯一索引名unique_xxx
10、所有表必须至少有一个主键,例如自增主键
原因:
a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率
c) 无主键的表删除,在row模式的主从架构,会导致备库夯住
11、禁止使用外键,如果要有外键完整性约束,必须使用程序进行控制
原因:外键会导致表之间耦合度增加,update与delete操作都会涉及相关联的表,非常影响sql 的性能,甚至会造成死锁。高并发情况下非常影响数据库性能,大数据高并发业务场景数据库使用以性能优先
三、字段的设计规范
12、所有字段都要定义为NOT NULL并提供默认值
原因:
1)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
2)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多
3)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识
4)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!=’shenjian’,如果存在name为null值的记录,查询结果就不会包含name为null值的记录
13、在多字段的表中禁止使用TEXT、BLOB类型
原因:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能
14、使用整数禁止使用小数存储货币
原因:价格乘以100来使用整数存储,小数在运算过程中会导致钱对不上
15、手机号必须使用varchar(20)进行存储
原因:
1)涉及到国家代号,可能出现类似+86
2)手机号会去做数学运算么?不会,所以不要使用int(11)
3)varchar可以支持模糊查询,例如:like“138%”
16、禁止使用ENUM,可使用TINYINT代替
原因:
1)增加新的ENUM值要做DDL操作
2)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?
四、索引的设计规范
17、表中索引的数量最好控制在5个以内
原因:
1)、索引也占用很大的空间
2)、索引在创建修改数据的情况需要大量更新索引
18、一个索引关联的字段在5个以内
原因:字段超过5个时,实际已经起不到有效过滤数据的作用了
19、禁止在更新十分频繁、或者区分度不高的属性上建立索引
原因:
1)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
2)“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似
20、建立组合索引,必须把区分度高的字段放在前面
解读:能够更加有效的过滤数据
五、sql优化
21、禁止使用SELECT *,只获取必要的字段,需要显示说明列属性
原因:
1)读取不需要的列会增加CPU、IO、NET消耗
2)不能有效的利用覆盖索引
3)使用SELECT *容易在增加或者删除字段后出现程序BUG
22、禁止使用INSERT INTO t_xxx VALUES(yyy),必须显示指定插入的列属性
原因:容易在增加或者删除字段后出现程序BUG
23、禁止使用属性隐式转换
原因:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引,
where 条件语句里,字段属性和赋给的条件,当数据类型不一样,这时候是没法直接比较的,需要进行一致转换,这种情况是无法使用索引的。
24、禁止在WHERE条件的属性上使用函数或者表达式
原因:SELECT uid FROM t_user WHERE from_unixtime(day)>=’2017-02-15′ 会导致全表扫描,而不能使用索引
正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15 00:00:00’)
25、禁止负向查询,以及%开头的模糊查询
解读:
a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描,而不使用索引
b)%开头的模糊查询,同样会导致全表扫描,不能使用索引
26、禁止在大表中使用JOIN查询,禁止大表使用子查询
解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能
27、禁止使用OR条件,都改为IN查询
原因:旧版本Mysql的OR查询是不能命中索引的,即使新版能命中索引,为何要让数据库耗费更多的CPU呢?

28、应用程序必须捕获SQL异常的功能,并有相应处理

http://www.architecy.com/archives/456

systemctl管理双redis启动、停止、开机自动启动

systemctl管理双redis启动、停止、开机自动启动
1. 创建服务
用service来管理服务的时候,是在/etc/init.d/目录中创建一个脚本文件,来管理服务的启动和停止,在systemctl中,也类似,文件目录有所不同,在/lib/systemd/system目录下创建一个脚本文件redis_6379.service,里面的内容如下:

[Unit]
Description=Redis
After=network.target

[Service]
ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis_6379.conf  –daemonize no
ExecStop=/usr/local/bin/redis-cli -h 127.0.0.1 -p 6379 shutdown

[Install]
WantedBy=multi-user.target
[Unit] 表示这是基础信息 
Description 是描述
After 是在那个服务后面启动,一般是网络服务启动后启动
[Service] 表示这里是服务信息 
ExecStart 是启动服务的命令
ExecStop 是停止服务的指令
[Install] 表示这是是安装相关信息 
WantedBy 是以哪种方式启动:multi-user.target表明当系统以多用户方式(默认的运行级别)启动时,这个服务需要被自动运行。
更详细的service文件说明请访问:这里

2. 创建软链接
创建软链接是为了下一步系统初始化时自动启动服务

ln -s /lib/systemd/system/redis_6379.service /etc/systemd/system/multi-user.target.wants/redis_6379.service
1
创建软链接就好比Windows下的快捷方式 
ln -s 是创建软链接 
ln -s 原文件 目标文件(快捷方式的决定地址)

如果创建软连接的时候出现异常,不要担心,看看/etc/systemd/system/multi-user.target.wants/ 目录是否正常创建软链接为准,有时候报错只是提示一下,其实成功了。

$ ll /etc/systemd/system/multi-user.target.wants/
total 8
drwxr-xr-x  2 root root 4096 Mar 30 15:46 ./
drwxr-xr-x 13 root root 4096 Mar 13 14:18 ../
lrwxrwxrwx  1 root root   31 Nov 23 14:43 redis_6379.service -> /lib/systemd/system/redis_6379.service
…略…

3. 刷新配置
刚刚配置的服务需要让systemctl能识别,就必须刷新配置

$ systemctl daemon-reload
如果没有权限可以使用sudo
$ sudo systemctl daemon-reload
4. 启动、重启、停止
启动redis

$ systemctl start redis_6379
重启redis

$ systemctl restart redis_6379 
停止redis

$ systemctl stop redis_6379
1
5. 开机自启动
redis服务加入开机启动

$ systemctl enable redis_6379
1
禁止开机启动

$ systemctl disable redis_6379
1
6. 查看状态
查看状态

$ systemctl status redis

● redis_6379.service – Redis
   Loaded: loaded (/usr/lib/systemd/system/redis_6379.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2018-11-12 14:32:32 CST; 2min 30s ago
  Process: 305 ExecStop=/usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6379 shutdown (code=exited, status=0/SUCCESS)
 Main PID: 335 (redis-server)
   CGroup: /system.slice/redis_6379.service
           └─335 /usr/local/redis/bin/redis-server 127.0.0.1:6379

Nov 12 14:32:32 10-13-35-210 systemd[1]: Started Redis.
Nov 12 14:32:32 10-13-35-210 systemd[1]: Starting Redis…

其他端口,复制一份

redis_6379

阿根廷DNS服务器地址

阿根廷 DNS服务器列表
DNS 主机名 详情
216.244.192.32 nscache1.sinectis.com.ar.
216.244.192.3 ns2.sinectis.com.ar.
201.251.124.157 201-251-124-157.static.speedy.com.ar.
190.210.59.45 mx-maipu-new.maruba.com.ar.
190.210.59.40 customer-static-210-59-40.iplannetworks.net.
64.76.6.126 64-76-6-126.dynamic.impsat.net.ar.
200.45.85.226 dns1.savantpharm.com.ar.
201.251.98.226
190.210.108.225 customer-static-210-108-225.iplannetworks.net.
186.136.21.210 210-21-136-186.fibertel.com.ar.
190.210.59.36 customer-static-210-59-36.iplannetworks.net.
200.16.163.83 dns2.irsacorp.com.ar.
201.234.24.25 201-234-24-25.static.impsat.net.ar.
179.41.14.223 179-41-14-223.speedy.com.ar.
186.153.224.111 host111.186-153-224.telecom.net.ar.
190.103.16.97 97.1-200.
190.7.58.142
201.234.24.49 201-234-24-49.static.impsat.net.ar.
200.59.229.170 inalambrico170-229-nqn.neunet.com.ar.
200.117.248.147 host117248147.arnet.net.ar.
209.13.156.34 www.centrojosit.com.ar.
201.251.101.135 201-251-101-135.static.indicom.com.ar.
181.14.245.186 host186.181-14-245.telecom.net.ar.
201.216.200.66 mail.fondosargentina.org.ar.
190.2.24.245 mail.amia-empleos.org.ar.
190.216.56.107
190.7.58.147
190.104.196.254 static.254.196.104.190.cps.com.ar.
190.105.165.1 proxy.pccp.net.ar.
200.123.249.170 host170.200-123-249.dialup.intercity.net.ar.
181.15.221.41 host41.181-15-221.telecom.net.ar.
186.0.193.220
200.68.88.93 customer-static-68-88-93.iplannetworks.net.
200.43.56.249
181.14.249.44 host44.181-14-249.telecom.net.ar.
200.43.233.3
181.15.221.106 host106.181-15-221.telecom.net.ar.
190.196.239.95
190.221.14.210 host210.190-221-14.telmex.net.ar.
170.210.83.60
181.189.223.142 host181-189-223-142.wilnet.com.ar.
190.196.239.94
186.0.181.253 186-0-181-253.iperactive.com.ar.
200.41.192.172 172.host.advance.com.ar.
190.108.192.216 host-190.108.192.216.after-wire.com.
200.69.10.181 mail.dodetodo.com.ar.
186.148.147.176 176.cxlvii.static.eternet.cc.
200.5.203.242
190.107.240.1
200.61.21.225 edna225.silicanetworks.net.ar.
181.15.245.179 host179.181-15-245.telecom.net.ar.
186.190.169.234 host234.186-190-169.nodosud.com.ar.
190.16.39.63 63-39-16-190.fibertel.com.ar.
190.196.238.209
190.3.67.54 mx1.cordialfinanciera.com.ar.
200.61.21.113 edna113.silicanetworks.net.ar.
190.225.164.11
190.224.207.129 host129.190-224-207.telecom.net.ar.
190.221.163.157 host157.190-221-163.telmex.net.ar.
170.210.83.34

centos7 系统 df 无反应问题处理说明

问题描述

近期陆续碰到几台主机 df 卡住的问题, 监控程序由于超时引起相关的警报, 系统环境和 strace df 如下所示:

kernel-3.10.0-514.21.2
systemd-219-57 

strace 显示卡在了 /proc/sys/fs/binfmt_misc 状态中:

# strace df
execve("/usr/bin/df", ["df"], [/* 29 vars */]) = 0
brk(0)                                  = 0x1731000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa7720a7000
access("/etc/ld.so.preload", R_OK)      = 0
open("/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=24, ...}) = 0
......
stat("/sys/fs/cgroup/memory", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat("/sys/kernel/config", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat("/", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0
stat("/proc/sys/fs/binfmt_misc", 

在 systemd 服务中, 挂载 /proc/sys/fs/binfmt_misc 的只有两个 unit, 分别为 proc-sys-fs-binfmt_misc.automount 和 proc-sys-fs-binfmt_misc.mount, 查看几台问题机器系统服务状态, 仅有 automount 服务启动:

 proc-sys-fs-binfmt_misc.automount             loaded    active   running   Arbitrary Executable File Formats File System Automount Point
  proc-sys-fs-binfmt_misc.mount                 loaded    inactive dead      Arbitrary Executable File Formats File System
  systemd-binfmt.service                        loaded    inactive dead      Set Up Additional Binary Formats 

问题机器分别进行过触发 mount 的操作, 但是没有进行过 systemctl stop proc-sys-fs-binfmt_misc.mount 操作:

May 30 17:34:53 cz1 systemd: Got automount request for /proc/sys/fs/binfmt_misc, triggered by 292977 (sysctl)
May 30 17:34:53 cz1 systemd: Mounting Arbitrary Executable File Formats File System...
May 30 17:34:53 cz1 systemd: Mounted Arbitrary Executable File Formats File System.
May 31 10:45:00 cz1 systemd: Unmounting Arbitrary Executable File Formats File System...
May 31 10:45:00 cz1 systemd: Unmounted Arbitrary Executable File Formats File System. 

查看 snoopy 日志, unmount 操作由 pid 13573 进程操作, 该 pid 的 sid 为 1, pid 为 1 的进程为 /usr/lib/systemd/systemd --switched-root --system --deserialize 22, snoopy 行为等同 automount 超时自动 unmount :

May 31 10:45:00 cz1 systemd[1]: Unmounting Arbitrary Executable File Formats File System...
May 31 10:45:00 cz1 snoopy[13573]: [uid:0 sid:1 tty:(none) cwd:/ filename:/bin/umount]: /bin/umount /proc/sys/fs/binfmt_misc
May 31 10:45:00 cz1 systemd[1]: Unmounted Arbitrary Executable File Formats File System. 

问题机器的 mount 信息包含如下, timeout 为 300, 这个是 systemd-219-32 之前版本的默认参数, 实际上在 219-32 版本之前红帽还未引入超时功能, 所以超过 300s 之后 automount 不会自动进行 unmount 操作, 下面的内容仅有一条 binfmt 信息, 也意味着两台机器中没有访问 /proc/sys/fs/binfmt_misc 目录的行为:

systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=30,pgrp=1,timeout=300,minproto=5,maxproto=5,direct 0 0 

备注: 问题主机由于在 yum 安装 perl 依赖的过程中更新了 systemd 到 219-57 新版, 但是没有做重启操作, 所以 mount 显示的 timeout 值还是 300, 重新 reload systemd 或 重启主机后新版 systemd 生效, timeout 值会变为默认 0.

automount 如何工作

systemd 通过 automount 实现了对文件系统挂载点进行自动挂载和控制的特性, 在用户访问指定目录的时候, 由 automount 判断自动进行挂载, nfs, sshfs 等使用较多, 目前为止在 centos7 系统中我们仅发现 binfmt_msic 类型是操作系统需要自动挂载的. 详见 systemd.automount

原因说明

从上述搜集信息来看, 更像是 systemd 认为 proc-sys-fs-binfmt_misc.mount 已经关闭, 不过系统或内核还持有 /proc/sys/fs/binfmt_misc 挂载点, 引起竞争, 这样 df 在访问挂载点的时候则一直处于挂起状态. 这个问题类似 nfs 服务端异常断掉, client 端直接访问挂载点也会挂起一样. 没有做超时处理则 df 一直处于等待状态.
详见: 1534701

触发条件

由于出现问题之前几台问题主机都有 unmount 行为, 所以不能按照下面两个 bug 来概述我们出现的问题:
1498318
1534701

不过目前已知的触发条件包含以下两种方式:

第一种

人为制造异常:

1. 修改 proc-sys-fs-binfmt_misc.automount 的 TimeoutIdleSec 为大于 0 的值, 219-30 版本默认300, 不用修改;
2. 访问 /proc/sys/fs/binfmt_misc/ 目录触发 aumount 自动挂载;
3. 在1中还没有超时的时候执行 systemctl stop proc-sys-fs-binfmt_misc.mount, 手动 unmount 掉挂载点; 

在执行第三步的时候 systemd 报以下异常, unmount 操作不能注册, 而系统内核会继续持有挂载点, 进而引起 df 卡住. 另外在默认 timeout 为 0 的情况下人为制造的异常不会引起 hang 住:

Jun  6 21:19:50 cz1 snoopy[162749]: [time_ms:357 login:root uid:0 pid:162749 ppid:162676 sid:162676 tty:/dev/pts/0 cwd:/root filename:/usr/bin/systemctl username:root]: systemctl stop proc-sys-fs-binfmt_misc.mount
Jun  6 21:19:50 cz1 snoopy[162750]: [time_ms:359 login:root uid:0 pid:162750 ppid:162749 sid:162676 tty:/dev/pts/0 cwd:/root filename:/usr/bin/systemd-tty-ask-password-agent username:root]: /usr/bin/systemd-tty-ask-password-agent --watch
Jun  6 21:19:50 cz1 snoopy[162751]: [time_ms:359 login:root uid:0 pid:162751 ppid:162749 sid:162676 tty:/dev/pts/0 cwd:/root filename:/usr/bin/pkttyagent username:root]: /usr/bin/pkttyagent --notify-fd 5 --fallback
Jun  6 21:19:50 cz1 polkitd[1036]: Registered Authentication Agent for unix-process:162749:2586612889 (system bus name :1.232140 [/usr/bin/pkttyagent --notify-fd 5 --fallback], object path /org/freedesktop/PolicyKit1/AuthenticationAgent, locale en_US.UTF-8)
Jun  6 21:19:50 cz1 snoopy[162755]: [time_ms:371 login:(unknown) uid:0 pid:162755 ppid:1 sid:1 tty:(none) cwd:/ filename:/bin/umount username:root]: /bin/umount /proc/sys/fs/binfmt_misc
Jun  6 21:19:50 cz1 polkitd[1036]: Unregistered Authentication Agent for unix-process:162749:2586612889 (system bus name :1.232140, object path /org/freedesktop/PolicyKit1/AuthenticationAgent, locale en_US.UTF-8) (disconnected from bus) 

执行 systemctl restart proc-sys-fs-binfmt_misc.automount 即可恢复所有堵住的命令. 另外在 TimeoutIdleSec 为 0 的情况下不会复现此问题, 在 TimeoutIdleSec 大于 0 的情况下, 给 systemd 发送 kill 信号的时候会导致 timeout 失效.

第二种

如下日志:

May 31 10:45:00 cz1 systemd[1]: Unmounting Arbitrary Executable File Formats File System...
May 31 10:45:00 cz1 snoopy[6313]: [uid:0 sid:1 tty:(none) cwd:/ filename:/bin/umount]: /bin/umount /proc/sys/fs/binfmt_misc
May 31 10:45:00 cz1 systemd[1]: Unmounted Arbitrary Executable File Formats File System. 

我们以 snoopy 日志的 umount 操作为出发点, 在 systemd 源文件中查找对应行为的触发条件, 以 systemd-219-31 版本为例, 只有 mount_enter_unmounting 函数进行了 /bin/umount 操作, 详见 src/core/mount.c 文件:

static void mount_enter_unmounting(Mount *m) {
...
   r = exec_command_set(m->control_command, "/bin/umount", m->where, NULL);
...
} 

而 mount_enter_unmounting 函数仅被两个函数调用, 分别为正常 stop 操作的响应函数 mount_stop 和信号事件处理函数 mount_sigchld_event :

...
#define RETRY_UMOUNT_MAX 32
...
static int mount_stop(Unit *u) {
...
        mount_enter_unmounting(m);
        return 1;
}

static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
...
        MountResult f;
...
        if (is_clean_exit(code, status, NULL))
                f = MOUNT_SUCCESS;
        else if (code == CLD_EXITED)
                f = MOUNT_FAILURE_EXIT_CODE;
        else if (code == CLD_KILLED)
                f = MOUNT_FAILURE_SIGNAL;
        else if (code == CLD_DUMPED)
                f = MOUNT_FAILURE_CORE_DUMP;
        else
                assert_not_reached("Unknown code");
...
        case MOUNT_UNMOUNTING:
        case MOUNT_UNMOUNTING_SIGKILL:
        case MOUNT_UNMOUNTING_SIGTERM:

                if (f == MOUNT_SUCCESS) {

                        if (m->from_proc_self_mountinfo) {

                                /* Still a mount point? If so, let's
                                 * try again. Most likely there were
                                 * multiple mount points stacked on
                                 * top of each other. Note that due to
                                 * the io event priority logic we can
                                 * be sure the new mountinfo is loaded
                                 * before we process the SIGCHLD for
                                 * the mount command. */

                                if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
                                        log_unit_debug(u->id, "%s: mount still present, trying again.", u->id);
                                        m->n_retry_umount++;
                                        mount_enter_unmounting(m);
                                } else {
                                        log_unit_debug(u->id, "%s: mount still present after %u attempts to unmount, giving up.", u->id, m->n_retry_umount);
                                        mount_enter_mounted(m, f);
                                }
                        } else
                                mount_enter_dead(m, f); 

在 src/core/manager.c 函数中可以看到仅有函数 invoke_sigchild_event 调用了 sigchld_event 函数, invoke_sigchild_event 函数则仅在子进程退出(CLD_EXITED) 或子进程被杀(CLD_KILLED) 或子进程异常中断(CLD_DUMPED) 的时候才会被 manager_dispatch_sigchild 函数调用, manager_dispatch_sigchild 函数分别在 manager_loop 和 manager_dispatch_signal_fd 中调用.

static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) {
......
        UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
}

static int manager_dispatch_sigchld(Manager *m) {
...
                if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
                ...
                        if (u1)
                                invoke_sigchld_event(m, u1, &si);
                        u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
                        if (u2 && u2 != u1)
                                invoke_sigchld_event(m, u2, &si);
                        u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
                        if (u3 && u3 != u2 && u3 != u1)
                                invoke_sigchld_event(m, u3, &si);
                }
...
}

int manager_loop(Manager *m) {
...
        /* There might still be some zombies hanging around from
         * before we were exec()'ed. Let's reap them. */
        r = manager_dispatch_sigchld(m);
}

static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
...
        if (sigchld)
                manager_dispatch_sigchld(m);
....
} 

问题主机的 umount 日志显示不是正常的 stop 操作, 但也未看找到相关的信号信息. 不过从整个 systemd 日志来看 umount 操作更像是属于上述 mount_sigchld_event 函数的行为, 即在子进程为 CLD_EXITED 或 CLD_KILLED 或 CLD_DUMPED 的时候, 调用 mount_sigchld_event 函数, 这时 systemd 的状态为 UNMOUNTING, 或者收到 SIGKILL, SIGTERM 信号的时候, 而系统或内核认为当前状态为 SUCCESS (f 变量), 在从 /etc/mtab(mtab 为 /proc/self/mountinfo 的软链) 读取到 mount 信息的时候, 当前的重试次数(n_retry_umount) 小于 RETRY_UMOUNT_MAX (32) 的时候则进行一次 mount_enter_unmounting 函数调用. 另外现在也并没有找到系统内核会和 systemd 的状态相反, 可能是子进程退出或子进程异常终止.

这种方式没有好的重现方法, 不过处理方式应该和第一种一样, 重启 proc-sys-fs-binfmt_misc.automount 即可.

解决方式

目前并没有找到真正的触发条件, 不过我们认为 df 卡住问题在本质上还是由于 systemd 和 kernel 之间存在竞争而引起的, 导致其它程序访问挂载点的时候出现 hang 住的现象, 根据 redhat bugzilla 的描述, 只要解决掉 mount 和 automount 过程中可能产生的竞争即可, 我们可以通过关闭 proc-sys-fs-binfmt_misc.automount 释放已经存在的竞争来解决 df hang 住的问题, 所以整体上包含以下三种解决方式:

1. systemctl restart proc-sys-fs-binfmt_misc.automount;
2. 升级到最新 systemd-219-57 版本; 
3. 按照红帽知识库的步骤对 proc-sys-fs-binfmt_misc.automount 进行 mask 操作, 只进行静态的 mount 操作; 

这几种方式对应用程序无害, 第一种方式影响最小. 不过我们在排错的过程中发现了一些其它相关的 bug, 所以采取第二种方式会更稳妥,新版的 systemd 对 1354410 和 1498318 两个 bug 做了状态反馈处理, 即便有问题也不会出现 hang 住的现象, 另外默认超时时间为 0, 对程序来讲相当于只做了重启操作, 不过后续的版本可能存在变更的可能, 所以保险起见可以将在 proc-sys-fs-binfmt_misc.automount 配置中指定 TimeoutIdleSec=0 参数值, 避免自动进行 unmount 操作. 最后重启机器即可; 第三种操作则可能影响其它有 automount 需求的软件(比如新版本的 postgresql), 不过很多软件在检测到没有启动 automount 的情况下会进行额外的 mount 操作, 不会有严重的影响.

参考链接:

1498318
1534701
1709649
github-5916
github-commit

红帽知识库

3346491 与我们的触发条件不一样, 并不是重新激活已经 mask 的 unit 问题引起的, 仅提供了类似问题的解决方法.

其它问题

在查找根源的过程中发现了几个相关的问题, 这些问题随 systemd 版本的变更进行了修复:

219-32
- automount: add expire support(TimeoutIdleSec) (#1354410)
219-46
- automount: if an automount unit is masked, don't react to activation anymore (#5445) (#1498318)
219-57
- BZ - 1498318 - du/df hang indefinitely (RHEL Atomic Host 7.4) 
https://arstercz.com/centos7-%E7%B3%BB%E7%BB%9F-df-hang-%E9%97%AE%E9%A2%98%E5%A4%84%E7%90%86%E8%AF%B4%E6%98%8E/

Caddy nginx服务器QUIC部署

Caddy 简介

Caddy是一个Go语言写的,易于使用的通用Web服务器。它具有如下的一些功能:

  • 配置简单:Caddy服务器的运行可以通过Caddyfile配置文件进行配置,Web服务配置起来非常简单。
  • 自动的HTTPS:它可以自动地为我们申请 Let’s Encrypt 域名证书,管理所有的密码学设施,并进行配置。
  • HTTP/2:默认支持HTTP/2(由Go标准库支持)
  • 虚拟主机托管:Caddy支持TLS的SNI。SNI是在2006年加入TLS的一个TLS扩展。客户端在TLS握手的Client Hello消息中,通过SNI扩展将请求的资源的域名发送给服务器,服务器根据SNI的域名来下发TLS证书。这样就可以在具有单个公网IP的同一台主机上部署多个不同的域名的服务。可以为Caddy服务的不同域名配置不同的证书和密钥。
  • QUIC支持:Caddy实验性地支持QUIC协议,以获取更好的性能。
  • TLS session ticket key rotation for more secure connections
  • 良好的可扩展性:因此Caddy非常方便针对自己的需求做定制。
  • 随处运行:这主要与Go应用程序的特性有关。Go的模块都被编译为静态库,这使得Go的应用程序在编译为可执行文件时都是静态链接的,因而依赖的动态库极少,这使得部署使用非常方便。

自动的HTTPS、HTTP/2支持、QUIC支持和随处运行这些特性非常有吸引力,特别是对QUIC的支持。

此外,Caddy的性能非常好。下面两幅图是我的静态个人博客站点,分别是用Caddy和nginx作为Web服务器,打开主页所需的加载时间对比:
Service with Caddy

Service with nginx

上面的图显示了以Caddy作为Web服务器,主页的加载时间只有680ms;下面的图显示以nginx作为Web服务器,主页的加载时间则长达1.99s,要慢接近2倍。

Caddy部署

Caddy应用程序不依赖于其它组件,且官方已经为不同的平台提供了二进制可执行程序。可以通过如下三种方式之一安装Caddy:

  • 在 下载页,通过浏览器定制自己需要的功能集,并下载相应的二进制可执行程序。
  • 预编译的 最新发行版 二进制可执行程序。
  • curl getcaddy.com 来自动安装:curl https://getcaddy.com | bash

将caddy的路径加如PATH环境变量中。之后可以 cd 进入网站的文件夹,并运行 caddy来提供服务。默认情况下,Caddy在2015端口上为网站提供服务。

要定制网站提供服务的方式,可以为网站创建名为Caddyfile的文件。当运行 caddy 命令时,它会自动地在当前目录下寻找并使用Caddyfile文件来为自己做配置。

要了解更多关于Caddyfile文件的写法,可以参考 Caddyfile 文档

注意生产环境网站默认是通过HTTPS提供服务的。

Caddy还有命令行接口。运行caddy -h 可以查看基本的帮助信息,或参考 CLI文档 来了解更多详情。

以Root运行:建议不要这样做。但依然可以通过像这样使用setcap来监听端口号小于1024的端口:sudo setcap cap_net_bind_service=+ep ./caddy

由源码运行

注意:需要安装 Go 1.7或更新的版本才可以。

  1. go get github.com/mholt/caddy/caddy
  2. cd 进入网站的目录
  3. 执行caddy(假设 $GOPATH/bin 已经在 $PATH 中了)

Caddy的 main() 再caddy子目录下。要编译Caddy,可以使用在那个目录下找到的 build.bash

在生产环境运行

Caddy项目官方不维护任何系统特有的集成方法,但下载的文档中包含了社区共享的 非官方资源,用以帮助在生产环境运行Caddy。

以何种方式运行Caddy全由自己决定。许多用户使用 nohup caddy & 就可以满足需求了。其他人使用 screen。有些用户需要再重启之后就运行Caddy,可以在触发重启的脚本中来做到这一点,通过给init脚本添加一个命令,或给操作系统配置一个service。

可以看一下我的个人博客站点的完整Caddyfile内容:

					
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
					
wolfcstech.com:80 www.wolfcstech.com:80 {
root /home/www-data/www/hanpfei-documents/public
redir 301 {
if {>X-Forwarded-Proto} is http
/ https://{host}{uri}
}
}
wolfcstech.com:443 www.wolfcstech.com:443 {
tls /home/www-data/www/ssl/chained.pem /home/www-data/www/ssl/domain.key
#tls test@admpub.com
root /home/www-data/www/hanpfei-documents/public
gzip
log ../access.log
}

启用QUIC

Caddy 0.9 已经实验性地提供了对QUIC的支持,这主要通过 lucas-clemente/quic-go 来实现。要尝试这个特性,可以在运行caddy时加上 -quic 标记:

					
1
					
$ caddy -quic

这样执行之后,则带有TLS加密的Web服务,在客户端支持QUIC时,将默认通过QUIC协议来完成数据的传输。

不启用QUIC时,在启动caddy之后,在服务器端查看已打开的端口号:

					
1
2
3
4
5
6
7
					
# lsof -i -P
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
AliYunDun 1120 root 10u IPv4 2023899 0t0 TCP 139.196.224.72:40309->106.11.68.13:80 (ESTABLISHED)
. . . . . .
caddy 6163 root 6u IPv6 2338478 0t0 TCP *:80 (LISTEN)
caddy 6163 root 8u IPv6 2338479 0t0 TCP *:443 (LISTEN)
. . . . . .

而在通过如下命令:

					
1
					
# nohup ./caddy -quic &

启用QUIC提供Web服务之后,在服务器端查看已打开端口号,则可以看到如下内容:

					
1
2
3
4
5
6
7
8
9
					
# lsof -i -P
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
AliYunDun 1120 root 10u IPv4 2023899 0t0 TCP 139.196.224.72:40309->106.11.68.13:80 (ESTABLISHED)
. . . . . .
caddy 6222 root 6u IPv6 2338880 0t0 TCP *:80 (LISTEN)
caddy 6222 root 8u IPv6 2338881 0t0 TCP *:443 (LISTEN)
caddy 6222 root 9u IPv6 2338883 0t0 UDP *:80
caddy 6222 root 10u IPv6 2338885 0t0 UDP *:443
. . . . . .

Caddy 除了监听http的TCP 80端口和https 的TCP 443端口之外,还监听了UDP的80和443端口。

客户端支持

Chrome 52+ 支持QUIC而无需白名单,但需要确认 #enable-quic 标记已经被启用了。通过在Chrome浏览器的地址栏输入chrome://flags/

Enable QUIC

并根据需要启用QUIC。

然后通过Chrome打开你的网站,则它应该是以QUIC提供服务的!可以通过打开inspector 工具并进入Security tab来验证这一点。重新加载页面并点击来查看连接详情:

caddy005.png

如果你使用老版的Chrome,则为了省事,可以升级一下。

如果你不想升级,则可以:你将需要以特殊的参数来运行Chrome。再Mac上 (将YOUR_SITE替换为你的网站的实际域名)执行如下命令:

					
1
2
3
4
5
					
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--user-data-dir=/tmp/chrome \
--no-proxy-server \
--enable-quic \
--quic-host-whitelist="YOUR_SITE" "YOUR_SITE"

QUIC的好处

QUIC是基于UDP的TLS+HTTP的可靠传输协议。它加速了TLS握手为只有一个往返,避免了TCP慢启动,并提供了网络切换时的可靠性。通过QUIC可以让网站加载更快且更可靠。

问题排解

首先,确保在Caddyfile文件中为域名做了适当的设置,还要确保在启动Chrome的命令行中为域名做了适当的设置。

接着,网站必须使用一个真实的可信的证书(至少,是在写的时候)。

如果那都是好的,而且你对Go语言比较了解,则你可以添加 import "github.com/lucas-clemente/quic-go/utils",并在Caddy的main()函数的某个地方调用utils.SetLogLevel(utils.LogLevelDebug)。那将提供非常详细的输出。(注意这个log设施不是一个公共的API)。

当你进入chrome://net-internals/#events,你应该看到一些QUIC事件被标为红色。

Net Events

坚持原创技术分享,您的支持将鼓励我继续创作!

https://www.wolfcstech.com/2017/01/09/Caddy%20Web%E6%9C%8D%E5%8A%A1%E5%99%A8QUIC%E9%83%A8%E7%BD%B2/

php安装IMAP依赖

[root@hexu.org imap]$ yum install -y libc-client-devel

[root@hexu.org imap]$ ln -s /usr/lib64/libc-client.so /usr/lib/libc-client.so

3. 进行安装

[root@hexu.org imap]$ /usr/local/php/bin/phpize

[root@hexu.org imap]$ ./configure –with-php-config=/usr/local/php/bin/php-config –with-imap –with-imap-ssl –with-kerberos

[root@hexu.org imap]$ make && make install

4. 最后调整php.ini

[root@hexu.org imap]$ vi /usr/local/php/lib/php.ini

##vi php.ini add following config

[imap]

extension = imap.so

5. 检查是否安装成功

[root@hexu.org imap]# php -v

PHP 5.3.23 (cli) (built: Apr  7 2013 23:20:21) 

Copyright (c) 1997-2013 The PHP Group

Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies

[root@hexu.org imap]# php -m | grep imap

imap ## 看到这里,说明成功安装了

按上面顺序安装应该不会有报错,如果发现错误根据提示找相应的依赖包安装即可,下面举例安装过程遇到的2个error.

1. 没有安装libc-client-devel导致,按上面第一步安装即可, Error info:

checking for utf8_mime2text signature… new

checking for U8T_DECOMPOSE… 

configure: error: utf8_mime2text() has new signature, but U8T_CANONICAL is missing. This should not happen. Check config.log for additional information.

2. 找不到libc-client.a library, 需要手动添加文件link, Errof info:

checking for crypt in -lcrypt… yes

configure: error: Cannot find imap library (libc-client.a). Please check your c-client installation.

解决方法:

# ln -s /usr/lib64/libc-client.so /usr/lib/libc-client.so

如果出现 Cannot find imap library (libc-client.a).

我们只要执行

# yum install libc-client-devel.x86_64

# ln -s /usr/lib64/libc-client.so /usr/lib/libc-client.so

python版本问题,iotop执行错误except ImportError, e:解决

# iotop 
  File “/usr/sbin/iotop”, line 10
    except ImportError, e:
                      ^
SyntaxError: invalid syntax
[root@eanquan ~]# vi /usr/sbin/iotop 
#!/usr/bin/python
# iotop: Display I/O usage of processes in a top like UI
# Copyright (c) 2007, 2008 Guillaume Chazarain <guichaz@gmail.com>, GPLv2
# See iotop –help for some help

import sys

try:
    from iotop.ui import main
except ImportError, e:
    print e
    print ‘To run an uninstalled copy of iotop,’
    print ‘launch iotop.py in the top directory’
else:
    try:
        main()
    except KeyboardInterrupt:
        pass

    sys.exit(0)

# ll /usr/bin/python2*0 B/s  0.00 %  0.00 % [md/1]
lrwxrwxrwx. 1 root root       6 Jul  8  2016 /usr/bin/python2 -> python
-rwxr-xr-x. 2 root root    4864 Jan 22  2014 /usr/bin/python2.6
-rwxr-xr-x  1 root root 6273947 Dec  4  2017 /usr/bin/python2.7

执行python的#!/usr/bin/python 改为

#!/usr/bin/python2.6就可以

MQ产品比较-ActiveMQ-RocketMQ

几种MQ产品说明:

ZeroMQ : 扩展性好,开发比较灵活,采用C语言实现,实际上他只是一个socket库的重新封装,如果我们做为消息队列使用,需要开发大量的代码

RabbitMQ: 结合erlang语言本身的并发优势,性能较好,但是不利于做二次开发和维护

ActiveMQ: 历史悠久的开源项目,已经在很多产品中得到应用,实现了JMS1.1规范,可以和spring-jms轻松融合,实现了多种协议,不够轻巧(源代码比RocketMQ多).,支持持久化到数据库,对队列数较多的情况支持不好,不过我们的项目中并不会建很多的队列.

Redis: 做为一个基于内存的K-V数据库,其提供了消息订阅的服务,可以当作MQ来使用,目前应用案例较少,且不方便扩展

RocketMQ: 阿里巴巴的MQ中间件,在其多个产品下使用,并能够撑住双十一的大流量,他并没有实现JMS规范,使用起来很简单。部署由一个 命名服务(nameserver)和一个代理(broker)组成,nameserver和broker以及producer都支持集群,队列的容量受机器硬盘的限制,队列满后可以支持持久化到硬盘(也可以自己适配代码,将其持久化到NOSQL数据库中),队列满后会影响吞吐量,可以采用主备来保证稳定性,支持回溯消费,可以在broker端进行消息过滤.

针对消息中间件的选择可以从以下方面进行考虑:(主要对比ActiveMQ和RocketMQ)

  • 优先级:我们的项目对此需求不是特别明显,RocketMQ需要新建一个特殊队列来接收优先级高的队列,无法实现从0-65535这种细粒度的控制,ActiveMQ可以精细控制

  • 顺序:我们的消息总线中的消息应该都是无状态的,所以对消息的处理顺序没有严格的要求,如果有特殊要求的话可以在业务层进行控制,activeMQ无法保证严格的顺序,RocketMQ可以保证严格的消费顺序

  • 持久化:都支持

  • 稳定性:RoketMQ在稳定性上可能更值得信赖,支持多种集群方案,毕竟已经撑过几个双十一

  • 消息过滤:ActiveMQ仅支持在客户端消费的时候进行判断是否是自己需要的消息,RocketMQ可以在broker端进行过滤,对于我们的消息总线,这里可以节省大量的网络传输是否会有消息重发造成的重复消费:RocketMQ可以保证,ActiveMQ无法保证

  • 回溯消费:即重新将某一个时刻之前的消息重新消费一遍,我们对于这种需求应该很少,RocketMQ支持,ActiveMQ不支持(RocketMQ的队列是持久化到硬盘的,定期进行清除

  • 事务:都支持

  • 定时消费:RocketMQ支持

  • 消息堆积:就是当缓存消息的内存满了之后的解决方案,一种是丢弃策略,这种不会影响吞吐量,还有一种就是将消息持久化到磁盘,这种会影响吞吐量,在评估影响程度上,RocketMQ的成绩稍微好一点

  • 客户端不在线:RocketMQ可以在客户端上线后继续将未消费的消息推送到客户端

    目前比较活跃的几种MQ中间件产品的对比如下:(仅统计开源的项目)

ActiveMQ RabbitMQ RocketMq ZeroMQ
关注度
成熟度 成熟 成熟 比较成熟 不成熟
所属社区/公司 Apache Mozilla Public License Alibaba
社区活跃度
文档
特点 功能齐全,被大量开源项目使用 由于Erlang 语言的并发能力,性能很好 各个环节分布式扩展设计,主从 HA;支持上万个队列;多种消费模式;性能很好 低延时,高性能,最高 43万条消息每秒
授权方式 开源 开源 开源 开源
开发语言 Java Erlang Java C
支持的协议 OpenWire、STOMP、REST、XMPP、AMQP AMQP 自己定义的一套(社区提供JMS–不成熟) TCP、UDP
客户端支持语言 Java、C、C++、Python、PHP、Perl、.net 等 Java、C、C++、Python、 PHP、Perl 等 Java C++(不成熟) python、 java、 php、.net 等
持久化 内存、文件、数据库 内存、文件 磁盘文件 在消息发送端保存
事务 支持 支持 支持 不支持
集群 支持 支持 支持 不支持
负载均衡 支持 支持 支持 不支持
管理界面 一般
部署方式 独立、嵌入 独立 独立 独立
评价 优点:成熟的产品,已经在很多公司得到应用(非大规模场景)。有较多的文档。各种协议支持较好,有多重语言的成熟的客户端;缺点:根据其他用户反馈,会出莫名其妙的问题,切会丢失消息。 其重心放到activemq6.0 产品—apollo 上去了,目前社区不活跃,且对 5.x 维护较少;Activemq 不适合用于上千个队列的应用场景 优点: 由于erlang语言的特性,mq 性能较好;管理界面较丰富,在互联网公司也有较大规模的应用;支持amqp系诶,有多中语言且支持 amqp 的客户端可用缺点:erlang语言难度较大。集群不支持动态扩展。 优点:模型简单,接口易用(JMS 的接口很多场合并不太实用)。在阿里大规模应用。目前支付宝中的余额宝等新兴产品均使用rocketmq。集群规模大概在50 台左右,单日处理消息上百亿;性能非常好,可以大量堆积消息在broker 中;支持多种消费,包括集群消费、广播消费等。开发度较活跃,版本更新很快。缺点:没有在 mq 核心中去实现JMS 等接口
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dreamsunday/article/details/52223288

nginx 对文件(动态页面) 进行缓存 ( nginx content caching)

refer to: http://nginx.com/resources/admin-guide/caching/ 首先看一个完整的例子:

  proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:100m
                       loader_threshold=300 loader_files=200 max_size=200m;
  server {
          listen 80;
          server_name  www.tidev.in tidev.in;
          client_max_body_size 500m;
          charset utf-8;
          location / {
              proxy_hide_header "cache-control";
              proxy_hide_header Expires;

              proxy_ignore_headers Set-Cookie;
              proxy_ignore_headers Cache-Control;
              proxy_ignore_headers Expires;
              proxy_ignore_headers X-Accel-Expires;

              proxy_cache cache_one;
              proxy_cache_valid any 60s; # 任何内容,都缓存60秒钟 proxy_pass          http://tidev_servers; proxy_redirect default;
              proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header    X-Real-IP $remote_addr;
              proxy_set_header    Host $http_host;
              proxy_next_upstream http_502 http_504 error timeout invalid_header;

              proxy_cache_key $host$uri$is_args$args;
          }
          location ~ ^/assets/ {
              root /opt/app/tidev.in/public;
              expires 1y;
              add_header Cache-Control public;
              add_header ETag ""; break;
          }
  }

  upstream tidev_servers{
         server localhost:3600;
         server localhost:3601;
         server localhost:3602;
         server localhost:3603;
  }

注意:  

1. nginx 建议使用 1.7.9 以上版本, 亲测 1.7.9, 1.8.0 可用. 1.4.x 不可用. 所以,不要使用ubuntu 默认的 apt-get install 方式的nginx. 它是1.4的.

2. proxy_cache_path 务必出现在 proxy_cache 关键字之前, (也就是说,如果你用了include sites-enabled/*; , 那么,这句话要放在 proxy_cache_path之后!) 否则会报错: nginx: [emerg] the size 10485760 of shared memory zone “cache_one” conflicts with already declared size 0  

3. proxy_cache_key 是关键. 

          location / { proxy_hide_header "cache-control"; proxy_hide_header Expires; proxy_ignore_headers Set-Cookie; proxy_ignore_headers Cache-Control; proxy_ignore_headers Expires; proxy_ignore_headers X-Accel-Expires; proxy_cache cache_one; proxy_cache_valid any 60s; # 任何内容,都缓存60秒钟 proxy_pass http://tidev_servers; proxy_redirect default; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_cache_key $host$uri$is_args$args;
          }

nginx可以对某个请求进行缓存,

例子:

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m; server {
        proxy_cache one;
        location / {
            proxy_pass http://localhost:8000;
        }
    }
}

设置好了允许缓存后,进一步可以设置它的过期时间: (iteration 如何解释。。需要动手弄一下)

参与cache 过程 的,有两个角色, cache manager 和 cache loader:

1. cache manager 会循环的检查 cache的状态。当它发现 缓存的文件超过了 max_size 这个数目后,就会删掉最少访问的cache page. 

2. cache loader: 仅仅在nginx启动后 随之启动一次。它把之前的cache 信息加载到 shared memory中去。这在nginx启动的前几分钟会拖累nginx的速度。

以上的iteration, 比 loader_threshold(默认是200ms) 要少。 每次加载的文件数目小于 loader_files(默认是100),每个iteration 间隔 loader_sleeps (默认50ms)。 

下面是个例子: 

proxy_cache_path /data/nginx/cache keys_zone=one:10m
                 loader_threshold=300 loader_files=200; 

指定某个URL 要缓存

如果某个response来自 proxy_server, 并且request是 GET/HEAD 方法,则nginx 默认会把它做缓存. 

而且默认使用的key就是 url ,你也可以指定这个key, 例如:

proxy_cache_key “$host$request_uri$cookie_user”;

如果我们希望某个 url 至少被请求5次之后才被缓存,就这样:

proxy_cache_min_uses 5;

如果希望对POST和 DELETE进行缓存:

proxy_cache_methods GET HEAD POST;

下面的例子:对于 200 , 302的response, 缓存 10分钟, 

proxy_cache_valid 200 302 10m;  # 对于 200, 302,缓存10分钟
proxy_cache_valid 404 1m;       # 缓存1分钟 
proxy_cache_valid any 10m;      # 对于所有的响应,都缓存10分钟。

也可以根据条件来判断是否使用cache: ( cookie 中的变量:nocache, parameter中的变量:nocache 或者 comment, 只要有一个 不是空,也不是 0, 那么这个request就不会使用cache) 

proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;

对于下面的例子:压根就不使用cache:

proxy_no_cache $http_pragma $http_authorization;

下面是一个更大的例子:

http {
    ... # 定义了一个 proxy_cache_path, : proxy_cache_path /data/nginx/cache keys_zone=one:10m
                     loader_threshold=300 loader_files=200 max_size=200m; # 这个server中有两个backend, 对应两种不同的cache策略 server {
        listen 8080; # cache的名字叫做 one (注意上面的 keys_zone=one:10m) proxy_cache one; # 对所有的 / 请求,都尽可能长久的缓存,不存在过期 location / {
            proxy_pass http://backend1; }

        
        location /some/path {
            proxy_cache_valid any 1m; # 任何内容,都缓存1分钟 proxy_cache_min_uses 3; # 访问3次后,触发缓存 proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment; # 设置好不使用缓存的规则 proxy_pass http://backend2; }
    }
}

注意: 如何调试呢?

1. 要设置log format, 把日志打印出来. 例如,配置文件为: (注意其中的 $upstream_cache_status, 这个变量最重要, 从它我们可以知道, 是HIT 还是MISS )

    log_format my_format '$remote_addr - $remote_user [$time_local]  ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $upstream_cache_status'; access_log logs/my_access.log my_format;

2. 要有对应的 ignore headers, 如果后端返回的结果中,增加了 cache-control (也有一说是 set-cookie) 或者 啥的,就不行了. 

server{
            proxy_ignore_headers "cache-control";
            proxy_hide_header "cache-control";
} 

下面是一个完整的 nginx.conf例子;

http{ # 其他内容  proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:10m
                     loader_threshold=300 loader_files=200 max_size=200m; log_format my_format '$remote_addr - $remote_user [$time_local]  ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" $upstream_cache_status';

    access_log logs/my_access.log my_format;

    server {
        listen 80; 

        location / { 
            proxy_ignore_headers "cache-control";
            proxy_hide_header "cache-control";
            proxy_cache cache_one;
            proxy_cache_valid any 10s; # 任何内容,都缓存10秒钟 proxy_pass http://rails_api;
        }

    }
    upstream rails_api{
        server localhost:3000;
    }

缓存用的哪些文件?

我们可以在 proxy_cache_path中设置, 例如:

    proxy_cache_path /tmp/nginx_cache keys_zone=cache_one:10m loader_threshold=300 loader_files=200 max_size=200m;

然后, 找到 /tmp/nginx_cache 目录, 如果某个 cache被命中过, 就会看到出现一个以md5 结果命名的文件:

:/tmp/nginx_cache$ ll
total 20 drwxrwxrwx 2 nobody sg552 4096 Sep 10 11:39 ./
drwxrwxrwt 10 root   root 12288 Sep 10 11:38 ../
-rw------- 1 nobody nogroup 594 Sep 10 11:39 f8924891f34a941a8342ccd19c4cf290 

上面中, 这个文件 “f89…” 就是缓存文件. 它的内容如下.

���U���������Ud����0""b4945c5f2d4b62faae53f44f44a5e946"
KEY: http://rails_api/prices/say_hi
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
ETag: "b4945c5f2d4b62faae53f44f44a5e946" Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 90fbc91b-e5a4-4279-8832-5d484cba7ba8
X-Runtime: 0.007120 Connection: close Server: thin 1.6.2 codename Doc Brown time is: 2015-09-10 11:39:58 +0800 

可以看出, 该静态文件, 以文本的形式缓存了 所有的response信息.


来源;http://siwei.me/blog/posts/nginx-nginx-content-caching

centos 7 开机自动挂载Windows共享文件夹

命令行中挂载Windows共享文件,重启后失效

#mount -t cifs -o username=linux,password=passwd //192.168.121.121/share /mnt/share



开机自动挂载,则需要在/etc/fstab中加入下面一行即可

//192.168.121.122/share /mnt/share cifs username=linux,password=passwd 0 0



取消挂载 umount

如果密码有特殊符号,一个从左上到右下的斜杠\即可,例如\&  \’,即可(开机自动挂载不用加斜杠)

如果在修改/etc/fstab文件后,运行mount -a命令验证一下配置是否正确,则可以避免重启移除此类问题。如果还是不行就要特殊字符吧。

问题的解决过程中,重新mount /是比较关键的一步(mount -o remount,rw /)。如果没有此步操作,则文件系统处于只读状态,导致不能修改配置文件并保存