1.FTP的传输模式:ASCII传输模式和二进制传输模式。
ASCII传输模式: 假定用户正在拷贝的文件包含的简单ASCII码文本,如果在远程机器上运行的不是UNIX,当文件传输时ftp通常会自
动地调整文件的内容以便于把文件解释成另外那台计算机存储文本文件的格式。但是常常有这样的情况,用户正在传输的文件包含的不是
文本文件,它们可能是程序,数据库,字处理文件或者压缩文件(尽管字处理文件包含的大部分是文本,其中也包含有指示页尺寸,字库
等信息的非打印符)。在拷贝任何非文本文件之前,用binary 命令告诉ftp逐字拷贝,不要对这些文件进行处理,这也是下面要讲的二进
制传输。
二进制传输模式: 在二进制传输中,保存文件的位序,以便原始和拷贝的是逐位一一对应的。即使目的地机器上包含位序列的文件是
没意义的。 如果两台通讯的机器为同一类型,则可优先使用二进制拷贝。
2.FTP的工作方式: Standard (也就是 PORT方式,主动方式)和 Passive (也就是PASV,被动方式)
主动方式: Port模式FTP 客户端首先和FTP服务器的TCP 21端口建立连接,通过这个通道发送命令,客户端需要接收数据的时候在这
个通道上发送PORT命令。 PORT命令包含了客户端用什么端口接收数据。在传送数据的时候,服务器端通过自己的TCP 20端口连接至客
户端的指定端口发送数据。 FTP server必须和客户端建立一个新的连接用来传送数据。
被动方式: Passive模式在建立控制通道的时候和Standard模式类似,但建立连接后发送的不是Port命令,而是Pasv命令。FTP服务
器收到Pasv命令后,随机打开一个高端端口(端口号大于1024)并且通知客户端在这个端口上传送数据的请求,客户端连接FTP服务器此
端口,然后FTP服务器将通过这个端口进行数据的传送,这个时候FTP server不再需要建立一个新的和客户端之间的连接
请注意: PORT模式建立数据传输通道是由服务器端发起的,服务器使用20端口连接客户端的某一个大于1024的端口;在PASV模式
中,数据传输的通道的建立是由FTP客户端发起的,他使用一个大于1024的端口连接服务器的1024以上的某一个端口。 在FTP客户连接
服务器的整个过程中,控制信道是一直保持连接的,而数据传输通道是临时建立的。
3.TFTP工作原理
建立连接:默认情况下,作为 TFTP 服务器的主机 A 会监听 69 端口,当作为客户端的主机 B 想要下载或上传文件时,会向主机 A 的
69 端口发送包含读文件(下载)请求或写文件(上传)请求的数据包。主机 A 收到读写请求后,会打开另外一个随机的端口,通过这个
端口向主机 B 发送确认包、数据包或者错误包。
下载:客户端向服务器的 69 端口(通常情况下)发送一个读请求,服务器收到这个读请求以后,会打开另外一个随机的端口(假设
端口号是 59509),然后在它默认的路径下寻找这个文件,找到这个文件以后,每次读入文件的 512 个字节,通过端口 59509 将这 512
个字节放入数据包中发送给客户端,数据包中还包含了操作码和数据块的编号,块编号从 1 开始计数;客户端收到数据包以后,会向服务
器的 59509 端口发送一个确认包,里面包含了它收到的数据包的块编号;服务器收到确认包以后,继续发送文件的下一个 512 个字节。
如此循环往复,直到文件的末尾,最后一个数据包的数据块的大小会小于 512 个字节,这时服务器就认为传输已经结束,等他接收到这最
后一个数据包的确认包之后就会主动关闭连接。而客户端收到这个小于 512 个字节的数据包后也认为传输已经结束,发送完确认包之后也
会关闭连接。也许会有一种极端情况,就是文件的大小正好是 512 字节的倍数,这样的话,最后一个数据包的大小也是 512 个字节,这
时服务器发送完包含文件数据的数据包以后,还会额外发送一个包含 0 字节的数据包,作为最后一个数据包,这样就可以保证客户端收到
的最后一个数据包的大小总是小于 512 个字节的。也就是说,对于客户端而言,只要它收到的数据包的大小小于 512 个字节,它就认为
传输已经结束,它就会关闭连接。
上传:客户端向服务器的 69 端口(通常情况下)发送一个写请求,服务器收到这个写请求以后,会打开另外一个随机的端口(假设
端口号是 59509),向客户端发送一个确认包,其中块编号是 0,以此来告诉客户端自己已经准备好接收文件,并且告诉客户端自己接收
文件的端口号。然后客户端就开始向服务器的 59509 端口发送数据包,服务器收到数据包后向客户端发送确认包,直到整个文件发送完
毕。这个过程和下载是一样的,只不过双方的角色互换了,客户端成了发数据的一方,而服务器是接收数据的一方。> 4.NFS工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxkm2EOH-1604371435750)(png/image-20200406105340809.png)]
一般企业在使用 rsync 时会配合 inotify 使用,使用 inotify 监听文件修改情况,一旦文件某些 属性改变,就通知 rsync 进行备份
max_queued_events:inotify事件队列最大长度,如值太小会出现 Event Queue Overflow 误,默认值:16384
max_user_instances:每个用户创建inotify实例最大值,默认值:128 max_user_watches:可以监视的文件数量(单进程),默认值:
8192
[root@centos8 ~]#vim /etc/sysctl.conf
fs.inotify.max_queued_events=66666
fs.inotify.max_user_watches=100000
[root@centos8 ~]#sysctl -p
fs.inotify.max_queued_events = 66666
fs.inotify.max_user_watches = 100000
[root@centos8 ~]#cat /proc/sys/fs/inotify/*
66666
128
100000
inotify-tools 包:
#监控一次性事件
inotifywait /data
#持续前台监控
inotifywait -mrq /data
#持续后台监控,并记录日志
inotifywait -o /root/inotify.log -drq /data --timefmt "%Y-%m-%d %H:%M" --format
"%T %w%f event: %e"
#持续前台监控特定事件
inotifywait -mrq /data --timefmt "%F %H:%M" --format "%T %w%f event: %;e" -e
create,delete,moved_to,close_write,attrib
rsync 软件包:
rsync,rsync-daemon(CentOS 8)
服务文件:/usr/lib/systemd/system/rsyncd.service
配置文件:/etc/rsyncd.conf 端口:873/tcp
rsync命令:
#Local:
rsync [OPTION...] SRC... [DEST]
#Access via remote shell:
Pull:
rsync [OPTION...] [USER@]HOST:SRC... [DEST]
Push:
rsync [OPTION...] SRC... [USER@]HOST:DEST
#Access via rsync daemon:
Pull:
rsync [OPTION...] [USER@]HOST::SRC... [DEST]
rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
Push:
rsync [OPTION...] SRC... [USER@]HOST::DEST
rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST
rsync 有三种工作方式:
防火墙概念
防火墙分类
Netfilter 中的 hook 函数
iptables -N # 自定义一条新的规则链
iptables -E # 重命名自定义链;引用计数不为 0 的自定义链不能够被重命名,也不能被删除
iptables -X # 删除自定义的空的规则链
iptables -P # 设置默认策略;对 filter 表中的链而言,其默认策略有:
# ACCEPT:接受 DROP:丢弃
iptables -vnL --line-numbers
iptables -A
iptables -I INPUT 2 ...
iptables -D 2
iptables -R
iptables -F # 清空指定规则
# (1) 可以指明规则序号 (2) 可以指明规则本身
iptables -Z # 清零计数
# (iptables 的每条规则都有两个计数器 (1) 匹配到的报文的个数 (2) 匹配到的所有报文的大小之和)
multiport 扩展 以离散方式定义多端口匹配
iptables -A INPUT -s 172.16.0.0/16 -d 172.16.100.10 -p tcp -m multiport --dports 20:22,80 -j ACCEPT
iprange 扩展 指明连续的(但一般不是整个网络)ip 地址范围 源 IP 地址范围 [!] --src-range from[-to]
目标 IP 地址范围 [!] -- dst-range from[-to]
iptables -A INPUT -d 172.16.1.100 -p tcp --dport 80 -m iprange --src-range 172.16.1.5-172.16.1.10 -j DROP
mac扩展 mac 模块可以指明源 MAC 地址
iptables -A INPUT -s 172.16.0.100 -m mac --mac-source 00:50:56:12:34:56 -j ACCEPT
iptables -A INPUT -s 172.16.0.100 -j REJECT
string 扩展 string 扩展用于对报文中的应用层数据做字符串模式匹配检测
iptables -A OUTPUT -p tcp --sport 80 -m string --algo bm --from 42 --string "google" -j REJECT
time 扩展 根据将报文到达的时间与指定的时间范围进行匹配
iptables -A INPUT -s 172.16.0.0/16 -d 172.16.100.10 -p tcp --dport 80 -m time --timestart 14:30 --timestop 18:30 --weekdays
Sat,Sun --kerneltz -j DROP```
**connlimit 扩展** 根据每客户端 IP 做并发连接数数量匹配可防止 Dos(Denial of Service,拒绝服务)攻击
```bash
iptables -A INPUT -d 172.16.100.10 -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT
# --connlimit-upto # :连接的数量小于等于#时匹配
# --connlimit-above # :连接的数量大于#时匹配
limit 扩展 基于收发报文的速率做匹 令牌桶过滤器
iptables -I INPUT -d 172.16.100.10 -p icmp --icmp-type 8 -m limit --limit 10/minute --limit-burst 5 -j ACCEPT
iptables -I INPUT 2 -p icmp -j REJECT
state 扩展 state 扩展模块,可以根据"连接追踪机制"去检查连接的状态,较耗资源, 会消耗内存,使用 conntrack 机制:追踪本机
上的请求和响应之间的关系
NEW
:新发出请求;连接追踪信息库中不存在此连接的相关信息条目,因此,将其 识别为第一次发出的请求ESTABLISHED
:NEW 状态之后,连接追踪信息库中为其建立的条目失效之前期间内 所进行的通信状态RELATED
:新发起的但与已有连接相关联的连接,如:ftp 协议中的数据连接与命令 连接之间的关系INVALID
:无效的连接,如 flag 标记不正确UNTRACKED
:未进行追踪的连接,如 raw 表中关闭追踪/proc/net/nf_conntrack
/proc/sys/net/netfilter/nf_conntrack_max
/proc/sys/net/netfilter/nf_conntrack_count
/proc/sys/net/netfilter/nf_conntrack_generic_timeout
iptables -A INPUT -d 172.16.1.10 -p tcp -m multiport --dports 22,80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -s 172.16.1.10 -p tcp -m multiport --sports 22,80 -m state --state ESTABLISHED -j ACCEPT
开放被动模式的 ftp 服务 :
IPTABLES_MODULES=“nf_conntrack_ftp"
modproble nf_conntrack_ftp
iptables –I INPUT -d LocalIP -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -d LocalIP -p tcp --dport 21 -m state --state NEW -j ACCEPT
iptables -I OUTPUT -s LocalIP -p tcp -m state --state ESTABLISHED -j ACCEPT
具体操作:
yum install vsftpd
systemctl start vsftpd
modprobe nf_conntrack_ftp
iptables -F
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 21 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -vnL
使用 iptables 命令定义的规则,手动删除之前,其生效期限为 kernel 存活期限
持久保存规则
iptables-save > /PATH/TO/SOME_RULES_FILE
# 将规则覆盖保存至/etc/sysconfig/iptables文件中
service iptables save
加载规则
iptables-restore < /PATH/FROM/SOME_RULES_FILE
-n, --noflush:不清除原有规则 -# 会自动从/etc/sysconfig/iptables 重新载入规则
service iptables restart
开机自动重载规则
iptables-restore < /PATH/FROM/IPTABLES_RULES_FILE
yum install iptables-services
iptables-save > /etc/sysconfig/iptables
systemctl enable iptables.service
SNAT:基于 nat 表的 target,适用于固定的公网 IP
例:10.0.1.0/24 网段的主机访问外部网络时,IP 数据包源地址被替换为 172.18.1.6-172.18.1.6 中的某一个地址
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 ! –d 10.0.1.0/24 -j SNAT --to-source 172.18.1.6-172.18.1.9
另一个 target 为MASQUERADE
,适用于动态的公网 IP,如拨号网络
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 ! –d 10.0.1.0/24 -j MASQUERADE
DNAT:nat 表的 target,适用于端口映射
iptables -t nat -A PREROUTING -s 0/0 -d 172.18.100.6 -p tcp --dport 22 -j DNAT --to-destination 10.0.1.22
iptables -t nat -A PREROUTING -s 0/0 -d 172.18.100.6 -p tcp --dport 80 -j DNAT --to-destination 10.0.1.22:8080
OUTPUT 链
iptables -t nat -A PREROUTING -d 172.16.100.10 -p tcp --dport 80 -j REDIRECT --to-ports 8080
1.加载BIOS的硬件信息,获取第一个启动设备
2.读取第一个启动设备MBR的引导加载程序(grub)的启动信息
3.加载核心操作系统的核心信息,核心开始解压缩,并尝试驱动所有的硬件设备
4.核心执行init程序,并获取默认的运行信息
5.init程序执行/etc/rc.d/rc.sysinit文件
6.启动核心的外挂模块
7.init执行运行的各个批处理文件(scripts)
8.init执行/etc/rc.d/rc.local
9.执行/bin/login程序,等待用户登录
10.登录之后开始以Shell控制主机
启动过程总结:/sbin/init --> (/etc/inittab) --> 设置默认运行级别 --> 运行系统初始脚本、完成系统初始化 --> (关闭对应下需要关闭 的服务)启动 需要启动服务 --> 设置登录终端
或者:POST --> Boot Sequence(BIOS) --> Boot Loader --> Kernel(ramdisk) --> rootfs --> switchroot --> /sbin/init --> (/etc/inittab, /etc/init/*.conf) --> 设定默认运行级别 --> 系统初始化脚本rc.sysinit --> 关闭或启动对应级别的服务 --> 启动终端
0:关机
1:单用户模式(root自动登录), single, 维护模式
2:多用户模式,启动网络功能,但不会启动NFS;维护模式
3:多用户模式,正常模式;文本界面
4:预留级别;可同3级别
5:多用户模式,正常模式;图形界面
6:重启
默认级别:3, 5
切换级别:init #
查看级别:
runlevel
who -r
- grub配置文件:/boot/grub/grub.conf
default=#: 设定默认启动的菜单项;落单项(title)编号从0开始
timeout=#:指定菜单项等待选项选择的时长
splashimage=(hd#,#)/PATH/XPM_FILE:菜单背景图片文件路径
password [–md5] STRING: 启动菜单编辑认证
hiddenmenu:隐藏菜单
title TITLE:定义菜单项“标题”, 可出现多次
root (hd#,#):查找stage2及kernel文件所在设备分区;为grub的根
kernel /PATH/TO/VMLINUZ_FILE [PARAMETERS]:启动的内核
initrd /PATH/TO/INITRAMFS_FILE: 内核匹配的ramfs文件
password [–md5|–encrypted ] STRING: 启动选定的内核或操作系统时进行认证
### Centos7
**CentOS 7 引导顺序**
```bash
UEFi或BIOS初始化,运行POST开机自检
选择启动设备
引导装载程序, centos7是grub2
加载程序的配置文件:
/etc/grub.d/
/etc/default/grub
/boot/grub2/grub.cfg
加载initramfs驱动模块
加载内核选项
内核初始化,centos7使用systemd代替init
执行initrd.target所有单元,包括挂载/etc/fstab
从initramfs根文件系统切换到磁盘根目录
systemd执行默认target配置,配置文件/etc/systemd/system/default.target
systemd执行sysinit.target初始化系统及basic.target准备操作系统
systemd启动multi-user.target下的本机与服务器服务
systemd执行multi-user.target下的/etc/rc.d/rc.local
Systemd执行multi-user.target下的getty.target及登录服务systemd执行graphical需要的服务
grub> root (hd#,#)
grub> kernel /vmlinuz-VERSION-RELEASE ro root=/dev/DEVICE
grub> initrd /initramfs-VERSION-RELEASE.img
grub> boot
grub 配置文件
[root@lab-server2 ~]# ll /boot/grub2/grub.cfg
-rw-r--r--. 1 root root 4465 Dec 31 08:43 /boot/grub2/grub.cfg
1.启动时任意键暂停启动
2.按e键进入编辑模式
3.将光标移动linux16开始的行,添加内核参数rd.break
4.按ctrl-x启动
5.mount –o remount,rw /sysroot
6.chroot /sysroot
7.passwd root
8.touch /.autorelabel
9.exit
10.reboot
1.启动时任意键暂停启动
2.按e键进入编辑模式
3.将光标移动linux16开始的行,改为rw init=/sysroot/bin/sh
4.按ctrl-x启动
5.hchroot /sysroot
6.passwd root
7.touch /.autorelabel
8.exit
9.reboot
GRUB“the Grand Unified Bootloader”引导提示时可以使用命令行界面
可从文件系统引导
主要配置文件 /boot/grub2/grub.cfg
修复配置文件
grub2-mkconfig > /boot/grub2/grub.cfg
修复grub
grub2-install /dev/sda BIOS环境
grub2-install UEFI环境
调整默认启动内核
vim /etc/default/grub
GRUB_DEFAULT=0
sysctl命令查看当前生效的内核参数
sysctl -w parameter=VALUE
sysctl -p [/path/to/conf_file]
sysctl -a
| 参数 | 对应文件 | 备注
---------------------------------------------------------------------------------------------------------- |
| net.core.somaxconn = 4096 | /proc/sys/net/core/somaxconn | 选项默认值是128,这
个参数用于调节系统同时发起的tcp连接数,在高并发请求中,默认的值可能会导致连接超时或重传,因此,需要结合并发请求数来调节此
值
|
| vm.swappiness = 1 | /proc/sys/vm/swappiness | 这个参数定义了系统对swap的
使用倾向,centos7默认值为30,值越大表示越倾向于使用swap。可以设为0,这样做并不会禁止对swap的使用,只是最大限度地降低了
使用swap的可能性
|
| net.ipv4.ip_forward = 1 | /proc/sys/net/ipv4/ip_forward | 开启路由转发功能
|
| net.ipv4.icmp_echo_ignore_all = 1 | /proc/sys/net/ipv4/icmp_echo_ignore_all | 禁ping
|
| vm.drop_caches | /proc/sys/vm/drop_caches | 用来控制是否清空文件缓存:
默认是0;1-清空页缓存;2-清空inode和目录树缓存;3-清空所有缓存
|| fs.file-max = 1020000 | /proc/sys/fs/file-max | 系统级别的可打开文件句柄数
|
| ulimit -n | /etc/security/limits.conf | 限制经过PAM模块认证的用户打开的文
件句柄数:* soft nofile 65535 * hard nofile 65535
|
| net.ipv4.tcp_syncookies = 1 | /proc/sys/net/ipv4/tcp_syncookies | 开启SYN Cookies,当
SYN等待队列溢出时,启用cookies来处理,可以防范少量的SYN攻击,默认为0,表示关闭
|
| net.ipv4.tcp_tw_reuse = 1 | /proc/sys/net/ipv4/tcp_tw_reuse | 允许将TIME_WAIT
sockets重新用于新的TCP连接,默认为0,表示关闭
|
| net.ipv4.tcp_tw_recycle = 1 | /proc/sys/net/ipv4/* | 允许将TIME_WAIT sockets快速
回收以便利用,默认为0,表示关闭,需要同时开启 net.ipv4.tcp_timestamps 才能生效
|
| net.ipv4.tcp_timestamps = 1 | /proc/sys/net/ipv4/* | 默认为1
|
| net.ipv4.tcp_fin_timeout = 30 | /proc/sys/net/ipv4/* | 表示如果套接字由本端要求关
闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。默认是60s
|
| net.ipv4.ip_local_port_range = 1024 65500 | /proc/sys/net/ipv4/* | 表示用于向外连接的端口
范围。缺省情况下很小:32768到61000,改为1024到65500
|
| net.ipv4.tcp_max_syn_backlog = 8192 | /proc/sys/net/ipv4/* | 表示SYN队列的长度,默
认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
|
| net.ipv4.tcp_max_tw_buckets = 5000 | /proc/sys/net/ipv4/* | 表示系统同时保持
TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。
|
| net.ipv4.ip_nonlocal_bind = 1 | /proc/sys/net/ipv4/ip_nonlocal_bind | 此参数表示是否允许服
务绑定一个本机不存在的IP地址; 使用场景:有些服务需要依赖一个vip才能启动,但是此vip不在本机上,当vip飘移到本机上时才存在;
但是服务又需要提前启动,例如haproxy,nginx等代理需要绑定vip时; 0:默认值,表示不允许服务绑定一个本机不存的地址 1:表示允
许服务绑定一个本机不存在的地址 |
| vm.overcommit_memory = 1 | | vm.overcommit_memory:表示系
统允许内存的分配情况 0:默认值; 表示内核将检查是否有足够的可用内存供应用进程使用; 如果有足够的可用内存,内存申请允
许; 否则,内存申请失败,并把错误返回给应用进程。 1:表示内核允许分配所有的物理内存,而不管当前的内存状态如何。redis要
把此参数设为1; 2: 表示允许分配的内存为:物理内存*vm.overcommit_ratio+交换空间; 与参数vm.overcommit_ratio结合使
用; |
| cat /proc/meminfo | awk ‘{print $1,$2/1024 " Mb"}’ | grep “Commit” | | 查看系统中可提交的内
存和已经申请的内存: CommitLimit:表示系统可分配的内存 Committed_AS:表示系统已经分配的内存
|
| kernel.msgmax | | 进程间的通信需要依靠内核来进行管理;是
通过消息列队来传递消息; 以字节为单位,规定消息的单大值; 默认为65536,即64k; 此值不能超过kernel.msgmnb的值,msgmnb
限定了消息队列的最大值;
|
| kernel.msgmnb | | 以字节为单位,规定了一个消息队列的最大
值; 默认值为:65536,即64k;
|
| kernel.mni | | 指定消息队列的最大个数;
|
| kernel.shmall | | 以页为单位,控制共享内存总量;Linux一
个内存页大小为4kb;
|| kernel.shmmax | | 定义单个共享内存段的最大值; shmmax 设
置应该足够大,设置的过低可能会导致需要创建多个共享内存段;
|
| kernel.shmmni | | 定义共享内存段的个数,默认为4096;
|
| net.ipv4.tcp_rmem = 4096/87380/67108864 net.ipv4.tcp_wmem = 4096/65536/67108864 | | 增加tcp
缓冲区大小,tcp_rmem表示接受数据缓冲区范围,tcp_wmem表示发送数据缓冲区范围,单位Byte,最大64M
|
| net.ipv4.tcp_retries2 = 5 | | TCP失败重传次数,默认值15,意味着重传
15次才彻底放弃,可减少到5,以尽早释放内核资源;在通讯过程中(已激活的sock),数据包发送失败后,内核要重试发送多少次后才决
定放弃连接
|
(1) 准备好开发环境
(2) 获取目标主机上硬件设备的相关信息
(3) 获取目标主机系统功能的相关信息
例如:需要启用相应的文件系统
(4) 获取内核源代码包
www.kernel.org
包组
Development Tools
目标主机硬件设备相关信息
CPU:
cat /proc/cpuinfo
x86info -a
lscpu
PCI设备:
lspci
-v
-vv
lsusb
-v
-vv
块设备
lsblk
了解全部硬件设备信息
hal-device:CentOS 6
下载源码文件
.config:准备文本配置文件
make menuconfig:配置内核选项
make [-j #] 或者用两步实现: make -j # bzImage ; make -j # modules
make modules_install:安装模块
make install :安装内核相关文件
安装bzImage为 /boot/vmlinuz-VERSION-RELEASE
生成initramfs文件
编辑grub的配置文件
支持“更新”模式进行配置:make help
(a) make config:基于命令行以遍历的方式配置内核中可配置的每个选项
(b) make menuconfig:基于curses的文本窗口界面
(c) make gconfig:基于GTK (GNOME)环境窗口界面
(d) make xconfig:基于QT(KDE)环境的窗口界面
支持“全新配置”模式进行配置
(a) make defconfig:基于内核为目标平台提供的“默认”配置进行配置
(b) make allyesconfig: 所有选项均回答为“yes“
(c) make allnoconfig: 所有选项均回答为“no“
全编译:make [-j #]
编译内核的一部分功能:
(a) 只编译某子目录中的相关代码
cd /usr/src/linux
make dir/
(b) 只编译一个特定的模块
cd /usr/src/linux
make dir/file.ko
示例:只为e1000编译驱动:
make drivers/net/ethernet/intel/e1000/e1000.ko
删除/lib/modules/目录下不需要的内核库文件
删除/usr/src/linux/目录下不需要的内核源码
删除/boot目录下启动的内核和内核映像文件
更改grub的配置文件,删除不需要的内核启动列表
centos7:vim /boot/grub2/grub.cfg
:/menuentrycentos8:
rm -f /boot/loader/entries/7e3e9120767340a8bd946a83d7c3b84d-$(uname -r)-80.el8.x86_64.conf
Systemd:系统启动和服务器守护进程管理器,负责在系统启动或运行时,激 活系统资源,服务器进程和其它进程
POST --> Boot Sequence --> Bootloader --> kernel + initramfs(initrd) --> rootfs --> /sbin/init
centos6 --> centos7:
启动:service name start ==> systemctl start name.service
停止:service name stop ==> systemctl stop name.service
重启:service name restart ==> systemctl restart name.service
状态:service name status ==> systemctl status name.service
禁止自动和手动启动: systemctl mask name.service
取消禁止: systemctl unmask name.service
服务查看
查看某服务当前激活与否的状态: systemctl is-active name.service
查看所有已经激活的服务: systemctl list-units --type|-t service
查看所有服务: systemctl list-units --type service --all|-a
重新加载配置 systemctl reload sshd.service
列出活动状态的所有服务单元 systemctl list-units --type=service
列出所有服务单元 systemctl list-units --type=service --all
查看服务单元的启用和禁用状态 systemctl list-unit-files --type=service
列出失败的服务 systemctl --failed --type=service
列出依赖的单元 systemctl list-dependencies sshd
验证sshd服务是否开机启动 systemctl is-enabled sshd
禁用network,使之不能自动启动,但手动可以 systemctl disable network
启用network systemctl enable network
禁用network,使之不能手动或自动启动 systemctl mask network
启用network systemctl unmask network
运行级别
0 ==> runlevel0.target, poweroff.target
1 ==> runlevel1.target, rescue.target
2 ==> runlevel2.target, multi-user.target
3 ==> runlevel3.target, multi-user.target4 ==> runlevel4.target, multi-user.target
5 ==> runlevel5.target, graphical.target
6 ==> runlevel6.target, reboot.target
获取默认运行级别: systemctl get-default
传统命令init,poweroff,halt,reboot都成为systemctl的软链接
关机:systemctl halt、systemctl poweroff
重启:systemctl reboot
挂起:systemctl suspend
休眠:systemctl hibernate
休眠并挂起:systemctl hybrid-sleep
linux文件系统中的文件名称区分大小写,其中以点(.)开头的文件为隐藏文件
文件由两类数据组成:
目录 | 功能 |
---|---|
/boot | 引导文件存放目录,内核文件(vmlinuz)、引导加载器(bootloader, grub)都存放于此目录 |
/bin | 所有用户使用的基本命令;不能关联至独立分区,OS启动即会用到的程序 |
/sbin | 管理类的基本命令;不能关联至独立分区,OS启动即会用到的程序 |
/lib | 启动时程序依赖的基本共享库文件以及内核模块文件(/lib/modules) |
/lib64 | 专用于x86_64系统上的辅助共享库文件存放位置 |
/etc | 配置文件目录 |
/home/USERNAME | 普通用户家目录 |
/root | 管理员的家目录 |
/media | 便携式移动设备挂载点 |
/mnt | 临时文件系统挂载点 |
/dev | 设备文件及特殊文件存储位置 |
c: character device,字符设备,线性访问 | |
/opt | 第三方应用程序的安装位置 |
/srv | 系统上运行的服务用到的数据 |
/tmp | 临时文件存储位置 |
/usr | universal shared, read-only data 全局共享的只读文件存放地 |
/usr/bin: 保证系统拥有完整功能而提供的应用程序 | |
/usr/sbin: centos7上访问/sbin实质是访问 /usr/sbin | |
/usr/lib:32位使用 | |
/usr/lib64:只存在64位系统 | |
/usr/include: C程序的头文件(header files) | |
/usr/share:结构化独立的数据,例如doc, man等 | |
/usr/local:第三方应用程序的安装位置:bin, sbin, lib, lib64, etc, share | |
/var | variable data files 可变文件存放地点 |
/var/cache: 应用程序缓存数据目录 | |
/var/lib: 应用程序状态信息数据 | |
/var/local:专用于为/usr/local下的应用程序存储可变数据 | |
/var/lock: 锁文件 | |
/var/log: 日志目录及文件 | |
/var/opt: 专用于为/opt下的应用程序存储可变数据 | |
/var/run: 运行中的进程相关数据,通常用于存储进程pid文件 | |
/var/spool: 应用程序数据池 | |
/var/tmp: 保存系统两次重启之间产生的临时数据 | |
/proc | 用于输出内核与进程信息相关的虚拟文件系统 |
/sys | 用于输出当前系统上硬件设备相关信息虚拟文件系统 |
/selinux | security enhanced Linux,selinux相关的安全策略等信息的存储位置 |
符号 | 文件类型 |
---|---|
- | 普通文件 |
d | 目录文件 |
b | 块设备 |
c | 字符设备 |
l | 符号链接文件 |
p | 管道文件pipe |
s | 套接字文件socket |
文件类型 | 存放的文件夹 |
---|---|
二进制程序 | /bin, /sbin, /usr/bin, /usr/sbin, /usr/local/bin, /usr/local/sbin |
库文件 | /lib, /lib64, /usr/lib, /usr/lib64, /usr/local/lib, /usr/local/lib64 |
配置文件 | /etc, /etc/DIRECTORY, /usr/local/etc |
帮助文件 | /usr/share/man, /usr/share/doc, /usr/local/share/man, /usr/local/share/doc |
绝对路径:表示从根开始完整的文件的位置路径,以正斜杠开始,可用于任何想指定一个文件名的时候相对路径:名指定相对于当前工作目录或某目录的位置,不以斜线开始,可以作为一个简短的形式指定一个文件名
查看路径的基名:basename /path/to/somefile
查看某路径的目录名:dirname /path/to/somefile
几乎每个文件系统都会需要大量不同的数据结构来保证其底层对各种文件存储目的的支持;在linux系统中(ext3或者ext4或者xfs文件系
统)就有一个很重要的数据结构叫inode(index node),一个inode包含某个文件或者某个目录的以下信息(也叫元数据):
信息项 | 英文术语 |
---|---|
----------- | |
文件类型(可执行文件,块文件,字符文件等) | File types ( executable, block special etc ) |
文件权限(读,写,执行等) | Permissions ( read, write etc ) |
文件属主全局唯一标识符 | UID ( Owner ) |
文件属组全局唯一标识符 | GID ( Group ) |
文件大小 | FileSize/bytes |
时间戳:最后访问时间、最后修改时间、最后改变时间和inode号变化信息 | Time stamps including last access, last modification |
and last inode number change. | |
文件链接数(硬链接和软链接) | Number of links ( soft/hard ) |
文件在磁盘的位置 | Location of ile on harddisk. |
该文件所分配的磁盘块数量 | |
其他信息 | Some other metadata about file. |
(Under ext2, each i-node contains 15 pointers. The first 12 of these pointers (num-bered 0 to 11 in Figure 14-2) point to the
location in the file system of the first 12 blocksof the file. The next pointer is a pointer to a block of pointers that give the
locations ofthe thirteenth and subsequent data blocks of the file.)
在ext2文件系统中,每个i-node包含有15和数据块指针。前12个数据块指针(0-11)指向分配给该文件的前12个数据块在磁盘的位置。接
下来的指针则指向下一个数据块或者指向一个指针块。
inode数据结构被存储在inode表中:由于每个inode代表某个文件的所有属性信息,所以inode表就记录了整个文件系统上的所有文
件的信息(元数据)
linux文件系统中每个目录下的文件被存储成目录项,每一项对应其inode号,通过inode号就可以访问到inode表的某一项,该项就记录了
该文件的元数据。如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hmTqcsX7-1604371435758)(png/20191224193127136.png)]
分配一个空闲的inode号,在inode表中生成新条目;在目录中创建一个目录项,将名称与inode编号关联;拷贝数据生成新的文件
链接数递减,从而释放的inode号可以被重用,把数据块放在空闲列表中,删除目录项数据实际上不会马上被删除,但当另一个文件使用
数据块时将被覆盖
如果mv命令的目标和源在相同的文件系统,作为mv 命令 :- 用新的文件名创建对应新的目录项
创建硬链接会增加额外的记录项以引用文件,对应于同一文件系统上同一个物理文件
每个目录引用相同的inode号,创建时链接数递增
删除文件时:
rm命令递减计数的链接
文件要存在,至少有一个链接数
当链接数为零时,该文件被删除
硬链接不能跨越驱动器或分区
为文件创建硬链接语法:
~# ln filename [linkname ]
符号链接,有时也称为软链接,是一种特殊的文件类型,其数据是另一文件的名称。
一个符号链接的数据内容是另一个文件的路径:
~# ln -s filename [linkname]
不论是硬链接或软链接都不会将原本的档案复制一份,只会占用非常少量的磁碟空间## 文件权限
属主、属组
目录与文件拥有相同的权限方案,只是对3种权限的含义另有所指。
读权限:可列出(比如,通过ls命令)目录之下的内容(即目录下的文件名)。
在实验验证对目录读权限位的操作时,应当了解有些Linux发行版对ls做了别名处理,
命令所携带的一些选项(比如,-F)需要访问目录中文件的i节点信息,而这又需要拥有对
目录的执行权限。为确保使用的是ls命令本身,执行时要给出命令的完整路径名(/bin/ls)
写权限:可在目录内创建、删除文件。注意,要删除文件,对文件本身无需有任何权限。
可执行权限:可访问目录中的文件。因此,有时也将对目录的执行权限称为search(搜索)权限。
访问文件时,需要拥有对路径名所列所有目录的执行权限。例如,想读取文件/home/mtk/x,则需拥有对目录/、/home以及/home/mtk
的执行权限(还要有对文件x自身的读权限)。若当前的工作目录为/home/mtk/sub1,访问相对路径名…/sub2/x时,需握有/home/mtk
和/home/mtk/sub2这两个目录的可执行权限(不必有对/或/home的执行权限)。 拥有对目录的读权限,用户只是能查看目录中的文件
列表。要想访问目录内文件的内容或是这些文件的 i节点信息,还需握有对目录的执行权限。 反之,若拥有对目录的可执行权限,而无读
权限,只要知道目录内文件的名称,仍可对其进行访问,但不能列出目录下的内容(即目录所含的其他文件名)。在控制对公共目录内容
的访问时,这是一种常用技术,简单而且实用。
要想在目录中添加或删除文件,需要同时拥有对该目录的执行和写权限。
ISO网络7层模型:
IP协议
IP是TCP/IP协议族中最为核心的协议。所有的TCP、UDP、ICMP及IGMP数据都以IP数据报格式传输。许多刚开始接触TCP/IP的人对IP提
供不可靠、无连接的数据报传送服务感到 很奇怪。IP提供的是一种不可靠的服务。也就是说,它只是尽可能快地把分组从源结点送到目的
结点,但是并不提供任何可靠性保证。而另一方面,TCP在不可靠的IP层上提供了 一个可靠的运输层。为了提供这种可靠的服务,TCP采
用了超时重传、发送和接收端到端的确认分组等机制
尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务。TCP提供一种面向连接的、可靠的字节流服务
面向连接:
意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。这一过程与打电话很相似,
先拨号振铃,等待对方摘机说“喂”,然后才说明是谁
可靠性的保证:
**TCP报文段被封装在一个IP数据报中** :
![](png/2019102511005542.png)
- 每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确
定一个TCP连接, 插口对(socket pair)(包含客户IP地址、客户端口号、服务器IP地址和服务器端口号的四元组)可唯一确定互联网络中
每个TCP连接的双方
- 序号用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。如果将字节流看作在两个应用程
序间的单向流动,则TCP用序号对每个字节进行计数。序号是32bit的无符号数,序号到达232-1后又从0开始
- TCP可以表述为一个没有选择确认或否认的滑动窗口协议(滑动窗口协议用于数据传输)。我们说TCP缺少选择确认是因为TCP首部中
的确认序号表示发方已成功收到字节,但还不包含确认序号所指的字节。当前还无法对数据流中选定的部分进行确认 、
- 在TCP首部中有6个标志比特。它们中的多个可同时被设置为1。
- URG 紧急指针(urgentpointer)有效。
- ACK 确认序号有效。
- PSH 接收方应该尽快将这个报文段交给应用层。
- RST 重建连接。
- SYN 同步序号用来发起一个连接。
- FIN 发端完成发送任务
- TCP的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期
望接收的字节。窗口大小是一个16bit字段,因而窗口大小最大为65535字节。新的窗口刻度选项允许这个值按比例变化以提供更大的窗口
- **检验和**:覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。
TCP检验和的计算和UDP检验和的计算相似
- 只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。TCP
的紧急方式是发送端向另一端发送紧急数据的一种方式。
- TCP报文段中的数据部分是可选的。接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果一方没有数据要发送,也使用没
有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段
### 三次握手
TCP是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接 。
- 1)请求端(通常称为客户)发送一个SYN段指明客户打算连接的服务器的端口,以及初始序号(ISN,在这个例子中为1415531521)。
这个SYN段为报文段1。
- 2)服务器发回包含服务器的初始序号的SYN报文段(报文段2)作为应答。同时,将确认序号设置为客户的ISN加1以对客户的SYN报文段
进行确认。一个SYN将占用一个序号。
- 3)客户必须将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认(报文段3)。通过这三个报文后就建立连接了。这个过
程也称为三次握手(three-way handshake)。
![](png/20191025110255379.png)
### 四次挥手建立一个连接需要三次握手,而终止一个连接要经过4次握手。这由TCP的半关闭(half-close)造成的。既然一个TCP连接是全双工(即
数据在两个方向上能同时传递),因此每个方向必须 单独地进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终
止这个方向连接。当一端收到一个FIN,它必须通知应用层另一端已经终止了那个方向的数据传送。发送FIN通常是应用层进行关闭的结果
。
收到一个FIN只意味着在这一方向上没有数据流动。**一个TCP连接在收到一个FIN后仍能发送数据**。而这对利用半关闭的应用来说是可
能的,尽管在实际应用中只有很少的TCP应用程序这样做。 首先进行关闭的一方(即发送第一个FIN)将执行主动关闭,而另一方(收到
这个FIN)执行被动关闭。通常一方完成主动关闭而另一方完成被动关闭,但也可能双方都执行主动关闭
当服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样, 一个FIN将占用一个序号。同时TCP服
务器还向应用程序(即丢弃服务器)传送一个文件结束符。 接着这个服务器程序就关闭它的连接,导致它的TCP端发送一个FIN(报文段
6),客户必须发回 一个确认,并将确认序号设置为收到序号加1(报文段7)。 发送FIN将导致 应用程序关闭它们的连接,这些FIN的
ACK是由TCP软件自动产生的。连接通常是由客户端发起的, 这样第一个SYN从客户传到服务器。每一端都能主动关闭这个连接(即首先
发送FIN)。然而,一般 由客户端决定何时终止连接,因为客户进程通常由用户交互控制,用户会键入诸如“quit”一样的 命令来终止进
程
#### 2MSL等待状态
TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。
它是任何报文段被丢弃前在网络内的最长时间。我们知道这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有
限制其生存时间的TTL字段
```bash
RFC 793 [Postel 1981c] 指出MSL为2分钟。然而,实现中的常用值是30秒,1分钟,或2分钟。
在实际应用中,对IP数据报TTL的限制是基于跳数,而不是定时器。对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动
关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢
失(另一端超时并重发最后的FIN)
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口
号)不能再被使用。这个连接只能在2MSL结束后才能再被使用
UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。这与面
向流字符的协议不同,如TCP,应用程序产生的全体数据与真正发送的单个IP数据报可能没有什么联系。UDP数据报被封装成一份IP数据
报的格式
当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报
文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用(Demultiplexing)
2.
Protocol Data Unit,协议数据单元是指对等层次之间传递的数据单位
1.A类地址:
0000 0000 - 0111 1111: 1-127
网络数:126, 127
每个网络中的主机数:2^24-2
默认子网掩码:255.0.0.0
私网地址:10.0.0.0
2.B类地址:
1000 0000 - 1011 1111:128-191
网络数:2^14
每个网络中的主机数:2^16-2
默认子网掩码:255.255.0.0
私网地址:172.16.0.0-172.31.0.0
3.C类地址:
1100 0000 - 1101 1111: 192-223
网络数:2^21
每个网络中的主机数:2^8-2
默认子网掩码:255.255.255.0
私网地址:192.168.0.0-192.168.255.0
4.D类地址:
D类地址为组播地址
1110 0000 - 1110 1111: 224-239
0.0.0.0
0.0.0.0不是一个真正意义上的IP地址。它表示所有不清楚的主机和目的网络
255.255.255.255
限制广播地址。对本机来说,这个地址指本网段内(同一广播域)的所有主机
127.0.0.1~127.255.255.254
本机回环地址,主要用于测试。在传输介质上永远不应该出现目的地址为“127.0.0.1”的数据包
224.0.0.0到239.255.255.255
组播地址,224.0.0.1特指所有主机,224.0.0.2特指所有路由器。224.0.0.5指OSPF路由器,地址多用于一些特定的程序以及多媒体程序
169.254.x.x
如果Windows主机使用了DHCP自动分配IP地址,而又无法从DHCP服务器获取地 址,系统会为主机分配这样地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8R9nvqUn-1604371435763)(png/20191025134727495-1586529686923.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2zmhZeiv-1604371435764)(png/20191025134807866-1586529702535.png)]
将多块网卡绑定同一IP地址对外提供服务,可以实现高可用或者负载均衡。直接给两块网卡设置同 一IP地址是不可以的。通过bonding,
虚拟一块网卡对外提供连接,物理网卡的被修改为相同的MAC地址。
Bonding工作模式
| mode
explain |
---|
--------------------------------------------------------------------- |
------------------------------------------------------------------------------------ |
Mode 0 (balance-rr) |
轮询(Round-robin)策略,从头到尾顺序的在每 一个slave接口上面发送数据包。本模式提供负载均衡和容错的能力 |
Mode 1 (active-backup) |
活动-备份(主备)策略,只有一个slave被激活,当且仅当活动的slave接口失败时才会激活其他slave.为了避免交换机发生混乱此时绑定 |
的MAC地址只有一个外部端口上可见 |
Mode 3 (broadcast) |
广播策略,在所有的slave接口上传送所有的报文,提供容错能力 |
active-backup、balance-tlb 和 balance-alb 模式不需要交换机的任何特殊配置。其他绑定模式需要配置交换机以便整合链接。如: |
Cisco 交换机需要在模式 0、2 和 3 中使用 EtherChannel,但在模式4中需要LACP和EtherChannel |
**桥接:**把一台机器上的若干个网络接口“连接”起来。其结果是,其中一个网收到的报文会被复制给其他网口并发送出去。以使得网
口之间的报文能够互相转发。网桥就是这样一个设备,它有若干个网口,并且这些网口是桥接起来的。与网桥相连的主机就能通过交换机
的报文转发而互相通信。## 网络工具
tcpdump通过将网络接口设置为混杂模式来嗅探网络数据包,可以抓取目标MAC地址为当前接口及广播MAC地址的数据包。
tcpdump [ -AbdDefhHIJKlLnNOpqStuUvxX# ] [ -B buffer_size ]
[ -c count ]
[ -C file_size ] [ -G rotate_seconds ] [ -F file ]
[ -i interface (指明接口)] [ -j tstamp_type ] [ -m module ] [ -M secret ]
[ --number ] [ -Q|-P in|out|inout ]
[ -r file ] [ -V file ] [ -s snaplen ] [ -T type ] [ -w file (保存到文件)]
[ -W filecount ]
[ -E spi@ipaddr algo:secret,... ]
[ -y datalinktype ] [ -z postrotate-command ] [ -Z user ]
[ --time-stamp-precision=tstamp_precision ]
[ --immediate-mode ] [ --version ]
[ expression ]
使用tcpdump时可以通过expression关键字指明抓取的是什么类型、方向的数据包
options:
-i interface
-w file
-nn: IP和端口数字显示
-A:ASCII码显示
-X:hex和ASCII显示
-XX:包含链路层信息
-v:详细信息 -vv:更详细信息
expression:
type: host/net/port/portrange
direction:src/dst/src or dst/src and dst
protocol: ether/ip/arp/tcp/udp/wlan
~# tcpdump -i eth0 # 抓取eth0接口的数据包
~# tcpdump -i eth0 -nn # 第一个n表示将主机名显示为IP数字,第二个n表示将服务端口名显示为数字端口
~# tcpdump -i eth0 -nn port 80 # 抓取访问80端口的数据包
~# tcpdump -i eth0 -nn dst port 80 #
~# tcpdump -i eth0 -nn src port 80 #
~# tcpdump -i eth0 -nn src port 80 and src host 192.168.100.40 #
~# tcpdump -i eth0 -nn src port 80 and src host 192.168.100.40 -A
~# tcpdump -i eth0 -nn src port 80 and src host 192.168.100.40 -X
~# tcpdump -i eth0 -nn src port 80 and src host 192.168.100.40 -XX~# tcpdump -i eth0 -nn src port 80 and src host 192.168.100.40 -vv
~# tcpdump -i eth0 -nn tcp src port 80 and src host 192.168.100.40 -vv #
hello
hello
| 内核功能 | 解释
------------------------ |
| 进程调度 | 现在的计算机一般会有多个物理核心用来执行程序的指令。Linux系统是一个抢占式多任务(preemptive
multitasking)的操作系统,多任务就意味着多个进程可以同时驻留在内存中得到CPU的处理,抢占式就意味着是哪个进程使用CPU或者使用
多久的规则由内核进程调度器决定而不是CPU自己决定处理哪个进程 |
| 内存管理 | 目前内存已近越来越大,但是软件体积也同样在增长;计算机的物理内存仍然是比较稀缺的资源,这就需要内核
合理的管理分配内存给系统的各个进程 |
| 提供文件系统功能 | 内核能够在磁盘上创建特定文件系统,以允许文件被创建,复制更新和删除等
|
| 创建和销毁进程 | 内核可以加载某个程序到内存,并为其分配其运行所需要的资源(CPU、内存、对文件的访问等),在内存中运
行的某个程序的实例就叫做进程。当程序被内核销毁或者自己运行结束后,内核还需要保证其使用的资源被释放以便于后续进程重用
|
| 访问设备 | 连接到计算机上的设备(如:麦克风、显示器、键盘鼠标、磁盘和磁带、软盘等)是的计算机内部和计算机之间及
计算机和外部世界可以进行信息交互,允许计算机进行输入、输出等操作。内核为每个上层应用程序提供了一个标准的接口来访问设备,
同时仲裁多个进程对设备的访问 |
| 提供网络功能 | 内核代替用户进程收发网络数据。
|
| 提供系统调用应用程序编程接口 | 进程可以通过一个入口请求内核完成多种操作,该入口就是系统调用
|
| 提供虚拟个人电脑抽象功能 | 在linux系统,多个用户可以同时登陆系统并进行不同的操作,内核提供了一个抽象层来保证各个用户的
操作和对设备的使用的隔离性 |## 程序?进程?线程?
| |
-------------------------------------------------- |
| 程序 | 一般程序以两种形式存在:源代码的形式和二进制可执行程序的形式。源代码是人类可以读的文本(包括一系列的使用列如C语言编
写的语句)。程序要被CPU执行,必须编译链接为计算机可以识别的二进制机器码指令(windows内的.exe文件;linux下的.o文件等);二者
被认为是同义词,都代表程序。 |
| 进程 | 简单的说,一个进程就是一个正在执行的程序的实例。当一个程序被执行时,内核会将该程序的指令加载进内存,分配内存空间给
程序变量并设置相应的数据结构来记录该进程的信息(Linux内核对该种数据结构的实现叫task list,保存有进程ID、退出状态、用户ID和组
ID等)。因此,进程也可以描述为:运行的程序及其包括的数据和操作。 |
| 线程 | 在现代的UNIX实现版中,每个进程可以有多个线程运行。一个理解线程比较好的表述是:共享同一块内存空间和其他属性的轻量
级进程的集合。每个线程都执行同一个代码块并共享相同的数据区域和堆。然而,每个线程都有自己的栈空间(包含本地变量和函数调用连
接信息)。线程之间可以通过其共享的全局变量进行通讯,进程亦可以通过IPC和同步机制进行通讯。 |
现代处理器架构一般都允许CPU在至少两个不同模式运行:用户模式(user mode)和内核模式 (kernel mode:内核模式有时候也被称为
监管模式),CPU自带的硬件指令允许在两个模式之间切换。 相应地,虚拟内存的某部分可被标记为用户空间(user space)或内核空间
(kernel space)。当在 用户模式运行某进程时CPU只可以访问标记为用户控件的内存区域,尝试访问被标记为内核空间的内存 将会导致一
个硬件异常(hardware exception)错误。
| |
-------------------------------------------------------------------------- |
创建状态 |
配。如果创建工作无法完 成,比如资源无法满足,就无法被调度运行,把此时进程所处状态称为创建状态 |
就绪状态 |
执行状态 |
阻塞状态 |
系统调用 |
终止状态 |
| |
-------------------------------------------------------------------- |
运行to就绪 |
系统中,当有更高优先级的进程要运行时,该进程就被迫让出CPU,该进程便由执行状态转变为就绪状态 |
运行to阻塞 |
阻塞to就绪 |
[root@centos7 ~]#cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
10 21 * * * steve /bin/echo “Hello there!!”
0 */3 * * * steve /bin/echo “Done something!”; wall “Done something again!”
crontab [-u user] [-l | -r | -e] [-i]
-l 列出所有任务
-e 编辑任务
-r 移除所有任务
-i 同-r一同使用,以交互式模式移除指定任务
-u user 仅root可运行,指定用户管理cron任务
| 命令 | 作用 | 备注 || ------- | ---------------------------------------------------------------------------------------------------------------------------------
----------- | ---- |
| pstree | pstree命令以树状结构显示当前系统进程 | |
| ps | ps是linux系统中查看进程的有力工具之一。man帮助指明其用于报告当前系统所有进程的一个快照
| |
| nice | 使用nice命令指定一个调度优先级来运行某程序 | |
| renice | renice命令可以更改一个正在运行的进程的优先级 | |
| pgrep | pgrep和pkill命令大部分选项相同,也就是大部分功能相同;但是pgrep一般用来基于进程名搜索某进程,
pkill一般基
于进程名来发送相关信号给某进程 | |
| kill | kill命令一般用来结束某进程,但是其还有其他功能,用于发送特定的信号给相关进程来控制某进程
| |
| killall | killall命令单纯的基于进程名来结束某进程 | |
| top | 监控系统进程 | |
| htop | 其使用不同的颜色来标识不同的信息,甚至支持鼠标点击相应的选项;来自EPEL源
| |
| free | 查看内存空间使用情况 | |
| vmstat | 命令查看虚拟内存信息 | |
| iostat | 统计CPU和设备IO信息 | |
| iftop | 显示带宽使用情况 | |
| pmap | 显示某进程对应的内存映射 | |
| dstat | dstat命令用来统计系统资源(代替vmstat和iostat) | |
| iotop | iotop命令用来监视磁盘I/O使用状况 | |
| nload | nload命令查看网络实时吞吐量 | |
查看由登陆用户启动而非系统启动的进程
lsof /dev/pts/1
指定进程号,可以查看该进程打开的文件
lsof -p 9527
查看指定程序打开的文件
lsof -c httpd
查看指定用户打开的文件
lsof -u root | more
查看指定目录下被打开的文件
lsof +D /var/log/
lsof +d /var/log/
参数+D为递归列出目录下被打开的文件,参数+d为列出目录下被打开的文件
查看所有网络连接
lsof -i –n
lsof [email protected]
通过参数-i查看网络连接的情况,包括连接的ip、端口等以及一些服务的连接情况,例如:sshd等。也可以通过
指定ip查看该ip的网络连接情况
查看端口连接情况
lsof -i :80 -n
通过参数-i:端口可以查看端口的占用情况,-i参数还有查看协议,ip的连接情况等查看指定进程打开的网络连接
lsof -i –n -a -p 9527
参数-i、-a、-p等,-i查看网络连接情况,-a查看存在的进程,-p指定进程
查看指定状态的网络连接
lsof -n -P -i TCP -s TCP:ESTABLISHED
-n:no host names, -P:no port names,-i TCP指定协议,-s指定协议状态通过多个参数可以
清晰的查看网络连接情况、协议连接情况等
恢复删除文件
lsof |grep /var/log/messages
rm -f /var/log/messages
lsof |grep /var/log/messages
cat /proc/653/fd/6
况
查看端口连接情况
lsof -i :80 -n
通过参数-i:端口可以查看端口的占用情况,-i参数还有查看协议,ip的连接情况等
查看指定进程打开的网络连接
lsof -i –n -a -p 9527
参数-i、-a、-p等,-i查看网络连接情况,-a查看存在的进程,-p指定进程
查看指定状态的网络连接
lsof -n -P -i TCP -s TCP:ESTABLISHED
-n:no host names, -P:no port names,-i TCP指定协议,-s指定协议状态通过多个参数可以
清晰的查看网络连接情况、协议连接情况等
恢复删除文件
lsof |grep /var/log/messages
rm -f /var/log/messages
lsof |grep /var/log/messages
cat /proc/653/fd/6
cat /proc/653/fd/6 > /var/log/messages
1、找出ifconfig “网卡名” 命令结果中本机的IPv4地址
ifconfig | head -n2 | tail -n1 | tr -s " " | cut -d" " -f3 # centos7,8
ifconfig | head -n2 | tail -n1 | tr -s " " | cut -d" " -f3 | cut -d: -f2 # centos6
ifconfig | grep -Eo "([0-9]{,3}\.){3}[0-9]{,3}" | head -n1 # centos6,7,8
ifconfig | grep -Eo '\<(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>' | head -n1
# 精确
2、查出分区空间使用率的最大百分比值
df -h | grep -e /dev/sd -e /dev/nvme | tr -s " " | cut -d" " -f5 | sort -nr | head -n3
3、查出用户UID最大值的用户名、UID及shell类型
getent passwd | sort -t : -k 3 -nr | head -n1 | cut -d: -f1,3,7
getent passwd | sort -t : -k 3 -nr | cut -d: -f1,3,7 | head -n14、查出/tmp的权限,以数字方式显示
stat /tmp/ | head -n4 | tail -n1 | cut -d \( -f2 | cut -d/ -f1
stat /tmp/ | grep -E "(\([0-7]{4})" | grep -Eo [0-9]{
4} |head -n1
5、统计当前连接本机的每个远程主机IP的连接数,并按从大到小排序
ss -tun |grep ESTAB | tr -s " " | cut -d" " -f6 | cut -d: -f1 | sort | uniq -c | sort -nr
tr -s " " ":" < ss.log | cut -d: -f6 | sort | uniq -c| sort -nr
ss -tun |grep ESTAB | tr -s " " ":" | cut -d: -f7 | sort | uniq -c | sort -nr
1、显示/var目录下所有以l开头,以一个小写字母结尾,且中间出现至少一位数字的文件或目录
ls -d /var/l*[0-9]*[[:lower:]]
2、显示/etc目录下以任意一位数字开头,且以非数字结尾的文件或目录
ls -d /etc/[[:digit:]]*[^[:digit:]]
3、显示/etc/目录下以非字母开头,后面跟了一个字母及其它任意长度任意字符的文件或目录
ls -d /etc/[^[:alpha:]][[:alpha:]]*
4、显示/etc/目录下所有以rc开头,并后面是0-6之间的数字,其它为任意字符的文件或目录
ls -d /etc/rc[0-6]*
5、显示/etc目录下,所有以.d结尾的文件或目录
ls /etc/*.d -d
6、显示/etc目录下,所有.conf结尾,且以m,n,r,p开头的文件或目录
ls /etc/[mnpr]*.conf -d
7、只显示/root下的隐藏文件和目录
ls -d /root/.* (ls -d .[^.]*)
8、只显示/etc下的非隐藏目录
ls /etc/*/ -d
"PATTERN" 为基本正则表达式
grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] -e PATTERN ... [FILE...]
grep [OPTIONS] -f FILE ... [FILE...]
-E # 使用扩展正则表达式
-o # 只显示匹配到的字符串而不显示其所在的行的内容
-v # 显示未匹配到的行
-i # 忽略大小写
-q # 静默模式,无论是否匹配到都不打印标准输出
-f file,--file=FILE # 从文件file中读取正则表达式
-e # 使用多个操作来匹配模式
grep -e root -e bash /etc/passwd # 匹配包含root和bash的行
-w # 匹配单词
-c, --count # 匹配到的行计算总数
-s, --no-messages # 对不存在或则不可读文件的错误不打印
-n, --line-number # 对匹配到的行加行号-A NUM, --after-context=NUM # 匹配到某模式时不仅打印匹配到的行还打印其后的NUM行
-B NUM, --before-context=NUM # 匹配到某模式时不仅打印匹配到的行还打印其前的NUM行
-C NUM, -NUM, --context=NUM # 匹配到某模式时不仅打印匹配到的行还打印其前和其后的的NUM行
--color=auto # 对匹配到的文本着色显示
-m # 匹配#次后停止
**正则表达式(REGEXP:Regular Expressions)**是由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意
义,而表示控制或通配的功能
正则表达式引擎:采用不同算法,检查处理正则表达式的软件模块 如:PCRE(Perl Compatible Regular Expressions)
. 匹配任意单个字符
[] 匹配指定范围内的任意单个字符,示例:[wang] [0-9] [a-z] [a-zA-Z]
[^] 匹配指定范围外的任意单个字符
[:alnum:] 字母和数字
[:alpha:] 代表任何英文大小写字符,亦即 A-Z, a-z
[:lower:] 小写字母 [:upper:] 大写字母
[:blank:] 空白字符(空格和制表符)
[:space:] 水平和垂直的空白字符(比[:blank:]包含的范围广)
[:cntrl:] 不可打印的控制字符(退格、删除、警铃...)
[:digit:] 十进制数字 [:xdigit:]十六进制数字
[:graph:] 可打印的非空白字符
[:print:] 可打印字符
[:punct:] 标点符号
用在要指定次数的字符后面,用于指定前面的字符要出现的次数
* 匹配前面的字符任意次,包括0次
贪婪模式:尽可能长的匹配
.* 任意长度的任意字符
\? 匹配其前面的字符0或1次
\+ 匹配其前面的字符至少1次
\{
n\} 匹配前面的字符n次
\{
m,n\} 匹配前面的字符至少m次,至多n次
\{
,n\} 匹配前面的字符至多n次
\{
n,\} 匹配前面的字符至少n次
^ 行首锚定,用于模式的最左侧
$ 行尾锚定,用于模式的最右侧
^PATTERN$ 用于模式匹配整行
^$ 空行
1*$ 空白行
< 或 \b 词首锚定,用于单词模式的左侧
> 或 \b 词尾锚定,用于单词模式的右侧
##### 分组
分组:() 将一个或多个字符捆绑在一起,当作一个整体处理如:(root)+
##### 后向引用
引用前面的分组括号中的模式所匹配字符,而非模式本身; 分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,这些
变量的命名方式为: \1, \2, \3, … ;
\1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符
```bash
示例: \(string1\(string2\)\)
\1 :string1\(string2\)
\2 :string2
或者:\|
a\|b
a或b
C\|cat
C或cat
\(C\|c\)at
Cat或cat
grep "^[sS].*" /proc/meminfo
grep "^[s]\|^[S].*" /proc/meminfo
grep "^s\|^S.*" /proc/meminfo
2、显示/etc/passwd文件中不以/bin/bash结尾的行
grep -v "\$" /etc/passwd
grep -v "\bbin/bash\b$" /etc/passwd
3、显示用户rpc默认的shell程序
getent passwd | grep -w "rpc" | cut -d: -f1,7
grep -w "rpc" /etc/passwd | cut -d: -f1,74、找出/etc/passwd中的两位或三位数
grep --color=auto -o "[0-9]\{2,3\}" /etc/passwd
5、显示CentOS7的/etc/grub2.cfg文件中,至少以一个空白字符开头的且后面有非
空白字符的行
grep "^[[:space:]].*" /etc/grub2.cfg
grep "^ .*" /etc/grub2.cfg
6、找出“netstat -tan”命令结果中以LISTEN后跟任意多个空白字符结尾的行
netstat -tan | grep "LISTEN \+"
7、显示CentOS7上所有UID小于1000以内的用户名和UID
grep --color=auto -v ".*[0-9]\{4\}.*" /etc/passwd | cut -d: -f1,3
8、添加用户bash、testbash、basher、sh、nologin(其shell为/sbin/nologin),找
出/etc/passwd用户名和shell同名的行
grep "^\(\<.*\>\).*/\1$" /etc/passwd
9、利用df和grep,取出磁盘各分区利用率,并从大到小排序
df -h | grep -o "\<[0-9]\{,3\}%" | sort -nr
df -h | grep -o "\<[0-9]\{,3\}%" | sort -nr | cut -d% -f1
1、显示三个用户root、mage、wang的uid和默认shell
getent passwd | grep -w -e ^root -e ^mage -e ^wang | cut -d: -f3,7
2、找出/etc/rc.d/init.d/functions文件中行首为某单词(包括下划线)后面跟一
个小括号的行
grep "^\(.*\)_\?\(.*\)()" /etc/rc.d/init.d/functions
3、使用egrep取出/etc/rc.d/init.d/functions中其基名
echo /etc/rc.d/init.d/functions|egrep -o "[^/]+/?$"
4、使用egrep取出上面路径的目录名
echo /etc/rc.d/init.d/functions/ |egrep -o "/.*[^/]"|egrep -o "/.*/"
5、统计last命令中以root登录的每个主机ip地址登录次数
last | grep root | egrep "\b(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b" | tr -s " "
| cut -d" " -f3 | uniq -c | sort -nr
6、利用扩展正则表达式分别表示0-9、10-99、100-199、200-249、250-255
[0-9]
[1-9][0-9]
1[0-9]{
2}
2[0-4][0-9]
25[0-5]
7、显示ifconfig命令结果中所有ipv4地址
ifconfig | egrep "\b(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b"8、将此字符串:welcome to magedu linux 中的每个字符去重并排序,重复次数多的排到前面
echo "welcome to magedu linux" | grep -o . | sort | uniq -c | sort -nr
sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着
用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。然后读入下行,执行下一个循环。如果没有使诸如‘D’的特殊
命令,那会在两个循环之间清空模式空间,但不会清空保留空间。这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定
向存储输出。
sed [option]... 'script' inputfile...
-n 不输出模式空间内容到屏幕,即不自动打印
-e 多点编辑
-f /PATH/SCRIPT_FILE 从指定文件中读取编辑脚本
-r 支持使用扩展正则表达式
-i.bak 备份文件并原处编辑
script: '地址命令'
注意:sed的脚本、地址定界和命令都一般写在单引号中间
(1) 不给地址:对全文进行处理
(2) 单地址:
#:指定的行
$:最后一行
/pattern/:被此处模式所能够匹配到的每一行
(3) 地址范围:
#,#
#,+#
/pat1/,/pat2/ #模式1到模式2之间的行
#,/pat1/
(4) ~:步进
1~2 奇数行
2~2 偶数行
d 删除模式空间匹配的行,并立即启用下一轮循环
p 打印当前模式空间内容,追加到默认输出之后a [\]text 在指定行后面追加文本,支持使用\n实现多行追加
i [\]text 在行前面插入文本
c [\]text 替换行为单行或多行文本
w /path/file 保存模式匹配的行至指定文件
r /path/file 读取指定文件的文本至模式空间中匹配到的行后
= 为模式空间中的行打印行号
! 模式空间中匹配行取反处理
s///
查找替换,支持使用其它分隔符,s@@@,s###
替换标记:
g 行内全局替换
p 显示替换成功的行
w /PATH/FILE 将替换成功的行保存至文件中
sed ‘2p’ /etc/passwd
sed -n ‘2p’ /etc/passwd
sed -n ‘1,4p’ /etc/passwd
sed -n ‘/root/p’ /etc/passwd
sed -n ‘2,/root/p’ /etc/passwd 从2行开始
sed -n ‘/^$/=’ file 显示空行行号
sed -n -e ‘/^$/p’ -e ‘/^$/=’ file
sed '/root/a\superman' /etc/passwd 在匹配到的行后追加superman
sed '/root/i\superman' /etc/passwd 在匹配到的行前追加superman
sed '/root/c\superman' /etc/passwd 匹配到的行替换为superman
sed ‘1,10d’ file
nl /etc/passwd | sed ‘2,5d’
nl /etc/passwd | sed ‘2a tea’
sed 's/test/mytest/g' example
sed –n ‘s/root/&superman/p’ /etc/passwd 在root单词后追加superman
sed –n ‘s/root/superman&/p’ /etc/passwd 在root单词前追加superman
sed -e ‘s/dog/cat/’ -e ‘s/hi/lo/’ pets
sed –i.bak ‘s/dog/cat/g’ pets
P: 打印模式空间开端至\n内容,并追加到默认输出之前
h: 把模式空间中的内容覆盖至保持空间中
H:把模式空间中的内容追加至保持空间中
g: 从保持空间取出数据覆盖至模式空间
G:从保持空间取出内容追加至模式空间
x: 把模式空间中的内容与保持空间中的内容进行互换
n: 读取匹配到的行的下一行覆盖至模式空间N:读取匹配到的行的下一行追加至模式空间
d: 删除模式空间中的行
D:如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使用合成的模式空间重新启
动循环。如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环
高级用法sed示例
sed -n 'n;p' FILE
sed '1!G;h;$!d' FILE
sed‘N;D’FILE
sed '$!N;$!D' FILE
sed '$!d' FILE
sed ‘G’ FILE
sed ‘g’ FILE
sed ‘/^$/d;G’ FILE
sed 'n;d' FILE
sed -n '1!G;h;$p' FILE
1、删除centos7系统/etc/grub2.cfg文件中所有以空白开头的行行首的空白字符
sed -inr 's#^ +(.*)#\1#p' /etc/grub2.cfg
2、删除/etc/fstab文件中所有以#开头,后面至少跟一个空白字符的行的行首的#和空白字符
sed -inr 's#^# +(.*)#\1#p' /etc/grub2.cfg
3、在centos6系统/root/install.log每一行行首增加#号
sed -inr 's#.*#\#p' /etc/grub2.cfg
4、在/etc/fstab文件中不以#开头的行的行首增加#号
sed -inr 's#^[^#](.*)#\#\1#p' /etc/grub2.cfg
5、处理/etc/fstab路径,使用sed命令取出其目录名和基名
echo /etc/fstab/ | sed -nr 's#(.*/)([^/]+)/?#\1#p'
echo /etc/fstab/ | sed -nr 's#(.*/)([^/]+)/?#\2#p'
6、利用sed 取出ifconfig命令中本机的IPv4地址
ifconfig eth0|sed -nr '2s@[^0-9]+([.0-9]+).*@\1@p'
7、统计centos安装光盘中Package目录下的所有rpm文件的以.分隔倒数第二个字段的重复次数
ls /misc/cd/{
BaseOS,AppStream}/Packages/ | sed -nr 's#.*\.(.*)\.rpm#\1#p' | sort |uniq -c | sort -nr
ls /misc/cd/{
BaseOS,AppStream}/Packages/ | rev | cut -d. -f2 | sort | uniq -c | uniq
8、统计/etc/init.d/functions文件中每个单词的出现次数,并排序(用grep和sed两种方法分别实现)
grep -Eow "[^[:punct:]]+([[:alpha:]0-9+])" /etc/init.d/functions | grep -Eow "[[:alpha:]]+|[0-9]+" | wc -l
sed -r "s/[^[:alpha:]]/\n/g" /etc/init.d/functions| sort | uniq -c | sort -nr
9、将文本文件的n和n+1行合并为一行,n为奇数行seq 10 | sed -nr '1~2N;s/\n/ /p'
awk [options] 'program' var=value file…
awk [options] -f programfile var=value file…
awk [options] 'BEGIN{action;… }pattern{action;… }END{action;… }' file ...
第一步:执行BEGIN{
action;… }语句块中的语句
第二步:从文件或标准输入(stdin)读取一行,然后执行pattern{
action;… }语句块,
它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
第三步:当读至输入流末尾时,执行END{
action;…}语句块
BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,
比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中
END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的
分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块
pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供
pattern语句块,则默认执行{
print },即打印每一个读取到的行,awk读取的每
一行都会执行该语句块
print item1, item2, …
(1) 逗号分隔符
(2) 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
(3) 如省略item,相当于print $0
awk '{print "hello,awk"}'
awk –F: '{print}' /etc/passwd
awk –F: '{print "wang"}' /etc/passwd
awk –F: '{print $1}' /etc/passwd
awk –F: '{print $0}' /etc/passwd
awk –F: '{print $1"\t"$3}' /etc/passwd
grep “?UID”/etc/fstab | awk '{print $2,$4}'
```### awk变量
可以使用内置变量也可以自定义变量
```bash
FS:Field Separator
输入字段分隔符,默认为空白字符
awk -v FS=':' '{print $1,FS,$3}' /etc/passwd
awk –F: '{print $1,$3,$7}' /etc/passwd
OFS:Output Field Separator
输出字段分隔符,默认为空白字符
awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd
RS:Record Seperator
输入记录分隔符,指定输入时的换行符
awk -v RS=' ' ‘{
print }’ /etc/passwd
ORS:Output Record Seperator
输出记录分隔符,输出时用指定符号代替换行符
awk -v RS=' ' -v ORS='###' '{print $0}' /etc/passwd
NF:Number Field
字段数量
awk -F:'{print NF}' /etc/fstab 引用变量时,变量前不需加$
awk -F:'{print $(NF-1)}' /etc/passwd
NR:Number Record
记录号
awk '{print NR}' /etc/fstab ; awk END '{print NR}' /etc/fstab
FNR:各文件分别计数,记录号
awk '{print FNR}' /etc/fstab /etc/inittab
FILENAME:当前文件名
awk '{print FILENAME}' /etc/fstab
ARGC:命令行参数的个数
awk '{print ARGC}' /etc/fstab /etc/inittab
awk 'BEGIN {print ARGC}' /etc/fstab /etc/inittab
ARGV:数组,保存的是命令行所给定的各参数
awk 'BEGIN {print ARGV[0]}' /etc/fstab /etc/inittab
awk 'BEGIN {print ARGV[1]}' /etc/fstab /etc/inittab
(1) -v var=value
(2) 在program中直接定义
awk -v test='hello gawk' '{print test}' /etc/fstab
awk -v test='hello gawk' 'BEGIN{print test}'
awk 'BEGIN{test="hello,gawk";print test}'
awk -F: '{sex=“male”;print $1,sex,age;age=18}' /etc/passwdcat awkscript
{
print script,$1,$2}
awk -F: -f awkscript script="awk" /etc/passwd
格式化输出:printf "FORMAT", item1, item2, ...
(1) 必须指定FORMAT
(2) 不会自动换行,需要显式给出换行控制符,\n
(3) FORMAT中需要分别为后面每个item指定格式符
格式符:与item一一对应
%c:显示字符的ASCII码
%d, %i:显示十进制整数
%e, %E:显示科学计数法数值
%f:显示为浮点数
%g, %G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%:显示%自身
修饰符
#[.#] 第一个数字控制显示的宽度;第二个#表示小数点后精度,%3.1f
- 左对齐(默认右对齐) %-15s
+ 显示数值的正负符号 %+d
awk -F: '{printf "%s",$1}' /etc/passwd
awk -F: '{printf "%s\n",$1}' /etc/passwd
awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
awk -F: '{printf “Username: %s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %15s,UID:%d\n",$1,$3}' /etc/passwd
awk -F: '{printf "Username: %-15s,UID:%d\n",$1,$3}' /etc/passwd
awk '$0~“^root"' /etc/passwd
awk '$0 !~ /root/' /etc/passwd
awk -F: '$3==0' /etc/passwd
awk -F: '$3>=0 && $3<=1000 {print $1}' /etc/passwd
awk -F: '$3==0 || $3>=1000 {print $1}' /etc/passwd
awk -F: '!($3==0) {print $1}' /etc/passwd
awk -F: '!($3>=500) {print $3}' /etc/passwd
awk '/?UID/{print $1}' /etc/fstab
awk '!/?UID/{print $1}' /etc/fstab
awk -F: 'i=1;j=1{print i,j}' /etc/passwd
awk '!0' /etc/passwd ;
awk '!1' /etc/passwd
awk -F: '$3>=1000{print $1,$3}' /etc/passwd
awk -F: '$3<1000{print $1,$3}' /etc/passwd
awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
awk -F: '$NF ~ /bash$/{print $1,$NF}' /etc/passwd
awk -F: ‘/^root\>/,/^nobody\>/{print $1}' /etc/passwd
awk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passwd
awk -F : 'BEGIN {print "USER USERID"} {print $1":"$3}END{print "END FILE"}' /etc/passwd
awk -F: '{print "USER USERID";print $1":"$3} END{print "END FILE"}' /etc/passwd
awk -F: 'BEGIN{print "USER UID \n--------------- "}{print $1,$3}' /etc/passwd
awk -F: 'BEGIN{print "USER UID \n--------------- "}{print $1,$3}END{print "=============="}' /etc/passwd
seq 10 | awk 'i=0'
seq 10 | awk 'i=1'
seq 10 | awk 'i=!i'
seq 10 | awk '{i=!i;print i}'
seq 10 | awk ‘!(i=!i)'
seq 10 | awk -v i=1 'i=!i'
root@server-ctl:/data/scripts# awk 'BEGIN{i=0;print ++i,i}'
1 1
root@server-ctl:/data/scripts# awk 'BEGIN{i=0;print i++,i}'
0 1
示例:
root@server-ctl:/data/scripts# awk -F: 'BEGIN{printf "SEQ USER UID\n"}(NR>=0&&NR<=5){printf "%d %10s
%6s\n",NR,$1,$3}' /etc/passwd
SEQ USER UID
1 root 0
2 daemon 1
3 bin 2
4 sys 3
5 sync 4
```#### awk if
语法:`if(condition1){statement1}else if(condition2){statement2}else{statement3}`
```bash
awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
awk '{if(NF>5) print $0}' /etc/fstab
awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
awk -F: '{if($3>=1000) printf "Common user: %s\n",$1; else printf "root or Sysuser: %s\n",$1}' /etc/passwd
df -h|awk -F% '/^\/dev/{print $1}'|awk '$NF>=80{print $1,$5}'
awk 'BEGIN{ test=100;if(test>90){print "very good"}
else if(test>60){ print "good"}else{print "no pass"}}'
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF)
{print $i,length($i); i++}}' /etc/grub2.cfg
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF)
{if(length($i)>=10){print $i,length($i)}; i++}}' /etc/grub2.cfg
awk 'BEGIN{ total=0;i=0;do{ total+=i;i++;}while(i<=100);print i}'
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
weekdays["mon"]="Monday"
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";
print weekdays["mon"]}‘
awk '!line[$0]++' dupfile
awk '{
!line[$0]++;print $0, line[$0]}' dupfile
awk 'BEGIN{
weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {
print weekdays[i]}}'
netstat -tan | awk '/^tcp/{
state[$NF]++}END{
for(i in state) {
print i,state[i]}}'
awk '{
ip[$1]++}END{
for(i in ip) {
print i,ip[i]}}' /var/log/httpd/access_log
awk 'BEGIN{srand(); for (i=1;i<=10;i++)print int(rand()*100) }'
echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}'
echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$0);print $0}'
netstat -tn | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
SHOW ENGINES\G;
查看支持的存储引擎
MariaDB [(none)]> show engines;
+--------------------+---------+----------------------------------------------------------------------------------+--------------+------
+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------------------------+--------------+------
+------------+
| InnoDB | DEFAULT | Percona-XtraDB, Supports transactions, row-level locking, and foreign keys | YES | YES |
YES |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| MyISAM | YES | Non-transactional engine with good performance and small data footprint | NO | NO |
NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO
|
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| CSV | YES | Stores tables as CSV files | NO | NO | NO |
| ARCHIVE | YES | gzip-compresses tables for a low storage footprint | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO
|
| FEDERATED | YES | Allows to access tables on other MariaDB servers, supports transactions and more | YES | NO
| YES |
| Aria | YES | Crash-safe tables with MyISAM heritage | NO | NO | NO |
+--------------------+---------+----------------------------------------------------------------------------------+--------------+------
+------------+
最常用的是InnoDB, MySQL5.5版本之后,MySQL的默认内置存储引擎已经是InnoDB
Innodb:frm是表定义文件,ibd是数据文件。所有InnoDB表的数据和索引放置于同一个表空间中
表空间文件:datadir定义的目录下
数据文件:ibddata1, ibddata2, …
每个表单独使用一个表空间存储表的数据和索引
两类文件放在对应每个数据库独立目录中
只读(或者写较少)、表较小(可以接受长时间进行修复操作)
大部分场景## 索引
Mysql选用B+树这种数据结构作为索引,可以提高查询索引时的磁盘IO效率,并且可以提高范围查询的效率,并且B+树里的元素也是有
序的。
通常我们认为B+树的非叶子节点不存储数据,只有叶子节点才存储数据;而B树的非叶子和叶子节点都会存储A据,会导致非叶子节点存
储的索引值会更少,树的高度相对会比B+树高,平均的I/O效率会比较低,所以使用B+树作为索引的数据结构,再加上B+树的叶子节点
之间会有指针相连,也方便进行范围查找。
对于B+树在MyISAM中来说, MyISAM中叶子节点的数据区域存储的是数据记录的地址 MyISAM存储引擎在使用索引查询数据时,
会先根据索引查找到数据地址,再根据地址查询到具体的数据。并且主键索引和辅助索引没有太多区别。
Innodb中的主键索引和实际数据时绑定在一起的,也就是说Innodb的一个表一定要有主键索引,如果一个表没有手动建立主键索引,
Innodb会查看有没有唯一索引,如果有则选用唯一索引作为主键索引,如果连唯一索引也没有,则会默认建立一个隐藏的主键索引(用户
不可见)。另外,Innodb的主键索引要比MyISAM的主键索引查询效率要高(少一次磁盘IO),并且比辅助索引也要高很多。所以,我
们在使用Innodb作为存储引擎时,我们最好:
对着上面Mysql中Innodb中对B+树的实际应用(主要看主键索引),可以发现B+树中的一个节点存储的内容是:
首先要知道Hash索引和B+树索引的底层实现原理:
hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路
平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据.
InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值
两者有什么区别 ?
在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的
值的是非主键索引,也被称之为非聚簇索引
在B+树的索引中,叶子节点可能存储了当前的key值,也可能存储了当前的key值以及整行的数据,这就是聚簇索引和非聚簇索引. 在InnoDB
中,只有主键索引是聚簇索引,如果没有主键,则挑选一个唯一键建立聚簇索引.如果没有唯一键,则隐式的生成一个键来建立聚簇索引.
当查询使用聚簇索引时,在对应的叶子节点,可以获取到整行数据,因此不用再次进行回表查询.
聚簇索引查询会更快 ;聚簇索引不是一种单独的索引类型,而是一种数据的存储方式,聚簇索引的顺序,就是数据在硬盘上的物理顺序。
在mysql通常聚簇索引是主键的同义词,每张表只包含一个聚簇索引(其他数据库不一定)。
因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过
主键的值再进行一次查询
聚簇索引的优点:
可以把相关数据保存在一起,如:实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取少量的数据页就能获取某个用户
全部邮件,如果没有使用聚集索引,则每封邮件都可能导致一次磁盘IO。数据访问更快,聚集索引将索引和数据保存在同一个btree中,因此从聚集索引中获取数据通常比在非聚集索引中查找要快。使用覆盖索引
扫描的查询可以直接使用页节点中的主键值。
聚簇索引的缺点:
聚簇数据最大限度地提高了IO密集型应用的性能,但如果数据全部放在内存中,则访问的顺序就没有那么重要了,聚集索引也没有什么优
势了
插入速度严重依赖于插入顺序,按照主键的顺序插入是加载数据到innodb表中速度最快的方式,但如果不是按照主键顺序加载数据,那么
在加载完成后最好使用optimize table命令重新组织一下表。
更新聚集索引列的代价很高,因为会强制innodb将每个被更新的行移动到新的位置。
基于聚集索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题,当行的主键值要求必须将这一行插入到
某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作,页分裂会导致表占用更多的磁盘空间。聚集
索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候。
使用覆盖索引可以实现只回表一次;也就是说取决于查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回
表查询.
举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行select age from employee where age < 20的查询时,在索引的叶子
节点上,已经包含了age信息,不会再次进行回表查询.
覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得(查询语句所要求的字段是否全部命中了索引),不必从数
据表中读取。也可以称之为实现了索引覆盖。
当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少
I/O提高效率。
在创建多列索引时,根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最
左优先,在检索数据时从联合索引的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了
(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则
建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合.如果需要建立联合索引的话,还需要考虑联合索引中
的顺序.此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力.这些都和实际的表结构以及查询方式有关.
MySQL可以使用多个字段同时建立一个索引,叫做联合索引.在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否
则无法命中索引.
具体原因为:MySQL使用索引时需要索引有序,假设现在建立了"name,age,school"的联合索引,那么索引的排序为: 先按照name排序,如果name相同,则
按照age排序,如果age的值也相等,则按照school进行排序.
当进行查询时,此时索引仅仅按照name严格有序,因此必须首先使用name字段进行等值查询,之后对于匹配到的列而言,其按照age字段严格
有序,此时可以使用age字段用做索引查找,以此类推.因此在建立联合索引的时候应该注意索引列的顺序,一般情况下,将查询需求频繁或者字
段选择性高的列放在前面.此外可以根据特例的查询或者表结构进行单独的调整.
Index Condition Pushdown:索引下推, MySQL 5.6引入了索引下推优化,默认开启,使用SET optimizer_switch =
‘index_condition_pushdown=off’;可以将其关闭。 有了索引下推优化,可以在有like条件查询的情况下,减少回表次数。
通过explain查看sql语句的执行计划,通过执行计划来分析索引使用情况
MySQL提供了explain命令来查看语句的执行计划,MySQL在执行某个语句之前,会将该语句过一遍查询优化器,之后会拿到对语句的分析,也
就是执行计划,其中包含了许多信息. 可以通过其中和索引有关的信息来分析是否命中了索引,例如possilbe_key,key,key_len等字段,分别说
明了此语句可能会使用的索引,实际使用的索引以及使用的索引长度.
EXPLAIN SELECT clause
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
以下情况,MySQL无法使用索引
一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。
在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。
这个成本最低的方案就是所谓的执行计划。优化过程大致如下:
1、根据搜索条件,找出所有可能使用的索引
2、计算全表扫描的代价
3、计算使用不同索引执行查询的代价
4、对比各种执行方案的代价,找出成本最低的那一个
缓存SELECT操作或预处理查询的结果集和SQL语句,当有新的SELECT语句或预处理查询语句请求,
先去查询缓存,判断是否存在可用的记录集,判断标准:当前的查询与缓存的SQL语句,是否完全一样,区分大小写
查询缓存的优缺点:
几乎所有需要处理并发读/写的系统都会实现相应的锁系统,一般锁系统包含两类锁:共享锁(shared locks) 和排他锁(exclusive
locks), 或者叫读锁(read locks)和写锁(write locks)。
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段
乐观锁: 总是假设最好的情况,认为竞争总是不存在,每次拿数据的时候都认为不会被修改,因此不会先上锁,在最后更新的时候比
较数据有无更新,可通过版本号或CAS实现。 乐观锁(optimistic locking)表现出大胆、务实的态度。使用乐观锁的前提是, 实际应用
当中,发生冲突的概率比较低。他的设计和实现直接而简洁。 目前Web应用中,乐观锁的使用占有绝对优势。
乐观锁使用场景: 用于读比较多的情况,避免了不必要的加锁的开销
悲观锁: 总是假设最坏的情况,认为竞争总是存在,每次拿数据的时候都认为会被修改,因此每次都会先上锁。其他线程阻塞等待释
放锁。 正如其名字,悲观锁(pessimistic locking)体现了一种谨慎的处事态度。其流程如下:
可以认为MVCC是行级锁的一个变种,它能在大多数情况下避免加锁操作,因此开销更低。无论怎样实现,它们大都实现了非阻塞的读操
作,写操作也只锁定指定的行。MVCC是通过保存数据在某一个时间点的快照来实现的,也就是说无论事务执行多久,每个事务看到的数
据都是一致的。
MVCC(多版本并发控制机制)只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。其 他两个隔离级别都和MVCC不
兼容,因为READUNCOMMITTED总是读取最新的数据行,而不是符合当前 事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加
锁
在MySQL中,事务(Transactions)表示一组原子性的SQL语句,或一个独立工作单元
隔离级别 | 说明 |
---|---|
-------------------------------- | |
READ UNCOMMITTED | 可读取到未提交数据,产生脏读(Dirty reads) |
READ COMMITTED | 可读取到提交数据,但未提交数据不可读,产生不可重复读(Non-repeatable reads),即可读取到多个提交数 |
据,导致每次读取数据不一致 | |
REPEATABLE READ | 可重复读,多次读取数据都一致,产生幻读(Phantom reads),即读取过程中,即使有其它提交的事务修改数 |
据,仍只能读取到未修改前的旧数据。此为MySQL默认设置 | |
SERIALIZABLE | 可串行化,未提交的读事务阻塞修改事务(加读锁,但不阻塞读事务),或者未提交的修改事务阻塞读事务(加写 |
锁,其它事务的读,写都不可以执行)。会导致并发性能差 | |
服务器变tx_isolation 指定,默认为REPEATABLE-READ,可在GLOBAL和SESSION级进行设置 SET tx_isolation='READ?UNCOMMITTED|READ-COMMITTED|REPEATABLE-READ|SERIALIZABLE' |
事务是一系列的操作,他们要符合ACID特性.最常见的理解就是:事务中的操作要么全部成功,要么全部失败.但是只是这样还不够的.
A=Atomicity
原子性,就是上面说的,要么全部成功,要么全部失败.不可能只执行一部分操作.
C=Consistency
系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态.
I=Isolation
隔离性: 通常来说:一个事务在完全提交之前,对其他事务是不可见的.注意前面的通常来说加了红色,意味着有例外情况.
D=Durability
持久性,一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果.
多事务的并发进行一般会造成以下几个问题:
MySQL的四种隔离级别如下:
未提交读(READ UNCOMMITTED)
这就是上面所说的例外情况了,这个隔离级别下,其他事务可以看到本事务没有提交的部分修改.因此会造成脏读的问题(读取到了其他事务未
提交的部分,而之后该事务进行了回滚).这个级别的性能没有足够大的优势,但是又有很多的问题,因此很少使用.
已提交读(READ COMMITTED)
其他事务只能读取到本事务已经提交的部分.这个隔离级别有 不可重复读的问题,在同一个事务内的两次读取,拿到的结果竟然不一样,因为另
外一个事务对数据进行了修改.
REPEATABLE READ(可重复读)
可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是仍然有一个新问题,就是 幻读,当你读取id> 10 的数据行时,对涉及到
的所有行加上了读锁,此时例外一个事务新插入了一条id=11的数据,因为是新插入的,所以不会触发上面的锁的排斥,那么进行本事务进行下
一次的查询时会发现有一条id=11的数据,而上次的查询操作并没有获取到,再进行插入就会有主键冲突的问题.SERIALIZABLE(可串行化)
这是最高的隔离级别,可以解决上面提到的所有问题,因为他强制将所以的操作串行执行,这会导致并发性能极速下降,因此也不是很常用.
InnoDB默认使用的是可重复读隔离级别.
当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制.就像酒店的房间,
如果大家随意进出,就会出现多人抢夺同一个房间的情况,而在房间上装上锁,申请到钥匙的人才可以入住并且将房间锁起来,其他人只有等他
使用完毕才可以再次使用.
从锁的类别上来讲,有共享锁和排他锁.
共享锁: 又叫做读锁. 当用户要进行数据的读取时,对数据加上共享锁.共享锁可以同时加上多个.
排他锁: 又叫做写锁. 当用户要进行数据的写入时,对数据加上排他锁.排他锁只可以加一个,他和其他的排他锁,共享锁都相斥.
用上面的例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的. 一种是真正的入住一晚,在这期间,无论是想入住的
还是想看房的都不可以.
锁的粒度取决于具体的存储引擎**,InnoDB实现了行级锁,页级锁,表级锁.**MyISAM锁粒度只到表级锁
他们的加锁开销从大大小,并发能力也是从大到小.
MySQL支持丰富的日志类型,包括:
日志类型 | 说明 |
---|---|
事务日志 | transaction log 事务日志的写入类型为“追加”,因此其操作为“顺序IO”;通常也被称为:预写式日志 write ahead |
logging | |
事务日志文件: /var/lib/mysql/ib_logfile0, /var/lib/mysql/ib_logfile1 | |
错误日志 | error log |
通用日志 | general log |
慢查询日志 | slow query log |
二进制日志 | binary log |
中继日志 | reley log,在主从复制架构中,从服务器用于保存从主服务器的二进制日志中读取的事件 |
- 配置innodb处理事务日志的行为优化性能` innodb_flush_log_at_trx_commit `:
![](png/20200410095101415.png)
```bash
1: 默认情况下,日志缓冲区将写入日志文件,并在每次事务后执行刷新到磁盘。 完全遵守ACID特性
innodb_flush_log_at_trx_commit设置为1,同时配合sync_binlog = 1表示最高级别的容错能力
另一项参数:innodb_use_global_flush_log_at_trx_commit的值确定是否可以使用SET语句重置innodb_flush_log_at_trx_commit变
量
0: 提交时没有写磁盘的操作; 而是每秒执行一次将日志缓冲区的提交的事务写入刷新到磁盘。 这样可提
供更好的性能,但服务器崩溃可能丢失最后一秒的事务
2: 每次提交后都会写入OS的缓冲区但是不立即3刷新到磁盘,每秒才会进行一次刷新到磁盘文件中。 性能
比值为0略差一些,但操作系统或停电可能导致最后一秒的交易丢失
3: 模拟MariaDB 5.5组提交(每组提交3个同步),此项MariaDB 10.0支持
错误日志为mysqld启动和关闭过程中输出的事件信息、mysqld运行中产生的错误信息、event scheduler 运行一个event时产生的日
志信息及在主从复制架构中的从服务器上启动从服务器线程时产生的信息
通用日志用于记录对数据库的通用操作,包括错误的SQL语句,通用日志可以保存在文件或者数据库中:file(默认值)或
table(mysql.general_log)
慢查询日志用来记录SQL语句执行时长超出指定时长的操作,这样有利于找到执行速度较慢的语句,以便针对性 优化查询性能。
slow_query_log=ON|OFF #开启或关闭慢查询
long_query_time=N #慢查询的阀值,单位秒
slow_query_log_file=HOSTNAME-slow.log #慢查询日志文件
log_slow_filter = admin,filesort,filesort_on_disk,full_join,full_scan,
query_cache,query_cache_miss,tmp_table,tmp_table_on_disk
#上述查询类型且查询时长超过long_query_time,则记录日志
log_queries_not_using_indexes=ON #不使用索引或使用全索引扫描,不论是否达到慢查询阀值的语
句是否记录日志,默认OFF,即不记录
log_slow_rate_limit = 1 #多少次查询才记录,mariadb特有
log_slow_verbosity= Query_plan,explain #记录内容
log_slow_queries = OFF 同slow_query_log #MariaDB 10.0/MySQL 5.6.1 版后已删除
profile 工具用来显示指明某SQL语句在当前session执行过程中所使用的资源情况的信息。可以使用下面的句 子开启profile功能。其
显示的SQL语句都是在最近发送给服务器执行的,显示SQL语句的多少由 profiling_history_size
会话及参数确定。默认为100,设置为0
失能profile功能。
说明 | |
---|---|
STATEMENT | 基于"语句"记录:记录语句,默认模式(MariaDB 10.2.3 版本以下),日志量较少 |
ROW | 基于"行"记录:记录数据,日志量较大 |
MIXED | 混合模式:让系统自行判定该基于哪种方式进行,默认模式(MariaDB 10.2.4及版本以上) |
**STATEMENT-BASED:**当使用基于STATEMENT(语句)的二进制日志时,从主到从的复制通过主服务器将SQL语句 写入二进制log。从
服务器将二进制记录的SQL语句在其数据运行来生成原来数据库,该行为对应于基于statement 的二进制日志。
**ROW-BASED:**当使用基于ROW(行/记录)的二进制日志时,主服务器将事务写入二进制log,意味这可以精确的 确定每个表的某个行
是如何变更的。主到从的复制通过将反映每个表的行更改的事务复制到到从服务器并执行 生成原来的表和数据库,该过程就对应于基于
ROW(行/记录)的二进制日志。
**MIXED:**当指定为基于MIXED的二进制日志格式时,默认是使用基于STATEMENT的记录格式;二进制日志自动切换为ROW- BASED
时取决于特定的SQL语句和特定的存储引擎。
MySQL 5.7.7 之前, 基于STATEMENT的二进制格式为默认格式;MySQL 5.7.7 及更新的版本使用ROW-BASED格式为 默认。
SHOW {BINARY | MASTER} LOGS;
SHOW MASTER STATUS
SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
FLUSH LOGS;
主从复制相关概念有:Master server(主服务器)、Slave server(从服务器)、 Binary logs(二进制日志)、Relay logs(中继日志)及相关的
信息文件。主服务器数据变动后,二进制日志记录下来,主服务器的 Binlog Dump 线程将二进制日志 发送给从服务器的 I/O Thread。
接着从服务器 I/O 线程将其写入中继日志 Relay log。后面的 SQL Thread 将中继日志应用到从服务器,执行相应的 SQL 语句生成同步数
据。
SHOW PROCESSLIST
命令看到,叫 Binlog Dump 线程如:Command: Binlog Dump
。START SLAVE
命令时其就 会创建该线(Master:Binlog Dump Thread ----> binlog ----> Slave:I/O Thread)
I/O 线程状态可以使用SHOW SLAVE STATUS
命令查看log_bin
log_slave_updates
主从复制,一主多从,主库提供读写功能,从库提供写功能。当一个事务在master 提交成功时,会把binlog文件同步到从库服务器上
落地为relay log给slave端执行,这个过程主库是不考虑从库是否有接收到binlog文件,有可能出现这种情况,当主库commit一个事务
后,数据库发生宕机,刚好它的binlog还没来得及传送到slave端,这个时候选任何一个slave端都会丢失这个事务,造成数据不一致情
况。 为了避免出现主从数据不一致的情况,MySQL引入了半同步复制,添加多了一个从库反馈机制,这个有两种方式设置:
#查看进程信息
SHOW PROCESSLIST;
#查看当前的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
#查看当前锁定的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查看当前等锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;#查看错误日志
SHOW GLOBAL VARIABLES LIKE 'log_error'
#查看从节点信息
SHOW SLAVE STATUS;
#查看主节点信息
SHOW MASTER STATUS;
#查看二进制文件和位置
SHOW MASTER LOGS;
#查看插件情况
SHOW PLUGINS;
#查看半同步复制变量和信息
SHOW GLOBAL VARIABLES LIKE '%semi%';
SHOW GLOBAL STATUS LIKE '%semi%';
#查看所有的二进制文件信息
SHOW BINARY LOGS
#查看二进制日志事件
SHOW BINLOG EVENTS
可以使用工具:pt-table-checksum 检查从节点数据是否和主节点数据一致
pt-table-checksum 通过在主节点上执行校验和查询来完成数据复制一致性检查,在数据与主节点 不一致的从节点上的到的结果与主节
点不一致。[DSN]用来指明主节点,如果数据不一致则该工具的退出 状态为非 0 值。
完全备份:整个数据集,包括配置文件,全部数据库,全部日志。
部分备份:只备份数据子集,如部分库或表或者某些SQL语句
增量备份:仅备份最近一次完全备份或增量备份(如果存在增量)以来变化的数据,备份较快,还原复杂
差异备份:仅备份最近一次完全备份以来变化的数据,备份较慢,还原简单
冷备:对数据库的读、写操作均不可进行,相当于停数据库;完全停业务;也叫离线备份温备:对数据库的读操作可执行,但写操作不可执行;影响业务
热备:对数据库的读、写操作均可执行;不影响业务 InnoDB:支持以上三种备份方式 MyISAM:只支持冷备和温备,不支持热备
关系型数据库,是建立在关系模型基础上的数据库,其借助于集合代数等数学概念和方法来处理数据库
中的数据。主流的 oracle、DB2、MS SQL Server 和 mysql 都属于这类传统数据库。
NoSQL 数据库,全称为 Not Only SQL,意思就是适用关系型数据库的时候就使用关系型数据库,不适
用的时候也没有必要非使用关系型数据库不可,可以考虑使用更加合适的数据存储。主要分为临时性键值存
储(memcached、Redis)、永久性键值存储(ROMA、Redis)、面向文档的数据库(MongoDB、
CouchDB)、面向列的数据库(Cassandra、HBase),每种 NoSQL 都有其特有的使用场景及优点。
Oracle,mysql 等传统的关系数据库非常成熟并且已大规模商用,为什么还要用 NoSQL 数据库呢?主
要是由于随着互联网发展,数据量越来越大,对性能要求越来越高,传统数据库存在着先天性的缺陷,即单
机(单库)性能瓶颈,并且扩展困难。这样既有单机单库瓶颈,却又扩展困难,自然无法满足日益增长的海量数据存储及其性能要求,所
以才会出现了各种不同的 NoSQL 产品,NoSQL 根本性的优势在于在云计算时代,简单、易于大规模分布式扩展,并且读写性能非常高
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到
不同 的数据库上面,这样也就将数据或者说压力分担到不同的库上面。垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各
业务之间的耦合度非常低,相互影响很小, 业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到
不同的数据库中。 根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。
垂直切分的优缺点:
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中 包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中
的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中。
拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比
如: 从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结
合日期来拆分, 不同的数据按照会员 ID 做分组,这样所有的数据查询 join 都会在单库内解决;如果从
商户的角度来讲,要查询某 个商家某天所有的订单数,就需要按照商户 ID 做拆分;但是如果系统既想
按会员拆分,又想按商家数据,则会有 一定的困难。如何找到合适的分片规则需要综合考虑衡量。
几种典型的分片规则包括:
对于架构师来说:可以这么理解 Mycat: Mycat 是一个强大的数据库中间件,不仅仅可以用作读写分离、以及分表分库、容灾备
份,而且可以用于多 租户1应用开发、云平台基础设施、让你的架构具备很强的适
应性和灵活性,借助于即将发布的 Mycat 智能优化模 块,系统的数据访问瓶颈和热点一目了然,根据这些统计分析数据,你可以自动或
手工调整后端存储,将不同的 表映射到不同存储引擎上,而整个应用的代码一行也不用改变。
**为什么互联网 企业对于其服务的高可用性要求高?**高可用代表着可以持续的提供服务,持续不断的营收,哪怕一分一秒,对于大企业
来 说都是不可轻易接受的,一个实实在在的例子是:在 2013 年 8 月份,亚马逊全球购物网站Amazon
服务停了仅仅 15 分钟 (包括 web 和移动端服务),结果损失接近 990000 美元,每分钟接近 66000 美元,也即是每分钟损失近 52 万人
民币。 即使对于如此体量的公司也是不小的数目。
1.Galera replication library (galera-3):
基于认证的复制(Certification-Based Replication)
wsrep_set_donor
参数事先指定某个节点为 donor,不指定,则集群会根据已有信息自动指定一个wsrep_sst_method = rsyncwsrep_sst_donor = "node1, node2"
PXC 常用的 4 个端口号
3306:数据库对外服务的端口号
4444:请求SST的端口号
4567:组成员之间进行沟通的端口号
4568:用于传输IST的端口号
root@ubuntu1904:~#ansible websrvs -a 'grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf'
172.20.1.79 | CHANGED | rc=0 >>
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://172.20.1.69,172.20.1.68,172.20.1.67,172.20.1.79
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=172.20.1.79
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-4
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
172.20.1.67 | CHANGED | rc=0 >>
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://172.20.1.69,172.20.1.68,172.20.1.67,172.20.1.79
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=172.20.1.67wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-3
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"
172.20.1.68 | CHANGED | rc=0 >>
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://172.20.1.69,172.20.1.68,172.20.1.67,172.20.1.79
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=172.20.1.68
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-2
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"
172.20.1.69 | CHANGED | rc=0 >>
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://172.20.1.69,172.20.1.68,172.20.1.67,172.20.1.79
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=172.20.1.69
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-1
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
数据库规范化,又称数据库或资料库的正规化、标准化,是数据库设计中的一系列原理和技术,以减少
数据库中数据冗余,增进数据的一致性。关系模型的发明者埃德加?科德最早提出这一概念,并于1970
年代初定义了第一范式、第二范式和第三范式的概念
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,不同的规范要求被称为不同范
式,各种范式呈递次规范,越高的范式数据库冗余越小
目前关系数据库有六种范式:
第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴德斯科范
式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式
(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类
推。一般数据库只需满足第三范式(3NF)即可
第一范式:1NF
无重复的列,每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有
多个值或者不能有重复的属性,确保每一列的原子性。除去同类型的字段,就是无重复的列
说明:第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据
库
第二范式:2NF
属性完全依赖于主键,第二范式必须先满足第一范式,要求表中的每个行必须可以被唯一地区分,通常
为表加上每行的唯一标识PK,非PK的字段需要与整个PK有直接相关性
第三范式:3NF
属性不依赖于其它非主属性,满足第三范式必须先满足第二范式。第三范式要求一个数据表中不包含已
在其它表中已包含的非主关键字信息,非PK的字段间不能有从属关系
续的删改查的时候可能更加快速以及确保操作数据范围安全.
推荐使用自增ID,不要使用UUID.因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了
主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,
会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降.
总之,在数据量大一些的情况下,用自增主键性能会好一些.
关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键.
密码散列,盐,用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率.
/etc/ansible/ansible.cfg 主配置文件,配置 ansible 工作特性
/etc/ansible/hosts 主机清单
/etc/ansible/roles/ 存放角色的目录
/etc/ansible/hosts
/etc/ansible/hosts
,inventory file 可以有多个,且也可以通过Dynamic Inventory 来动态生成[webwebsrvss]
172.20.1.67
172.20.1.68
172.20.1.69
[webwebsrvss:vars]
web67=172.20.1.67
web68=172.20.1.68
web69=172.20.1.69
[appwebsrvss]
172.20.1.84
172.20.1.86
172.20.1.87
[appwebsrvss:vars]
app84=172.20.1.84
app86=172.20.1.86
app87=172.20.1.87
[dnswebsrvss]
172.20.1.79
172.20.1.88
172.20.1.89
[dnswebsrvss:vars]
dns79=172.20.1.79
dns88=172.20.1.88
dns89=172.20.1.89
```### Ansible 附带的工具
- 安装 Ansible 时会附带一些必要的工具
- 加粗的工具经常使用,其它不常使用
| | |
| :---------------------------: | :--------------------------------------: |
| **/usr/bin/ansible** | 主程序,临时命令执行工具 |
| **/usr/bin/ansible-doc** | 查看配置文档,模块功能查看工具 |
| /usr/bin/ansible-galaxy | 下载/上传优秀代码或 Roles 模块的官网平台 |
| **/usr/bin/ansible-playbook** | 定制自动化任务,编排剧本工具 |
| /usr/bin/ansible-pull | 远程执行命令的工具 |
| /usr/bin/ansible-vault | 文件加密工具 |
| /usr/bin/ansible-console | 基于 Console 界面与用户交互的执行工具 |
- `ansible-doc` 命令,用来显示个模块的帮助,一般直接跟模块名即可
### Ansible 命令
- ansible 命令通过 ssh 协议,实现对远程主机的配置管理、应用部署、任务执行等功能,使用-k 选项来输入远程主机的密码。由于每台
主机的密码可能不一样。建议:使用此工具前,先配置 ansible 主控端能基于密钥认证的方式与各个被管理节点通讯。如下面的脚本可以
实现 ansible 主控机与被控主机基于 Key 验证
范例:利用 sshpass 批量实现基于 key 验证
```bash
#!/bin/bash
#
#*******************************************************************************
#Author: steveli
#QQ: 1049103823
#Data: 2019-12-08
#FileName: key_cert.sh
#URL: https://blog.csdn.net/YouOops
#Description: Test scrpting.
#Copyright (C): 2019 All rights reserved
#*******************************************************************************
ssh-keygen -f /root/.ssh/id_rsa -P ''
NET=172.20.1
export SSHPASS=stevenux
for IP in {
80..99}; do
sshpass -e ssh-copy-id ${NET}.${IP}
done
~# ansible [ options [-m module_name] [-a args] ]
用于匹配控制的主机列表,筛选出特定的主机来执行特定任务 :
| 模式 | 意义 | 例子 |
| :--------: | :-------------------------: | :--------------------------------------------------------------------------------------------------
-----------: |
| all | 所有 Inventory 中定义的主机 | ansible all –m ping |
| * | 通配符 | ansible “*” -m ping |
| | | ansible 192.168.1.* -m ping |
| | | ansible “websrvss” -m ping |
| : | 逻辑或关系 | ansible “webwebsrvss:appwebsrvss” -m ping |
| | | ansible “192.168.1.10:192.168.1.20” -m ping |
| :& | 逻辑与关系 | ansible “webwebsrvss:&dbwebsrvss” –m ping #在 webwebsrvss 组并且在
dbwebsrvss 组中的主机 |
| :! | 逻辑非关系 | ansible ‘webwebsrvss:!dbwebsrvss’ –m ping #在 webwebsrvss 组,但不在 dbwebsrvss 组中的
主机。 注意:此处为单引号 |
| 正则表达式 | | ansible “webwebsrvss:&dbwebsrvss” –m ping |
| 正则表达式 | | ansible “~(web \| db).*.magedu.com” –m ping |
#### ansible 命令执行过程
1. 加载自己的配置文件 默认`/etc/ansible/ansible.cfg`
2. 加载自己对应的模块文件,如:command
3. 通过 ansible 将模块或命令生成对应的临时 py 文件,并将该文件传输至远程服务器的对应执行用户
`$HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY` 文件
4. 给文件加执行权限执行
5. 执行并返回结果
6. 删除临时 py 文件,退出
**ansible 执行后返回状态可以在配置文件自定义`/ect/ansible/ansible.cfg`**
`green`:绿色表示执行成功且不需要做改变
`yellow`:执行成功并且对被控主机有改动
`red`:执行失败
- **ansible 使用范例**
```bash
#以stevenux用户执行ping存活检测ansible all -m ping -u stevenux -k
#以stevenux sudo至root执行ping存活检测
ansible all -m ping -u stevenux -k -b
#以stevenux sudo至steve用户执行ping存活检测
ansible all -m ping -u stevenux -k -b --become-user=steve
#以stevenux sudo至root用户执行ls
ansible all -m command -u stevenux -a 'ls /root' -b --become-user=root -k
$VARNAME < > | ; &
等符号和相应功能,用 shell 模块实现root@ubuntu1904:~#ansible all -m command -a "echo 'Hello ansibleansible'"
172.20.1.79 | CHANGED | rc=0 >>
Hello ansibleansible
172.20.1.67 | CHANGED | rc=0 >>
Hello ansibleansible
172.20.1.68 | CHANGED | rc=0 >>
Hello ansibleansible
cat /tmp/test.md | awk -F‘|’ ‘{print $1,$2}’ &> /tmp/example.txt
这些复杂命令,即使使写到脚本-->copy 到远程主机-->执行
;再把 需要的结果 fetch 回来主控机器。root@ubuntu1904:~#ansible all -m shell -a 'ip addr | sed -nr "s#.(1
72.20.1...).#\1#p"'
172.20.1.67 | CHANGED | rc=0 >>
172.20.1.67
172.20.1.68 | CHANGED | rc=0 >>
172.20.1.68
172.20.1.84 | CHANGED | rc=0 >>
172.20.1.84
#!/bin/bash
echo `ip a` |sed -nr 's/inet (172.20.*\/..) .*/\1/p'
root@ubuntu1904:~#ansible webwebsrvss -m script -a './script.sh'
172.20.1.67 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 172.20.1.67 closed.\r\n",
"stderr_lines": [
"Shared connection to 172.20.1.67 closed."
],
"stdout": "172.20.1.67/16\r\n",
"stdout_lines": [
"172.20.1.67/16" # 返回ip地址
]
}
172.20.1.68 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 172.20.1.68 closed.\r\n",
"stderr_lines": [
"Shared connection to 172.20.1.68 closed."
],
"stdout": "172.20.1.68/16\r\n",
"stdout_lines": [
"172.20.1.68/16"
]
}
172.20.1.69 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 172.20.1.69 closed.\r\n",
"stderr_lines": [
"Shared connection to 172.20.1.69 closed."
],
"stdout": "172.20.1.69/16\r\n",
"stdout_lines": [
"172.20.1.69/16"
]
}
root@ubuntu1904:~#ansible all -m copy -a 'src=~/script.sh dest=/data/'
172.20.1.67 | CHANGED => {
"changed": true,
"checksum": "c3e89fbe08ae1d3edfa22b1c9968abd470ba8e81",
"dest": "/data/script.sh",
"gid": 0,
"group": "root",
"md5sum": "6a33896eae3ee660a6b0c0bb60a5b326",
"mode": "0644",
"owner": "root",
"size": 67,
"src": "/root/.ansible/tmp/ansible-tmp-1575851039.0323763-219776225171533/source",
"state": "file",
"uid": 0
}
......
# 将文本内容直接在被控机生成文件
root@ubuntu1904:~#ansible all -m copy -a 'content="Hello asible" dest=/data/hello.txt'
172.20.1.87 | CHANGED => {
"changed": true,
"checksum": "8754e6aac9f6b9b741c3bdf974e6a33bbc72b321",
"dest": "/data/hello.txt",
"gid": 0,
"group": "root",
"md5sum": "a131edb4211ac4a39aac74d8a04126f6",
"mode": "0644",
"owner": "root",
"size": 12,
"src": "/root/.ansible/tmp/ansible-tmp-1575851292.1085637-80128185007427/source",
"state": "file",
"uid": 0
}
root@ubuntu1904:~#ansible all -a 'cat /data/hello.txt'
172.20.1.89 | CHANGED | rc=0 >>
Hello asible
172.20.1.67 | CHANGED | rc=0 >>
Hello asible
172.20.1.68 | CHANGED | rc=0 >>
Hello asible
# 拷贝/etc/sysconfig/文件夹下的文件,不包括文件夹本身
root@ubuntu1904:~#ansible all -m copy -a 'src=/etc/sysconfig/ dest=/backup'
root@ubuntu1904:~#ansible all -m fetch -a 'src=/data/hello.txt dest=/data/'
172.20.1.87 | CHANGED => {
"changed": true,
"checksum": "8754e6aac9f6b9b741c3bdf974e6a33bbc72b321","dest": "/data/172.20.1.87/data/hello.txt",
"md5sum": "a131edb4211ac4a39aac74d8a04126f6",
"remote_checksum": "8754e6aac9f6b9b741c3bdf974e6a33bbc72b321",
"remote_md5sum": null
}
root@ubuntu1904:~#cat /data/172.20.1.67/data/hello.txt
Hello asible
# 创建文件
root@ubuntu1904:~#ansible all -m file -a 'path=/data/hello.txt state=touch'
# 删除文件
root@ubuntu1904:~#ansible all -m file -a 'path=/data/hello.txt state=absent'
# 修改文件属主和权限
root@ubuntu1904:~#ansible all -m file -a 'path=/data/hello.txt owner=steve mode=0700'
# 创建文件夹
root@ubuntu1904:~#ansible all -m file -a 'path=/data/dir state=directory mode=0700'
# 为远程主机某个文件创建文件夹
root@ubuntu1904:~#ansible all -m file -a 'src=/data/hello.txt dest=/data/hello-link state=link'
root@ubuntu1904:~#ansible all -m unarchive -a 'src=/data/bar.tar.gz dest=/var/lib/bar'
root@ubuntu1904:~#ansible all -m unarchive -a 'src=/data/bar.zip dest=/data copy=no mode=0700'
root@ubuntu1904:~#ansible all -m unarchive -a 'src=https://suosuoli.cn/god-of-editor.pdf.zip dest=/data
remove=True
可以删除源文件。
root@ubuntu1904:~#ansible all -m archive -a 'path=/data/ dest=/data/dir.tar.bz2 format=bz2 mode=0700'
172.20.1.87 | CHANGED => {
"archived": [],
"arcroot": "/data/",
"changed": true,
"dest": "/data/dir.tar.bz2",
"expanded_exclude_paths": [],
"expanded_paths": [
"/data/"
],
"gid": 0,
"group": "root",
"missing": [],
"mode": "0700",
"owner": "root",
"size": 115,
"state": "file",
"uid": 0
}
......
root@ubuntu1904:~#ansible all -m fetch -a 'src=/data/dir.tar.bz2 dest=/data'
172.20.1.87 | CHANGED => {
"changed": true,
"checksum": "e74d1ab8aad31d62cacad3a65a2a0ccc5623871c",
"dest": "/data/172.20.1.87/data/dir.tar.bz2",
"md5sum": "706f8ee46597a3c62e6c4597f37739a5",
"remote_checksum": "e74d1ab8aad31d62cacad3a65a2a0ccc5623871c",
"remote_md5sum": null
}
......
oot@ubuntu1904:~#ll /data/172.20.1.87/data/
total 8
-rwx------ 1 root root 116 Dec 9 09:04 dir.tar.bz2
-rw-r--r-- 1 root root 12 Dec 9 08:35 hello.txt
root@ubuntu1904:~#ansible webwebsrvss -m hostname -a 'name=webhost'
172.20.1.69 | CHANGED => {
"ansible_facts": {
"ansible_domain": "",
"ansible_fqdn": "webhost",
"ansible_hostname": "webhost",
"ansible_nodename": "webhost"
},
"changed": true,
"name": "webhost"
}
172.20.1.67 | CHANGED => {
"ansible_facts": {
"ansible_domain": "",
"ansible_fqdn": "webhost",
"ansible_hostname": "webhost",
"ansible_nodename": "webhost"
},
"changed": true,
"name": "webhost"
}
172.20.1.68 | CHANGED => {
"ansible_facts": {
"ansible_domain": "",
"ansible_fqdn": "webhost",
"ansible_hostname": "webhost",
"ansible_nodename": "webhost"
},
"changed": true,
"name": "webhost"
}
root@ubuntu1904:~#ansible webwebsrvss -a 'hostname'
172.20.1.68 | CHANGED | rc=0 >>
webhost
172.20.1.69 | CHANGED | rc=0 >>
webhost
172.20.1.67 | CHANGED | rc=0 >>
webhost
#备份数据库脚本
[root@centos8 ~]#cat mysql_backup.sh
mysqldump -A -F --single-transaction --master-data=2 -q -uroot |gzip >
/data/mysql_</span><span class="token function">date</span> +%F_%T<span class="token variable">.sql.gz
#创建任务
ansible 192.168.39.28 -m cron -a 'hour=2 minute=30 weekday=1-5 name="backup
mysql" job=/root/mysql_backup.sh'
ansible websrvs -m cron -a "minute=/5 job='/usr/sbin/ntpdate 172.20.0.1&>/dev/null' name=Synctime"
#禁用计划任务
ansible websrvs -m cron -a "minute=/5 job='/usr/sbin/ntpdate 172.20.0.1
&>/dev/null' name=Synctime disabled=yes"
#启用计划任务
ansible websrvs -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.20.0.1
&>/dev/null' name=Synctime disabled=no"
#删除任务
ansible websrvs -m cron -a "name='backup mysql' state=absent"
ansible websrvs -m cron -a ‘state=absent name=Synctime’
# 安装
root@ubuntu1904:~#ansible webwebsrvss -m yum -a 'name=nginx state=present'
# 删除
root@ubuntu1904:~#ansible webwebsrvss -m yum -a 'name=nginx state=absent'
root@ubuntu1904:~#ansible webwebsrvss -m service -a 'name=httpd state=started enabled=yes'
root@ubuntu1904:~#ansible webwebsrvss -m service -a 'name=httpd state=stopped'
root@ubuntu1904:~#ansible webwebsrvss -m service -a 'name=httpd state=reloaded'
root@ubuntu1904:~#ansible webwebsrvss -m service -a 'name=httpd state=restarted'
# 创建系统用户nginx
root@ubuntu1904:~#ansible webwebsrvss -m user -a 'name=nginx groups="root,daemon" system=yes shell=/sbin/nologin
createhome=no home=/data/nginx non_unique=yes'
172.20.1.67 | CHANGED => {
"changed": true,
"comment": "",
"create_home": false,"group": 995,
"groups": "root,daemon",
"home": "/data/nginx",
"name": "nginx",
"shell": "/sbin/nologin",
"state": "present",
"system": true,
"uid": 995
}
172.20.1.69 | CHANGED => {
"changed": true,
"comment": "",
"create_home": false,
"group": 992,
"groups": "root,daemon",
"home": "/data/nginx",
"name": "nginx",
"shell": "/sbin/nologin",
"state": "present",
"system": true,
"uid": 994
}
172.20.1.68 | CHANGED => {
"changed": true,
"comment": "",
"create_home": false,
"group": 995,
"groups": "root,daemon",
"home": "/data/nginx",
"name": "nginx",
"shell": "/sbin/nologin",
"state": "present",
"system": true,
"uid": 995
}
# 删除用户
root@ubuntu1904:~#ansible webwebsrvss -m user -a 'name=nginx state=absent remove=yes'
172.20.1.69 | CHANGED => {
"changed": true,
"force": false,
"name": "nginx",
"remove": true,
"state": "absent",
"stderr": "userdel: nginx mail spool (/var/spool/mail/nginx) not found\nuserdel: nginx home directory (/data/nginx) not
found\n",
"stderr_lines": [
"userdel: nginx mail spool (/var/spool/mail/nginx) not found",
"userdel: nginx home directory (/data/nginx) not found"
]
}
......
```#### Group 模块
- 功能: 管理用户组
- 注意点:
- 例子:
```bash
# 创建组
root@ubuntu1904:~#ansible webwebsrvss -m group -a 'name=nginx gid=123 state=present system=yes'
172.20.1.69 | CHANGED => {
"changed": true,
"gid": 123,
"name": "nginx",
"state": "present",
"system": true
}
172.20.1.67 | CHANGED => {
"changed": true,
"gid": 123,
"name": "nginx",
"state": "present",
"system": true
}
172.20.1.68 | CHANGED => {
"changed": true,
"gid": 123,
"name": "nginx",
"state": "present",
"system": true
}
root@ubuntu1904:~#ansible webwebsrvss -a 'getent group nginx'
172.20.1.69 | CHANGED | rc=0 >>
nginx:x:123:
172.20.1.68 | CHANGED | rc=0 >>
nginx:x:123:
172.20.1.67 | CHANGED | rc=0 >>
nginx:x:123:
# 删除组
root@ubuntu1904:~#ansible webwebsrvss -m group -a 'name=nginx state=absent'
root@ubuntu1904:~#ansible webwebsrvss -m setup
172.20.1.67 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.20.1.67"
],
"ansible_all_ipv6_addresses": [
"fe80::4d59:6b61:25f9:d67d"
],
"ansible_apparmor": {
"status": "disabled"
},
......
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_nodename"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_hostname"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_domain"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_memtotal_mb"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_memory_mb"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_memfree_mb"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_os_family"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_distribution_major_version"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_distribution_version"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_processor_vcpus"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_all_ipv4_addresses"
root@ubuntu1904:~#ansible websrvs -m setup -a "filter=ansible_architecture"
# 一些比较有用的参数
{
{
ansible_default_ipv4.address }}
{
{
ansible_distribution}}
{
{
ansible_distribution_major_version}}
{
{
ansible_fqdn}}
{
{
ansible_hostname}}
{
{
ansible_machine}}
{
{
ansible_memtotal_mb}}
{
{
ansible_memory_mb.nocache.free}}
{
{
ansible_memory_mb.nocache.used}}
{
{
ansible_memory_mb.real.total}}
{
{
ansible_memory_mb.real.free}}
{
{
ansible_memory_mb.real.used}}
{
{
ansible_service_mgr}}
{
{
ansible_processor_cores}}
{
{
ansible_processor_count}}
{
{
ansible_processor_threads_per_core}}
{
{
ansible_pkg_mgr}}
Playbook 中包含多个 role,每个 role 对应于在远程主机完成某个比较复杂的工作,事先构建的 role 包含各个细分的 task,每个 task 会
调用 ansible 提供的相应模块在远程主机完成部分工作,多个 task 共同完成 role所需要完成的工作。
Hosts
被控制和管理的的远程主机列表Tasks
任务集,每个 task 完成某个简单任务- Variables
内置变量或自定义变量在 playbook 中调用Templates
模板,可替换模板文件中的变量并实现一些简单逻辑的文件Handlers
和 notify
结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行tags
标签 指定某条任务执行,用于选择运行 playbook 中的部分代码。ansible 具有幂等性,因此会自动跳过没有变化的部分,即便URI: Uniform Resource Identifier 统一资源标识,分为URL和URN
URN: Uniform ResourceNaming,统一资源命名 示例: P2P下载使用的磁力链接是URN的一种实现 magnet:?
xt=urn:btih:660557A6890EF888666
URL: Uniform Resorce Locator,统一资源定位符,用于描述某服务器某特定资源位置 两者区别:URN如同一个人的名称,而URL
代表一个人的住址。换言之,URN定义某事物的身份,而URL提供查找该事物的方法。URN仅用于命名,而不指定地址
URL组成:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FPuitCW3-1604371435767)(png/image-20200411132728557.png)]
scheme:方案,访问服务器以获取资源时要使用哪种协议
user:用户,某些方案访问资源时需要的用户名
password:密码,用户对应的密码,中间用:分隔
Host:主机,资源宿主服务器的主机名或IP地址
port:端口,资源宿主服务器正在监听的端口号,很多方案有默认端口号
path:路径,服务器资源的本地名,由一个/将其与前面的URL组件分隔
params:参数,指定输入的参数,参数为名/值对,多个参数,用;分隔
query:查询,传递参数给程序,如数据库,用?分隔,多个查询用&分隔
frag:片段,一小片或一部分资源的名字,此组件在客户端使用,用#分隔
IP(独立IP):即Internet Protocol,指独立IP数。一天内来自相同客户机IP 地址只计算一次,记录远程客户机IP地址的计算机访问网站
的次数,是衡量网站流量的重要指标
PV(访问量): 即Page View, 页面浏览量或点击量,用户每次刷新即被计算一次,PV反映的是浏览某网站的页面数,PV与来访者的数
量成正比,PV并不是页面的来访者数量,而是网站被访问的页面数量UV(独立访客):即Unique Visitor,访问网站的一台电脑为一个访客。一天内相同的客户端只被计算一次。可以理解成访问某网站的电
脑的数量。网站判断来访电脑的身份是通过cookies实现的。如果更换了IP后但不清除cookies,再访问相同网站,该网站的统计中UV数是
不变的
http/0.9
1991,原型版本,功能简陋,只有一个命令GET。GET /index.html ,服务器只能回应HTML格式字符
串,不能回应别的格式
http/1.0
1996年5月,支持cache, MIME( Multipurpose Internet Mail Extensions ), method 每个TCP连接只能发送一个请求,发送数据完毕,连
接就关闭,如果还要请求其他资源,就必须再新建一个连接 引入了POST命令和HEAD命令 头信息是 ASCII 码,后面数据可为任何格式。
服务器回应时会告诉客户端,数据是什么格式,即Content-Type字段的作用。这些数据类型总称为MIME 多用途互联网邮件扩展,每个
值包括一级类型和二级类型,预定义的类型,也可自定义类型, 常见Content-Type值:text/xml image/jpeg audio/mp3
http/1.1
1997年1月,引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复
用,不用声明Connection: keep-alive。对于同一个域名,大多数浏览器允许同时建立6个持久连接引入
了管道机制,即在同一个TCP连接里,客户端可以同时发送多个请求,进一步改进了HTTP协议的效率
新增方法:PUT、PATCH、OPTIONS、DELETE 同一个TCP连接里,所有的数据通信是按次序进行的。
服务器只能顺序处理回应,前面的回应慢,会有许多请求排队,造成"队头堵塞"(Head-of-line
blocking) 为避免上述问题,两种方法:一是减少请求数,二是同时多开持久连接。
网页优化技巧,如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等 HTTP
协议不带有状态,每次请求都必须附上所有信息。请求的很多字段都是重复的,浪费带宽,影响速度
缓存处理:在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,
HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None?Match等更多可供选择的缓存头来控制缓存策略
带宽优化及网络连接的使用:HTTP1.0中,存在一些浪费带宽的现象,例如:客户端只是需要某个
对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头
引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),方便了
开发者自由的选择以便于充分利用带宽和连接 错误通知的管理,在HTTP1.1中新增24个状态响应
码,如409(Conflict)表示请求的资源与资源当前状态冲突;410(Gone)表示服务器上的某个
资源被永久性的删除
Host头处理:在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并
没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个
虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应
消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
长连接:HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在
一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在
HTTP1.1中默认开启Connection: keep-alive,弥补了HTTP1.0每次请求都要创建连接的缺点
HTTP1.x在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端
更为突出
HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,无
法保证数据的安全性
HTTP1.x在使用时,header里携带的内容过大,增加了传输的成本,并且每次请求header基本不
怎么变化,尤其在移动端增加用户流量
虽然HTTP1.x支持了keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样
会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间
为解决安全问题,网景在1994年创建了HTTPS,并应用在网景导航者浏览器中。 最初,
HTTPS是与SSL一起使用的;在SSL逐渐演变到TLS时(其实两个是一个东西,只是名字不同而已),最
新的HTTPS也由在2000年五月公布的RFC 2818正式确定下来。HTTPS就是安全版的HTTP,目前大型网
站基本实现全站HTTPS
SPDY:2009年,谷歌研发,综合HTTPS和HTTP两者有点于一体的传输协议,主要特点:
降低延迟,针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通
过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了
带宽的利用率
请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有
可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,重要的请求就会优先得到响
应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文
件等加载,可以保证用户能第一时间看到网页内容
header压缩。HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大
小和数量
基于HTTPS的加密协议传输,大大提高了传输数据的可靠性
服务端推送(server push),采用了SPDY的网页,例如网页有一个sytle.css的请求,在客户端收
到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js
时就可以直接从缓存中获取到,不用再发请求了
http/2.0:2015年,HTTP2.0是SPDY的升级版
一次http事务包括:
http请求:http request
http响应:http response
Web资源:web resource, 一个网页由多个资源(文件)构成,打开一个页面,通常会有多个资源展
示出来,但是每个资源都要单独请求。一个“Web 页面”通常并不是单个资源,而是一组资源的集合
资源类型:
静态文件:无需服务端做出额外处理 文件后缀:.html, .txt, .jpg, .js, .css, .mp3, .avi一旦创建好,文件不再改变。图片数目多,占用磁盘
空间多,一般使用单独的图片服务器;HTML、CSS、JavaScript:这些文本是文本的,前端工程师可以修改这些文件,但修改次数较少,
一段时间都不变
动态文件:服务端执行程序,返回执行的结果 文件后缀:.php, .jsp ,.asp内容由后台程序动态生成,比如查询数据库后,将查询结果生成
为HTML### 后台应用架构
单体架构JSP、Servlet
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gpmIGyHa-1604371435768)(png/image-20200410170855166.png)]
GET
,POST
或是名词如OPTIONS
,HEAD
用来定义客户 端想要发出的动作。一般,客户GET
方法来从服务器获取资源,使用POST
方法来 提交HTML表单的值个服务器,其它操作可能需要使用特定的方法。POST
方法时向服务器发送资源时会有GET
: 从服务器获取一个资源HEAD
: 只从服务器获取文档的响应首部POST
: 向服务器输入数据,通常会再由网关程序继续处理PUT
: 将请求的主体部分存储在服务器中,如上传文件100-101
信息提示200-206
成功300-307
重定向400-415
错误类信息,客户端错误500-505
错误类信息,服务器端错误200
: OK 成功,请求数据通过响应报文的entity-body部分发送301
: Moved Permanently 请求的URL指向的资源已经被删除;但在响应报文中通过首部Location指明了资源现在所处的新位置;302
: Moved Temporarily 响应报文Location指明资源临时新位置304
: Not Modified 客户端发出了条件式请求,但服务器上的资源未曾发生改变,则通过响应此响应状态码通知客户端;401
: Unauthorized 需要输入账号和密码认证方能访问资源;:403
: Forbidden 请求被禁止;404
: Not Found服务器无法找到客户端请求的资源;500
: Internal Server Error 服务器内部错误;502
: Bad Gateway 代理服务器从后端服务器收到了一条伪响应,如无法连接到网关;503
: 服务不可用,临时服务器维护或过载,服务器无法处理请求504
: 网关超时三种模式都是 httpd 的 MPM 多路处理模块提供的功能,它们的主要区别为是否是基于线程级
的服务和请求处理,以及是否是多进程多线程 I/O 复用。
ThreadsPerChild
指令中指定),以及一个监听器线程,监听请求并在请求StartServers
指令MinSpareThreads
和MaxSpareThreads
指定的范围内。ThreadsPerChild
指令中指定),以及一个监听线程(监听请求并在请求到达sockets
、处于keep-alive
状态的所有sockets
sockets
。这样就提高了在高并发场景下的处理能力。集群:同一个业务系统,部署在多台服务器上。集群中,每一台服务器实现的功能没有差别,数据和代码都是一样的
分布式:一个业务被拆成多个子业务,或者本身就是不同的业务,部署在多台服务器上。分布式中,每一台服务器实现的功能是有差
别的,数据和代码也是不一样的,分布式每台服务器功能加起来,才是完整的业务。一句话:分布式是通过缩短单个任务的执行时间来提高效率,集群是通过增加单位时间内执行的任务数量来提高效率。
查看内核对LVS的支持
grep -i -C 10 ipvs /boot/config-$(uname -r)
VS
:Virtual Server,Director Server(DS), Dispatcher(调度器),Load Balancer
RS
:Real Server(lvs), upstream server(nginx), backend server(haproxy)
CIP
:Client IP
VIP
:Virtual serve IP VS 外网的 IP
DIP
:Director IP VS 内网的 IP
RIP
:Real server IP
访问流程:CIP <--> VIP == DIP <--> RIP
LVS 有四种工作模式
vs-nat:本质是多目标 IP 的 DNAT,通过将请求报文中的目标地址和目标端口修改为某挑出的 RS 的 RIP 和 PORT 实现转发,特
点:
LVS-DR:Direct Routing,直接路由,LVS 默认模式,应用最广泛,通过为请求报文重新封装一个 MAC 首部进行转发,源 MAC 是
DIP 所在的接口的 MAC,目标 MAC 是某挑选出的 RS 的 RIP 所在接口的 MAC 地址;源IP/PORT,以及目标 IP/PORT 均保持不变
DR 模式的特点:
arptables -A OUT -s $VIP -j mangle --mangle-ip-s $RIP
在 RS 上修改内核参数以限制 arp 通告及应答级别[^1]
/proc/sys/net/ipv4/conf/all/arp_ignore
/proc/sys/net/ipv4/conf/all/arp_announce
转发方式:不修改请求报文的 IP 首部(源 IP 为 CIP,目标 IP 为 VIP),而在原 IP 报文之外再封装一个 IP 首部(源 IP 是 DIP,目标
IP 是 RIP),将报文发往挑选出的目标 RS;RS 直接响应给客户端(源 IP 是 VIP,目标 IP 是 CIP)
TUN 模式特点:
循环调度算法:该调度算法是 LVS 最简单的调度策略,其将每个传入的请求发送到其列表的下一个服务器。例如:在三个服务器的集
群中(A、B 和 C 服务器),请求 1将被 调度到服务器 A,请求 2 将被调度到服务器 B,请求 3 将被 C 服务器处理,请求 4 又被调度到服务
器 A。从而让集群中的服务器循环的提供服务,该调度策略平等对待所有服务器,其不考虑到来的连接或请求数量和服务器的响应时间(或
是负载情况),其性能高于传统的 DNS 轮询。
加权轮询调度算法:该调度策略旨在处理不同的性能的服务器在接收请求和处理请求时的权重呵呵优先顺序。在调度期间,每个服务
器的性能有差异,各自会被分配不同的权重值,一个表示处理能力的整数值。权重较大的服务器比权重小的服务器优先处理新连接,权重
较大的服务器也比权重较小的服务器获得更多的连接,权重相同的服务器获得相同的连接数。例如:RS 服务器分别为 A、B 和 C,它们分
别有权重4、3 和 2,那么一个比较理想的调度序列会是 AABABCABC,可以看到在三次轮询过程中,不同权重的 RS 被分配了不同的连接
数,但是整个过程中各自总的处理连接比例接近 4:3:2(A 处理了 4 次,B 处理了 3 次,C 处理了 2 次)。
在加权轮询调度的实现中,在管理员修改 VS 的服务器调度策略后,会根据具体的配置权重来生成调度序列。基于该序列以轮询调度方式
将不同的网络连接定向到后端不同的 RS 服务器。
当后端 RS 服务器的处理性能不一时,此算法是比单纯的轮询调度算法优异的。但是,如果有不间断的高负载的请求到来时,可能造成各
服务器的动态负载严重不均衡。也就是说,可能会有很大部分的高负载请求都被调度到相同的 RS 上。此时,该 RS的复制将会很高。
而实际上,轮询是加权轮询策略的特例,即是加权轮询的权重值都为 1 的情况。
目标地址哈希调度: 目标地址散列调度(Destination Hashing Scheduling)算法也是针对目标IP地址的负载均衡,但它是一种静态
映射算法,通过一个散列(Hash)函数将一个目标IP地址映射到一台服务器。 目标地址散列调度算法先根据请求的目标IP地址,作为散
列键(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。该调度
策略的典型使用场景是正向代理缓存场景中的负载均衡,如:宽带运营商
源地址散列调度(Source Hashing Scheduling)算法正好与目标地址散列调度算法相反,它根据请求的源IP地址,作为散列键
(Hash Key)从静态分配的散列表找出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器,否则返回空。它采用的散列函数与目标地址散列调度算法的相同。
在实际应用中,源地址散列调度和目标地址散列调度可以结合使用在防火墙集群中,它们可以保证整个系统的唯一出入口
最少连接调度算法:该调度策略将网络连接调度给建立连接数量最少的 RS 服务器。该调度算法会动态的记录各服务器的活动连接
数。如果后端的一群 RS 服务器的处理性能相近,那么最少连接调度策略有助于在请求的负载有大幅变化时平滑分配负载。VS 将把请求定
向到有最少的活动连接的 RS。乍一看,该调度策略应该可以在服务器的处理性能各不相同的情况下表现良好,因为处理性能高的服务器会
接到更多的请求并处理。然而由于 TCP 协议的TIME_WAIT
状态导致了其表现并不好。TCP 定义的TIME_WAIT
状态为 2 分钟,在这两
分钟内某个繁忙的站点可能已经收到了好几千个请求。例如:RS-A 的处理性能是 RS-B 的两倍,RS-A 可能已经按照正常的速度完成了这
些请求并将它们保持在了TIME_WAIT
状态(也就是在 2 分钟内完成了分配的请求),而 RS-B 正挣扎着完成分配到自己的好几千的请求,
在这种情况下就没法均衡负载了。
加权的最少连接调度算法:该调度策略允许为每个 RS 服务器分配一个性能权重值。具有较高权重值的服务器在任何时候都将接收到
较大百分比的活动连接。VS 管理员可以给每个 RS 服务器分配一个权重值,网络连接会被调度到每个 RS 服务器并且每个服务器的当前活
动连接数的百分比与其权重成比例。
加权的最少连接调度算法相比最少连接算法需要额外的除法来区分不同比例。为了在服务器处理能力接近相同的情况下最小化调度工作的
开销,才有了非加权和加权的最少连接算法。
基于位置的最少连接调度算法:该调度策略专用于目的 IP 的负载均衡。一般被用于缓存集群。该算法通常会将到某个 IP 地址的数据
包定向到负责处理其的服务器,前提是该服务器是活动的并且负载不高。如果该服务过载(活动连接数大于其权重)并且有一个服务器目前
只有一半的负载,那就会将加权的最少连接服务器(一般负载的服务器)分配给该 IP。
基于位置的带复制的最少连接调度算法:该调度策略也用于目的 IP 的负载均衡。一般用于缓存集群。在以下方面其和 LBLC 调度策略不
同:调度服务器 LVS 会维护某个目的 IP 到可能为其提供服务的服务器节点的映射。发往目标 IP 的请求将被调度到该目标 IP 对应的服务
器节点集中拥有最少连接的节点。如果该服务器集的节点均过载,那么 LVS 会在整个集群中挑选一个最少连接的节点加入到这个服务器集
(可能为该目标IP 服务的节点集合)。
最短期望时延调度算法:该算法将网络连接调度分配给预期最小时延的服务器。发送到集群中的第 i 个节点最小时延可表示为:(Ci +
1)/Ui。Ci 是第 i 个服务器的连接数,Ui 是第 i 个服务器的权重。
不排队算法: 该调度策略在有空闲的服务器时将请求调度到该空闲服务器。如果没有空闲服务器,请求将被调度到最少期望时延的节点
(SED)。
FO(Weighted Fail Over)调度算法,在此 FO 算法中,会遍历虚拟服务所关联的真实服务
器链表,找到还未过载(未设置 IP_VS_DEST_F_OVERLOAD 标志)的且权重最高的真实服务器,进行调度
OVF(Overflow-connection)调度算法,基于真实服务器的活动连接数量和权重值实现。
将新连接调度到权重值最高的真实服务器,直到其活动连接数量超过权重值位置,之后调
度到下一个权重值最高的真实服务器,在此 OVF 算法中,遍历虚拟服务相关联的真实服务器
链表,找到权重值最高的可用真实服务器。一个可用的真实服务器需要同时满足以下条件:
ipvsadm 工具用法
# 管理集群服务
ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask]
[--pe persistence_engine] [-b sched-flags]
ipvsadm -D -t|u|f service-address # 删除
ipvsadm –C # 清空
ipvsadm –R # 重载
ipvsadm -S [-n] # 保存
# 管理集群中的RS
ipvsadm -a|e -t|u|f service-address -r server-address [options]
ipvsadm -d -t|u|f service-address -r server-address
ipvsadm -L|l [options]
ipvsadm -Z [-t|u|f service-address]
保存规则:建议保存至/etc/sysconfig/ipvsadm
ipvsadm-save > /PATH/TO/IPVSADM_FILE
ipvsadm -S > /PATH/TO/IPVSADM_FILE
systemctl stop ipvsadm.service
重新载入
ipvsadm-restore < /PATH/FROM/IPVSADM_FILE
systemctl restart ipvsadm.service
目录 | 该目录下文件 | 说明 |
---|---|---|
bin | / | 存放 Tomcat 的启动、停止等脚本文件和批处理文件 |
startup.sh/startup.bat | Linux 下的启动脚本/Win 下的启动脚本 | |
shutdown.sh/shutdown.bat | Linux 下/Win 下的停止脚本 | |
conf | / | 存放配置文件 |
catalina | 存放针对每个虚拟机的 Context 的配置 | |
context.xml | 用于定义 web 应用都需要加载的 Context 配置,如果 web 应用指定了自己的 context.xml,该文件内容 | |
将被覆盖 | ||
catalina.properties | Tomcat 的环境变量,Java 属性的定义文件,用于设定类加载器路径,以及一些与 JVM 调优相关参数 | |
catalina.policy | Tomcat 运行的安全策略配置 | |
loggging.properties | Tomcat 的日志配置文件,可以通过该文件修改 Tomcat 的日志级别和日志路径等 | |
server.xml | Tomcat 的核心配置文件 | |
tomcat-user.xml | 定义 Tomcat 默认的用户及角色映射信息的配置 | |
web.xml | Tomcat 中所有应用默认的部署描述文件,主要定义了基础的 Servlet 和 MIME 映射关系 | |
lib | / | Tomcat 服务器的依赖包存放处 |
logs | / | Tomcat 默认的日志存放目录 |
webapps | / | Tomcat 默认的 Web 应用部署目录 |
work | / | Web 应用 JSP 代码生成和编译使用的临时目录 |
顶级组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9uybRddJ-1604371435769)(png/tomcat-arch.png)]
当用户请求某个资源时,HTTP 服务器会用一个 ServletRequest 对象把客户的请求信息封装起来,然后调用 Servlet 容器的 service 方
法,Servlet 容器拿到请求后,根据请求的 URL 和 Servlet 的映射关系,找到相应的 Servlet,如果 Servlet 还没有被加载,就用反射机制
创建这个 Servlet,并调用 Servlet 的 init 方法来完成初
始化,接着调用 Servlet 的 service 方法来处理请求,把 ServletResponse 对象返回给 HTTP 服务器,HTTP 服务器把响应发送给客户
端。
送请求并接受响应。
Coyote 封装了底层的网络通信(Socket 请求及响应处理),为 Catalina 容器提供了统一的接口,使得 Catalina 容器与具体的请求协议及
IO 操作方式完全解耦。Coyote 将 Socket 输入转换封装为 Request 对象,交由 Catalina 容器进行处理,处理完成后,Catalina 通过
Coyote 提供的 Response 对象将结果写入输出流。
Coyote 作为独立模块,只负责具体的协议和 IO 相关操作,与 Servlet 规范实现没有直接关系,因此即便是 Request 和 Response 对象
也并未实现 Servlet 规范对应的接口,而是在 Catalina 中将它们进一步封装为 ServletRequest 和ServletResponse。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2YwEPjFi-1604371435770)(https://suosuoli.cn/wp-content/uploads/2020/01/Coyote%E4%B8%8ECatalina%E7%9A%84%E4%BA%A4%E4%BA%92.png)]
在 Coyote 中,Tomcat 支持多种 I/O 模块和应用层协议,下表为 Tomcat 支持的 IO
模型 (自 8.9/9.0 版本其,Tomcat 移除了对 BIO 的支持)
IO 模型 | 描述 |
---|---|
NIO | 非阻塞 I/O,采用 Java NIO 类库实现 |
NIO2 | 异步 I/O,采用 JDK 7 最新的 NIO2 类库实现 |
APR | 采用 Apache 可移植运行库实现,是 c/c++编写的本地库。如果选择该模型,需要安装 APR 库 |
Tomcat 支持的应用层协议:
协议 | 说明 |
---|---|
HTTP/1.1 | 这是大部分 web 应用采用的协议 |
AJP | 用于和 web 服务器集成(Apache),以实现对静态资源的优化以及集群部署,当前支持 AJP/1.3 |
HTTP/2 | HTTP 2.0 大幅度的提升了 web 性能。Tomcat8.5 以及 9.0 版本后支持。 |
Tomcat 中的协议层关系
应用层 | HTTP | AJP | HTTP2 |
传输层 | NIO | NIO2 | APR |
Tomcat 为了实现支持多种 I/O 模型和应用层协议,一个容器可以对接多个连接器。但是单独的连接器或者容器都不能对外提供服务,需
要把它们组合起来才能够工作,组合后的整体叫做 Service 件。值得注意的是,Service 本并没有涉及到请求的处理,只是起到封装
连接器和容器的作用。Tomcat 服务器可以配置多个 service,这样的设计处于灵活性考虑。这样可以通过在 Tomcat 配置多个
Service,通过不同的端口来访问同一台服务器上部署的不同应用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtW0afjq-1604371435770)(https://suosuoli.cn/wp-content/uploads/2020/01/%E8%BF%9E%E6%8E%A5%E5%99%A8%E7%BB%84%E4%BB%B6.png)]
连接器组件中的各个子组件的作用:Endpoint
Tomcat 是由一系列可配置的组件构成的 web 容器 ,而 catalina 实质就是 Tomcat 的servlet 容器。Catalina 是 servlet 容器的实现。它
通过松耦合的方式集成 coyote ,以完成按照请求协议进行数据读写。
Tomcat 分层结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZU39dG0-1604371435771)(https://suosuoli.cn/wp-content/uploads/2020/01/Catalina%E5%9C%B0%E4%BD%8D.png)]
Tomcat 本质上就是一款 servlet 容器,因此 catalina 才是 Tomcat 的核心,其他模块都是为 catalina 提供支撑的。 比如:通过 coyote
模块提供链接通信, Jasper 模块提供JSP 引擎, Naming 提供 JNDI 服务, Juli 提供日志服务。
Catalina 的主要组件结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4VCeu7qj-1604371435771)(https://suosuoli.cn/wp-content/uploads/2020/01/Catalina%E7%BB%93%E6%9E%84.png)]
如上图所示,Catalina 负责管理 Server ,而 Server 表示着整个服务器。Server 下面有多个服务 service ,每个服务都包含着多个连接器组
件 connector ( coyote 实现)和一个容器组件 container。在 Tomcat 启动的时候,会初始化一个 catalina 的实例。Catalina各个组件的
作用:
| 组件 | 作用 || :-------- | :-------------------------------------------------------------------------------------------------------------------------------
-------------------------------------- |
| Catalina | 负责解析 romcat 的配置文件,以此来创建服务器 server 组件,并根据命令来对其进行管理
|
| Server | 服务器:表示整个 Catalina servlet 容器以及其它组件,负责组装并启动 servlet 引擎,Tomcat 连接器。server 通过实现
Lifecycle 接口,提供了一种优雅的启动和关闭整个系统的方式 |
| service | 服务是 server 内部的组件,一个 server 包含多个 service。 它将若干个 connector 组件绑定到一个 Container ( Engine)上
|
| Connector | 连接器,处理与客户端的通信,它负责接收客户请求,然后转给相关的容器处理,最后向客户返回响应结果
|
| Container | 容器,负责处理用户的 servlet 请求,并返回对象给 web 用户的模块
|
Tomcat 设计了 4 种容器,分别是 Engine、Host、 Context 和 Wrapper. 这 4 种容器不是平行关系,而是父子关系。Tomcat 通过分层的
架构使得 Servlet 容器具有很好的灵活性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pou5g98J-1604371435772)(https://suosuoli.cn/wp-content/uploads/2020/01/Container%E7%BB%93%E6%9E%84.png)]
各个组件的含义:
容器组件 | 说明 |
---|---|
Engine | 表示整个 catalina 的 servlet 引擎,用来管理多个虚拟站点, 一个 Service 最多只能有一个 Engine,但是一个引擎可包含多个 |
Host | |
Host | 代表一个虚拟主机,或者说一个站点。可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可包含多个 Context |
Context | 表示一个 Web 应用程序,一个 web 应用可包含多个 Wrapper |
Wrapper | 表示一个 Servlet,Wrapper 作为容器中的最底层,不能包含子容器 |
我们也可以再通过 Tomcat 的 server.xm1 配置文件来加深对 Tomcat 容器的理解。Tomcat 采用了组件化的设计,它的构成组件都是可配
置的,其中最外层的是 server ,其他组件按照一定的格式要求配置在这个顶层容器中。配置框架如下:
<Server ... >
<Service ... >
<Connector ... > ... Connector>
<Connector ... > ... Connector>
<Engine ... >
<Realm ... > ... Realm>
<Realm ... > ... Realm>
<Host ... >
<Valve ... />
Host>
Engine>
Service>
Server>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0SbEPioo-1604371435773)(https://suosuoli.cn/wp-content/uploads/2020/01/%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8BUML.png)]
步骤:
设计了这么多层次的容器, Tomcat 是怎么确定每一个请求应该由哪 个 Wrapper 容器里的 servlet 来处理的呢?
答案是:Tomcat 是用 Mapper 组件来完成这个任务的。
Mapper 组件的功能就是将用户请求的 URL 定位到一个 servlet ,它的工作原理是:Mapper 组件里保存了 web 应用的配置信息,其实就是
容器组件与访问路径的映射关系,比如 Host 容器里配置的域名、Context 容器里的 web 应用路径,以及 wrapper 容器里 servlet 映射的路
径,你可以想象这些配置信息就是一个多层次的 Map。
当一个请求到来时, Mapper 组件通过解析请求 URI 里的域名和路径,再到自己保存的Map 里去查找,就能定位到一个 servlet。 请注意,一
个 URL 最后只会定位到一个Wrapper 容器,也就是一个 servlet.
下面的示意图中, 就描述了 当用户请求链接 http://www.suosuoli.cn/articles/tomcat之后, 是如何找到最终处理业务逻辑的 servlet 的
过程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGup9VhF-1604371435773)(https://suosuoli.cn/wp?content/uploads/2020/01/%E5%A4%84%E7%90%86http%E8%AF%B7%E6%B1%82%E8%AF%A6%E7%BB%86%E6%B5%81%E7%A8%8B.png)]
上面这幅图只是描述了根据请求的 URI 如何查找到需要执行的 servlet,下面再来解析一下从 Tomcat 的设计架构层面来分析 Tomcat 的
请求处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3R3G4Llq-1604371435774)(https://suosuoli.cn/wp?content/uploads/2020/01/%E4%BB%8E%E6%9E%B6%E6%9E%84%E7%9C%8BHTTP%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86.png)]
具体步骤如下:
对于基于 JSP 的 web 应用来说,我们可以直接在 Jsp 页面中编写 Java 代码,添加第三方的标签库,以及使用 EL 表达式。但是无论经过何种
形式的处理,最终输出到客户端的都是标准的 HTMI 页面(包含 js , css… ) ,并不包含任何的 java 相关的语法。也就是说,我们可以把 jsp 看
做是一种运行在服务端的脚本。那么服务器是如何将 Jsp 页面转换为HTML 页面的呢?
Jasper 模块是 Tomcat 的 JsP 核心引擎,我们知道 JSP 本质上是一个 servlet。Tomcat 使用 Jasper 对 Jsp 语法进行解析,生成 servlet 并
生成 Class 字节码,用户在进行访问 jsp 时,会访问 Servlet ,最终将访问的结果直接响应在浏览器端。另外,在运行的时候, Jasper 还会检 Jsp
文件是否修改,如果修改,则会重新编译 Jsp 文件。
一句话:Jasper 解析.JSP
页面并生成对应的Servlet
,最终编译为.class
字节码文件
在实际生产环境中,单台 Tomcat 服务器的负载能力或者说并发能力在四五百左右。大部分情况下随着业务增长,访问量的增加(并发量不
止四五百),单台 Tomcat 服务器是无法承受的。这时就需要将多台 Tomcat 服务器组织起来,共同分担负载。
所以在生产环境中,一部分会使用单机部署方式,这样的部署方式在比较小的公司会见到,大部分会使用多机部署。具体的 Tomcat 部署
架构有以下几种:
upstream serverpool{
server localhost:8000; # server指令指明后端服务器
server localhost:9000;server www.suosuoli.cn weight=1 fail_timeout=5s max_fail=3;
}
server {
listen 88;
server_name localhost;
location / {
proxy_pass http://serverpool/;
}
}
upstream 模块中的 server 指令参数说明:
参数 | 描述 |
---|---|
----- | |
fail_timeout | 与 max_fails 结合使用 |
max_fails | 设置在 fail_timeout 参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服 |
务器会被访为是停机了 | |
fail_time | 服务器会被认为停机的时间长度,默认为 10s |
backup | 标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里 |
down | 标记服务器永久停机 |
upstream serverpool{
1p_hash;
server 192.168.192.122:8080 weight=3;
server 192.168.192.133:8080 weight=1;
}
当单机的 Tomcat,演化出多机多级部署的时候,一个问题便凸显出来,这就是Session。而这个问题的由来,是由于 HTTP 协议在设计
之初没有想到未来的发展。
HTTP 的无状态、有连接和短连接特性
由一个用户发起的请求,只会被调度到特定的 TomcatA 服务器上进行处理,另一个用户发起的请求只在 TomcatB 上进行处理。那么这
个时候,同一个用户发起的请求,都会通过 nginx 的 ip_hash
策略,将请求转发到其中的一台 Tomcat 上。在 Nginx 的反向代理中
ip_hash 策略也叫 source ip,即源地址哈希;
Session 复制的原理是通过 Tomcat 集群的内部多播机制将所有的不同 Session 在集群的所有 Tomcat 主机上复制,即所有 Tomcat 服务
器都存有当前的所有 Session信息。
缺点
session 共享服务器,共享的 Session 服务器使用 memcached 或者 redis 做存储,来存储 session 信息,供 Tomcat 服务器查询。
使用不同技术实现了 session 持久机制
session 绑定
,基于 IP 或 session cookie 的。其部署简单,尤其基于session 复制集群
,基于 tomcat 实现多个服务器内共享同步所有 session。session 服务器
,将所有的 session 存储到一个共享的内存空间中,使用多个对于系統性能,用户最直观的感受就是系统的加载和操作时间,即用户执行某项操作的耗时。从更为专业的角度上讲,性能测试可以从以下两
个指标量化。
ApacheBench(ab)是一款 ApacheServer 基准的测试工具,用户测试 Apache server 的服务能力 (每秒处理请求数), 它不仅可以用户
Apache 的测试。还可以用于测试 TomcatNginx、lighthttp、IIS 等服务器。
yum instll httpd-tools
ab -V
ab -n 1000 -e 100 -p data.json -T applicat1on/json http://1ocalhost:9000/articles/search.do?cat=Tomcat&topic=test
{
"Tomcat": "test", "Nginx": "ReverseProxy" }
参数说明
参数 | 含义描述 |
---|---|
-n | 在测试会话中所执行的请求个数,默认只执行一-次请求 |
-c | 一次产生的请求个数,默认一次一个 |
-p | 包含了需要 post 的数据文件 |
-t | 测试所进行的最大秒数,默认没有时间限制 |
-T | POST 数据所需要使用的 Content-Type 头信息 |
测试结果指标说明
指标 | 含义 |
---|---|
--------------------------------------------- | |
Server Software | 服务器软件 |
Server Hostname | |
Server Port | 端口号 |
Document Path | 测试的页面 |
Document Length | 测试的页面大小 |
Concurrency Level | 并发数 |
Time taken for tests | 整个测试持续的时间 |
Complete requests | 完成的请求数量 |
Failed requests | 失败的请求数量,这里的失败是指请求的连接服务器、发送数据、接收数据等环节发生异 |
常,以及无响应后超时的情况。 | |
write errors | 输出错误数量 |
Total transferred | 整个场景中的网络传输量,表示所有请求的响应数据长度总和,包括每个 http 响应数据的 |
头信息和正文数据的长度。 | |
HTML transferred | 整个场景中的 HTNL 内容传输量,表示所有请求的响应数据中正文数据的总和 |
Requests per second | 每秒钟平均处理的请求数(相当于 LR 中的每秒事务数)这便是我们重点关注的吞吐 |
率,它等于: Complete requests/Time taken for tests | |
Time per reguest | 每个线程处理请求平均消耗时间(相当于 LR 中的平均事务响应时间)用户平均请求等待 |
时间 | |
Transfer rate | 平均每秒网络上的流星 |
Percentage of the requests served within a certain time (ms) | 指定时间里,执行的请求百分比 |
与tomcat相关的JVM 参数调优
Tomcat 是一款 Java 应用 ,那么 JVM 的配置便与其运行性能密切相关,而 JVM 优化的重点则集中在内存分配和 GC 策略的调整上,因为内
存会直接影响服务的运行效率和吞吐量,JVM 垃圾回收机制则会不同程度地导致程序运行中断。可以根据应用程序的特点,选择不同的垃
圾回收策略,调整 JVM 垃圾回收策略,可以极大减少垃圾回收次数,提升垃圾回收效率,改善程序运行性能。
工作时间(排除 GC 时间)占总时间的百分比
,工作时间并不仅是程序运行的时间,还包含内存分配时间。应用程序停止响应次数/时间
。吞吐量收集器
,以并行的方式执行年轻代的垃圾回收
,该方式可以显著降低垃圾回收调整tomcat/conf/server.xml
中关于连接器的配置可以提升应用服务器的性能。| 参数 | 说明
ulimit -a
查看服务器限制。对于 CPU 要求更高(计算型)时,建议不要配置过大;对于 CPU 要求不是特别高时,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UWqDIYJO-1604371435775)(png/2020-01-17-14-10-51.png)]
使用 Java 语言编写.java
Source Code 文件,通过 javac 编译成.class
Byte Code 文件,JVM 就可以识别.class
文件,并使用 JIT
compiler 将.class
文件编译为机器码后运行。
class loader 类加载器:将所需的类加载到内存,必要时将类实例化成实例。
图中中间部分是进程的内存逻辑结构,称为 Jvm 运行时区域,由下面几部分构成:
Method Area方法区:所有线程共享的内存空间,存放已加载的类信息、常量和静态变量。
Heap 堆:所有线程共享的内存空间,存放创建的所有对象。堆是靠 GC 垃圾回收器管理的。
Java Stack栈:每个线程会分配一个栈,存放线程用的本地变量、方法参数和返回值等。
PC 寄存器:PC, 即 Program Counter,每一个线程用于记录当前线程正在执行的字节码指令地址。因为线程需要切换,当一个线程
被切换回来需要执行的时候,知道执行到哪里了。
Native Method Stack本地方法栈:为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等。
所谓本地方法,简单的说是非 Java 实现的方法,例如操作系统的 C 编写的库提供的本地方法,Java 调用这些本地方法接口执行。但
是要注意,本地方法应该避免直接编程使用,因为 Java 可能跨平台使用,如果用了 Windows API,换到了 Linux 平台部署就有了问题。
堆和栈是程序运行的关键,很有必要把他们的关系说清楚。
栈是运行时的单位,而堆是存储的单位
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。
而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状
态、方法返回值等等;而堆只负责存储对象信息。
为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?
第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模
块化的思想在软件设计的方方面面都有体现。
第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的。一方面
这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。
第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的
能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地
址即可。
第四,面向对象就是堆和栈的完美结合。其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是,面向对象的引
入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放
在堆中;而对象的行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻
辑。不得不承认,面向对象的设计,确实很美。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6qzzKfP-1604371435776)(png/JVM内存模型.png)]
堆内存在 JVM 使用的内存中占比最大,Heap 堆内存分为:
新生代
:刚刚创建的对象伊甸园区
存活区 Servivor Space
:有 2 个存活区,一个是 from 区,一个是 to 区。老年代
:长时间存活的对象持久代
:JVM 的类和方法起始时,所有新建对象都出生在 eden,当 eden 满了,启动 GC。这个称为 Young GC
或Minor GC
。先标记 eden 存活对象,然后将
存活对象复制到 s0(假设本次是 s0,也可以是 s1,它们可以调换),eden 剩余所有空间都"清空",GC 回收内存完成。继续新建对象,当
eden 满了,启动 GC。先标记 eden 和 s0 中存活对象,然后将存活对象复制到 s1。将 eden 和 s0 “清空”。继续新建对象,当 eden 满
了,启动 GC。先标记eden 和 s1 中存活对象,然后将存活对象复制到 s0。将 eden 和 s1 “清空”。以后的内存回收重复上面步骤。大多数对象都不会存活很久,而且创建活动非常多,新生代就需要频繁垃圾回收。但是,如果一个对象一直存活,它最后就在 from、to
来回复制,如果 from 区中对象复制次数达到阈值,就直接复制到老年代。##### 老年代回收
进入老年代的数据较少,所以老年代区被占满的速度较慢,所以垃圾回收也不频繁。老年代GC 称为 Old GC
或Major GC
。由于老年代
对象一般来说存活次数较长,所有较常采用标记-压缩
算法。
对所有"代"的内存进行垃圾回收
Minor GC 比较频繁,Major GC 较少。但一般 Major GC 时,由于老年代对象也可以引用新生代对象,所以先进行一次 Minor GC,然
后在 Major GC 会提高效率。可以认为回收老年代的时候完成了一次 Full GC。
每一个堆内对象上都与一个私有引用计数器,记录着被引用的次数,引用计数清零,该对象所占用堆内存就可以被回收。循环引用的对象
都无法引用计数归零,就无法清除。无法解决循环引用问题。
顾名思义,标记和清除垃圾收集器有两个阶段
1.标记阶段
标记阶段,找到所有可访问对象打个标记。需要注意的是,在标记阶段,会停止应用程序线程,以避免在标记阶段对对象状态进行更改。
2.清除阶段
清理阶段,遍历整个堆,对未标记对象清理。在清除阶段,所有来自标记阶段的未标记对象将从内存中删除,从而释放空间。
从上面的图中可以看出,在清除阶段之后可能存在大量的可用内存区域。但是,由于这些内存为小段的碎片,如果下一个内存分配大于所
有现有的空闲区域,那么它可能会分配失败。因此,需要引入第三个阶段,即压缩阶段。
分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。内存清理阶段时,整理时将对象向内存一端移动,整理后存
活对象连续的集中在内存一端。
压缩整理:在扫描阶段之后,所有内存位置将重新安排,以提供更紧凑的内存分配。这种方法
的缺点是 GC 暂停时间增加,因为它需要将所有对象复制到一个新位置并更新对这些对象的
所有引用
标记-清除-压缩
算法好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。缺点是内存整理过程有时间和回收开
销。
先将可用内存分为大小相同两块区域 A 和 B,每次只用其中一块,比如 A。当 A 用完后,则将 A 中存活的对象复制到 B。复制到 B 的时
候连续的使用内存,最后将 A 一次性清除干净。缺点是比较浪费内存,能使用原来一半的内存,因为内存对半划分了,复制过程毕竟也是
有代价。好处是没有碎片,复制过程中保证对象使用连续空间。
这种垃圾回收机制类似于标记-清除
,但内存空间分为两部分。最初,将对象分配给一个内存空间(fromspace),并标记活动对象。在复制阶段,被标记的对象被复制到另一个内存空间(tospace),同时被压缩。然后,fromspace 被清空。
既然上述垃圾回收算法都有优缺点,能不能对不同数据进行区分管理,不同分区对数据实施不同回收策略,分而治之?Tomcat 中 JVM 使
用堆内存的分代回收机制
来针对不同数据区域进行内存回收。
Tomcat1.7 及以前,堆内存分为新生代、老年代、持久代。
Tomcat1.8 开始,持久代没有了,取而代之使用 MetaSpace。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngi6N0Dk-1604371435777)(https://suosuoli.cn/wp-content/uploads/2020/01/JVM%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B.png)]
在分代垃圾回收中,内存空间被划分为不同的代(如年轻代和老年代)。最初,所有的对象将驻留在年轻代。然而,当垃圾收集周期发生
时,在垃圾回收之后存活的对象将被提升到老年代。
一次 Minor Collection 回收(新生代回收)前 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N2dkqhlU-1604371435777)(png/before-minor.png)]
一次 Minor Collection 回收后 :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PufiHR8y-1604371435778)(png/after-minor.png)]
回收后,可以清除年轻代中遗留的对象,因为所有活动对象都将移动到老代中。
老一代的垃圾回收周期比年轻一代的长。这种方法背后的关键思想是,在第一次垃圾收集后存活下来的对象往往寿命更长。因此,可以减
少老一代对象的垃圾收集频率。
对于大多数垃圾回收算法而言,GC 线程工作时,需要停止所有工作的线程,称为Stop The World
。GC 完成时,恢复其他工作线程运
行。这也是 JVM 运行中最头疼的问题。
Minor GC 触发条件:当 eden 区满了触发
Full GC 触发条件: 老年代满了,新生代搬向老年代,老年代空间不够,持久代满了
使用 System.gc()手动调用,不推荐
按回收线程数个数分为:
JVM中的垃圾回收器,主要包括串行回收器、并行回收器以及CMS回收器、G1回收器
1.Serial收集器
Serial收集器是最基本、发展历史最悠久的收集器。是单线程的收集器。它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收
集完成。 Serial收集器依然是虚拟机运行在Client模式下默认新生代收集器,对于运行在Client模式下的虚拟机来说是一个很好的选择。
2.ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参
数、收集算法、Stop The Worl、对象分配规则、回收策略等都与Serial 收集器完全一样。 ParNew收集器是许多运行在Server模式下的
虚拟机中首选新生代收集器,其中有一个与性能无关但很重要的原因是,除Serial收集器之外,目前只有ParNew它能与CMS收集器配合工
作。
3.Parallel Scavenge(并行回收)收集器
Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器
该收集器的目标是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,
即 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
4.Serial Old 收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记整理算法。这个收集器的主要意义也是在于给Client模式
下的虚拟机使用。
如果在Server模式下,主要两大用途:
(1)在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用
(2)作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用
5.Parallel Old 收集器
Parallel Old 是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器在1.6中才开始提供。
6.CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者
B/S系统的服务端上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合
这类应用的需求
CMS收集器是基于“标记-清除”算法实现的。它的运作过程相对前面几种收集器来说更复杂一些,整个过程分为4个步骤:(1)初始标记: 标记一下 GC Roots 能直接关联到的对象,速度较快。
(2)并发标记: 进行 GC Roots Tracing,标记出全部的垃圾对象,耗时较长。
(3)重新标记: 修正并发标记阶段引用户程序继续运行而导致变化的对象的标记记录,耗时较短。
(4)并发清除: 用标记-清除算法清除垃圾对象,耗时较长。
其中,初始标记、重新标记这两个步骤仍然需要“Stop The World”.
CMS收集器主要优点:并发收集,低停顿。
CMS三个明显的缺点:
(1)CMS收集器对CPU资源非常敏感。CPU个数少于4个时,CMS对于用户程序的影响就可能变得很大,为了应付这种情况,虚拟机提
供了一种称为“增量式并发收集器”的CMS收集器变种。所做的事情和单CPU年代PC机操作系统使用抢占式来模拟多任务机制的思想
(2)CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。在JDK1.5的默认设置
下,CMS收集器当老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在应用中蓝年代增长不是太快,可以适当调高参
数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以便降低内存回收次数从而获取更好的性能,在JDK1.6中,CMS收集
器的启动阀值已经提升至92%。
(3)CMS是基于“标记-清除”算法实现的收集器,手机结束时会有大量空间碎片产生。空间碎片过多,可能会出现老年代还有很大空间
剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前出发FullGC。为了解决这个问题,CMS收集器提供了一个-
XX:+UseCMSCompactAtFullCollection开关参数(默认就是开启的),用于在CMS收集器顶不住要进行FullGC时开启内存碎片合并整理
过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间变长了。虚拟机设计者还提供了另外一个参数-
XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,标识每次进
入Full GC时都进行碎片整理)
7. G1收集器
G1收集器的优势:
(1)并行与并发
(2)分代收集
(3)空间整理 (标记整理算法,复制算法)
(4)可预测的停顿(G1处处理追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段
内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经实现Java(RTSJ)的来及收集器的特征)
使用G1收集器时,Java堆的内存布局是整个规划为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代
和老年代不再是物理隔离的了,它们都是一部分Region的集合。
G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在真个Java堆中进行全区域的垃圾收集。G1跟踪各个Region
里面的垃圾堆积的价值大小(回收所获取的空间大小以及回收所需要的时间的经验值),在后台维护一个优先列表,每次根据允许的收集
时间,优先回收价值最大的Region(这也就是Garbage-First名称的又来)。这种使用Region划分内存空间以及有优先级的区域回收方
式,保证了G1收集器在有限的时间内可以获取尽量可能高的灰机效率
G1 内存“化整为零”的思路在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会遗漏。
如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为以下步骤:
(1)初始标记: 标记出 GC Roots 直接关联的对象,这个阶段速度较快,需要停止用户线程,单线程执行。
(2)并发标记: 从 GC Root 开始对堆中的对象进行可达新分析,找出存活对象,这个阶段耗时较长,但可以和用户线程并发执行。
(3)最终标记: 修正在并发标记阶段引用户程序执行而产生变动的标记记录。
(4)筛选回收: 筛选回收阶段会对各个 Region 的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来指定回收计划(用最少的
时间来回收包含垃圾最多的区域,这就是 Garbage First 的由来——第一时间清理垃圾最多的区块),这里为了提高回收效率,并没有采
用和用户线程并发执行的方式,而是停顿用户线程。
buffer:缓冲也叫写缓冲,一般用于写操作,可以将数据先写入内存再写入磁盘,buffer 一般用于写缓冲,用于解决不同介质的
速度不一致的缓冲,先将数据临时写入到距离自己最近的地方,以提高写入速度,CPU 会把数据先写到内存的磁盘缓冲区,然后就认为数
据已经写入完成,然后由内核在后续的时间在写入磁盘,所以服务器突然断电会丢失内存中的部分数据。buffer 内的数据一般会停留一
次,就被移走。buffer意味着数据被存储间隔一段时间后被使用。
cache:缓存也叫读缓存,一般用于读操作,CPU 读文件从内存读,如果内存没有就先从硬盘读到内存再读到 CPU,将需要频繁
读取的数据放在里自己最近的缓存区域,下次读取的时候即可快速读取。cache 中的数据则可以部分的常时间驻于其中。cache可以看作
是 buffer 的特例。cache 意味着数据被暂存后被使用多次或用于多种用途。
cache 内的数据都会过期,过期的时间长短由 cache 的设计算法决定
在 CPU 的 L1 和 L2 及 L3 级 cache 中,会将最近经常使用的指令或数据缓存在其中,包括了数据 cache 和指令 cache。其设计使用了多
种算法来淘汰数据或者缓存特定数据,每次 CPU 从 cache 中得到指令或数据就叫命中,不从 cache 取得指令或数据就未命中。cache 的
重要指标之一就是命中率。
浏览器对 DNS 记录的缓存时间默认为 60 秒,即 60 秒之内在访问同一个域名就不在进行 DNS 解析: 查看 chrome 浏览器的 DNS 缓
存:chrome://net-internals/#dns
早期的 chrome 支持管理 DNS 缓存,新的版本已不支持,只有清除 cache 选项
火狐浏览器缓存:about:cache
览器就使用的本地的缓存展示资源。
如果服务端返回最后修改时间没有发生过变化,则直接使用浏览器的本地缓存,状态码就是 304
基于 Etag 标记是否一致做判断页面是否发生过变化,比如基于 Nginx 的配置指令etag on 来实现 Etag 标记。
以上两种都需要发送请求,即不管资源是否过期都要发送请求进行协商,这样会消耗不必要的时间,因此有了缓存的过期时间,即第一次
请求资源的时候带一个资源的过期时间,默认为 30 天,当前这种方式使用的比表较多,但是无法保证客户的时间都是准确并且一致的,
因此会加入一个最大生存周期,使用用户本地的时间计算缓存数据是否超过多少天,下面的过期时间为 2030 年(2),但是缓存的最大生存
周期计算为天等于 3650 天即 10 年(1),过期时间如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-anP3TJ25-1604371435779)(png/2020-02-11-19-33-34.png)]
内容分发网络(Content Delivery Network),通过将服务内容分发至全网加速节点,利用全球调度系统使用户能够就近获取,有效降
低访问延迟,提升服务可用性,CDN第一降低机房的使用带宽,因为很多资源通过 CDN 就直接返回用户了,第二解决不同运营商之间的
互联,因为可以让联通的网络访问联通让电信的网络访问电信,起到加速用户访问的目的, 第三:解决用户访问的地域问题,就近返回用
户资源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkvUtnc4-1604371435780)(png/CDN.png)]
CDN 服务器会提前对静态内容进行预缓存,避免大量的请求回源,导致主站网络带宽被占用而导致数据无法更新,另外 CDN 可以将数据
根据访问的热度不同而进行不同级别的缓存,例如访问量最高的资源访问 CDN 边缘节点的内存,其次的放在 SSD 或者 SATA,再其次的
放在云存储,这样兼顾了速度与成本。缓存到最快的地方如内存,缓存的数据准确命中率高,访问速度就快。
简要的说 CDN 主要有以下特点
Nginx、PHP 等 web 服务可以设置应用缓存以加速响应用户请求,另外有些解释性语言比如 PHP/Python/Java 不能直接运行,需要先
编译成字节码,但字节码需要解释器解释为机器码之后才能执行,因此字节码也是一种缓存,有时候会出现程序代码上线后字节码没有更
新的现象,此时就需要设计相应的应用层缓存来避免此类情况。## cookie 和 session
Cookie 是访问某些网站以后在本地存储的一些网站相关的信息,下次再访问的时候减少一些步骤,比如加密后的账户名密码等信息
Cookies 是服务器在客户端浏览器上存储的小段文本并随每一个请求发送至同一个服务器,是一种实现客户端保持状态的方案。
session 称为会话信息,位于 web 服务器上,主要负责访问者与网站之间的交互,当浏览器请求 http 地址时,可以基于之前的
session 实现会话保持、session 共享等。
session 和 cookie 主要区别如下:
Redis 是一个基于内存的高性能 key-value 数据库。
Redis 本质上是一个 Key-Value 类型的内存数据库,很像 memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把
数据库数据 flush 到硬盘上进行保存。因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快
的 Key-Value DB。
Redis 的出色之处不仅仅是性能,Redis 最大的魅力是支持保存多种数据结构,此外单个 value 的最大限制是 1GB,不像 memcached 只
能保存 1MB 的数据,因此 Redis 可以用来实现很多有用的功能,比方说用他的 List 来做 FIFO 双向链表,实现一个轻量级的高性 能消息
队列服务,用他的 Set 可以做高性能的 tag 系统等等。另外 Redis 也可以对存入的 Key-Value 设置 expire 时间,因此也可以被当作一
个功能加强版的 memcached 来用。
Redis 的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的
高性能操作和运算上。
1)String
常用命令:set/get/decr/incr/mget 等;
应用场景:String 是最常用的一种数据类型,普通的 key/value 存储都可以归为此类;
实现方式:String 在 redis 内部存储默认就是一个字符串,被 redisObject 所引用,当遇到 incr、decr 等操作时会转成数值型进行计
算,此时 redisObject 的 encoding 字段为 int。2)Hash
常用命令:hget/hset/hgetall 等
应用场景:我们要存储一个用户信息对象数据,其中包括用户 ID、用户姓名、年龄和生日,通过用户 ID 我们希望获取该用户的姓名或者
年龄或者生日;
实现方式:Redis 的 Hash 实际是内部存储的 Value 为一个 HashMap,并提供了直接存取这个 Map 成员的接口。如图所示,Key 是用
户 ID, value 是一个 Map。这个 Map 的 key 是成员的属性名,value 是属性值。这样对数据的修改和存取都可以直接通过其内部 Map
的 Key(Redis 里称内部 Map 的 key 为 field), 也就是通过 key(用户 ID) + field(属性标签) 就可以操作对应属性数据。当前 HashMap 的
实现有两种方式:当 HashMap 的成员比较少时 Redis 为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的
HashMap 结构,这时对应的 value 的 redisObject 的 encoding 为 zipmap,当成员数量增大时会自动转成真正的 HashMap,此时
encoding 为 ht。
hash
3)List
常用命令:lpush/rpush/lpop/rpop/lrange 等;
应用场景:Redis list 的应用场景非常多,也是 Redis 最重要的数据结构之一,比如 twitter 的关注列表,粉丝列表等都可以用 Redis 的
list 结构来实现;
实现方式:Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis 内部的
很多实现,包括发送缓冲队列等也都是用的这个数据结构。
4)Set
常用命令:sadd/spop/smembers/sunion 等;
应用场景:Redis set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的,当你需要存储一个列表数据,
又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不
能提供的;
实现方式:set 的内部实现是一个 value 永远为 null 的 HashMap,实际就是通过计算 hash 的方式来快速排重的,这也是 set 能提供判
断一个成员是否在集合内的原因。
5)Sorted Set
常用命令:zadd/zrange/zrem/zcard 等;
应用场景:Redis sorted set 的使用场景与 set 类似,区别是 set 不是自动有序的,而 sorted set 可以通过用户额外提供一个优先级
(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择 sorted set
数据结构,比如 twitter 的 public timeline 可以以发表时间作为 score 来存储,这样获取时就是自动按时间排好序的。
实现方式:Redis sorted set 的内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap 里放的是成员到 score 的映
射,而跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上
比较简单。
作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略.
. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
. no-enviction(驱逐):禁止驱逐数据
注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的 lru、
ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。
使用策略规则:
1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru
2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random
ttl 和 random 比较容易理解,实现也会比较简单。主要是 Lru 最近最少使用淘汰策略,设计上会对 ke y 按失效时间排序,然后取
最先失效的 key 进行淘汰
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。如
果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。在内存越来越便宜的今天,redis 将会越来越受欢迎。如果设置了最大
使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
redis 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销
Redis 为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis 本身没有锁的概念,Redis 对于多个客户端连接并不存在竞
争,但是在 Jedis 客户端对 Redis 进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客
户端连接混乱造成。对此有 2 种解决方法:
和众多其它数据库一样,Redis 作为 NoSQL 数据库也同样提供了事务机制。在 Redis 中MULTI/EXEC/DISCARD/WATCH 这四个命令是
我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出Redis 中事务
的实现特征:
在 Redis 的事务中,WATCH 命令可用于提供 CAS(check-and-set)功能。假设我们通过 WATCH 命令在事务执行之前监控了多个
Keys,倘若在 WATCH 之后有任何 Key 的值发生了变化,EXEC 命令执行的事务都将被放弃,同时返回 Null multi-bulk 应答以通知调用
者事务执行失败。
例如,我们再次假设 Redis 中并未提供 incr 命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如
下:
val = GET mykey
val = val + 1
SET mykey $val
以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出
现多线程程序中经常出现的一种错误场景–竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该
值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似
的问题,我们需要借WATCH命令的帮助,见如下代码:WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
和此前代码不同的是,新代码在获取 mykey 的值之前先通过 WATCH 命令监控了该键,此后又将 set 命令包围在事务中,这样就可
以有效的保证每个连接在执行 EXEC 之前,如果当前连接获取的 mykey 的值被其它连接的客户端修改,那么当前连接的 EXEC 命令将执
行失败。这样调用者在判断返回值后就可以获悉 val 是否被重新设置成功。
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。
这时候对方会告诉你说你回答得不错,然后接着问如果在 setnx 之后执行 expire 之前进程意外 crash 或者要重启维护了,那会怎么样?
这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结
果是你主动思考出来的,然后回答:我记得 set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和 expire 合成一条指令来用的!
对方这时会显露笑容,心里开始默念:摁,这小子还不错。
15.假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?
使用 keys 指令可以扫出指定模式的 key 列表。
对方接着追问:如果这个 redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?
这个时候你要回答 redis 关键的一个特性:redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行
完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,
在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。
一般使用 list 结构作为队列,rpush 生产消息,lpop 消费消息。当 lpop 没有消息的时候,要适当 sleep 一会再重试。如果对方追问可不
可以不用 sleep 呢?list 还有个指令叫 blpop,在没有消息的时候,它会阻塞住直到消息到来。
如果对方追问能不能生产一次消费多次呢?使用 pub/sub 主题订阅者模式,可以实现 1:N 的消息队列。
如果对方追问 pub/sub 有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 rabbitmq 等。
如果对方追问 redis 如何实现延时队列?
我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用
sortedset,拿时间戳作为 score,消息内容作为 key 调用 zadd 来生产消息,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进
行处理。到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,
使得过期时间分散一些。
aof 来配合使用。在 redis 实例重启时,会使用 bgsave 持久化文件重新构建内存,再使用 aof 重放近期的操作指令来实现完整恢复重启
之前的状态。
**对方追问那如果突然机器掉电会怎样?**取决于 aof 日志 sync 属性的配置,如果不要求性能,在每条写指令时都 sync 一下磁盘,就不
会丢失数据。但是在高性能的要求下每次都 sync 是不现实的,一般都使用定时 sync,比如 1s1 次,这个时候最多就会丢失 1s 的数据。
对方追问 bgsave 的原理是什么?你给出两个词汇就可以了,fork 和 cow。fork 是指 redis 通过创建子进程来进行 bgsave 操作,cow
指的是 copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
可以将多次 IO 往返的时间缩减为一次,前提是 pipeline 执行的指令之间没有因果相关性。使用 redis-benchmark 进行压测的时候可以
发现影响 redis 的 QPS 峰值的一个重要因素是 pipeline 批次指令的数目。
Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将
rdb 文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复
制节点进行重放就完成了同步过程。
Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为 master,继续提供服务。
Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行分片存储。
缓存穿透,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经
过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。
有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap
中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们
采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期
时间会很短,最长不超过五分钟。
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿
破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
缓存击穿解决方案:业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即
去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,
当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
缓存雪崩,是指在某一个时间段,缓存集中过期失效。产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假
设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库
而言,就会产生周期性的压力波峰。
比如做电商项目的时候,一般是采取不同分类商品,缓存不同周期。在同一分类中的商品,加上一个随机因子。这样能尽可能分散缓存过
期时间,而且,热门类目的商品缓存时间长一些,冷门类目的商品缓存时间短一些,也能节省缓存服务的资源。
缓存集中失效解决:大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,从而避免失效时大量的并发请求落
到底层存储系统上。这里分享一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-
5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
ZooKeeper是源代码开放的分布式协调服务,由雅虎创建,是Google CHubby的实现。
ZooKeeper是一个高性能的分布式数据一致性解决方案,它将那些复杂的、容易出错的分布式一致性服务封装起来,构成一个高效可靠得
原语集,并提供一系列简单易用的接口给用户使用
知识要点:(1)源代码开源
(2)是分布式协调服务,它解决分布式数据一致性问题
A :顺序一致性 B:原子性 C:单一视图
D:可靠性 E:实时性
(3)高性能
(4)我们可以通过挑用ZooKeep提供的接口来解决一些分布式应用中中的实际问题。
1.数据发布/订阅
2.负载均衡
3.命名服务
4.分布式协调和通知
(1)源代码开源
(2)已经被证实是高性能,易用稳定的工业级产品
(3)有着广泛的应用:Hadoop,HBase,Storm,Solr
Leader,Follower,Observer
Leader服务器是整个ZooKeeper集群工作机制中的核心
Follower服务器是ZooKeeper集群状态的跟随者
Observer服务器充当一个观察者角色
相关设计模式
Leader,Follower设计模式
Observer 观察者设计模式
会话是指客户端和ZooKeeper服务器的连接,Zookeeper中的会话叫Session,客户端与服务器建议一个TCP的长连接来维持一个
Session,客户端在启动的时候所限会与服务器建立一个TCP连接,通过这个连接,客户端能通过心跳检测与服务器保持有效得 会话,也能向ZooKeeper服务器发送请求并获得相应。
ZooKeeper中的节点有两类
(1)集群中的一台机器为一个节点
(2)数据模型中的数据单元Znode,分为持久节点和临时节点
版本类型 | 说明 |
---|---|
version | 当前数据节点数据内容的版本号 |
cversion | 当前数据节点数据内容的版本号 |
aversion | 当前数据节点ACL(权限控制)变更版本号 |
事件监听器
ZooKeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化时候,ZooKeeper服务器会把这个变化通知发送给感兴趣的客
户端。
ACL是Access Control Lists的简写,ZooKeeper采用ACL策略进行权限控制,有以下权限:
CREATE:创建子节点的权限
REAN:获取节点数据和子节点列表的权限
WRITE:更新节点数据的权限
DELETE:删除子节点的权限
ADMIN:设置节点ACL的权限
局部变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
环境变量:生效范围为当前shell进程及其子进程
本地变量:生效范围为当前shell进程中某代码片断,通常指函数
位置变量:$1, 2 , . . . 来 表 示 , 用 于 让 脚 本 在 脚 本 代 码 中 调 用 通 过 命 令 行 传 递 给 它 的 参 数 特 殊 变 量 : 2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数 特殊变量: 2,...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数特殊变量:?, $0, $, $@, KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲,$
$? 存储脚本、命令或者函数的退出状态值;脚本的退出状态为脚本中最后一条命令的退出状态;函数退出状态也为最后一条命令的退出
状态;一般成功执行退出状态为0;命令执行失败退出状态为1-255之间的整数.
$0 执行脚本时脚本的路径名
$ 将所有位置参数视为单个字符串
$@ 每个位置参数存储为单独引用的字符串,分开对待每个位置参数
$# 脚本后所跟的参数的个数
$$ 为PID变量,存储其出现在的脚本所属的进程的进程号
局部变量赋值和引用
```bash
变量赋值:name='value'
(1) 可以是直接字串:name='root'
(2) 变量引用:name="$USER"
(3) 命令引用:name=`COMMAND`
name=$(COMMAND)
变量引用:${name} 或者 $name
-v VAR
测试变量VAR是否设置
示例:判断 NAME 变量是否定义
[ -v NAME ]
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等于
-lt 是否小于
-le 是否小于等于
```### 字符串测试
```bash
-z "STRING" 字符串是否为空,空为真,不空为假
-n "STRING" 字符串是否不空,不空为真,空为假
= 是否等于
> ascii码是否大于ascii码
< 是否小于
!= 是否不等于
== 左侧字符串是否和右侧的PATTERN相同
=~ 左侧字符串是否能够被右侧的PATTERN所匹配
注意:此表达式用于[[ ]]中,PATTERN为通配符
存在性测试
-a FILE:同 -e
-e FILE: 文件存在性测试,存在为真,否则为假
存在性及类别测试
-b FILE:是否存在且为块设备文件
-c FILE:是否存在且为字符设备文件
-d FILE:是否存在且为目录文件
-f FILE:是否存在且为普通文件
-h FILE 或 -L FILE:存在且为符号链接文件
-p FILE:是否存在且为命名管道文件
-S FILE:是否存在且为套接字文件 # 大写s
文件权限测试:
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
-x FILE: 是否存在且可执行
文件特殊权限测试:
-u FILE:是否存在且拥有suid权限
-g FILE:是否存在且拥有sgid权限
-k FILE:是否存在且拥有sticky权限
文件大小测试:
-s FILE: 是否存在且非空
文件是否打开:
-t fd: fd 文件描述符是否在某终端已经打开
-N FILE:文件自从上一次被读取之后是否被修改过-O FILE:当前有效用户是否为文件属主
-G FILE:当前有效用户是否为文件属组
FILE1 -ef FILE2: FILE1是否是FILE2的硬链接
FILE1 -nt FILE2: FILE1是否新于FILE2(mtime)
FILE1 -ot FILE2: FILE1是否旧于FILE2
第一种方式:
[ EXPRESSION1 -a EXPRESSION2 ] 并且
[ EXPRESSION1 -o EXPRESSION2 ] 或者
[ ! EXPRESSION ]
取反
-a 和 -o 需要使用测试命令进行,[[ ]] 不支持
第二种方式:
COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN
COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE
! COMMAND 非
示例:
[ -f “$FILE” ] && [[ “$FILE”=~ .*\.sh$ ]]
示例:
test "$A" = "$B" && echo "Strings are equal"
test "$A"-eq "$B" && echo "Integers are equal"
[ "$A" = "$B" ] && echo "Strings are equal"
[ "$A" -eq "$B" ] && echo "Integers are equal"
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
[ -z "$HOSTNAME" -o $HOSTNAME = "localhost.localdomain" ] && hostname suosuoli.cn
反斜线(\)会使随后的字符按原意解释
echo Your cost: \$5.00
Your cost: $5.00
加引号来防止扩展
?单引号(’’)防止所有扩展
?双引号(”“)也可防止扩展,但是以下情况例外:
$(美元符号) 变量扩展
` ` (反引号) 命令替换
\(反斜线) 禁止单个字符扩展
!(叹号) 历史命令替换
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
**按功能划分,存在两类:**
- Profile类
```bash
profile类:为交互式登录的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
个人:~/.bash_profile
功用:
(1) 用于定义环境变量
(2) 运行命令或脚本
bashrc类:为非交互式和交互式登录的shell提供配置
全局:/etc/bashrc
个人:~/.bashrc
功用:
(1) 定义命令别名和函数
(2) 定义本地变量
(1)直接通过终端输入账号密码登录
(2)使用“su - UserName” 切换的用户
执行顺序:/etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile -->
~/.bashrc --> /etc/bashrc
(1)su UserName
(2)图形界面下打开的终端
(3)执行脚本(4)任何其它的bash实例
执行顺序: /etc/profile.d/*.sh --> /etc/bashrc -->~/.bashrc
修改profile和bashrc文件后需生效
两种方法:
1重新启动shell进程
2使用.命令或source 命令
例:
~# . ~/.bashrc
~# source ~/.bashrc
任务保存在~/.bash_logout文件中(用户)
在退出登录shell时运行
作用
?创建自动备份
?清除临时文件
root@server-ctl:/data/scripts# echo $-
himBHs
root@server-ctl:/data/scripts# echo $_
himBHs
##########
h:hashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每
次都要查询。通过set +h将h选项关闭
i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的
shell。所谓的交互式shell,在脚本中,i选项是关闭的
m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继
续,后台或者前台执行等
B:braceexpand,大括号扩展
H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来
完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令
[root@lab-server2 ~]# set +i # 关闭该功能
[root@lab-server2 ~]# echo $-
hmBH
[root@lab-server2 ~]# set -i # 开启该功能
[root@lab-server2 ~]# echo $-
himBH
[root@lab-server2 ~]# set +h
[root@lab-server2 ~]# echo $-imBH
[root@lab-server2 ~]# set -h
[root@lab-server2 ~]# echo $-
himBH
使用set命令可以避免一些灾难性的错误
-u 在扩展一个没有设置的变量时,显示错误信息
等同set -o nounset
-e 如果一个命令返回一个非0退出状态值(失败)就退出
等同set -o errexit
while read line; do
Do something here...
done < /PATH/FROM/SOMEFILE
上面的用法会依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将整行赋值给变量line在循环体中对其进行处理。
select variable in list
do
Do something here...
done
select循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入
用户输入菜单列表对应的某个数字,执行相应的命令,用户输入被保存在内置变量REPLY中
**注意:**select是个无限循环,因此要记住用break命令退出循环,或用exit命令终止脚本。也可以按Ctrl+c退出循环
select经常和case联合使用
例如:
root@server-ctl:/data/scripts# select i in ni wo ta; do echo $i; break ; done
1) ni
2) wo
3) ta
#? 3
ta
root@server-ctl:/data/scripts# select i in ni wo ta; do echo $i; break ; done
1) ni
2) wo3) ta
#? 4
root@server-ctl:/data/scripts# select i in *.sh; do echo $i; break ; done
1) awk_sum_blank_lines.sh
2) awk_sumid.sh
3) check_ip.sh
4) demo_01.sh
5) file_type.sh
6) read.sh
#? 6
read.sh
root@server-ctl:/data/scripts# select i in *.sh; do echo $i; break ; done
1) awk_sum_blank_lines.sh
2) awk_sumid.sh
3) check_ip.sh
4) demo_01.sh
5) file_type.sh
6) read.sh
#? 1
awk_sum_blank_lines.sh
f_name()
{
...function body...
}
function f_name
{
...function body...
}
function f_name()
{
...function body...}
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回
该dir函数将一直保留到用户从系统退出,或执行了如下所示的unset命令
~# unset dir
. filename 或 source filename
<点> <空格> <文件名> 这里的文件名要带函数文件的完整路径名
[root@centos7 ~]#hello (){ echo 'Hello , im a function!' ; }
[root@centos7 ~]#hello
Hello , im a function!
unset function_name
声明:export -f function_name
查看:export -f 或 declare -xf
函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可
例如:
root@server-ctl:/data/scripts# cat test.sh
#!/bin/bash
fun_hello(){
for i in $@; do
echo $1
shift
echo "hello, im a function.!"
done
}
main(){
fun_hello $@}
main $@
root@server-ctl:/data/scripts# bash test.sh 1 2 3
1
hello, im a function.!
2
hello, im a function.!
3
hello, im a function.!
在函数体中当中,可以在函数体后跟$1, $2,…调用传入的参数;还可以使用$@, $*, $#等特殊变量
local NAME=VALUE
:(){
:|:& };:
bomb() {
bomb | bomb & }; bomb
#!/bin/bash
./$0|./$0&
declare -A ARRAY_NAME 关联数组
(1) 一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE
weekdays[0]="Sunday"
weekdays[4]="Thursday"
(2) 一次赋值全部元素
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
(3) 只赋值特定元素
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
(4) 交互式数组值对赋值
read -a ARRAY
${ARRAY_NAME[INDEX]}
引用单个元素的值${ARRAY_NAME[*]}
引用数组所有元素${ARRAY_NAME[@]}
引用数组所有元素${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
unset ARRAY[INDEX]
unset ARRAY
${ARRAY[@]:offset:number}
offset 要跳过的元素个数
number 要取出的元素个数
${ARRAY[@]:offset}
ARRAY[${#ARRAY[*]}]=value
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)
#!/bin/bash
declare -i min max
declare -a nums
for ((i=0;i<10;i++));do
nums[$i]=$RANDOM
[ $i -eq 0 ] && min=${nums[$i]} && max=${nums[$i]}&& continue
[ ${nums[$i]} -gt $max ] && max=${nums[$i]}
[ ${nums[$i]} -lt $min ] && min=${nums[$i]}
done
echo “All numbers are ${nums[*]}”
echo Max is $max
echo Min is $min
[:space:] ↩︎