本文来自董神,一个和曹大谈笑风生的男人:

服务为什么需要 timeout 呢?提前释放资源。

记得在上家公司时,一个 python 服务与公网交互,request 库发出去的请求没有设置 timeout... 而且还是个定时任务,占用了超多 fd。

同时微服务场景下某下游的服务阻塞卡顿,这样会造成他的级联上下游都雪崩了。

语言层面:对于使用线程池的语言,会消耗所有线程,worker 不够用。其实对于 Go 来说,创建大量 goroutine 也会有 runtime 开销的, 只是慢性死亡罢了。

内核层面:还有一点超时配置的必要性,如果某服务挂了,那么内核会帮忙收尾,根据情况或走 RST 或走 FIN,访问者就知道连接关了。但如果主机挂了,或者中间网络设备挂了,客户端没有超时配置,就只能通过 tcp keepalive 来判断死链接,按照默认内核配置语言两个多小时,文末提到 redis 就是例子。

Latency

业界都用 P99 分位来衡量服务的 latency,即使这样如果 QPS 非常高,另外 1% 的请求也会出现 long tail。再来看几个不同侧重点的概念:

Server Side P99 统计的只是 server handler 处理时间。

Client P99 =  client framework 时间 + client 内核处理时间 + 网络传输时间 + server 处理时间。

当你发现 latency 比较高,想去 challenge 下游时,请对好口径。通常 client p99 > server p99。

这还是普通的 server/client 模式,如果中间涉及了 lb, 或是 mesh 排查问题更要命。

可观测性

现在都是微服务场景,一个订单全链路涉及几十个服务,查起问题非常困难,所以分布式的 tracing 系统非常重要。

另外现在也都拥抱云原生环境,如果引入 service mesh 的话更难以排查问题。

一般 tracing 系统都是根据 google 论文 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure[1]发展而来的。

除了自己造轮子,主流的有zipkin[2], opentelemetry[3]

底层实现

定时器这块业务早有标准实现:小顶堆, 红黑树时间轮。感兴趣的同学可以搜索相关文章

原理不难,但是有公司面试都要求手写红黑树,这就过份了吧。

Linux 内核和 Nginx 的定时器采用了 红黑树 实现,好多长连接系统多采用 时间轮。

Go 使用 小顶堆,四叉堆,比较矮胖,不是最朴素的二叉堆。

最早版本只有一个 timer 堆,所以性能非常差,精度也有问题。一般都用户实现多堆,或是用时间轮实现。这方面的轮子比写公众号的码农都多 ^_^

后来经过优化 Go 内置多堆实现,每个 P 一个 timer 堆,性能好了很多。注意,Go 的 conn timeout 是通过用户层 timer 实现的,而不是内核的 setsockopt。

HTTP

这里要区分 http1 和 http2,以前写过一篇 HOL blocking 的文章,感兴趣可以翻下历史

Http1 如果超时到了,那么底层库是要关闭 tcp connection 的,强制丢弃未读到的数据,这时会产生大量的 timewait,要注意。

但是对于 Http2 来说,虚拟出来了 stream,做到了多路复用,只要关闭 stream 即可,底层 socket 还可以正常使用。

对于 Go Http 还有一个坑,可以参考 i/o timeout,希望你不要踩到这个net/http包的坑。

func init() {tr = &http.Transport{MaxIdleConns: 100,Dial: func(netw, addr string) (net.Conn, error) {conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时if err != nil {return nil, err}err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时if err != nil {return nil, err}return conn, nil},}
}

