范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

grpc链接池(6)超时的设置和传递

  我们经常看到下面的日志:   rpc error: code = DeadlineExceeded desc = context deadline exceeded
  我们需要思考两个问题:1,这个错误码来源是哪里?2,超时是如何设置和生效的?
  首先我们看下第一个问题:我们可以发现这段错误文案是golang源码里的错误文案:src/context/context.go   var DeadlineExceeded error = deadlineExceededError{} func (deadlineExceededError) Error() string   { return "context deadline exceeded" }
  什么时候会返回这个错误呢?同样是golang源码的context包里     func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {   if cur, ok := parent.Deadline(); ok && cur.Before(d) {     // The current deadline is already sooner than the new one.     return WithCancel(parent)   }     dur := time.Until(d)   if dur <= 0 {     c.cancel(true, DeadlineExceeded) // deadline has already passed     return c, func() { c.cancel(false, Canceled) }   }     if c.err == nil {     c.timer = time.AfterFunc(dur, func() {       c.cancel(true, DeadlineExceeded)     })   }     return c, func() { c.cancel(true, Canceled) } }func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {   return WithDeadline(parent, time.Now().Add(timeout)) }
  了解了上面的背景后,我们就可以排查grpc-go的client在何时使用了  WithTimeout
  google.golang.org/grpc@v1.50.1/clientconn.go   type ClientConn struct { dopts           dialOptions }func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {     if cc.dopts.timeout > 0 {       var cancel context.CancelFunc       ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)       defer cancel()     } }
  可以看到,在发起连接的时候会有,当server超过超时时间没有响应的时候就会报上面的错误。
  第二个地方就是我们发送请求的时候,我先会获取一个连接   func invoke(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error {   cs, err := newClientStream(ctx, unaryStreamDesc, cc, method, opts...)func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {     var newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) {     return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, done, opts...)     }     rpcConfig, err := cc.safeConfigSelector.SelectConfig(rpcInfo)func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc serviceconfig.MethodConfig, onCommit, doneFunc func(), opts ...CallOption) (_ iresolver.ClientStream, err error) {     if mc.Timeout != nil && *mc.Timeout >= 0 {     ctx, cancel = context.WithTimeout(ctx, *mc.Timeout)     } else {       ctx, cancel = context.WithCancel(ctx)     }
  可以看到,如果方法配置了超时,在超时时间完成之前,没有响应,也会报错。
  还有没有其它地方可以配置超时呢?答案是肯定的,Interceptor里我们也可以定义超时。下面就是我们常用的两种设置的超时的方法,分别是连接维度和请求方法维度。   clientConn, err := grpc.Dial(serverAddress, grpc.WithTimeout(5 * time.Second), grpc.WithInsecure()) if err != nil {         log.Println("Dial failed!")         return err }c := pb.NewGreeterClient(conn) c.SayHello(context.Background(), &pb.HelloRequest{Name: "world"},             WithForcedTimeout(time.Duration(10)*time.Second))
  那么上述设置是如何生效的?如何传递到服务端的呢?先看下
  grpc.WithTimeout 源码位于google.golang.org/grpc@v1.50.1/dialoptions.go   func WithTimeout(d time.Duration) DialOption {   return newFuncDialOption(func(o *dialOptions) {     o.timeout = d   }) }
  它修改了dialOptions的timeout   type dialOptions struct {     timeout                     time.Duration }type DialOption interface {   apply(*dialOptions) }
  而dialOptions是ClientConn的一个属性   type ClientConn struct {     dopts           dialOptions    }
  我们发起连接的时候用的就是这个属性上的timeout   func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {     if cc.dopts.timeout > 0 {     var cancel context.CancelFunc     ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)     defer cancel()   } }
  Interceptor  是如何让超时生效的呢,逻辑更简单,我们看下它的定义,在发起真正调用之前先调用Interceptor  ,这个时候设置超时时间: func TimeoutInterceptor(t time.Duration) grpc.UnaryClientInterceptor {     return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,         invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {         timeout := t         if v, ok := getForcedTimeout(opts); ok {             timeout = v         }         if timeout <= 0 {             return invoker(ctx, method, req, reply, cc, opts...)         }         ctx, cancel := context.WithTimeout(ctx, timeout)         defer cancel()         return invoker(ctx, method, req, reply, cc, opts...)     } }func getForcedTimeout(callOptions []grpc.CallOption) (time.Duration, bool) {     for _, opt := range callOptions {         if co, ok := opt.(TimeoutCallOption); ok {             return co.forcedTimeout, true         }     }     return 0, false }
  而超时时间是我们发起调用的时候通过option传递下来的   type TimeoutCallOption struct {     grpc.EmptyCallOption     forcedTimeout time.Duration } func WithForcedTimeout(forceTimeout time.Duration) TimeoutCallOption {     return TimeoutCallOption{forcedTimeout: forceTimeout} }
  弄清楚客户端的超时时间是如何设置和生效的以后,服务端怎么保证,客户端超时以后,马上终止当前任务呢?回答这个问题之前,我们看下超时是如何传递的。首先,给出答案:grpc协议将超时时间放置在HTTP Header 请求头里面。客户端设置的超时时间为5秒,http2的header如下   grpc-timeout: 4995884u
  其中u表示时间单位为纳秒,4995884u 约等于 5秒。然后服务端接收到该请求后,就可以根据这个时间计算出是否超时,由header 超时设置。
  那么header是何时由client设置的,以及何时由服务端解析的呢?
  google.golang.org/grpc@v1.50.1/internal/transport/http2_client.go
  发起客户端请求的时候会调用   func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error) {     headerFields, err := t.createHeaderFields(ctx, callHdr)
  内部我们可以看到,它从context里面取出超时截止时间,然后写入header   "grpc-timeout"  里面 func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) ([]hpack.HeaderField, error) {     if dl, ok := ctx.Deadline(); ok {     // Send out timeout regardless its value. The server can detect timeout context by itself.     // TODO(mmukhi): Perhaps this field should be updated when actually writing out to the wire.     timeout := time.Until(dl)     headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: grpcutil.EncodeDuration(timeout)})   }
  解析的过程:
  google.golang.org/grpc@v1.50.1/internal/transport/handler_server.go   func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []stats.Handler) (ServerTransport, error) {   if v := r.Header.Get("grpc-timeout"); v != "" {   to, err := decodeTimeout(v)   if err != nil {   return nil, status.Errorf(codes.Internal, "malformed time-out: %v", err)   }   st.timeoutSet = true   st.timeout = to   }      if timeoutSet {   s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout)   } else {   s.ctx, s.cancel = context.WithCancel(t.ctx)   }
  可以看到,首先从header里面取出超时时间,然后设置context.WithTimeout   func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) { if ht.timeoutSet { ctx, cancel = context.WithTimeout(ctx, ht.timeout) } else { ctx, cancel = context.WithCancel(ctx) }
  google.golang.org/grpc@v1.50.1/internal/transport/http2_server.go   func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (fatal bool) { case "grpc-timeout": timeoutSet = true var err error if timeout, err = decodeTimeout(hf.Value); err != nil { headerError = true }

初春,试试莫兰迪色系穿搭,高级感爆表,又很温柔低调嗨,我是柒作作。不知道你有没有注意到,近些年时尚圈流行一个词淡漠。对就是那种清冷中带着疏离的味道,彰显出一种低调而高级的品质感。在穿搭中,我们第一眼看到的就是颜色,所以穿搭配色很重作为科技大小便护理机器人入选深圳市创新产品推广应用目录近日,深圳市工业和信息化局公布深圳市创新产品推广应用目录(2022年第三批次),深圳作为科技有限公司大小便智能护理机器人成功入选目录名单,成为政府机构优先采购及推荐的产品,后续将在揭秘大宗贸易批油骗局成品油作为国民经济的重要商品,从1950年至1985年的30多年里,一直是由原商业部统购统销。经营业务的重点主要是批发实行三级批发体制和三固定(固定批发环节固定供应范围固定倒扣率)为期三天!150个绵阳造精品农特产品亮相成都宽窄巷子活动现场吸引了众多人前来选购1月11日,绵阳好物蓉城相荟优质农产品推介活动在成都市宽窄巷子景区举行。红星新闻记者获悉,现场采取展会结合模式,33家优质农产品企业和文旅企业将在此开展跨境电商的冰与火越过阵痛转向产品与品牌21世纪经济报道记者董静怡上海报道如果用一个词总结2022年的跨境电商,是什么?或许是挑战。2022年是疫情三年经济环境最错综复杂的一年,由2021年延续的寒冬尚未消散,疫情冲击消摩尔线程推出GPU云桌面产品,支持最高20路1080p30帧并发IT之家1月11日消息,国产GPU厂商摩尔线程昨日举办了GPU云桌面产品发布会,推出全新vPC云桌面虚拟化GPU产品MTvGPU1。0MTGPU直通和MTGPU加速协议编码等基于摩银行春节专享理财产品冷清,今年还能手握哪些产品理财过年?记者曾仰琳编辑元旦假期刚结束不久,农历新年又将悄然而至。往年,每逢春节假期前夕,各大银行都会推出新春专属理财产品,以满足投资者年终奖或闲置资金的理财需求,迎接新一年的开门红。202轻巧便携高输出功率的新款户外移动电源比亚迪联合研发还能当UPS大家好,我是air04。最近开始上手玩户外和精致露营。在户外活动的过程中我发现,像手机无人机相机,灯具电风扇电磁炉电吹风这些户外常用的电器,如果有电能随充随用,就不需要在出发前逐个变频微波炉和普通微波炉的区别使用微波炉要注意哪些?变频微波炉可以实现强火到弱火的自动调整,通过输出功率的改变满足不同食物对于不同火力的要求。比如说变频微波炉火力设置成高档,功率从1200W减弱到9001000W,输出功率会随着加热机械革命新款旷世X游戏本将搭载CHERRY机械键盘IT之家1月10日消息,据机械革命官方消息,机械革命即将发布新一代旗舰级游戏本,其中旷世X将搭载由CHERRY提供的机械键盘,支持单颗按键独立发光。机械革命表示,通过旷世X游戏本内TCLQ10G电视影院级体验,MiniLEDM1智慧芯片直降1000元,真香刚搬了新家,总觉得少了点什么,一开始老婆说要在客厅弄投影,但是老人们不同意,他们用不惯投影仪,所以还是要选择传统的电视。虽然现在电视的发展速度已经远超我的想象,甚至还有1000多元
上海海港外援奥斯卡未来大概率留在中国新华社南京1月7日电(记者王恒志)中超上海海港队队长奥斯卡7日表示,自己目前会专注帮助球队拿下中国足协杯冠军。至于未来,他大概率会和家人继续留在中国。2022年1月4日,奥斯卡(左汤加去年火山喷发6小时内引发近40万次闪电,全球一半闪电集中在周围楚天都市报极目新闻记者李力力胡秀文2022年1月汤加海底火山喷发,不仅引发了海啸,还向大气喷出大量气体与水蒸气。据美国有线电视新闻网1月8日报道,现在研究人员还发现,在汤加火山喷发国门洞开,越南计划这样来欢迎中国客人,你想到了吗?越南旅游业需要考虑研究并出台通过边境口岸欢迎中国游客的具体机制近期,在没有机制的情况下,将成立团体和俱乐部,就如何接待客人通过协商达成一致。这是广宁省旅游局局长范玉翠先生在1月9日这才是翅根最好吃做法,不焯水不油炸,鲜嫩多汁,比红烧肉还好吃冬日生活打卡季大家好,我是不二,我的小棉袄吵着要吃可乐吃根可乐吃根,好多小朋友都喜欢吃,今天就来分享一个不一样的做法,用我这个做法做出的翅根是相当的嫩,点个赞,接着往下看。可乐翅根喜迎春节,上门送这3款礼酒,酒质高也拿得出手,重点是倍有面子一年走到了尽头,春节的脚步越来越近,团圆夜马上就要到来,相信大家都做好了充足的过年的准备工作吧,今天我们就来聊一聊过年。喜迎春节,上门送这3款礼酒,酒质高也拿得出手,重点是倍有面子武都万象洞冬日游客络绎不绝新甘肃每日甘肃网通讯员后斌玉摄影报道冬日,笔者走进陇南市武都区万象洞景区,在景区看到,游客们每到一处都随手拿起相机和手机照相留念,他们都被万象洞雄奇秀丽的景色所深深吸引,生怕错过这隆冬闲游永乐镇周末闲暇,偶遇入冬后难得的好天气,天高云淡,艳阳高照,夫人提议去周边小镇转转。说实话,西安和咸阳周边几乎所有稍有名气的小镇都去过了,想想找个就近的,重游永乐镇就成了首选。永乐镇古称昭通绥江首个半山酒店开业云南网讯(记者谢毅通讯员罗洪邓雾军)1月1日,昭通市绥江县千年渔村半山酒店开门迎客,这是该县首个建成投用的半山酒店。据悉,千年渔村半山酒店于2022年2月启动建设,总投资4000万观音山上观山水最接近大奖佳对欣赏风景这边独好观音山征联已截稿数日,高境下对屏上出现寥寥无几。只有数幅看似沾边却不沾门的半吊子联在征联大戏前闹台,挺热闹的,好玩儿!我属闹台之流中的C角儿,愿给友们献上新年快乐。之所以不见高对,江苏南京无想山风景区冬景如画来源人民网江苏南京无想山风景区冬景如画江苏南京无想山风景区冬景如画2江苏南京无想山风景区冬景如画3江苏南京无想山风景区冬景如画4江苏南京无想山风景区冬景如画5江苏南京无想山风景区冬非热门景区,今年冬天新晋的6处耍雪地,槽点和亮点并存成都的冬天怎么能没有雪呢?虽然成都市区难得下雪,但成都人对于雪的热爱因子,每到冬季就会被激活。元旦的时候,已经有不少家长带娃出去耍了雪,感受玩雪的乐趣。今年玩雪,有没有小众不远景美