Nginx 反向代理腾讯云 COS 的一个坑

有一个朋友开发的手机 app,把大量文件都保存在腾讯云 COS 上,然后通过 CDN 分发。

最近有一个特殊的需求,希望通过 CVM 来提供部分 COS 文件的访问。因为服务器用的是 Nginx ,所以事情也很简单:

1、 到COS的管理页面上查询一下内网访问域名。

2、 给 nginx 增加一个标准的 upstream 配置,上游指向腾讯云 COS 的内网域名。

照理说,配置好域名解析就可以开始工作了。但是一开始工作就出现很奇怪的现象:下载开始很快,随后变得很慢,最终有很大概率失败。

首先排除网络原因的可能性。登录服务器用 wget 通过访问本机 localhost 验证:

现象就是前面都下载的飞快,到了最后一部分就突然下载不动了。

打开 nginx 的 error_log,发现了 upstream timed out (Connection timed out) 错误。

再排除 COS 有问题的可能性:

现在问题就很诡异了:上游没有问题,经过反向代理后文件的前面一大部分也都没有问题,就是最后一小截文件要等待很久很久,并且发生了 upstream timed out 超时。

通过肥龙找到了熟悉nginx的ares同学协助抓包,才定位到了这个问题:

这里的 UA 是 wget,wget 默认使用的是 http1.0 协议。当前服务器使用的 nginx 是1.0.15这个比较古老的稳定版,还不支持 proxy_http_version 1.1这样的参数(要到1.1.4版本以后才支持)。所以也是采用http1.0协议代理了请求。

照理说 innercos 服务接到这样的请求应该按照 http1.0 的方式返回数据,但是我们看到服务器返回了 HTTP/1.1 200 OK 。也就是说不管客户端支持什么 http 版本 cos 服务总是用 http1.1协议来工作。

http1.1有一个重要的特性是 keep-alive,也就是说 http 数据传输完毕后 TCP 连接继续保持一段时间不断开,可以给后续的 http 请求重用。而 http1.0的客户端原则上并不知道 http1.1的这套原理。所以对于这个老版本的 Nginx 来讲,它收到完整的数据以后,看到 TCP 链接一直没有断开,以为 upstream 还有话说,就一直挂在那里,等上游继续送数据,直到上游连接超时,才在 error_log 里面记录一个 timed out 错误,然后断开下游的连接。

把 proxy_buffering 关掉让上下游直接对上话可以绕过这个问题,但是有附带的损失。更好的办法是把nginx升级到1.1.4以上的版本,并且开启proxy_http_version 1.1 。

至此圆满解决。

总结一下,腾讯云COS的后台服务假设客户端都支持http1.1协议,对http1.0协议没有做很好的兼容,而腾讯云CVM提供的带Nginx的系统镜像里面的Nginx版本又有点儿老旧了,proxy还只能工作在http1.0上,导致了这个问题的出现。

续集

这样做还有可能带来的一个副作用是,媒体文件用Safari浏览器播放的时候会变成“实时广播”模式

也就是说不显示总长度,不显示进度条,无法前进后退,只能顺序播放。

正常的媒体问题件应该是这样的:

这个问题在Stack Overflow上有网友分析过:

It appears that Safari doesn`t trust a server when it says it can provide partial content: Safari (or technically Quicktime I think) requests bytes 0-1 of a file with a range header like this:

Range: bytes=0-1

as its first request to the file. If the server returns the whole file - it treats the file as a 'stream', which has no beginning or end.

看起来safari并不信任服务器送出的内容是一个文件,自己要先请求一下文件的头两个字节看看。如果头两个字节不能正确返回,就断定服务器在进行“实时广播”。

在这种回源访问方式下,由于COS本身是支持分段下载的,所以nginx回源到COS服务器的时候,服务器总是会返回一个Content-Range: bytes 0-xxxx/xxxx 表示整个文件是作为一个大分段返回的。而Nginx发现用户请求的是头两个字节,字节也要返回一个Content-Range: bytes 0-1/xxxx 的http头部。此时nginx发现源服务器输出了同名的http头,就采取了一个最简单的处理策略:把两个头部合并一下,返回了这样一个自相矛盾的http头部:

Content-Range: bytes 0-xxxx/xxxx,bytes 0-1/xxxx

这样在safari浏览器看起来当然是一个“乱来”的服务器,于是safari果断决定吧这个服务器当成是直播服务,进入“实时广播”模式。

解决的方案也很简单,在nginx配置文件中吧来自源服务器的Content-Range头部隐藏掉:

proxy_hide_header Content-Range;

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏全华班

互联网云快速开发框架

--我的信息 --我的通告 --文件 2、会员 --用户中心 --用户地图 --积分活动平台 --极光推送 3、营销 --营销活动 --活动汇 --现场活动平台...

791
来自专栏phodal

如何解构单体前端应用——前端应用的微服务式拆分

1292
来自专栏安智客

TUI设计概要

TUI是TEE的一个重要基础模块。最初人们认识了解TEE最直观的展示就是TUI,早在指纹识别成为手机的标配之前,TEE的主要应用是围绕着TUI进行,但由于普适性...

1134
来自专栏一个会写诗的程序员的博客

JVM语言生态结构原理图 从Java,Kotlin,Scala,Groovy等语言的编译、执行全过程图示解析

JVM语言生态结构原理图 从Java,Kotlin,Scala,Groovy等语言的编译、执行全过程图示解析

673
来自专栏Java学习网

Web开发在过去20多年时间里如何改变了我

web在过去20年时间里改变得相当快。越来越多的逻辑从服务器端移动到了客户端。不但需要在客户端编写更复杂的JavaScript代码,而且最近几年还发生了一些奇特...

3206
来自专栏即时通讯技术

全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等

本文引用了腾讯工程师廖伟健发表于“鹅厂网事”公众号上的《【鹅厂网事】全局精确流量调度新思路-HttpDNS服务详解》一文部分内容,感谢原作者的分享。

1074
来自专栏EAWorld

Java开发者的PaaS指南

本文获得codingthearchitecture.com授权翻译发表,转载需要注明来自公众号EAWorld。

1203
来自专栏大魏分享(微信公众号:david-share)

容器的超融合 | 容器的适用场景的讨论、以及容器超融合的探究

容器适合的应用 笔者在IT行业从业十余年,有幸亲自经历到了IT基础架构的三个阶段和两次大的变革。三个阶段分别是:硬件定义数据中心(HDDC)、软件定义数据中心...

3315

为什么说Linux容器对于物联网而言很重要

Linux容器已成为云开发和部署工作流中的标准工具。使用它的好处有很多,包括跨平台的可移植性,最小的开销,以及开发人员对他们代码运行方式的更多控制。容器的普及率...

3246
来自专栏Hongten

通过手机远程开/关机

作为开发人员中的一员,我想我们都有这样的一个想法,就是让我们写的代码去实现我们生活中的一些日常事务,如远程开/关机。

1752

扫码关注云+社区