上面代码是错误使用,这个导致每次 conn 连接后只设置一次超时时间:

    client := &http.Client{Transport: tr,Timeout: 3*time.Second,  // 超时加在这里,是每次调用的超时}

正确的应该在 http.Client 结构体里设置,感兴趣的去参考全文吧。

另外服务端也要设置 timeout,以防把服务端压跨,请参考So you want to expose Go on the Internet[4]

  srv := &http.Server{ReadTimeout:  5 * time.Second,WriteTimeout: 10 * time.Second,IdleTimeout:  120 * time.Second,TLSConfig:    tlsConfig,Handler:      serveMux,
}
log.Println(srv.ListenAndServeTLS("", ""))

数据库相关

做为 CRUD Boy,经常和 DB 打交道,让我们来看下常见的超时设置与坑。

Redis 服务端要注意两个参数:timeouttcp-keepalive。

其中 timeout 用于关闭 idle client conn,默认是 0 不关闭,为了减少服务端 fd 占用,建议设置一个合理的值。

tcp-keepalive 在很早的 redis 版本是不开启的,这样经常会遇到因为网格抖动等原因,socket conn 一直存在,但实际上 client 早已经不存在的情况。

Redis Client 实现有一个重大问题,对于集群环境下,有些请求会做 Redirect 跳转,默认是 16 次,如果 tcp read timeout 设置了 100ms,那总时间很可能超过了 1s。

这就是一直强调的问题,tcp timeout 设置不代表实际的调用时间,因为业务层会多次调用 socket 读写。最好外面包一层 context 或是 circuit breaker。

MySQL 也同样服务端可以设置 MAX_EXECUTION_TIME 来控制 sql 执行时间。不同发行版本还不一样,有的只支持 select,有的同时支持 dml ddl...

其它

Q: timeout 与 sla 什么关系?

A: 要大于 sla。没有经过 toB 业务的重锤,感触不深,有朋友了解的可以留言讲讲 toB 业务的玩法。

Q: 如何传递 timeout?

A: 一般都是框架层传递的,比如 grpc 会在 header 里传递服务的 timeout, 每经过一个 backend,减去相应的耗时。

Q: 依赖的下游出现大量超时,应该如何处理?

A: 要做到 fast fail,一定得有降级 (circuit breaker 熔断)措施,否则会拖垮整条链路。

这次分享就这些,以后面还会分享更多的内容,如果感兴趣,可以关注并点击左下角的分享转发哦~

参考资料

[1]

Dapper, a Large-Scale Distributed Systems Tracing Infrastructure: https://research.google/pubs/pub36356/

[2]

zipkin: https://zipkin.io/

[3]

opentelemetry: https://opentelemetry.io/docs/concepts/distributions/

[4]

So you want to expose Go on the Internet: https://blog.cloudflare.com/exposing-go-on-the-internet/


最后,欢迎关注我的公众号~

你真的了解 timeout 吗?相关推荐

  1. 你真的懂 timeout 吗?

    服务为什么需要 timeout 呢?提前释放资源 记得在上家公司时,一个 python 服务与公网交互,request 库发出去的请求没有设置 timeout ... 而且还是个定时任务,占用了超多 ...

  2. 你的项目真的需要Session吗? redis保存session性能怎么样?

    在web开发中,Session这个东西一直都很重要,至少伴随我10年之久, 前一段时间发生一个性能问题,因为Redis session 问题,后来想想 其实我的项目session 是不需要的. 先看看 ...

  3. “三次握手,四次挥手”你真的懂吗?

    来源:码农桃花源 解读:"拼多多"被薅的问题出在哪儿?损失将如何买单? 之前有推过一篇不错的干货<TCP之三次握手四次挥手>,前几天有兄弟投稿,开始还以为是同一篇,后经 ...

  4. 昼猫笔记 JavaScript -- 异步执行 | 定时器真的定时执行?

      本篇主要内容:异步.定时器引发的思考 预计阅读时间:8分钟 了解 我们都知道在js中定时器有两种  setInterval()  . setTimeout()   setInterval() :按 ...

  5. 为什么nodejs是单进程的_nodejs真的是单线程吗?

    一.多线程与单线程 像java.python这个可以具有多线程的语言.多线程同步模式是这样的,将cpu分成几个线程,每个线程同步运行. 而node.js采用单线程异步非阻塞模式,也就是说每一个计算独占 ...

  6. qt定时器是阻塞的吗_吊打面试官 | 面试官:TCP真的可靠吗

    点击蓝字关注我哦 以下是本期干货视频视频后还附有文字版本哦 ▼<面试官:TCP真的可靠吗>▼ ps:请在WiFi环境下打开,如果有钱任性请随意 TCP真的可靠吗 面试官经常会问的一个问题是 ...

  7. 【原创】“三次握手,四次挥手”你真的懂吗?

    记得刚毕业找工作面试的时候,经常会被问到:你知道"3次握手,4次挥手"吗?这时候我会"胸有成竹"地"背诵"前期准备好的"答案&qu ...

  8. lostash Timeout executing grok 问题排查

    文章目录 Timeout executing grok 问题排查 1. 问题背景 1. 基础配置 2. pipeline配置 2. 问题表现 3. 尝试通过修改 memory-queue+ poll- ...

  9. ES集群新增节点无法加入集群 timed out while waiting for initial discovery state - timeout: 30s

    ES : 7.5.0 ES集群新增节点无法加入集群 timed out while waiting for initial discovery state - timeout: 30s   ES集群需 ...

最新文章

  1. linux timeline
  2. 变形积木装饰科技发起创始人郭辉:I'm the Business Bible
  3. 最新详细的JMM内存模型(三天熬夜血肝)
  4. 自定义Spring命名空间使JAXB更容易
  5. python-字典-定义-增删改取
  6. axios学习笔记(二):轻松弄懂XHR的使用及如何封装简易axios
  7. centos 关机命令_Linux anacron命令用法详解
  8. 随想录(中间件接口的定义方法)
  9. catch Floating point expection
  10. JavaSE----常用类(String、StringBuilder、StringBuffer)
  11. 多普达,D600,Coreplayer可用的序列号(版本未查看)。
  12. 近6年被引用次数最多的深度学习论文top100(附下载地址)
  13. 把触发器说透(转载)
  14. Java之父:Solaris前景堪忧
  15. APS 生产排单系统设计
  16. 房子出租后房东能随意进入租户家中吗?
  17. GPU显存占满但利用率却很低
  18. 今年的情人节,给心爱的她一个不一样的礼物吧
  19. laptop外接显示器
  20. 数组下标越界异常是如何产生的

热门文章

  1. 210106阶段三 文件I/O
  2. ngnix的upstream模块配置详解
  3. java中JScrollPane不显示水平滚动条的解决办法
  4. Windows环境下使用 Caffe在ImageNet上训练网络
  5. VMware克隆centos系统后不能识别eth0
  6. Android常用的工具类
  7. 解决ubuntu上网慢
  8. 网络病毒源的排查(2005年3月22日维护记录)
  9. CodeForces - 1579G Minimal Coverage(dp)
  10. 2021牛客多校7 - xay loves monotonicity(线段树区间合并)