具有Symfony和PHP-FPM的NGINX SSI

问题描述

在需要用作技术堆栈的应用程序方面,我需要您的帮助:

  • DOCKER Nginx
  • 具有PHP-FPM和Symfony的DOCKER

我想将页面分成不同的部分并缓存其中的一些部分,因为生成速度很慢。

因此,我尝试按照文档https://symfony.com/doc/current/http_cache/ssi.html

中的说明使用SSI(服务器端包含)

这是我的码头工人的配置:

Nginx

FROM Nginx:1.19.2
copY docker-compose/Nginx /
ADD docker-compose/Nginx/Nginx.conf /etc/Nginx/Nginx.conf
ADD docker-compose/Nginx/symfony.dev.conf /etc/Nginx/conf.d/default.conf

配置文件

Nginx.conf

user www-data;
worker_processes 4;
pid /run/Nginx.pid;

events {
  worker_connections  2048;
  multi_accept on;
  use epoll;
}

http {
  server_tokens on;
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 15;
  types_hash_max_size 2048;
  include /etc/Nginx/mime.types;
  default_type application/octet-stream;
  access_log on;
  error_log on;
  access_log /dev/stdout;
  error_log /dev/stdout;
  gzip on;
  gzip_disable "msie6";
  include /etc/Nginx/conf.d/*.conf;
  include /etc/Nginx/sites-enabled/*;
  open_file_cache max=300;
  client_body_temp_path /tmp 1 2;
  client_body_buffer_size 256k;
  client_body_in_file_only off;
}

symfony.dev.conf

proxy_cache_path  /tmp/Nginx levels=1:2   keys_zone=default:10m;

server {
    listen 80;
    root /var/www/html/symfony/public;
    client_max_body_size 40M;

    location = /health {
        return 200 "healthy\n";
    }

    location = /ping {
        return 200 "pong\n";
    }

    location / {
        ssi on;
        try_files $uri /index.PHP$is_args$args;
    }

    location ~ ^/index\.PHP(/|$) {
        ssi on;
        fastcgi_pass PHP-fpm:9000;
        fastcgi_split_path_info ^(.+\.PHP)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $document_root;
        fastcgi_param REQUEST_METHOD  $request_method;
        fastcgi_param CONTENT_TYPE    $content_type;
        fastcgi_param CONTENT_LENGTH  $content_length;
        fastcgi_read_timeout 300;
        internal;
    }

    location ~ \.PHP$ {
        return 404;
    }

    location /status {
        access_log off;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass PHP-fpm:9000;
        fastcgi_index status.html;
    }

    error_log /var/log/Nginx/error.log;
    access_log /var/log/Nginx/access.log;
}

如您所见,我在Web服务器上启用了SSI。

此外,我将其添加到框架的配置中(例如doc):

framework:
    ssi: { enabled: true }
    fragments: { path: /_fragment }

在模板/控制器中,我遵循文档:

模板

    {{ render_ssi(controller('App\\Controller\\Pages\\HomeController::xxxx')) }}

控制器

    public function xxxx() {
        sleep(2);
        $response = $this->render('pages/home/xxxx.html.twig',[
        ]);
        $response->setSharedMaxAge(Constants::SSI_CACHE_TTL);
        return $response;
    }

sleep命令用于测试高速缓存和iss是否正常工作...

更多信息:

我在阅读文档后看到了供应商: render_ssi确保仅当请求具有诸如Surrogate-Capability这样的标头要求时才生成SSI指令:device =“ SSI / 1.0”(通常由网络服务器)。否则,它将直接嵌入子响应。

所以我尝试在代码中找到决定是否使用SSI的块:

vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.PHP

在此行:

    /**
     * {@inheritdoc}
     */
    public function hasSurrogateCapability(Request $request)
    {
        if (null === $value = $request->headers->get('Surrogate-Capability')) {
            return false;
        }

        return false !== strpos($value,sprintf('%s/1.0',strtoupper($this->getName())));
    }

因此,我认为我的网络服务器不会将ISS-Header(Surrogate-Capability)发送到我的PHP-fpm。

对于如何进行测试我没有任何想法...

谢谢大家能帮我...

致谢

编辑:

我创建的存储库以前遇到过相同的问题,您可以直接对其进行测试。

https://github.com/alessandro-candon/ssi-symfony

解决方法

我正在分享我以前私下给您的解决方案,因此每个人都可以使用它。

  1. 首先,由于您使用的是 fastcgi ,因此必须使用fastcgi_cache_*指令,

例如:

fastcgi_cache_path  /tmp/nginx  levels=1:2  keys_zone=foobar:10m;

代替

proxy_cache_path    /tmp/nginx  levels=1:2  keys_zone=foobar:10m;
  1. 由于Symfony使用唯一的查询字符串标识ssi片段,因此您必须在高速缓存键参数中包含查询字符串,

否则,您将始终缓存整个页面。您可以使用:

fastcgi_cache_key   $scheme://$host$saved_uri$is_args$args;
  1. 从您在https://github.com/alessandro-candon/ssi-symfony上的代码中,

我看到你打电话:

http://localhost:8101/home

它将匹配“ /”位置,然后nginx会向 ^ / index.php(/ | $)

问题在于,这样,当前的$uri变量将被更改为“ index.php”,因此您将丢失“ / home”并且无法将其传递给Symfony(在该处进行处理)通过Symfony路由)。要解决此问题,请将其保存到自定义的nginx变量中:

