Akka实战:再谈断点上传

去年的文章:《Akka实战:HTTP大文件断点上传、下载,秒传》 通过示例介绍了在Akka HTTP里怎样实现断点上传功能。通过一段时间的应用,发现了些问题,这篇文章再深入介绍下。在 https://github.com/yangbajing/scala-applications/tree/master/file-upload能找到完整的源码。

Nginx代理

Nginx代理时默认会在Nginx端收到完整的请求数据以后再将其转发到被代理的服务,这样会造成通过Akka HTTP实现的断点上传功能用不上。原因:

  1. 客户端断开或网络问题会造成Nginx端返回错误响应,根本就不向被代理的服务发送数据。
  2. 需要断点上传的一般都是大文件,若文件太大会使Nginx服务的资源造成不必要的浪费。因为我们使用Akka HTTP实现的断点上传是通过stream处理实时将上传数据存储到磁盘上的,这样可以保证在一定的内存使用情况下支持大文件上传。

对于第2点,就因为Nginx默认缓冲整个请求数据,造成我们第一版文件服务申请了一台64G内存的服务器(第一版未使用Akka HTTP来实现断点上传功能,是直接使用Spring写的一个文件服务)。

这个问题要解决也比较简单,在Nginx中添加如下配置即可:

1
2
3
client_max_body_size 4g;
proxy_request_buffering off;
proxy_http_version 1.1;
  • client_max_body_size 指令告诉Nginx在判断Content-Length头时,超过4G的请求才报错。
  • proxy_request_buffering 指令关闭Nginx的代理缓冲,将客户端的请求数据直接转发到被代码服务。
  • proxy_http_version 强制代理使用HTTP 1.1版本,否则通过proxy_request_buffering off在通过Chunk的方式进行的请求时还是会在Nginx端缓冲数据。

但请注意:Tengine(Taobao的Nginx发行版)或低于1.7.11的官方发行版是没法通过这种方式来调整的。

  1. proxy_request_buffering指令在1.7.11版才添加到官方Nginx。
  2. Tengine的proxy_request_buffering语言与官方的不一样,它始终会在Nginx端缓冲数据,区别是在内存缓冲还是在磁盘缓冲。

这种情况下需要将Akka HTTP服务直接开发出去,或通过TCP代理来转发请求到Akka HTTP。

Source[ByteString, Any]

TODO