第四周,nginx
1,编译安装nginx应用,提供wordpress服务
wget http://nginx.org/download/nginx-1.14.2.tar.gz #下载nginx源码包
tar -zxf nginx-1.12.1.tar.gz #解压包
yum groupinstall "Development Tools" #下载编译包组
yum install openssl-devel pcre-devel libevent-devel -y #下载所需依赖的包
cd nginx-1.14.2 #进入解压目录
nginx-1.14.2]# ./configure \ #执行编译操作指定选项
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/lock/subsys/nginx.lock \
--user=nginx --group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_dav_module \
--with-threads \
--with-file-aio \
--with-http_stub_status_module
make && make install #编译并且复制文件到指定的目录下
nginx-1.14.2]# useradd nginx #同名组自动创建
因为在进行编译配置的时候我们就将nginx的二进制文件写到了“/usr/sbin/”下,所以我们可以直接使用命令来启动nginx:
nginx-1.14.2]# nginx
接下来我们使用命令来查看nginx是否启动成功:
nginx-1.14.1=2]# ss -tnl
使用命令停掉httpd(如果没启动就不用管了):
~]# service httpd stop
此时在真实主机上就可以通过浏览器访问“192.168.1.198”(这个IP是刚才虚拟机的IP)
这样的Nginx的配置就完成了,下面开始配置1个虚拟主机,1个虚拟主机的根目录我存放在“/myweb/wordpress
mkdir -pv /myweb/wordpress
nginx-1.12.1]# vim /etc/nginx/nginx.conf
在“http”的大括号中添加下面这条:
include /etc/nginx/conf/*.conf"
接下来我们在“/etc/nginx/conf/”下创建一个以“.conf”结尾的片段配置文件并写入内容:
server {
listen 80;
server_name myweb.wordpress.com;
location / {
root /myweb/wordpress;
index index.html;
}
}
保存并退出之后,使用“nginx -t”命令检查配置文件是否有错误,如果没有错误,就使用“nginx -s reload”命令重新载入配置:
wordpress需要依赖数据库,所以需要先安装数据库,我这里默认安装为mariadb
yum install mariadb-server
https://cn.wordpress.org/latest-zh_CN.zip
然后分别解压到各自对应的目录中:
~]# tar -zxf wordpress-4.9.4-zh_CN.tar.gz -C /myweb/wordpress/
systemctl start mariadb 启动数据库
CREATE USER 'wordpress'@'%' IDENTIFIED BY '123456'; 这种方法允许任何远程连接
[root@centos7 wordpress]# cp ../wordpress/wp-config-sample.php ./wp-config.php
[root@centos7 wordpress]# vim wp-config.php
先拷贝一份wordpress自带的配置文件,并修改其配置信息:
yum install php php-fpm && systemctl start php #安装并启动phpfpm协议
置完成之后,我们还需要回到刚才配置虚拟主机那里把刚才的配置增加几条选项,变成下面的样子(因为Nginx默认将PHP注释掉了,需要我们自己开启):
server {
listen 80;
server_name 192.168.1.198;
location / {
root /myweb/wordpress/wordpress;
index index.html index.php;
}
location ~ \.php$ {
root /myweb/wordpress/wordpress;
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
yum install php php-fpm && systemctl start php #安装并启动phpfpm协议
搭建成功
2,简述nginx特性,并与apache简单对比
程序环境
配置文件的组成部分:
主配置文件:nginx.conf
include conf.d/*.conf
fastcgi, uwsgi,scgi等协议相关的配置文件
mime.types:支持的mime类型
主程序文件:/usr/sbin/nginx
Unit File:nginx.service
注意:
(1) 指令必须以分号结尾;
(2) 支持使用配置变量;
内建变量:由Nginx模块引入,可直接引用;
自定义变量:由用户使用set命令定义;
set variable_name value;
引用变量:$variable_name
主配置文件结构:
main block:主配置段,也即全局配置段;
event {
...
}:事件驱动相关的配置;
http {
...
}:http/https 协议相关的配置段;
mail {
...
}
stream {
...
}
http协议相关的配置结构
http {
...
...:各server的公共配置也称全局配置
server {
...
}:每个server用于定义一个虚拟主机;
server {
...
listen
server_name
root
alias
location [OPERATOR] URL {
...
if CONDITION {
...
Nginx(2)
配置指令:
main配置段常见的配置指令:
分类:
正常运行必备的配置
优化性能相关的配置
用于调试及定位问题相关的配置
事件驱动相关的配置
正常运行必备的配置:
1、user
Syntax: user user [group];
Default: user nobody nobody;
Context: main
Defines user and group credentials used by worker processes. If group is omitted, a group whose name equals that of user is used.
2、pid /PATH/TO/PID_FILE;
指定存储nginx主进程进程号码的文件路径;
3、include file | mask;
指明包含进来的其它配置文件片断;
4、load_module file;
指明要装载的动态模块;
性能优化相关的配置:
1、worker_processes number | auto;
worker进程的数量;通常应该等于小于当前主机的cpu的物理核心数;
auto:当前主机物理CPU核心数;
2、worker_cpu_affinity cpumask ...;
worker_cpu_affinity auto [cpumask];
CPU MASK:
00000000:
00000001:0号CPU
00000010:1号CPU
... ...
3、worker_priority number;
指定worker进程的nice值,设定worker进程优先级;[-20,20]
4、worker_rlimit_nofile number;
worker进程所能够打开的文件数量上限;
调试、定位问题:
1、daemon on|off;
是否以守护进程方式运行Nignx;
2、master_process on|off;
是否以master/worker模型运行nginx;默认为on;
3、error_log file [level];
事件驱动相关的配置:
events {
...
}
1、worker_connections number;
每个worker进程所能够打开的最大并发连接数数量;
worker_processes * worker_connections
2、use method;
指明并发连接请求的处理方法;
use epoll;
3、accept_mutex on | off;
处理新的连接请求的方法;on意味着由各worker轮流处理新请求,Off意味着每个新请求的到达都会通知所有的worker进程;
web服务中的http中字段配置
1、server { ... }
配置一个虚拟主机;
server {
listen address[:PORT]|PORT;
server_name SERVER_NAME;
root /PATH/TO/DOCUMENT_ROOT;
}
2、listen PORT|address[:port]|unix:/PATH/TO/SOCKET_FILE
listen address[:port] [default_server] [ssl] [http2 | spdy] [backlog=number] [rcvbuf=size] [sndbuf=size]
default_server:设定为默认虚拟主机;
ssl:限制仅能够通过ssl连接提供服务;
backlog=number:后援队列长度;
rcvbuf=size:接收缓冲区大小;
sndbuf=size:发送缓冲区大小;
3、server_name name ...;
指明虚拟主机的主机名称;后可跟多个由空白字符分隔的字符串;
支持*通配任意长度的任意字符;server_name *.magedu.com www.magedu.*
支持~起始的字符做正则表达式模式匹配;server_name ~^www\d+\.magedu\.com$
匹配机制:
(1) 首先是字符串精确匹配;
(2) 左侧*通配符;
(3) 右侧*通配符;
(4) 正则表达式;
定义路径相关的配置:
6、root path;
设置web资源路径映射;用于指明用户请求的url所对应的本地文件系统上的文档所在目录路径;可用的位置:http, server, location, if in location;
7、location [ = | ~ | ~* | ^~ ] uri { ... }
Sets configuration depending on a request URI.
在一个server中location配置段可存在多个,用于实现从uri到文件系统的路径映射;ngnix会根据用户请求的URI来检查定义的所有location,并找出一个最佳匹配,而后应用其配置;
=:对URI做精确匹配;例如, http://www.magedu.com/, http://www.magedu.com/index.html
location = / {
...
}
~:对URI做正则表达式模式匹配,区分字符大小写;
~*:对URI做正则表达式模式匹配,不区分字符大小写;
^~:对URI的左半部分做匹配检查,不区分字符大小写;
不带符号:匹配起始于此uri的所有的url;
匹配优先级:=, ^~, ~/~*,不带符号;
root /vhosts/www/htdocs/
http://www.magedu.com/index.html --> /vhosts/www/htdocs/index.html
server {
root /vhosts/www/htdocs/
location /admin/ {
root /webapps/app1/data/
}
}
alias path;
定义路径别名,文档映射的另一种机制;仅能用于location上下文;
注意:location中使用root指令和alias指令的意义不同;
(a) root,给定的路径对应于location中的/uri/左侧的/;
(b) alias,给定的路径对应于location中的/uri/右侧的/;
9、index file ...;
默认资源;http, server, location;
10、error_page code ... [=[response]] uri;
Defines the URI that will be shown for the specified errors.
# error_page 404 /404.html; #自定义404的错误页面
# location = /40x.html {
# }
ngx_http_access_module模块:
实现基于ip的访问控制功能
location / {
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
deny all;
}
ngx_http_stub_status_module模块
用于输出nginx的基本状态信息;
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
Active connections: 活动状态的连接数;
accepts:已经接受的客户端请求的总数;
handled:已经处理完成的客户端请求的总数;
requests:客户端发来的总的请求数;
Reading:处于读取客户端请求报文首部的连接的连接数;
Writing:处于向客户端发送响应报文过程中的连接数;
Waiting:处于等待客户端发出请求的空闲连接数;
ngx_http_log_module模块
he ngx_http_log_module module writes request logs in the specified format.
31、log_format name string ...;
string可以使用nginx核心模块及其它模块内嵌的变量;
32、access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;
访问日志文件路径,格式及相关的缓冲的配置;
buffer=size
flush=time
33、open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
缓存各日志文件相关的元数据信息;
max:缓存的最大文件描述符数量;
min_uses:在inactive指定的时长内访问大于等于此值方可被当作活动项;
inactive:非活动时长;
valid:验正缓存中各缓存项是否为活动项的时间间隔;
格式内容
log_format compression '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" "$gzip_ratio"';
access_log /spool/logs/nginx-access.log compression buffer=32k;
http://nginx.org/en/docs/varindex.html nginx常用变量查看
ngx_http_gzip_module:#nginx的压缩模块
ngx_http_gzip_module模块是一个使用“gzip”方法压缩响应的过滤器。 这通常有助于将传输数据的大小减少一半甚至更多。
1、gzip on | off;
Enables or disables gzipping of responses.
2、gzip_comp_level level;
设置响应的gzip压缩级别。 可接受的值范围为1到9。
3、 gzip_disable regex ...;
对具有与任何指定正则表达式匹配的“User-Agent”标头字段的请求禁用gzipping响应。
4、 gzip_min_length length;
启用压缩功能的响应报文大小阈值(下限);
5、gzip_buffers number size;
支持实现压缩功能时为其配置的缓冲区数量及每个缓存区的大小;
6、gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any ...;
nginx作为代理服务器接收到从被代理服务器发送的响应报文后,在何种条件下启用压缩功能的;
off:对代理的请求不启用
no-cache, no-store,private:表示从被代理服务器收到的响应报文首部的Cache-Control的值为此三者中任何一个,则启用压缩功能;
示例:
gzip on;#开启压缩
gzip_comp_level 6;#压缩登记
gzip_min_length 64;#低于64字节不压缩
gzip_proxied any; #任何被代理的也需要压缩
gzip_types text/xml text/css application/javascript; #压缩类型
Module ngx_http_ssl_module
http { .
.. server{
listen 443 ssl;
keepalive_timeout 70; #连接超时时长
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_certificate /usr/local/nginx/conf/cert.pem; ##私钥
ssl_certificate_key /usr/local/nginx/conf/cert.key;#证书路径
#ssl_session_cache shared:SSL:10m;#ssl会话
ssl_session_timeout 10m;##会话超时时长
}
mkdir /etc/nginx/certs
100 cd /etc/nginx/certs/
openssl genrsa-out nginx.key 2048
openssl genrsa -out nginx.key 2048 openssl req -new -x509 -key nginx.key -out nginx.cert -days 3650 -subj "/CN www.ilinux.io"
openssl req -new -x509 -key nginx.key -out nginx.cert -days 3650 -subj "/CN=www.ilinux.io"
生成秘钥并且自签名证书实现ssl
ngx_http_rewrite_module模块:
The ngx_http_rewrite_module module is used to change request URI using PCRE regular expressions, return redirects, and conditionally select configurations.
bbs.magedu.com/ --> www.magedu.com/bbs/, http://www.magedu.com/ --> https://www.magedu.com/
http://www.magedu.com/login.php;username=tom --> http://www.magedu.com/tom/
http://www.ilinux.io/bbs/ --> http://bbs.ilinux.io/
将用户请求的URI基于regex所描述的模式进行检查,而后完成替换;
1、rewrite regex replacement [flag]
将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为replacement指定的新的URI;
注意:如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查;被某条件规则替换完成后,会重新一轮的替换检查,因此,隐含有循环机制;[flag]所表示的标志位用于控制此循环机制;
如果replacement是以http://或https://开头,则替换结果会直接以重向返回给客户端;
301:永久重定向;
[flag]:
last:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后对新的URI启动新一轮重写检查;提前重启新一轮循环;
break:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环;
redirect:重写完成后以临时重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;不能以http://或https://开头;302
permanent:重写完成后以永久重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;301
ngx_http_referer_module模块:
The ngx_http_referer_module module is used to block access to a site for requests with invalid values in the “Referer” header field.
1、valid_referers none | blocked | server_names | string ...;
定义referer首部的合法可用值;
none:请求报文首部没有referer首部;
blocked:请求报文的referer首部没有值;
server_names:参数,其可以有值作为主机名或主机名模式;
arbitrary_string:直接字符串,但可使用*作通配符;
regular expression:被指定的正则表达式模式匹配到的字符串;要使用~打头,例如 ~.*\.magedu\.com;
配置示例:
valid_referers none block server_names *.magedu.com *.mageedu.com magedu.* mageedu.* ~\.magedu\.;
if($invalid_referer) {
return http://www.magedu.com/invalid.jpg;
}
ngx_http_proxy_module模块:
The ngx_http_proxy_module module allows passing requests to another server.
1、proxy_pass URL;
Context: location, if in location, limit_except
注意:proxy_pass后面的路径不带uri时,其会将location的uri传递给后端主机;
server {
...
server_name HOSTNAME;
location /uri/ {
proxy http://hos[:port];
}
...
}
http://HOSTNAME/uri --> http://host/uri
proxy_pass后面的路径是一个uri时,其会将location的uri替换为proxy_pass的uri;
server {
...
server_name HOSTNAME;
location /uri/ {
proxy http://host/new_uri/;
}
...
}
http://HOSTNAME/uri/ --> http://host/new_uri/
如果location定义其uri时使用了正则表达式的模式,或在if语句或limt_execept中使用proxy_pass指令,则proxy_pass之后必须不能使用uri; 用户请求时传递的uri将直接附加代理到的服务的之后;
server {
...
server_name HOSTNAME;
location ~|~* /uri/ {
proxy http://host;
}
...
}
http://HOSTNAME/uri/ --> http://host/uri/;
docker run --name websrv1 -it -v /vols/websrv1:/web/htdocs busybox #启动容器充当后端服务器 并且添加首页文件,运行程序在前端
/ # httpd -f -h /web/htdocs/
[root@centos7 websrv1]# curl 172.17.0.2 #请求
hello proxy web1 返回成功
配置代理服务,指向后端服务器2、proxy_set_header field value;
设定发往后端主机的请求报文的请求首部的值;Context: http, server, location
proxy_set_header X-Real-IP $remote_addr; #修改向后端发送请求的真正客户段ip地址
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
修改容器中的日志格式IP地址为X-Real-IP选项指定为真实机器ip
修改nginx代理添加首部X-Real-IP 和ip地址至后端
kill -1 在容器重新加载配置文件
测试成功9、proxy_hide_header field;
默认情况下,nginx不会从代理服务器对客户端的响应中传递标题字段“Date”,“Server”,“X-Pad”和“X-Accel -...”。 proxy_hide_header指令设置了不会传递的其他字段。
proxy_connect_timeout time;
定义与代理服务器建立连接的超时。 应该注意,此超时通常不会超过75秒。
proxy_read_timeout time;
定义从代理服务器读取响应的超时。 仅在两个连续的读操作之间设置超时,而不是为整个响应的传输。
proxy_send_timeout time;
设置将请求传输到代理服务器的超时。 仅在两次连续写操作之间设置超时,而不是为整个请求的传输。 如果代理服务器在此时间内未收到任何内容,则关闭连接。
ngx_http_fastcgi_module模块:
The ngx_http_fastcgi_module module allows passing requests to a FastCGI server.
配置示例1:
前提:配置好fpm server和mariadb-server服务;
location ~* \.php$ {
root /usr/share/nginx/html;
fastcgi_pass 127.0.0.1:9000;#指定fpm地址和端口
fastcgi_index index.php; #指定首页
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;#指定参数,fastcgi_script_name为uri路径变量,前面的路径为fastcgi协议网页的根目录
include fastcgi_params; #包含/etc/nginx/fast_param的文件
}
配置示例2:通过/pm_status和/ping来获取fpm server状态信息;
location ~* ^/(pm_status|ping)$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}
4、fastcgi_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
定义fastcgi的缓存;缓存位置为磁盘上的文件系统,由path所指定路径来定义;
levels=levels:缓存目录的层级数量,以及每一级的目录数量;levels=ONE:TWO:THREE
leves=1:2:2
keys_zone=name:size
k/v映射的内存空间的名称及大小
inactive=time
非活动时长
max_size=size
磁盘上用于缓存数据的缓存空间上限
5、fastcgi_cache zone | off;
调用指定的缓存空间来缓存数据;http, server, location
6、fastcgi_cache_key string;
定义用作缓存项的key的字符串;
7、fastcgi_cache_methods GET | HEAD | POST ...;
为哪些请求方法使用缓存;
8、fastcgi_cache_min_uses number;
缓存空间中的缓存项在inactive定义的非活动时间内至少要被访问到此处所指定的次数方可被认作活动项;
9、fastcgi_cache_valid [code ...] time;
不同的响应码各自的缓存时长;
示例:
http {
...
fastcgi_cache_path /var/cache/nginx/fastcgi_cache levels=1:2:1 keys_zone=fcgi:20m inactive=120s;
...
server {
...
location ~* \.php$ {
...
fastcgi_cache fcgi;
fastcgi_cache_key $request_uri;
fastcgi_cache_valid 200 302 10m;
fastcgi_cache_valid 301 1h;
fastcgi_cache_valid any 1m;
...
}
...
}
...
}
3,简述I/O模型及同步/异步消息通知机制
普及:
用户空间与内核空间:
现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。
进程切换:
为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。
从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:
保存处理机上下文,包括程序计数器和其他寄存器。
更新PCB信息。
把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
选择另一个进程执行,并更新其PCB。
更新内存管理的数据结构。
恢复处理机上下文。
注:总而言之就是很耗资源,具体的可以参考这篇文章:
http://guojing.me/linux-kernel-architecture/posts/process-switch/
进程阻塞:
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的。
文件描述符:
文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
缓存IO:
缓存 IO 又被称作标准 IO,大多数文件系统的默认 IO 操作都是缓存 IO。在 Linux 的缓存 IO 机制中,操作系统会将 IO 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。
缓存IO的缺点:
数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。
Linux IO模型:
网络IO的本质是socket的读取,socket在linux系统中被抽象为流,IO可以理解为对流的操作.对于一次IO访问,数据会先被拷到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间,所以说当一个read操作发生时,它会经理两个阶段:
1
2
第一阶段:等待数据准备 (Waiting for the data to be ready)。
第二阶段:将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)。
对socket流而言:
1
2
第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。
第二步:把数据从内核缓冲区复制到应用进程缓冲区。
网络应用需要处理的无非就是两大类问题,网络IO,数据计算。相对于后者,网络IO的延迟,给应用带来的性能瓶颈大于后者。网络IO的模型大致有如下几种:
同步模型(synchronous IO)
阻塞IO(bloking IO)
非阻塞IO(non-blocking IO)
多路复用IO(multiplexing IO)
信号驱动式IO(signal-driven IO)
异步IO(asynchronous IO)
同步4个:阻塞IO、非阻塞IO、多路复用IO、信号驱使式IO
异步1个:异步IO
注:由于信号驱动式(signal driven IO)在实际中并不常用,所以我这只提及剩下的四种IO Model。
在深入介绍Linux IO各种模型之前,让我们先来探索一下基本 Linux IO 模型的简单矩阵。如下图所示:
每个 IO 模型都有自己的使用模式,它们对于特定的应用程序都有自己的优点。
同步和异步主要针对C(client)端
同步:
所谓的同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用步返回,也就是说必须一件一件事做,等前一件事完了之后才做后一件事。
如:普通的B/S模式(同步):提交请求->等待服务器处理->处理完毕返回,这期间客户端浏览器不能干任何事
异步:
与同步相对。当C端一个异步过程调用发出之后,调用者不能立即得到结果,实际处理这个调用的部件在完成后,通过状态,通知和回调来通知调用者。
如:请求通过事件触发->服务器处理(浏览器仍然可以做其他事情)->处理完毕
阻塞和非阻塞主要针对S端(server)
阻塞:
阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态,cpu不会分配时间片,线程暂停运行)函数只有得到结果返回。
阻塞调用和同步调用的区别:对同步来说,很多时候当前线程还是激活的,只是逻辑上没有返回,如,在socket编程中调用recv函数,如果缓冲区没有数据,这个函数就会一直等待,直到有数据返回。而此前当前线程还有可能继续处理各种各样的消息。
阻塞的例子:比如去取A楼一层(假设是内核缓冲区)取快递,但是比不知道什么时候来,你有不能干别的事情,只能死等着但是可以睡觉(进程处于休眠状态),因为你知道快递把货送来时一定会给比大电话
非阻塞:
非阻塞与阻塞概念想对应,指在不能立即得到结果之前,该函数不会阻塞当前线程,而会立即返回。
非阻塞的例子:还是等快递,如果用轮询的方式,每隔5分钟去A楼一层(内核缓冲区)去看快递来了没,没来,立即返回,如果快递来了,就放到A楼一层,等你去取。
对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞,而对于非阻塞对象,调用函数可以进入阻塞调用,对于select:
1:同步
1我客户端(C端调用者)一个功能,该功能没有结束前,我死等结果。
2:异步
我(c端调用者)调用一个功能,不知道该功能结果,该功能有结果后通知我,即回调通知
同步和异步主要针对c端,但是跟s端不是完全没关系,同步和异步必须s端配合才能实现,同步和异步由c端控制,但是s端是否为阻塞还是非阻塞,c端不关心。
3:阻塞
1就是调用我(s端被调用者,函数),我(s端被调用者,函数)没有完全接受完数据或者没有得到结果之前,我不会返回。
4:非阻塞
1就是调用我(s端被调用者,函数),我(s端被调用者,函数)立即返回,通过select通知调用者
同步I/O与异步I/O的区别在与数据访问的时候进程是否阻塞
阻塞I/O与非阻塞I/O的区别在与:应该程序的调用是否立即返回。
阻塞和非阻塞是指server端的进程访问的数据如果尚未就绪,进程是否需要等待,简单说这相当于函数内部的实现区别,也就是未就绪时时直接返回还是等待就绪。
就同步和异步是指client端访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后再读写额时候必须阻塞,异步则指主动请求数据后便可以继续处理其他任务,随后等待I/O,操作完毕的通知。
一、阻塞I/O模型:
简介:进程会一直阻塞,直到数据拷贝完成
应用程序调用一个I/O函数,导致应用程序阻塞,等待数据准备好,如果数据没有准备好,一直等待。。数据准备好,从内核拷贝到用户空间,I/O函数返回成功
阻塞I/O模型图:在调用recv()/recvfrom(),发生在内核中等待数据和复制数据过程。
当调用recv()函数时,系统首先检查是否有准备好的数据,如果数据没有准备好,那么系统就处于等待状态,当数据准备好后,将数据从系统缓冲区复制到用户空间,然后函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数处于等待状态
二、非阻塞I/O模型:
简介:我们把一个套接口设置为非阻塞就是告诉内存,当所请求的I/O操作无法完成时,不要惊进程睡眠,而是返回一个错误,河阳I/O函数会不断的测试数据是否准备好,没有准备好,继续测试,直到数据准备好为止。在测试的过程中会占用大量的CPU时间。
三、I/O复用模型:
简介:主要是select和epoll;对于一个I/O端口,两次调用,两次返回,比阻塞I/O并没有什么优势,只是能实现同时对多个I/O端口进行监听。
I/O复用模型会调用select,poll函数,这几个函数也会使进程阻塞,但是和阻塞I/O不同的,这个函数可以同时阻塞多个I/O操作,而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
四、信号驱动I/O:
简介:两次调用,两次返回
首先允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。昂数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
五、异步I/O模型:
简介:数据拷贝的时候进程无需阻塞
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态,通知和回调通知调用者输入输出操作。
ki同步I/O引起进程阻塞,直到I/O操作完成
异步I/O不会引起进程阻塞
I/O复用先通过select调用阻塞
NGINX:
nginx 支持多种并发模型,并发模型的具体实现根据系统平台而有所不同。
在支持多种并发模型的平台上,nginx 自动选择最高效的模型。但我们也可以使用 use 指令在配置文件中显式地定义某个并发模型。
NGINX中支持的并发模型:
select:
1IO多路复用、标准并发模型。在编译 nginx 时,如果所使用的系统平台没有更高效的并发模型,select 模块将被自动编译。configure 脚本的选项:--with-select_module 和 --without-select_module 可被用来强制性地开启或禁止 select 模块的编译
poll:
1IO多路复用、标准并发模型。与 select 类似,在编译 nginx 时,如果所使用的系统平台没有更高效的并发模型,poll 模块将被自动编译。configure 脚本的选项:--with-poll_module 和 --without-poll_module 可用于强制性地开启或禁止 poll 模块的编译
epoll:
1IO多路复用、高效并发模型,可在 Linux 2.6+ 及以上内核可以使用
kqueue:
1IO多路复用、高效并发模型,可在 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, and Mac OS X 平台中使用
/dev/poll:
1高效并发模型,可在 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+, and Tru64 UNIX 5.1A+ 平台使用
eventport:
1高效并发模型,可用于 Solaris 10 平台,PS:由于一些已知的问题,建议 使用/dev/poll替代。
为什么epoll快?
比较一下Apache常用的select,和Nginx常用的epoll
select:
1、最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Select 模型的最大并发数就被相应限制了。自己改改这个 FD_SETSIZE ?想法虽好,可是先看看下面吧 …
2、效率问题, select 每次调用都会线性扫描全部的 FD 集合,这样效率就会呈现线性下降,把 FD_SETSIZE 改大的后果就是,大家都慢慢来,什么?都超时了。
3、内核 / 用户空间 内存拷贝问题,如何让内核把 FD 消息通知给用户空间呢?在这个问题上 select 采取了内存拷贝方法,在FD非常多的时候,非常的耗费时间。
总结为:1、连接数受限 2、查找配对速度慢 3、数据由内核拷贝到用户态消耗时间
epoll:
1、Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, 一般来说这个数目和系统内存关系很大 ,具体数目可以 cat /proc/sys/fs/file-max 查看。
2、效率提升, Epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。
3、内存共享, Epoll 在这点上使用了“共享内存 ”,这个内存拷贝也省略了。
4,,实现lnmp提供多个虚拟主机,nginx和php使用不同主机
使用容器搭建
docker pull php:7-fpm-alpine
docker run --name fpmsrv1 -d --network bridge -v /vols/phpsrv1:/appdata php:7-fpm-alpine #创建容器
[root@centos7 nginx]# vim /vols/phpsrv1/index.php #创建测试页
<?php
phpinfo();
?>
[root@centos7 nginx]# vim /etc/nginx/nginx.conf #修改配置文件
server {
listen 8080;
server_name 192.168.1.198;
root "/web/nginx/html";
location / {
root "/web/nginx/html";
index index.html;
}
location = /ngx_status {
stub_status;
}
location /bbs/ {
rewrite ^/bbs/(.*)$ /froum/$1 break;
}
location ~* \.php$ {
fastcgi_pass 172.17.0.3:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /appdata$fastcgi_script_name;将后端fpm地址映射至本地路径
include fastcgi_params;#将本地的/etc/nginx/fastcgi_params包含进来
}
}
测试成功5,自定义错误404和5xx错误页,文本静态内容传输压缩
10、error_page code ... [=[response]] uri;
Defines the URI that will be shown for the specified errors.
# error_page 404 /404.html; #自定义404的错误页面
# location = /40x.html {
# }
error_page 502 503 /50x.html;
location = /50x.html {
root /usr/share/nginx/50x.html;
}
error_page 404 403 /40x.html;
location = /50x.html {
root /usr/share/nginx40x.html;
}
error_page 404 /404.html 可显示自定义404页面内容,正常返回404状态码。
error_page 404 = /404.html 可显示自定义404页面内容,但返回200状态码。
error_page 404 /404.php 如果是动态404错误页面,包含 header 代码(例如301跳转),将无法正常执行。正常返回404代码。
error_page 404 = /404.php 如果是动态404错误页面,包含 header 代码(例如301跳转),加等号配置可以正常执行,返回php中定义的状态码。但如果php中定义返回404状态码,404状态码可以正常返回,但无法显示自定义页面内容(出现系统默认404页面),这种情况可以考虑用410代码替代( header("HTTP/1.1 410 Gone"); 正常返回410状态码,且可正常显示自定义内容)。
ngx_http_gzip_module:#nginx的压缩模块
ngx_http_gzip_module模块是一个使用“gzip”方法压缩响应的过滤器。 这通常有助于将传输数据的大小减少一半甚至更多。
1、gzip on | off;
Enables or disables gzipping of responses.
2、gzip_comp_level level;
设置响应的gzip压缩级别。 可接受的值范围为1到9。
3、 gzip_disable regex ...;
对具有与任何指定正则表达式匹配的“User-Agent”标头字段的请求禁用gzipping响应。
4、 gzip_min_length length;
启用压缩功能的响应报文大小阈值(下限);
5、gzip_buffers number size;
支持实现压缩功能时为其配置的缓冲区数量及每个缓存区的大小;
6、gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any ...;
nginx作为代理服务器接收到从被代理服务器发送的响应报文后,在何种条件下启用压缩功能的;
off:对代理的请求不启用
no-cache, no-store,private:表示从被代理服务器收到的响应报文首部的Cache-Control的值为此三者中任何一个,则启用压缩功能;
示例:
gzip on;#开启压缩
gzip_comp_level 6;#压缩登记
gzip_min_length 64;#低于64字节不压缩
gzip_proxied any; #任何被代理的也需要压缩
gzip_types text/xml text/css application/javascript; #压缩类型
6,实现4个虚拟主机,混合使用三种类型的虚拟主机,仅开放给来自于本地网络中的主机访问
server {
listen 8088;
server_name www.stephenzhong.com;
proxy_set_header X-Real-IP $remote_addr;
location / {
proxy_pass http://172.17.0.2/;
}
location ~* \.(jpg|jepg|png) {
proxy_pass http://172.17.0.2;
}
}
server {
listen 8080;
server_name 192.168.1.198;
root "/web/nginx/html";
location / {
root "/web/nginx/html";
index index.html;
}
location = /ngx_status {
stub_status;
}
location /bbs/ {
rewrite ^/bbs/(.*)$ /froum/$1 break;
}
location ~* \.php$ {
fastcgi_pass 172.17.0.3:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /appdata$fastcgi_script_name;
include fastcgi_params;
}
}
mkdir /etc/nginx/certs
100 cd /etc/nginx/certs/
openssl genrsa-out nginx.key 2048
openssl genrsa -out nginx.key 2048 openssl req -new -x509 -key nginx.key -out nginx.cert -days 3650 -subj "/CN www.ilinux.io"
openssl req -new -x509 -key nginx.key -out nginx.cert -days 3650 -subj "/CN=www.ilinux.io"
生成秘钥并且自签名证书实现ssl
7,实现lnmp,提供多个虚拟主机
1)http,提供wordpress
wget http://nginx.org/download/nginx-1.14.2.tar.gz #下载nginx源码包
tar -zxf nginx-1.12.1.tar.gz #解压包
yum groupinstall "Development Tools" #下载编译包组
yum install openssl-devel pcre-devel libevent-devel -y #下载所需依赖的包
cd nginx-1.14.2 #进入解压目录
nginx-1.14.2]# ./configure \ #执行编译操作指定选项
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/lock/subsys/nginx.lock \
--user=nginx --group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_dav_module \
--with-threads \
--with-file-aio \
--with-http_stub_status_module
make && make install #编译并且复制文件到指定的目录下
nginx-1.14.2]# useradd nginx #同名组自动创建
因为在进行编译配置的时候我们就将nginx的二进制文件写到了“/usr/sbin/”下,所以我们可以直接使用命令来启动nginx:
nginx-1.14.2]# nginx
接下来我们使用命令来查看nginx是否启动成功:
nginx-1.14.1=2]# ss -tnl
使用命令停掉httpd(如果没启动就不用管了):
~]# service httpd stop
此时在真实主机上就可以通过浏览器访问“192.168.1.198”(这个IP是刚才虚拟机的IP)
这样的Nginx的配置就完成了,下面开始配置1个虚拟主机,1个虚拟主机的根目录我存放在“/myweb/wordpress
mkdir -pv /myweb/wordpress
nginx-1.12.1]# vim /etc/nginx/nginx.conf
在“http”的大括号中添加下面这条:
include /etc/nginx/conf/*.conf"
接下来我们在“/etc/nginx/conf/”下创建一个以“.conf”结尾的片段配置文件并写入内容:
server {
listen 80;
server_name myweb.wordpress.com;
location / {
root /myweb/wordpress;
index index.html;
}
}
保存并退出之后,使用“nginx -t”命令检查配置文件是否有错误,如果没有错误,就使用“nginx -s reload”命令重新载入配置:
wordpress需要依赖数据库,所以需要先安装数据库,我这里默认安装为mariadb
yum install mariadb-server
https://cn.wordpress.org/latest-zh_CN.zip
然后分别解压到各自对应的目录中:
~]# tar -zxf wordpress-4.9.4-zh_CN.tar.gz -C /myweb/wordpress/
systemctl start mariadb 启动数据库
CREATE USER 'wordpress'@'%' IDENTIFIED BY '123456'; 这种方法允许任何远程连接
[root@centos7 wordpress]# cp ../wordpress/wp-config-sample.php ./wp-config.php
[root@centos7 wordpress]# vim wp-config.php
先拷贝一份wordpress自带的配置文件,并修改其配置信息:
yum install php php-fpm && systemctl start php #安装并启动phpfpm协议
置完成之后,我们还需要回到刚才配置虚拟主机那里把刚才的配置增加几条选项,变成下面的样子(因为Nginx默认将PHP注释掉了,需要我们自己开启):
server {
listen 80;
server_name 192.168.1.198;
location / {
root /myweb/wordpress/wordpress;
index index.html index.php;
}
location ~ \.php$ {
root /myweb/wordpress/wordpress;
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
yum install php php-fpm && systemctl start php #安装并启动phpfpm协议
搭建成功
2)https,提供pma