深入理解 Golang HTTP Timeout

深入理解 Golang HTTP Timeout

背景

前段时间,线上服务器因为部分微服务提供的 HTTP API 响应慢,而我们没有用正确的姿势来处理 HTTP 超时(当然,还有少量 RPC 超时), 同时我们也没有服务降级策略和容灾机制,导致服务相继挂掉?。服务降级和容灾需要一段时间的架构改造,但是以正确的姿势使用 HTTP 超时确是马上可以习得的。

超时的本质

所有的 Timeout 都构建于 Golang 提供的 Set[Read|Write]Deadline 原语之上。

服务器超时

server timeout

  • ReadTimout 包括了TCP 消耗的时间,可以一定程度预防慢客户端和意外断开的客户端占用文件描述符
  • 对于 https请求,ReadTimeout 包括了 TLS 握手的时间;WriteTimeout 包括了 TLS握手、读取 Header 的时间(虚线部分), 而 http 请求只包括读取 body 和写 response 的时间。

此外,http.ListenAndServe, http.ListenAndServeTLS and http.Serve 等方法都没有设置超时,且无法设置超时。因此不适合直接用来提供公网服务。正确的姿势是:

客户端超时

client timeout

  • http.Client 会自动跟随重定向(301, 302), 重定向时间也会记入 http.Client.Timeout, 这点一定要注意。

取消一个 http request 有两种方式:

  1. Request.Cancel
  2. Context (Golang >= 1.7.0)

后一种因为可以传递 parent context, 因此可以做级联 cancel, 效果更佳。代码示例:

Credits

  1. The complete guide to Go net/http timeouts
  2. Go middleware for net.Conn tracking (Prometheus/trace)

GitLab搭建笔记

GitLab搭建笔记

GitLab使用Omnibus package搭建是分分钟就可以搞定的事情,但是我搭建的GitLab服务:

  1. 服务器在墙内;
  2. 服务器在一个路由器后面;

因此,浪费了一个晚上时间才把几分钟计划完成的事情搞定。这里给遇到类似情况的程序员做个简单的笔记,希望可以为他们节约一点时间吧。

服务器在墙内

Omnibus package(大小约340MB)通过AWS S3(AWS Region为美国新泽西)进行分发。墙内下载该package的速度极慢(电信百兆宽带下载速度约8KB/S),且部分时间段packages-gitlab-com.s3.amazonaws.com域名疑似被污染(四川成都地区)。因此,只好通过墙外的VPS中转下载。当前最新版(2015.08.30)为gitlab-ce_7.14.1-ce.0_amd64.deb. 上传了一份到百度网盘, 密码: qf38。阅兵在即,墙外要中转一个文件进来真是浪费生命,唉…

服务器在路由器后

这个比较好解决,需要映射两个端口。

  1. 映射Web端口: 映射路由器外部GitLab端口到GitLab配置端口(/etc/gitlab/gitlab.rb external_url ‘http://your-host:PORT’ );
  2. 映射SSH端口: 映射路由器外部GitLab SSH端口到22端口, 然后修改/etc/gitlab/gitlab.rb文件:
    • gitlab_rails[‘gitlab_shell_ssh_port’] = 路由器外部GitLab SSH端口
    • 注意:gitlab_rails[‘gitlab_shell_ssh_port’]只是配置代码库地址的端口,不会修改SSH真实使用的端口。
  3. sudo gitlab-ctl reconfigure

socket编程模型总结

2013年,做车联网OBD项目的时候,需要处理的基本问题是如何保持并维护大量车辆与服务器的网络连接(需要做到汽车时时在线,以实现部分对汽车远程控制的功能)。这也是网络编程中中的基本问题,前辈们也提出了多种网络编程模型解决此类问题:select, poll, epoll, I/O重叠,完成端口。

select, poll, epoll模型

需要强调的是,以上三个模型,其实都是同步I/O。

I/O重叠,完成端口模型

以上两种模型都是异步I/O。