简介
在使用grpc的过程中, 有错误的情况下不管是服务器端还是客户端, 直接返回错误之后在另外一端出现了 rpc error: code = Unknown desc = xxxxxx, 所以, 不好进行判断, 下面就使用grpc包内的status 和 codes包, 使返回值方便解析和更好复用grpc错误, 顺道将一些技巧顺带使用
代码
download.proto
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 | syntax = "proto3"; //package download; //option csharp_namespace = "Google.Protobuf.WellKnownTypes"; option go_package = "../proto"; //option java_package = "com.google.protobuf"; //option java_outer_classname = "AnyProto"; //option java_multiple_files = true; option objc_class_prefix = "GPB"; service DownloadService{ rpc Download(DownloadRequest) returns (stream DownloadResponse) {}; } message DownloadRequest { string FilePath = 1; int32 MaxSize = 2; // 每一次下载的最大大小 单位MB } message DownloadResponse { string Name = 1; bytes Buffer = 2; bool IsDir = 3; } |
指令生成 download.pb.go
protoc -I ./ --go_out=plugins=grpc:. ./download.proto
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 | package main import ( pb "download/proto" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) var gDownloader = &Downloader{} type DownloadServer struct { } func (d *DownloadServer) Download(in *pb.DownloadRequest, stream pb.DownloadService_DownloadServer) error { err := gDownloader.Download(in.FilePath, func (buffer []byte) error{ return stream.Send(&pb.DownloadResponse{ Buffer: buffer, }) }) if EndOfError == err { return status.Error(codes.Code(EOF), CodeToStr(EOF)) } if NormallyCloseError == err { return status.Error(codes.Code(NORMALLY_EXIST), CodeToStr(NORMALLY_EXIST)) } return err } |
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 | package main import ( "context" pb "download/proto" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/status" "log" ) func download(client pb.DownloadServiceClient, r *pb.DownloadRequest) error { stream, err := client.Download(context.Background(), r) if err != nil { return err } for { resp, err := stream.Recv() status := status.Convert(err) if status.Code() == EOF { fmt.Println("文件正常结束...") break } if err != nil { return err } fmt.Println("返回值:", resp) } return nil } func Download(host string) error { var ( conn *grpc.ClientConn err error grpcClient pb.DownloadServiceClient ) conn, err = grpc.Dial(host, grpc.WithInsecure()) if err != nil { log.Fatalf("grpc.Dial err: %v", err) return err } defer conn.Close() grpcClient = pb.NewDownloadServiceClient(conn) return download(grpcClient, &pb.DownloadRequest{FilePath: string("D:\\Softwares\\360\\360Safe\\360Base.dll")}) } |
service.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 | package main import ( pb "download/proto" "google.golang.org/grpc" "log" "net" ) func RunDownloadService(network, host string) error { server := grpc.NewServer() pb.RegisterDownloadServiceServer(server, &DownloadServer{}) lis, err := net.Listen(network, host) if err != nil { log.Fatalf("net.Listen err: %v", err) return err } err = server.Serve(lis) if err != nil { log.Fatalf("net.Listen err: %v", err) } return err } |
codes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main import "io" const ( EOF = 1024 + iota NORMALLY_EXIST ) var codeToStr = map[int] string { EOF: io.EOF.Error(), NORMALLY_EXIST: "normally close", } func CodeToStr(code int) string { v, _ := codeToStr[code] return v } |
downloader.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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | package main import ( "errors" "io" "os" "sync" ) var NormallyCloseError = errors.New(CodeToStr(NORMALLY_EXIST)) var EndOfError = errors.New(CodeToStr(EOF)) type Downloader struct { waiter sync.WaitGroup mu sync.Mutex exitChans []chan int } func (d *Downloader) WaitForDone() { d.waiter.Wait() } func (d *Downloader) CacelAllAndWaitForDone() { d.mu.Lock() defer d.mu.Unlock() d.SendToExitChans() d.waiter.Wait() d.freeAllExitChan() } func (d *Downloader) NewExitChan() chan int { d.mu.Lock() defer d.mu.Unlock() return d.newExitChan() } func (d *Downloader) FreeExitChan(c chan int) { d.mu.Lock() defer d.mu.Unlock() d.freeExitChan(c) } func (d *Downloader) FreeAllExitChan() { d.mu.Lock() defer d.mu.Unlock() d.freeAllExitChan() } func (d *Downloader) SendToExitChans() { d.mu.Lock() defer d.mu.Unlock() for i, _ := range d.exitChans { go func() { d.exitChans[i] <- 1 } () } } func (d *Downloader) newExitChan() chan int { if nil == d.exitChans { d.exitChans = make([]chan int, 0) } var chn = make(chan int, 1) d.exitChans = append(d.exitChans, chn) return chn } func (d *Downloader) freeExitChan(c chan int) { for i, chn := range d.exitChans { if chn == c { d.exitChans[i] = nil d.exitChans = append(d.exitChans[:i], d.exitChans[i+1:]...) break } } } func (d *Downloader) freeAllExitChan() { for i, _ := range d.exitChans { d.exitChans[i] = nil } d.exitChans = nil } func (d *Downloader) Download(dst string, f func (buffer []byte) error) error { d.waiter.Add(1) defer d.waiter.Done() exitChan := d.NewExitChan() defer func() { d.FreeExitChan(exitChan) } () var file, err = os.Open(dst) if nil != err { return err } defer file.Close() var maxSize = 1024 var buffer = make([]byte, maxSize) var n int for { select { case <-exitChan: return NormallyCloseError default: n, err = file.Read(buffer) if nil != err { if io.EOF != err { return err } } if 0 < n { err = f(buffer[:n]) if nil != err { return nil } } } if n < maxSize { return EndOfError } } } |
资源
代码 - https://download.csdn.net/download/halo_hsuh/12547363