脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Go gRPC进阶教程服务超时设置

Go gRPC进阶教程服务超时设置

2022-10-21 11:52烟花易冷人憔悴 Golang

这篇文章主要为大家介绍了Go gRPC进阶,gRPC请求的超时时间设置,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

gRPC默认的请求的超时时间是很长的,当你没有设置请求超时时间时,所有在运行的请求都占用大量资源且可能运行很长的时间,导致服务资源损耗过高,使得后来的请求响应过慢,甚至会引起整个进程崩溃。

为了避免这种情况,我们的服务应该设置超时时间。

前面的入门教程

Go gRPC环境安装教程示例详解

Go gRPC教程实现Simple RPC

Go gRPC服务端流式RPC教程示例

Go gRPC服务客户端流式RPC教程

Go gRPC服务双向流式RPC教程

提到当客户端发起请求时候,需要传入上下文context.Context,用于结束超时或取消的请求。

本篇以简单RPC为例,介绍如何设置gRPC请求的超时时间。

客户端请求设置超时时间

修改调用服务端方法

1.把超时时间设置为当前时间+3秒

?
1
2
3
clientDeadline := time.Now().Add(time.Duration(3 * time.Second))
ctx, cancel := context.WithDeadline(ctx, clientDeadline)
defer cancel()

2.响应错误检测中添加超时检测

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   // 传入超时时间为3秒的ctx
res, err := grpcClient.Route(ctx, &req)
if err != nil {
    //获取错误状态
    statu, ok := status.FromError(err)
    if ok {
        //判断是否为调用超时
        if statu.Code() == codes.DeadlineExceeded {
            log.Fatalln("Route timeout!")
        }
    }
    log.Fatalf("Call Route err: %v", err)
}
// 打印返回值
log.Println(res.Value)

完整的client.go代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main
import (
    "context"
    "log"
    "time"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
    pb "go-grpc-example/6-grpc_deadlines/proto"
)
// Address 连接地址
const Address string = ":8000"
var grpcClient pb.SimpleClient
func main() {
    // 连接服务器
    conn, err := grpc.Dial(Address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("net.Connect err: %v", err)
    }
    defer conn.Close()
    ctx := context.Background()
    // 建立gRPC连接
    grpcClient = pb.NewSimpleClient(conn)
    route(ctx, 2)
}
// route 调用服务端Route方法
func route(ctx context.Context, deadlines time.Duration) {
    //设置3秒超时时间
    clientDeadline := time.Now().Add(time.Duration(deadlines * time.Second))
    ctx, cancel := context.WithDeadline(ctx, clientDeadline)
    defer cancel()
    // 创建发送结构体
    req := pb.SimpleRequest{
        Data: "grpc",
    }
    // 调用我们的服务(Route方法)
    // 传入超时时间为3秒的ctx
    res, err := grpcClient.Route(ctx, &req)
    if err != nil {
        //获取错误状态
        statu, ok := status.FromError(err)
        if ok {
            //判断是否为调用超时
            if statu.Code() == codes.DeadlineExceeded {
                log.Fatalln("Route timeout!")
            }
        }
        log.Fatalf("Call Route err: %v", err)
    }
    // 打印返回值
    log.Println(res.Value)
}

服务端判断请求是否超时

当请求超时后,服务端应该停止正在进行的操作,避免资源浪费。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Route 实现Route方法
func (s *SimpleService) Route(ctx context.Context, req *pb.SimpleRequest) (*pb.SimpleResponse, error) {
    data := make(chan *pb.SimpleResponse, 1)
    go handle(ctx, req, data)
    select {
    case res := <-data:
        return res, nil
    case <-ctx.Done():
        return nil, status.Errorf(codes.Canceled, "Client cancelled, abandoning.")
    }
}
func handle(ctx context.Context, req *pb.SimpleRequest, data chan<- *pb.SimpleResponse) {
    select {
    case <-ctx.Done():
        log.Println(ctx.Err())
        runtime.Goexit() //超时后退出该Go协程
    case <-time.After(4 * time.Second): // 模拟耗时操作
        res := pb.SimpleResponse{
            Code:  200,
            Value: "hello " + req.Data,
        }
        // //修改数据库前进行超时判断
        // if ctx.Err() == context.Canceled{
        //  ...
        //  //如果已经超时,则退出
        // }
        data <- &res
    }
}

一般地,在写库前进行超时检测,发现超时就停止工作。

