【问题标题】:NGINX SSI with Symfony and PHP-FPM带有 Symfony 和 PHP-FPM 的 NGINX SSI
【发布时间】:2020-11-08 01:54:21
【问题描述】:

我需要你的帮助来处理一个用作技术堆栈的应用程序:

  • Docker NGINX
  • 带有 PHP-FPM 和 Symfony 的 DOCKER

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

所以我尝试按照文档中的说明使用 SSI(服务器端包含):https://symfony.com/doc/current/http_cache/ssi.html

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

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;
}

如您所见,我在网络服务器上启用了 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命令是为了测试cache和iss是否正常工作...

更多信息:

在文档中阅读此内容后,我在供应商处看到:render_ssi 确保仅当请求具有诸如 Surrogate-Capability: device="SSI/1.0" 之类的标头要求时才会生成 SSI 指令(通常由网络服务器)。否则会直接嵌入子响应。

所以我尝试在代码中找到决定是否使用 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

【问题讨论】:

    标签: php docker symfony nginx ssi


    【解决方案1】:

    我正在私下分享我之前给你的解决方案,所以每个人都可以访问它。

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-11-26
      • 2013-02-14
      • 1970-01-01
      • 1970-01-01
      • 2017-05-11
      • 2017-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多