• 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端就必须有所响应,不然就会发生意料之外的事情。

            代码片段一,设置超时

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

     1 C++
     2 
     3 ClientContext context;
     4 time_point deadline = std::chrono::system_clock::now() + 
     5     std::chrono::milliseconds(100);
     6 context.set_deadline(deadline);
     7 
     8 
     9 Go
    10 
    11 clientDeadline := time.Now().Add(time.Duration(*deadlineMs) * time.Millisecond)
    12 ctx, cancel := context.WithDeadline(ctx, clientDeadline)
    13 
    14 
    15 Java
    16 
    17 response = blockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).sayHello(request);

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

            代码片段二,检查超时

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

     1 C++
     2 if (context->IsCancelled()) {
     3   return Status(StatusCode::CANCELLED, "Deadline exceeded or Client cancelled, abandoning.");
     4 }
     5 
     6 
     7 Go
     8 if ctx.Err() == context.Canceled {
     9     return status.New(codes.Canceled, "Client cancelled, abandoning.")
    10 }
    11 
    12 
    13 Java
    14 if (Context.current().isCancelled()) {
    15   responseObserver.onError(Status.CANCELLED.withDescription("Cancelled by client").asRuntimeException());
    16   return;
    17 }

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

            代码片段三,调整超时

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

     1 C++
     2 #include <gflags/gflags.h>
     3 DEFINE_int32(deadline_ms, 20*1000, "Deadline in milliseconds.");
     4 
     5 ClientContext context;
     6 time_point deadline = std::chrono::system_clock::now() + 
     7     std::chrono::milliseconds(FLAGS_deadline_ms);
     8 context.set_deadline(deadline);
     9 
    10 
    11 Go
    12 var deadlineMs = flag.Int("deadline_ms", 20*1000, "Default deadline in milliseconds.")
    13 
    14 ctx, cancel := context.WithTimeout(ctx, time.Duration(*deadlineMs) * time.Millisecond)
    15 
    16 
    17 Java
    18 @Option(name="--deadline_ms", usage="Deadline in milliseconds.")
    19 private int deadlineMs = 20*1000;
    20 
    21 response = blockingStub.withDeadlineAfter(deadlineMs, TimeUnit.MILLISECONDS).sayHello(request);

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

  • 相关阅读:
    Mybatis批处理
    Mybatis兼容C3P0连接池
    一对多,多对一查询
    缓存
    动态sql
    mybatis 日志记录
    python学习day07-encode和decode
    python学习day07---三级目录优化
    python学习day06练习---三级目录
    python学习day06--02字典增删差改以及字符串的一些方法
  • 原文地址:https://www.cnblogs.com/029zz010buct/p/9487568.html
Copyright © 2020-2023  润新知