简介 在使用grpc的过程中, 有错误的情况下不管是服务器端还是客户端, 直接返回错误之后在另外一端出现了 rpc error: code = Unknown desc = xxxxxx, 所以, 不好进行判断, 下面就使用grpc包内的status 和 codes包, 使返回值方便解析和更好复用grpc错误, 顺道将一些技巧顺带使用
代码
download.proto
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;
}message DownloadResponse {
bytesBuffer = 2;
}
指令生成 download.pb.go protoc -I ./ --go_out=plugins=grpc:. ./download.proto
server.go
package mainimport (
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
package mainimport (
"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
errerror
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
package mainimport (
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
package mainimport "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
package mainimport (
"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
【Go : grpc下载文件+Status+Code】