gRPC-08传递取消操作 [golang版]

20 篇文章 11 订阅
本文介绍了gRPC中的超时取消和单方面取消请求的实现,通过context.Context来传递取消操作。示例代码展示了如何在客户端和服务器端使用context进行取消,并检查grpc.status.Code()获取错误码。客户端通过goroutines实现心跳检测,当长时间无通话时关闭连接。
摘要由CSDN通过智能技术生成

代码地址

超时取消

https://github.com/wanmei002/grpc-learn/tree/master/ch06

单方面取消

https://github.com/wanmei002/grpc-learn/tree/master/ch07

简单介绍

取消请求又分超时取消请求 和 单方面的主动取消。取消主要是用 context.Context[golang版] 来远程传递取消操作。可以用
grpc 的 status.Code() 来获取错误码,不同的错误码对应不同的错误。完整的错误码列表可以在gRPC官方文档查看;或者看 grpc/codes/codes.go 源码里有错误码的描述

错误码1数字描述
OK0成功
CANCELLED1操作已被(调用者)取消
DEADLINE_EXCEEDED4在操作完成前,就已超过了截止时间

在客户端请求中传递context的时候,请求会监测 context 的状态:监测context的状态

现在我们直接上代码吧

protobuffer 定义

// 简单模拟两个人对话
service ChatSvr {
    rpc SendMessage(stream MsgInfo) returns (stream MsgInfo);
}

message MsgInfo {
    uint64 ChatRoomID = 1;
    uint64 UserID = 2;
    string UserName = 3;
    string UserHeadImg = 4;
    string Msg = 5;
    string Ext = 6;
}

客户端代码模拟的是聊天室

main函数代码
func main(){
    input := os.Args
    say := "你好"
    if len(input) >= 2 {
        say = input[1]
    }
    // grpc 自带的拨号连接,采用http2
    d, err := grpc.Dial(":8093", grpc.WithInsecure())
    if err != nil {
        log.Println("grpc dial failed; err:", err)
        return
    }
    defer d.Close()
    client := chat.NewChatSvrClient(d)
    // 创建可以取消的上下文
    ctx, cancel := context.WithCancel(context.Background())
    clientStream, err := client.SendMessage(ctx)
    if err != nil {
        log.Println("get client stream failed; err:", err)
        return
    }
    msg := &chat.MsgInfo{ChatRoomID:1,UserID:4567,UserName:"zyn",UserHeadImg:"love.jpg",Msg:say}
    fmt.Println("zyn: ", say)
    err = clientStream.Send(msg)
    if err != nil {
        log.Println("client send data failed; err:", err)
        return
    }
    sendMsgTime := new(int64)
    
    *sendMsgTime = time.Now().Unix()
    // 起一个 goroutine 来接收客户端发送的数据
    go getRecvMsg(clientStream)
    ch := make(chan struct{})
    // 起一个 goroutine 来检查是否长时间未通话,未通话关闭连接
    go heartBeat(sendMsgTime, ch)
    // 起一个 goroutine 来发送数据
    go sendMsg(clientStream, ctx, sendMsgTime)
    // 阻塞,上面检查心跳可以给这个chan赋值,来取消阻塞
    <- ch
    // 长时间未通话,关闭 grpc 连接
    cancel()
    time.Sleep(1e9)
    log.Println("client end")
    
}
其它go协程代码

代码比较粗糙,大家简单的看下

// 客户端发送数据
func sendMsg(clientStream chat.ChatSvr_SendMessageClient, ctx context.Context, sendMsgTime *int64) {
    for {
        select {
        case <-ctx.Done(): // 检查是否关闭了连接
            return
        default:
            err := client2svr(clientStream, sendMsgTime)
            if err != nil {
                log.Println("send msg failed; err:", err)
                return
            }
        }
    }
}

func client2svr(clientStream chat.ChatSvr_SendMessageClient, sendMsgTime *int64) error {
    say := ""
    fmt.Scanf("%s\n", &say)
    if say == "over" {
        clientStream.CloseSend()
        fmt.Printf("    通话结束    \n")
        return nil
    }
    msg := &chat.MsgInfo{ChatRoomID:1,UserID:4567,UserName:"zyn",UserHeadImg: "love.jpg",Msg:say}
 
    msg.Msg = say
    err := clientStream.Send(msg)
    if err != nil {
        log.Println("send msg failed; err:", err)
        return err
    }
    *sendMsgTime = time.Now().Unix()
    return nil
}
// 检查多长时间未通话了
func heartBeat(t *int64, ch chan struct{}) {
    defer func(){
        ch <- struct{}{}
    }()
    for {
        time.Sleep(time.Second)
        nowTime := time.Now().Unix()
        sub := nowTime - *t
        // 超过 3 秒 关闭连接
        if sub > 3 {
            log.Println("长时间没有通话了[自动关闭连接]")
            return
        }
    }
}

func getRecvMsg(client chat.ChatSvr_SendMessageClient) {
    for {
        msg, err := client.Recv()
        if err != nil {
            if err == io.EOF {
               fmt.Println("END")
                return
            } else if status.Code(err) == codes.Canceled {// 获取 grpc 错误码状态,看是不是对方关闭了连接
                log.Println("服务端已经关闭了请求")
                return
            }
            log.Println("client recv failed; err:", err)
            return
        }
        fmt.Println("        ", msg.Msg, " :", msg.UserName)
    }
}

服务端代码

type server struct {}
func (s *server) SendMessage(recvStream chat.ChatSvr_SendMessageServer) error {
    // for 循环接收客户端发送的信息
    for {
        msgInfo, err := recvStream.Recv()
        if err != nil {
            if err == io.EOF {
                log.Println("客户端请求结束发送")
                return nil
            } else if status.Code(err) == codes.Canceled {// 获取错误码状态,看是否是客户端关闭了连接
                log.Println("客户端取消了连接")
                // 返回给客户端 grpc 错误码
                return status.Error(codes.Canceled, "server closed")
            }
            log.Println("recv failed; err:", err)
            return err
        }
        sendMsg := &chat.MsgInfo{ChatRoomID:msgInfo.ChatRoomID,UserID:123,UserName:"zzh",UserHeadImg:"img.jpg",Msg:""}
        switch msgInfo.Msg {
        case "你好":
            sendMsg.Msg = "你好"
            err = recvStream.Send(sendMsg)
        case "你在哪呢":
            sendMsg.Msg = "在家里呢"
            err = recvStream.Send(sendMsg)
        default:
            sendMsg.Msg = "没听清, 你再说一次"
            err = recvStream.Send(sendMsg)
        }
        if err != nil {
            log.Println("send msg failed; err : ", err)
            return err
        }
    }
}

上一篇:gRPC-07 客户端拦截器的实现

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值