热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GolanggRPC学习(04):Deadlines超时限制

#当我们使用gRPC时,gRPC库关系的是连接,序列化,反序列化和超时执行。Deadlines允许gRPC客户端设置自己等待多长时间来完成rpc操作,直到出现这个错误 DEADLI

#

当我们使用gRPC时,gRPC库关系的是连接,序列化,反序列化和超时执行。Deadlines 允许gRPC客户端设置自己等待多长时间来完成rpc操作,直到出现这个错误 DEADLINE_EXCEEDED。但是在正常情况下,这个DEADLINE_EXCEEDED默认设置是一个很大的数值。
一些语言的API用deadline,一些用 timeout。

在正常情况下,你没有设置deadline,那么所有的请求可能在最大请求时间过后才超时。这样你对于你的服务器资源,可能存在风险,比如内存,可能因为这个长期运行的服务而增长很快,从而耗尽资源。
为了避免这种情况,需要给你的客户端请求程序设置一个默认的超时时间,在这段时间内请求没有返回,那么就超时报错。

#

#

1.设置deadlines

Copy

2.检查deadlines

Copy
if ctx.Err() == context.Canceled {
        return status.New(codes.Canceled, "Client cancelled, abandoning.")
}

#

1. 建立连接时超时控制:
客户端建立连接时,使用的Dial()函数,它位于
google.golang.org/grpc/clientconn.go 中,我们看看这个函数内容:

Copy
(*ClientConn, error) {    
   return DialContext(context.Background(), target, opts...)
}

它里面调用的 DialContext() 函数,这个函数非常长,他们在同一个文件中,它是实际执行的函数,这里面就有context的timeout和Done相关操作。你也可以到google.golang.org/grpc/clientconn.go文件中去看看这个函数DialContext具体是干嘛的。

使用的时候传入设置timeout的context,如下:

Copy
5)
defer cancel()
conn, err := grpc.DialContext(ctx, address, grpc.WithBlock(), grpc.WithInsecure())
  • grpc.WithInsecure() ,这个参数啥意思?
    gRPC是建立在HTTP/2上的,所以对TLS提供了很好的支持。如果在客户端建立连接过程中设置 grpc.WithInsecure() 就可以跳过对服务器证书的验证。写练习时可以用这个参数,但是在真实的环境中,不要这样做,因为有泄露信息的风险。
  • grpc.WithBlock()
    这个参数会阻塞等待握手成功。
    因为用Dial连接时是异步连接,连接状态为正在连接,如果设置了这个参数就是同步连接,会阻塞等待握手成功。
    这个还和超时设置有关,如果你没有设置这个参数,那么context超时控制将会失效。

2. 调用时超时:
函数的调用超时控制

Copy
5)
defer cancel()
result, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

#

用grpc官方的例子来练习下

目录结构:

Copy
grpc-tutorial
    -- 04deadlines
        --client
            - main.go
        --server
           - main.go
       --proto/echo
           - echo.proto
           - echo.pb.go

#

Copy
echo;message 

EchoRequest {   
  string message = 1;
}

message EchoResponse {   
  string message = 1;
}

service Echo {    
  rpc UnaryEcho(EchoRequest) returns (EchoRequest) {}    
  rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {}   
  rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {}    
  rpc BidirectionalStreamingEcho(stream EchoRequest) returns (stream EchoResponse){}
}

进入到proto/echo目录,生成go文件,命令如下:

protoc -I . --go_out=plugins=grpc:. ./echo.proto

#

client\main.go
有2个主要的函数,2端都是stream和都不是stream,先看都不是stream的函数

都不是stream的函数

Copy
// unaryCall 不是stream的请求
func unaryCall(c pb.EchoClient, requestID int, message string, want codes.Code) {   
    ctx, cancel := context.WithTimeout(context.Background(), time.Second) //超时设置   
    defer cancel()   

    req := &pb.EchoRequest{Message: message} //参数部分

    _, err := c.UnaryEcho(ctx, req) //调用函数发送请求给服务端
    got := status.Code(err)   //
    fmt.Printf("[%v] wanted = %v, got = %v\n", requestID, want, got)
}

上面的code设置在文件 grpc/codes/codes.go

Copy
uint32

const (
        OK Code = 0
        Canceled Code = 1
        Unknown Code = 2
        InvalidArgument Code = 3
        DeadlineExceeded Code = 4
   ... ...
)

2端都是stream的函数:

