Skip to content

Offload媒体文件及Nginx配置

umonaca edited this page Mar 6, 2021 · 11 revisions

简介

以下配置适用于v0.6.0 RC 5,目前尚未发布。
目前默认情况下Express Server既负责API,又负责媒体文件,由于Javascript实际上是单线程的,当负载较高(非个人用)的情况下对服务器压力很大。v0.6.0 RC 5提供一个offload功能用来解决这个问题,可以不再使用Express来提供媒体文件,而是302重定向到指定的路由目前已经重构,使用由后端分发的路由。这样如果有Nginx等提供这些媒体路由里的静态文件,就可以极大提高负载能力。offload以后Kikoeru主程序就可以只负责API路由。

2021-03-06 更新

后端commit 159af84
前端commit f2a3552
增加两个设置项,今后前端将直接使用后端发送的路由。不过后端仍然保留原路由在offload开启时的302跳转。

  mediaStreamBaseUrl: '/api/media/stream/',
  mediaDownloadBaseUrl: '/api/media/download/',

准备工作

  1. 挂载所有媒体文件。假定我们有两个音声库,config/config.json中有以下内容
"rootFolders": [
	{
		"name": "VoiceWork",
		"path": "/mnt/media/Voices"
	},
	{
		"name": "chs_voices",
		"path": "/mnt/s3/chs"
	}
]

那么按照以下方式挂载:

/storage/VoiceWork -> /mnt/media/Voices
/storage/chs_voices -> /mnt/s3/chs

格式是某个统一的挂载点/{rootFolders.name}指向其实际物理路径。路径最后的名称必须与相应的{rootFolders.name}一致。

  1. 打开config/config.json,按下面调整以下三个字段:
  offloadMedia: true,
  offloadStreamPath: '/media/stream/',
  offloadDownloadPath: '/media/download/'

这样在线访问相应的媒体文件时会重定向到对应的路径上。

比如在VoiceWork音声库中,根目录下有RJ123456,其内部根目录有01.mp3,那么这个媒体文件的URL就是

https://example.com/media/stream/VoiceWork/RJ123456/01.mp3
https://example.com/media/download/VoiceWork/RJ123456/01.mp3

实际访问的物理路径都是

/storage/VoiceWork/RJ123456/01.mp3

修改配置文件请尽量让offloadStreamPathoffloadDownloadPath/media/开头,原因是如果你使用PWA,Service Worker里设置了除外路径/api//media/,使用其它路径某些情况下(比如浏览器新窗口打开)会导致Service Worker拦截路径显示404。

  1. 配置Nginx。以Ubuntu为例,新建文件/etc/nginx/site-available/kikoeru.conf,写入以下内容:
map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

upstream backend {
    server 127.0.0.1:8888;
}

upstream streaming {
    server 127.0.0.1:8888;
}

server {
    listen 80;
    listen [::]:80;
    server_name kikoeru.com;
    root /location/to/your/source/code/kikoeru-express/dist;
    # If you are using Let's Encrypt
    location /.well-known/acme-challenge/ { allow all; }
    # Redirect to HTTPS
    location / { return 301 https://$host$request_uri; }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name kikoeru.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    ssl_certificate     /etc/letsencrypt/live/kikoeru.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/kikoeru.com/privkey.pem;

    sendfile on;
    
    root /location/to/your/source/code/kikoeru-express/dist;
    index index.html;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000";

    location / {
        # fall back to SPA
        try_files $uri /index.html;
    }

    location /api/ {
        proxy_set_header Host $host;	    
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://backend;
        proxy_buffering on;
        proxy_redirect off;
	# Note: /api/ does not contain socket.io yet, but this may change in future
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_cache off;

        tcp_nodelay on;
    }

    location /media/stream/ {
	# Location to your storage folder on your disk
        alias /storage/;
        add_header Strict-Transport-Security "max-age=31536000";
        add_header Cache-Control "public, max-age=31536000, immutable";
        try_files $uri =404;
    }

    location /media/download/ {
	# Location to your storage folder on your disk
        alias /storage/;
        add_header Strict-Transport-Security "max-age=31536000";
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Content-Disposition 'attachment';
        try_files $uri =404;
    }

    location /socket.io {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://streaming;
    }
}

注意很多路径的末尾都要求有/, nginx里有无末尾/是不一样的。以上配置假定你在使用Let's Encrypt的证书。然后建立软链接

ln -s /etc/nginx/sites-available/kikoeru.conf /etc/nginx/sites-enabled/kikoeru.conf

使用nginx -t检查是否有语法错误,然后nginx -s reload

  • 签发证书的方法:可以采用certbot或者acme.sh。 用certbot需要有root或sudoer权限
certbot --nginx -d example.com

笔者曾经遇到过某个新版本Nginx的bug,必须先有一个证书才能nginx reload,但颁发证书前又没有证书,颁发证书验证又需要打开Nginx。这个时候可以先保证其它部分在HTTP下是正确可用的,然后先不重启Nginx先签发证书,再调整配置文件里的证书路径,最后重启nginx。

  • 关于两个路由的说明:

/media/stream是用来在线播放的,/media/download是用来下载的,它们都指向相同的文件,只不过后者有Content-Disposition header。Kikoeru内部播放器播放时,使用的是/api/media/stream/:id/:index?token=xxxx。前面设置offloadMedia: true以后,就会302跳转到/media/stream/相对路径,从而使得HTML 5播放器能播放它。Kikoeru曲目上右键下载时,使用的是/api/media/download/:id/:index?token=xxxx,同样在开启offloadMedia: true以后,就会302跳转到/media/down/相对路径。遇到问题可以通过观察浏览器控制台报错来调整修复。

  1. 设置MIME类型

Nginx默认的MIME类型列表非常古老,许多Kikoeru支持的格式并不在其中。请编辑/etc/nginx/mime.types,确保程序源代码中filesystem/utils.js中列出的格式全部支持。以nginx/1.18.0 (Ubuntu)为例,需要添加以下几行:

audio/wav    wav;
audio/ogg    opus;
audio/aac    aac;
audio/flac    flac;

这样浏览器访问时会得到正确的Content-Type。

常见MIME类型列表:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types

已知Safari存在一些问题,比如对于flac文件,设置audio/x-flac会导致Safari无法识别和播放,所以这里设定为audio/flac。对于Safari还有一个扩展名容易出问题:m4a。nginx/1.18.0 (Ubuntu)默认为audio/x-m4a,如果遇到问题请改为audio/mp4,不影响其它浏览器。

  1. 设置behindProxy: true

当使用Nginx反代时应当在Kikoeru的config.json里开启这个选项。这个设置启用以后,express可以正确识别IP地址,而不是全部视为127.0.0.1。

这一功能目前还没有作用,将来加入express-brute和限流组件以后会有用。