我们每天都打开网页,让我们来看看,一次典型的网页请求的整个过程:
1, 用户端:
1.1 用户打开一个浏览器,在地址栏输入一个http/https url(这里不涉及ftp,stmp等协议),点回车;
1.2 浏览器截取出域名,尝试读取本地hosts文件, 查找hostname对应的ip服务器地址,查到第一个就返回,不继续向下查;
1.3 如果找不到,则从dns中查找,先从本机获取,如linux下
cat /etc/resolv.conf# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTENnameserver 127.0.1.1
这个看起来像localhost的地址究竟是什么意思?
因为 ubuntu下有一个本地的dns服务叫做dnsmasq,它是由NetworkManager控制的
ps -ef | grep dnsmasqnobody 2111 1165 0 11月09 ? 00:00:08 /usr/sbin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.1.1 --cache-size=0 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.dc80k2 29357 28457 0 22:23 pts/54 00:00:00 grep --color=auto dnsmasq
你就可以看到它监听的本地地址,--listen-address=127.0.1.1 (ubuntu12.04及之前的版本 是 127.0.0.1), 这个地址是一个本地回环地址
而你真实的dns服务器地址,是被这个服务管理维护着的
local process -> local dnsmasq -> router -> ISP dns
dns域名解析过程一般分为递归查询recursive query和迭代查询iterative query,获取到所输url的根域名和服务器ip;
1.4 请求的参数根据method方式不一样,get通过url, post通过request body携带过去。
1.5 请求到达ip所指向的服务器,由监听80端口的web server进行处理,有可能通过负载均衡LB或者反向代理(upstream),指向到某台特定的服务器;
2 服务器上的过程
2.1 服务器上的web server,如nginx, Apache, tengine等,通过读取它的配置,如 /etc/nginx/sites-available/default 或者 apache的httpd.conf配置,这里以nginx为例
#loaclhost server { listen 80; server_name localhost; root /opt/wwwroot/; index index.html index.htm index.php; location @rewrite { rewrite ^/(.*)$ /index.php?_url=/$1; } location / { allow all; } location ~ ^(.+\.php)(.*)$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $DOCUMENT_ROOT$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $DOCUMENT_ROOT/$fastcgi_script_name; include fastcgi_params; }}
我们一行一行来看看
server { ->表示这是一个server,可对外提供服务
listen 80; //该server监控的服务器端口,这里默认是80 server_name localhost; //server_name,就是url解析之后的根地址 root /opt/wwwroot/; //指向到的根目录 index index.html index.htm index.php; //指向到根目录下的任意文件location { //重写:
rewrite ^/(.*)$ /index.php?_url=/$1; }location / { //对url进行匹配
allow all; }location ~ ^(.+\.php)(.*)$ {
fastcgi_pass 127.0.0.1:9000; //后面的address为后端的fastcgi server的地址#fastcgi_pass unix:/run/php/php5.6-fpm.sock; //系统本身的socket管道
fastcgi_index index.php; // fastcgi默认的主页资源 fastcgi_split_path_info ^(.+\.php)(.*)$; //这里通过新版的nginx的fastcgi_split_path_info将请求的url后半部分进行split操作 fastcgi_param PATH_INFO $fastcgi_path_info; //参见以下示例 fastcgi_param PATH_TRANSLATED $DOCUMENT_ROOT$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $DOCUMENT_ROOT/$fastcgi_script_name; include fastcgi_params; } }示例:
请求的网址是/abc/index.php/def
DOCUMENT_ROOT的值是项目的根目录
PATH_INFO的值是/abc
SCRIPT_FILENAME的值是$doucment_root/abc/index.php SCRIPT_NAME /abc/index.php
具体的location匹配信息可参见: https://segmentfault.com/a/1190000002797606
2.2 web服务器将请求通过master-worker的方式,抛给对应的进程管理器,比如fastcgi_pass,如下:
fastcgi_pass一般有两种模式:
一种是TCP/IP模式,如本机的9000端口监听,也就是127.0.0.1:9000,或者其他机器的9000端口。
另一种是socket模式,由本机的php的socket管道进行监听,比如unix:/run/php/php5.6-fpm.sock。
当web server和脚本应用在运行在同一台机器上时,由于socket管道之间之间连通,相对于nginx=>socket=>TCP/IP=>socket=>php这种方式来说,路径更短,速度更快,推荐使用socket方式。当然,如果不在同一台机器上,则需要使用TCP/IP模式。
nginx进程
ps -aux | grep nginxroot 1646 0.0 0.0 123396 236 ? Ss 11月30 0:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;www-data 1649 0.0 0.0 123788 684 ? S 11月30 0:13 nginx: worker processwww-data 1651 0.0 0.0 123788 604 ? S 11月30 0:18 nginx: worker processwww-data 1652 0.0 0.0 123788 1236 ? S 11月30 0:05 nginx: worker processwww-data 1653 0.0 0.0 123788 1040 ? S 11月30 0:19 nginx: worker processroot 11563 0.0 0.0 4512 0 pts/0 Ss 11月30 0:00 /bin/sh -c service nginx start && service php7.0-fpm start && /bin/bash /bin/bashroot 11649 0.0 0.0 125120 32 ? Ss 11月30 0:00 nginx: master process /usr/sbin/nginxwww-data 11650 0.0 0.0 125444 88 ? S 11月30 0:02 nginx: worker processwww-data 11651 0.0 0.0 125444 0 ? S 11月30 0:21 nginx: worker processwww-data 11652 0.0 0.0 125444 8 ? S 11月30 0:18 nginx: worker processwww-data 11653 0.0 0.0 125444 0 ? S 11月30 0:21 nginx: worker process
php进程
ps -aux | grep phproot 1338 0.0 0.1 399000 9268 ? Ss 11月30 0:13 php-fpm: master process (/etc/php/5.6/fpm/php-fpm.conf)www-data 1771 0.0 0.2 403764 23084 ? S 11月30 0:00 php-fpm: pool wwwwww-data 1773 0.0 0.1 405616 15748 ? S 11月30 0:00 php-fpm: pool wwwc80k2 7137 0.0 0.0 9776 1644 ? S 11月30 0:18 /opt/phpstorm/bin/fsnotifier64root 11563 0.0 0.0 4512 0 pts/0 Ss 11月30 0:00 /bin/sh -c service nginx start && service php7.0-fpm start && /bin/bash /bin/bashroot 11791 0.0 0.0 366700 96 ? Ss 11月30 0:10 php-fpm: master process (/etc/php/7.1/fpm/php-fpm.conf)www-data 11792 0.0 0.0 374524 1584 ? S 11月30 0:03 php-fpm: pool wwwwww-data 11793 0.0 0.0 374504 1880 ? S 11月30 0:02 php-fpm: pool www
如果通过TCP/IP协议,三次握手四次挥手,滑动窗口控制数据传输,标准输入,输出等。同时,nginx里面的proxy_connect_timeout, proxy_read_timeout, proxy_send_out等参数控制连接的时长,返回相应的http状态码。
而socket的方式,其实是对TCP/IP的抽象,通过内置的connect, listen, accept, send, read, write等方法来实现数据的传输。
2.3 fastcgi收到请求后,会wrap出php-fpm进程,然后通过master-worker方式抛给对应的php-fpm worker进行处理。而php-fpm启动的过程,则通过调用Zend engine内核中的SAPI模块进行处理,对php脚本文件进行解析(包括词法分析,语法规则等),将它们生成可以直接运行的中间代码,也称为操作码(Operate Code,Opcode),如果开启了Opcache扩展,则会对解析后的操作码进行缓存。详细的php脚本命周期会另外写文章进行阐述。
2.4 请求携带的参数也通过fastcgi_params传递到php脚本。
2.5 php脚本返回数据,然后走一个反向的路径,一直将数据传输到浏览器客户端。进行页面的展示和渲染。