最近在将应用的 RPC 更换为 gRPC,使用过程中发现报错 rpc error: code = DeadlineExceeded desc = context deadline exceeded。这是啥?原来是某位仁兄设置了环境的超时时间,但是设置了 1s——看好了,是 1s。所以,任何稍微费时的交互,都直接报错了。
如果不显式设置的话,gRPC 自己默认的超时时间是一个很大的值,那就不会出现这种问题。但是给出的错误信息也真是无语了:既然是超时,不能给个 timeout 的提示吗?搞了个什么 deadline exceeded,初次看见还是有点懵。Anyway,谷歌出品,必属精品,出问题还是先反思自己的使用吧。
Rule 1:显式指定超时时间
建议显式地指定一个超时时间。一方面可以节省资源,不然所有的请求都无休止地发送、在 server 中运行,有可能导致资源耗尽以致系统崩溃;另一方面,业务本身肯定也需要有一个返回时间,而不是提交请求之后,就傻傻地等着,到天荒地老吗?
Rule 2:合适的超时时间没有标准答案
没有明确的规则规定什么样的超时时间是合适的,这需要开发人员自己加以考虑:需要考虑网络,如果网络耗时很明显,超时时间就要相应放宽;需要考虑服务器的资源、内存、CPU 以及负载;需要考虑外部交互。更重要的,其实是搞清楚业务规则,这个 request 所对应的业务对时间的要求。虽然只是设置了个时间,但也不是那么容易,不然程序员怎么那么值钱。
Rule 3:client 和 server 两方面都要考虑
当设置超时时间时,需要考虑 client 和 server 两方面的情况。首先,设置超时的方式不一样:server 端或者在 proto 文件中指定就行,client 端就需要编码了。其次,当 client 端设置了超时时间,server 端就必须有所响应,不然就会发生意料之外的事情。
客户端:设置超时
作为客户端,你应该知道自己希望最晚多长时间拿到返回结果,那就设置它吧。示例代码中,将超时时间统一设置为 100 毫秒。
服务端:检查超时
作为服务器端,如果客户端设置了超时,服务器端就需要去检测下,否则客户端已经超时了,服务器端还傻乎乎地干活?岂不是真傻了。
一般而言,server 在拿到 request 之后就应该检测 client 的超时时间,如果超时了,就不再执行逻辑。不过,如果在 server 开始执行逻辑但并没有结束的时候 client 超时了怎么办?当然可以在 server 执行逻辑的同时检测是否超时,如果超时,就 cancel 掉逻辑。但是也有特殊情况,比如这个逻辑很耗费资源,但是结果对客户端而言是可重用的,或者说结果是可以缓存的,那么就需要把结果保存下来,别 cancel 逻辑了。So, it depends。
动态调整超时
需求一直在变,不过没办法,哲学不就是说变是永恒的、不变是幻觉吗?那么设置了超时,怎么改?怎么设置的就怎么改啊。这里要说的是,在不进行新版本发布的情况下怎么改:不要把超时时间硬编码在代码里,而是做成可以动态设定的配置,直到我们的业务和程序运行都收敛了,或许就可以真正硬编码一个超时时间了。