grpc deadlines

最近在将应用的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两方面的情况。首先,设置超时的方式不一样,server端或者在proto文件中指定就行,client端就需要编码了。其次,当client端设置了超时时间,server端就必须有所响应,不然就会发生意料之外的事情。

代码片段一,设置超时

作为客户端,你应该知道自己希望最晚多长时间拿到返回结果吧,那就设置他吧。

 C++

 ClientContext context;
time_point deadline = std::chrono::system_clock::now() +
std::chrono::milliseconds(100);
context.set_deadline(deadline); Go clientDeadline := time.Now().Add(time.Duration(*deadlineMs) * time.Millisecond)
ctx, cancel := context.WithDeadline(ctx, clientDeadline) Java response = blockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).sayHello(request);

如上的代码中,都设置了超时时间为100毫秒。

代码片段二,检查超时

作为服务器端,如果客户端设置了超时,服务器端就需要去检测下,否则如果客户端已经超时了,服务器端还傻乎乎的干活?岂不是真傻了。

 C++
if (context->IsCancelled()) {
return Status(StatusCode::CANCELLED, "Deadline exceeded or Client cancelled, abandoning.");
} Go
if ctx.Err() == context.Canceled {
return status.New(codes.Canceled, "Client cancelled, abandoning.")
} Java
if (Context.current().isCancelled()) {
responseObserver.onError(Status.CANCELLED.withDescription("Cancelled by client").asRuntimeException());
return;
}

一般而言,server在拿到request之后就应该检测client的超时时间,如果超时了,就不在执行逻辑。不过,如果在server开始执行逻辑但并没有结束的时候client超时了怎么办,当然可以在server执行逻辑的同时检测是否超时,如果超时,cancel掉逻辑。但是也有特殊情况,比如这个逻辑很耗费资源,但是结果对客户端而言是可重用的,或者说结果是可以缓存的,那么就需要把结果保存下来,别cancel逻辑了。so, it depends。

代码片段三,调整超时

程序员的苦逼就在于需求一直在变,不过没办法,我们学的哲学不就是说变是永恒的,不变是幻觉吗?那么设置了超时,怎么改?废话,怎么设置的就怎么改啊,这么说当然是废话,这里要说的是在不进行新的版本发布的情况下,怎么改。

 C++
#include <gflags/gflags.h>
DEFINE_int32(deadline_ms, 20*1000, "Deadline in milliseconds."); ClientContext context;
time_point deadline = std::chrono::system_clock::now() +
std::chrono::milliseconds(FLAGS_deadline_ms);
context.set_deadline(deadline); Go
var deadlineMs = flag.Int("deadline_ms", 20*1000, "Default deadline in milliseconds.") ctx, cancel := context.WithTimeout(ctx, time.Duration(*deadlineMs) * time.Millisecond) Java
@Option(name="--deadline_ms", usage="Deadline in milliseconds.")
private int deadlineMs = 20*1000; response = blockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).sayHello(request);

看到了吧,这样的话,超时时间就不用硬编码了,我们可以动态的设定,直到我们的业务和程序运行都收敛了,或许就可以真正硬编码一个超时时间了。

上一篇:CENSORING


下一篇:Centos开机自启动脚本的制作