基于Nginx+PHP驱动Web应用

配置文件与虚拟主机

查看Nginx的配置文件nginx.conf(通常位于/etc/nginx/nginx.conf):

user vagrant;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
        worker_connections 768;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        # server_tokens off;

        server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        ##
        # Gzip Settings
        ##

        gzip on;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

该配置文件中提供了Nginx服务器的一些基本配置,Nginx是由模块驱动的,负责HTTP服务的是http模块。

基于Nginx驱动的所有Web站点都是通过server模块以虚拟主机的方式配置在各自的配置文件中,然后在nginx.conf中通过include /etc/nginx/sites-enabled/*;这行代码引入的。

我们看下 Nginx 自带的一个虚拟主机配置 /etc/nginx/sites-enabled/default

server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        root /usr/share/nginx/html;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        }    
}

如果Nginx服务器没有配置其他站点,则访问IP地址解析到该服务器上的所有域名都会指向这个配置文件,因为这个配置文件监听端口上指定了default_server

由于是默认虚拟主机配置,所以一个Nginx服务器只允许配置一个标识为default_server的虚拟主机。如果配置了多个,启动Nginx的时候会报错。

查看一个laravel项目的配置文件

server {
    listen 80;
    listen 443 ssl http2;
    server_name .blog.test;
    root "/home/vagrant/code/blog/public";

    index index.html index.htm index.php;

    charset utf-8;



    location / {
        try_files $uri $uri/ /index.php?$query_string;

    }



    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/blog.test-error.log error;

    sendfile off;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;


        fastcgi_intercept_errors off;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
        fastcgi_connect_timeout 300;
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;
    }

    location ~ /\.ht {
        deny all;
    }

    ssl_certificate     /etc/nginx/ssl/blog.test.crt;
    ssl_certificate_key /etc/nginx/ssl/blog.test.key;
}

Nginx服务器支持几个Web站点,就配置几个虚拟主机,通常的做法是将虚拟主机配置到/etc/nginx/sites-available目录下,然后对于启用的站点,在/etc/nginx/sites-enabled目录下创建对应的软连接。

配置文件的含义及用途:

  • 监听端口(listen):本站点监听的端口号,一般默认为80;
  • 站点域名(server_name):本站点域名,由于一天服务器上搭建了多个站点,而TCP连接的标识中只有IP地址和端口号,服务器如何识别客户端访问的是哪个站点呢?HTTP/1.1的做法是要求请求首部中必须包含Host字段来指定访问的域名,Nginx在接收请求时,会将解析出来的Host首部字段值与虚拟主机的server_name值进行匹配,匹配成功则应用该虚拟主机中的配置。
  • 项目根目录(root):站点部署的目录,一般是入口索引文件所在的目录;
  • 索引文件:请求URL中未指定具体资源时默认的入口文件,可配置多个,然后以空格分割。
  • location 配置块:会与请求起始行中的相对 URL 路径进行匹配,匹配成功则应用对应配置块中的配置,location / {...} 可以匹配所有请求,try_files 会依次访问后面配置的每个路径,如果通过对应 URL 可以直接访问($uri),比如静态资源文件,则直接返回响应给客户端;否则尝试以目录方式访问($uri/);最后尝试访问 /index.php$is_args$args,即以 Laravel 入口文件 + 动态参数形式访问资源,由于该路径包含了 .php,所以会进入下一个匹配的 location 配置块 —— location ~ \.php$ {...},然后通过 FastCGI 网关(PHP-FPM)让后端 PHP 程序来处理动态请求。指定 PHP-FPM 进程时,可以通过 Unix 套接字,比如 unix:/run/php/php7.1-fpm.sock,也可以通过 IP 地址+端口号的形式,比如 http://127.0.0.1:9000,前者仅适用于 PHP-FPM 与 Nginx 运行在一台服务器,后者适用于所有场景,不过前者直接读取本地文件,没有额外的网络开销,因此从性能上来说更优,然后我们将请求的路径、参数传递给 PHP-FPM,同时设置缓存和超时配置;
  • 日志信息:可以通过 error_log 指定错误日志路径,access_log 指定访问日志路径。

请求处理与响应发送

建立连接

Nginx服务启动后会启动一个master进程和多个worker进程(一般与CPU个数相同),master主要负责处理Nginx主服务的启动、关闭与重载,以及维护worker进程的运行状态,具体的HTTP连接与请求处理工作由worker进程来完成,每个worker进程上可以处理多个连接请求,底层实现的原理是事件驱动和多路IO复用。

当我们在客户端浏览器输入应用 URL 进行访问时,在发送请求报文前,会先通过 DNS 查询域名对应的服务器 IP 地址(如果在本地 /etc/hosts 文件有定义,会直接从这里返回 IP 地址,不走 DNS 服务),对于 HTTP 应用来说,默认端口号是 80,有了对方的 IP 地址和端口号,就可以通过三次握手建立与对端 Web 服务器应用的 TCP 连接了,这个对端 Web 服务器应用正是 Nginx,Nginx 的 master 进程在接收客户端连接信号后会将这个网络事件发送给某个 worker 进程,由该 worker 进程来接管后续的连接建立和请求处理,经过这一步,就建立起了 Nginx 服务器与本地客户端的连接。

关于 Nginx 默认监听端口,也可以通过应用对应的 Nginx 虚拟主机配置文件进行修改,如果配置为其它端口号,需要在客户端访问该应用的时候手动指定,这样对用户来说不太方便,所以一般都使用默认值:

listen 80;

接收请求

Nginx的worker进程在与客户端建立HTTP连接后(这一步对应Socket编程中的accept操作),就开始从这条连接上读取请求报文数据(对应Socket编程中的read操作)并进行解析,将解析出的数据保存到Nginx对应的数据结构ngx_http_request_s中。

处理请求

Nginx能映射到对应的虚拟主机配置文件,主要依靠Nginx将从请求首部解析出的Host字段值与所有虚拟主机配置文件中的server_name配置项做对比。

通过root配置项可以获取到应用部署的根目录。在通过URL访问指定资源时,为了安全起见,我们并不会在请求中显示指定服务器资源的绝对路径,而是仅仅指定资源的相对路径,在与服务器上的root配置项拼接成对应资源的绝对路径。

构建& 发送响应

Nginx通过ngx_http_send_header方法构造HTTP响应的起始行、响应首部,并将响应头信息保存在ngx_http_request_sheaders_out数据结构中,然后通过 ngx_http_header_filter 方法按照 HTTP 规范将其序列化为字节流缓冲区,最后通过 ngx_http_write_filter 方法将响应头部发送出去

我们在 PHP 代码中通过 headerset_cookie 等网络函数设置的响应头也会通过 PHP-FPM 发送给 Nginx

posted @ 2020-09-14 18:20  _大可乐  阅读(209)  评论(0编辑  收藏  举报