Copy
// streamingCall,2端都是stream
func streamingCall(c pb.EchoClient, requestID int, message string, want codes.Code) {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)//超时设置
    defer cancel()

    stream, err := c.BidirectionalStreamingEcho(ctx)//双向stream
    if err != nil {
        log.Printf("Send error : %v", err)
        return
    }

    err = stream.Send(&pb.EchoRequest{Message: message})//发送
    if err != nil {
        log.Printf("Send error : %v", err)
        return
    }

    _, err = stream.Recv() //接收
   
    got := status.Code(err)
    fmt.Printf("[%v] wanted = %v, got = %v\n", requestID, want, got)
}

main 执行函数

Copy
() {
    flag.Parse()

    conn, err := grpc.Dial(*addr, grpc.WithInsecure(), grpc.WithBlock())
    if err != nil {
        log.Fatalf("did not connect : %v ", err)
    }
    defer conn.Close()

    c :=pb.NewEchoClient(conn)

    // 成功请求
    unaryCall(c, 1, "word", codes.OK)
    // 超时 deadline
    unaryCall(c, 2, "delay", codes.DeadlineExceeded)
    // A successful request with propagated deadline
    unaryCall(c, 3, "[propagate me]world", codes.OK)
    // Exceeds propagated deadline
    unaryCall(c, 4, "[propagate me][propagate me]world", codes.DeadlineExceeded)
    // Receives a response from the stream successfully.
    streamingCall(c, 5, "[propagate me]world", codes.OK)
    // Exceeds propagated deadline before receiving a response
    streamingCall(c, 6, "[propagate me][propagate me]world", codes.DeadlineExceeded)
}

#

定义一个struct

Copy
struct {    
    pb.UnimplementedEchoServer    
    client pb.EchoClient    
    cc *grpc.ClientConn
}

2端不是stream的函数:

Copy
(*pb.EchoResponse, error) {
    message := req.Message
    if strings.HasPrefix(message, "[propagate me]") {//判断接收的值
        time.Sleep(800 * time.Millisecond)
        message := strings.TrimPrefix(message, "[propagate me]")
        return s.client.UnaryEcho(ctx, &pb.EchoRequest{Message:message}) //<1>
    }

    if message == "delay" {
        time.Sleep(1500 * time.Millisecond) // message=delay 时睡眠1500毫秒,大于client的设置的1秒,这里就超时了
    }

    return &pb.EchoResponse{Message:message}, nil
}

上面函数标注 <1> 这个地方比较有意思,当client端发送的字符串包含 [propagate me] 字符串时,先睡眠800毫秒,然后在重新执行客户端请求服务端的函数 s.client.UnaryEcho() , 在次运行到服务端的 UnaryEcho(),客户端已经超时了。
也就是说client/main.go 先请求了一次服务端,然后在server/main.go 的函数 func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest)又执行了一次请求服务端,所以会导致超时。

2端设置stream的函数:

Copy
error {
    for {
        req, err := stream.Recv()
        if err == io.EOF {
            return status.Error(codes.InvalidArgument, "request message not received")
        }
        if err != nil {
            return err
        }

        message := req.Message
        if strings.HasPrefix(message, "[propagate me]") {
            time.Sleep(800 * time.Millisecond)
            message = strings.TrimPrefix(message, "[propagate me]")
            res, err := s.client.UnaryEcho(stream.Context(), &pb.EchoRequest{Message:message})//再次执行客户端请求服务端函数,这里可能会超时
            if err != nil {
                return err
            }
            stream.Send(res)
        }

        if message == "delay" {
            time.Sleep(1500 * time.Millisecond)
        }
        stream.Send(&pb.EchoResponse{Message:message})
    }
}

main函数

Copy
() {
    flag.Parse()

    address := fmt.Sprintf(":%v", *port)
    lis, err := net.Listen("tcp", address)
    if err != nil {
        log.Fatalf("failed to listen: %v ", err)
    }

    echoServer := newEchoServer()
    defer echoServer.Close()

    grpcServer := grpc.NewServer()
    pb.RegisterEchoServer(grpcServer, echoServer)

    if err := grpcServer.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v ", err)
    }
}

#

先运行 /server/main.go , go run main.go

在运行 /client/main.go, go run main.go

执行结果:

Copy
 go run main.go
[1] wanted = OK, got = OK
[2] wanted = DeadlineExceeded, got = DeadlineExceeded
[3] wanted = OK, got = Unavailable
[4] wanted = DeadlineExceeded, got = Unavailable
[5] wanted = OK, got = Unavailable
[6] wanted = DeadlineExceeded, got = Unavailable

#

  • 01hello grpc helloworld
  • 02fourinteractionmode grpc 四种传输方式
  • 03customer grpc 一个小练习demo
  • 04deadlines grpc 超时限制

#

  • gRPC and Deadlines
  • grpc-go deadline

推荐阅读
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
author-avatar
xtalk
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有