set $saved_uri $uri;

然后,将其传递给fastcgi:

fastcgi_param REQUEST_URI  $saved_uri;

注意默认情况下,REQUEST_URI fastcgi参数设置为$request_uri。如果您不更改它,Symfony也将始终收到curl(“ / home”)为ssi片段请求提供的路径!因此,在解决ssi包含项时,您将陷入无限循环。(请参阅:Wrong cache key for SSI-subrequests with FastCGI

我建议您尽快添加默认的 fastcgi_params ,以便以后可以覆盖它们。如果将 include fastcgi_params 放在配置的底部,则默认值将覆盖您的自定义值。

  1. 您还必须考虑到,在内部请求ssi包含时,nginx 不会更新$uri变量。要解决此问题,请显式更新它。 (注意:由于先前描述的问题,这里我使用的是$saved_uri而不是$uri)。假设您正在使用 _fragment 来标识Symfony生成的ssi框架的路径,

您需要添加:

set $saved_uri /_fragment;
  1. 关于SSI标头:告诉Symfony nginx已启用ssi,ssi on指令还不够,因为nginx不会自动发送 Surrogate-Capability:device =“ SSI / 1.0“ 标头指向Symfony。

为此,请使用:

fastcgi_param HTTP_SURROGATE_CAPABILITY "device=\"SSI/1.0\"";
  1. 最后但并非最不重要的一点,请记住,定义缓存路径还不够,

您还需要告诉nginx使用它:

fastcgi_cache foobar;

最后,完整的配置将是:

fastcgi_cache_path  /tmp/nginx  levels=1:2  keys_zone=foobar:10m;
fastcgi_cache_key   $scheme://$host$saved_uri$is_args$args;  # The query string must be used here too,because Symfony uses it to identify the ssi fragment

server {
    listen 80;
    root /var/www/html/symfony/public;
    client_max_body_size 40M;

    include fastcgi_params;  # We must put this here ahead,to let locations override the params

    location = /health {
        return 200 "healthy\n";
    }

    location = /ping {
        return 200 "pong\n";
    }

    location /_fragment {
        set $saved_uri /_fragment;  # We hardcode the value because internal ssi requests DO NOT update the $uri variable !
        try_files $uri /index.php$is_args$args;
        internal;
    }

    location / {
         set $saved_uri $uri;  # We need this because the $uri is renamed later when making the internal request towards "index.php",so we would lose the original request !
         try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {

        fastcgi_cache foobar;  # Remember to not use the directive "proxy_cache" fastcgi
        add_header X-Cache-Status $upstream_cache_status;  # Used for debugging
                                                           # NOTE nginx<->Symfony cache is NOT considered in this

        fastcgi_param HTTP_SURROGATE_CAPABILITY "device=\"SSI/1.0\"";  # Nginx doesn't pass this http header to Symfony even if ssi is on,but Symfony needs it to know if the proxy is able to use ssi
        ssi on;

        fastcgi_param REQUEST_URI  $saved_uri;  # IMPORTANT The included default "fastcgi_params" uses $request_uri,so internal requests are skipped ! This causes an infinite loop because of ssi inclusion.
        fastcgi_param QUERY_STRING $args;  # For some reason,we need to pass it again even if the included default "fastcgi_params" looks correct

        fastcgi_pass php-fpm:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT   $document_root;
        fastcgi_param REQUEST_METHOD  $request_method;
        fastcgi_param CONTENT_TYPE    $content_type;
        fastcgi_param CONTENT_LENGTH  $content_length;
        fastcgi_read_timeout 300;
        internal;
    }

    location ~ \.php$ {
        return 404;
    }

    location /status {
        access_log off;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass php-fpm:9000;
        fastcgi_index status.html;
    }

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