Nginx的HTTP2 PUSH与开启WordPress支持

扯一下

都知道 HTTP 协议都是 Client Request,然后 Server Response 这种一来一回的模式,并且一次请求都要重开一个连接.而 HTTP/2 的第一大特性就是真正意义上的复用一个链接,这减轻了服务端的性能压力;然后 HTTP/2 PUSH 这可以由服务端主动同时推送多个资源,大大加快了一个有大量资源页面的打开速度.

网上有好多关于HTTP/2的介绍,前不久 Nginx 终于宣布在 1.13.9 起加入 HTTP/2 PUSH 的支持,HTTP 服务端的主动推送,

如果是手动编译 Nginx 程序的话,需要添加参数--with-http_v2_modul,这样才可以启用ngx_http_v2_module模块.而我这使用的 Nginx 官方的 Docker Image: nginx:alpine,重新 docker pull nginx:alpine更新最新的即可.

这次加入了 3 个参数:

Syntax: http2_max_concurrent_pushes number;
Default:
http2_max_concurrent_pushes 10;
Context: http, server
This directive appeared in version 1.13.9.

很明显,这个是限制 Server 侧主动推送资源时的并发上限值.

Syntax: http2_push uri | off;
Default:
http2_push off;
Context: http, server, location
This directive appeared in version 1.13.9.

这个http2_push是指定要主动推送的资源,比如说指定了root /var/www/site/为网站根目录,想要主动推送网站根目录下的 JS 文件,绝对路径为/var/www/site/js/jquery.js/var/www/site/js/diy.js, 则可以写成:

  http2_push /js/jquery.js;
  http2_push /js/diy.js;

Syntax: http2_push_preload on | off;
Default:
http2_push_preload off;
Context: http, server, location
This directive appeared in version 1.13.9.

这个http2_push_preload是为了解决动态页面下的资源推送问题.后端服务在响应请求时,在响应的Header中加入Link字段,然后 Nginx 拿到后就会主动推送 Link 中标记的资源,这个是W3C制定的 HTTP/2 协议标准中的一项功能.

显然,相较于http2_push,明显http2_push_preload更给力.因为如果你要用http2_push,先不说资源变动的问题,就说你一项项手动添加,都是个麻烦事.

WordPress设置

好的,不扯.

我去年就把博客转到 Docker 容器中运行了,现在docker-compose.yaml的配置大致如下:

version: '2'
services:
  db:
    image: mysql:latest
    restart: always
    command: '--default-authentication-plugin=mysql_native_password'
    volumes:
    - ./mysql:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: wordpress  
  wordpress:
    depends_on:
    - db
    image: wordpress:fpm
    container_name: wordpress
    restart: always
    logging:
      options:
        max-size: "2m"
        max-file: "5"
    volumes:
    - ./wordpress:/var/www/html
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: root
      WORDPRESS_DB_PASSWORD: password
      WORDPRESS_DB_NAME: wordpress
  nginx:
    depends_on:
    - wordpress
    image: nginx:alpine
    restart: always
    container_name: nginx
    logging:
      options:
        max-size: "2m"
        max-file: "5"
    ports:
    - "80:80"
    - "443:443"
    volumes:
    - ./wordpress:/var/www/html
    - ./conf/default.conf:/etc/nginx/conf.d/default.conf

编辑给 Nginx 的default.conf文件,直接在server段内添加http2_push_preload on:

server {
  listen 443 ssl http2 fastopen=3 reusport;
  server_name xxx;
  # ...
  add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;
  # ...
  http2_push_preload on;
  # ...
}

最后编辑 WordPress 使用的主题中的 functions.php,添加如下内容:

# this for http2 push preload
function add_http2_push() {
    push_res = array(
        # 这里要注意,如果资源超过了 10 个, 那么要将参数 http2_max_concurrent_pushes 改大
        # 并且如果是css文件的话,并不建议push,除非css真的大且多,因为浏览器拿到页面的第一时间就去获取css文件,因此延迟是可以接收的,而js文件的话,可以加属性里添加 async 与 defer 进行异步和延后加载.
        '</wp-includes/css/dist/block-library/style.min.css?ver=1.2.3>; as=style; rel=preload',
        '</wp-includes/js/wp-emoji-release.min.js?ver=1.2.3>; as=script; rel=preload',
        '</wp-content/themes/jinrishici.js>; as=script; rel=preload',
        '</wp-includes/js/wp-embed.min.js?ver=1.2.3>; as=script; rel=preload'
    );preload_link = join(', ', push_res );
    header('link: '.preload_link);
}

add_action( 'send_headers', 'add_http2_push' );

后记

仔细研究了一番 Nginx 的 HTTP/2 ,目前 Nginx 的转发代理却未能支持 HTTP/2(不是stream模式的TCP反代),并且看官方的回复也不准备支持,所以说大概率也不会支持 H2C(HTTP/2 over TCP).

这个功能其实很重要,Nginx 反向代理的超高性能使得它的用户数十分庞大,而许多后端项目在传输中都希望开启HTTP/2,如果能加入 HTTP/2 流量转发的支持,那么可以直接在 Nginx 这中间层进行一些自定义的操作,比如 Header 的自定义动作,又或者一些统计什么的.

BTW, Apache 和 Caddy 是支持 HTTP/2 协议的流量转发代理.并且,gRPC(基于HTTP/2 )的传输代理却早在Nginx 1.13.10中被支持了. 但是真的很难理解 Nginx 开发者是怎么想的.