完整server.go代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main
import (
    "context"
    "log"
    "net"
    "runtime"
    "time"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
    pb "go-grpc-example/6-grpc_deadlines/proto"
)
// SimpleService 定义我们的服务
type SimpleService struct{}
const (
    // Address 监听地址
    Address string = ":8000"
    // Network 网络通信协议
    Network string = "tcp"
)
func main() {
    // 监听本地端口
    listener, err := net.Listen(Network, Address)
    if err != nil {
        log.Fatalf("net.Listen err: %v", err)
    }
    log.Println(Address + " net.Listing...")
    // 新建gRPC服务器实例
    grpcServer := grpc.NewServer()
    // 在gRPC服务器注册我们的服务
    pb.RegisterSimpleServer(grpcServer, &SimpleService{})
    //用服务器 Serve() 方法以及我们的端口信息区实现阻塞等待,直到进程被杀死或者 Stop() 被调用
    err = grpcServer.Serve(listener)
    if err != nil {
        log.Fatalf("grpcServer.Serve err: %v", err)
    }
}
// Route 实现Route方法
func (s *SimpleService) Route(ctx context.Context, req *pb.SimpleRequest) (*pb.SimpleResponse, error) {
    data := make(chan *pb.SimpleResponse, 1)
    go handle(ctx, req, data)
    select {
    case res := <-data:
        return res, nil
    case <-ctx.Done():
        return nil, status.Errorf(codes.Canceled, "Client cancelled, abandoning.")
    }
}
func handle(ctx context.Context, req *pb.SimpleRequest, data chan<- *pb.SimpleResponse) {
    select {
    case <-ctx.Done():
        log.Println(ctx.Err())
        runtime.Goexit() //超时后退出该Go协程
    case <-time.After(4 * time.Second): // 模拟耗时操作
        res := pb.SimpleResponse{
            Code:  200,
            Value: "hello " + req.Data,
        }
        // //修改数据库前进行超时判断
        // if ctx.Err() == context.Canceled{
        //  ...
        //  //如果已经超时,则退出
        // }
        data <- &res
    }
}

运行结果

服务端:

:8000 net.Listing...
goroutine still running

客户端:

Route timeout! 

总结

超时时间的长短需要根据自身服务而定,例如返回一个hello grpc,可能只需要几十毫秒,然而处理大量数据的同步操作则可能要很长时间。需要考虑多方面因素来决定这个超时时间,例如系统间端到端的延时,哪些RPC是串行的,哪些是可以并行的等等。

教程源码地址:https://github.com/Bingjian-Zhu/go-grpc-example

参考:https://grpc.io/blog/deadlines/

以上就是Go gRPC进阶服务超时设置的详细内容,更多关于Go gRPC超时设置的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/FireworksEasyCool/p/12702959.html

延伸 · 阅读

精彩推荐
  • GolangGolang单元测试与覆盖率的实例讲解

    Golang单元测试与覆盖率的实例讲解

    这篇文章主要介绍了Golang单元测试与覆盖率的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    pirlo-san5312021-02-02
  • GolangMac OS系统安装golang教程

    Mac OS系统安装golang教程

    这篇文章主要介绍了Mac OS系统安装golang教程,本文还同时介绍了Sublime Text开发工具的配置,需要的朋友可以参考下 ...

    junjie1982020-04-12
  • GolangGo语言中break label与goto label的区别

    Go语言中break label与goto label的区别

    这篇文章主要介绍了Go语言中break label与goto label的区别,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    itbsl9272021-06-06
  • GolangGo语言中的函数式编程实践

    Go语言中的函数式编程实践

    这篇文章主要介绍了Go语言中的函数式编程实践,主要讲解Go语言中的函数式编程概念和使用。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一...

    Oo若离oO4982020-05-15
  • GolangGo语言string,int,int64 ,float之间类型转换方法

    Go语言string,int,int64 ,float之间类型转换方法

    Go语言中int类型和string类型都是属于基本数据类型,两种类型的转化都非常简单。下面通过本文给大家分享Go语言string,int,int64 ,float之间类型转换方法,感...

    哪来的查克拉42162020-05-07
  • GolangGolang中List的实现方法示例详解

    Golang中List的实现方法示例详解

    最近决定复习下Go,所以下面这篇文章主要给大家介绍了关于Golang中List的实现方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的...

    脚本之家9342020-05-09
  • GolangGolang 空map和未初始化map的注意事项说明

    Golang 空map和未初始化map的注意事项说明

    这篇文章主要介绍了Golang 空map和未初始化map的注意事项说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    菌菇9522021-06-07
  • GolangGo语言图片处理和生成缩略图的方法

    Go语言图片处理和生成缩略图的方法

    这篇文章主要介绍了Go语言图片处理和生成缩略图的方法,涉及Go语言针对图片操作的技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    不吃皮蛋8822020-04-15