gitee.com/h79/goutils@v1.22.10/rpc/client.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "fmt" 6 "gitee.com/h79/goutils/alarm" 7 "gitee.com/h79/goutils/common/logger" 8 "gitee.com/h79/goutils/common/result" 9 "google.golang.org/grpc" 10 "google.golang.org/grpc/credentials/insecure" 11 "google.golang.org/grpc/health/grpc_health_v1" 12 "sync" 13 "time" 14 ) 15 16 func NewClient(addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { 17 logger.D("rpc", "NewClient, addr= %s", addr) 18 var dialOpts []grpc.DialOption 19 if len(opts) <= 0 { 20 dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) 21 } else { 22 dialOpts = append(dialOpts, opts...) 23 } 24 conn, err := grpc.Dial(addr, dialOpts...) 25 if err != nil { 26 return nil, result.Errorf(result.ErrClientConnectInternal, "did not connect failure, err= %v", err).Log() 27 } 28 return conn, nil 29 } 30 31 func Close(client *grpc.ClientConn) { 32 if client != nil { 33 _ = client.Close() 34 } 35 } 36 37 type Client struct { 38 Config 39 health HealthClient 40 conn *grpc.ClientConn 41 rm sync.Mutex 42 } 43 44 func (cli *Client) CreateConnect(opts ...grpc.DialOption) (*grpc.ClientConn, error) { 45 cli.rm.Lock() 46 defer cli.rm.Unlock() 47 err := cli.connect(opts...) 48 return cli.conn, err 49 } 50 51 func (cli *Client) connect(opts ...grpc.DialOption) error { 52 if cli.conn != nil { 53 return nil 54 } 55 conn, err := NewClient(cli.Server, opts...) 56 if err != nil { 57 return err 58 } 59 cli.conn = conn 60 return nil 61 } 62 63 func (cli *Client) Close() { 64 cli.close(cli.conn) 65 } 66 67 func (cli *Client) close(conn *grpc.ClientConn) { 68 var c = conn 69 cli.rm.Lock() 70 if conn == cli.conn { 71 c = cli.conn 72 cli.conn = nil 73 } 74 cli.rm.Unlock() 75 Close(c) 76 } 77 78 func (cli *Client) CheckHealth() (int, error) { 79 start := time.Now() 80 conn, err := cli.CreateConnect() 81 if err != nil { 82 return 0, err 83 } 84 // Set up a connection to the server. 85 if conn == nil { 86 return int(grpc_health_v1.HealthCheckResponse_UNKNOWN), result.RErrNil 87 } 88 89 rsp, err := cli.health.Check(conn, cli.Service, cli.Timeout) 90 91 if err != nil { 92 const method = "Check" 93 return rsp, cli.HandlerError(conn, err, start, method) 94 } 95 return rsp, nil 96 } 97 98 func (cli *Client) WatchHealth() (grpc_health_v1.Health_WatchClient, error) { 99 start := time.Now() 100 conn, err := cli.CreateConnect() 101 if err != nil { 102 return nil, err 103 } 104 // Set up a connection to the server. 105 if conn == nil { 106 return nil, result.RErrNil 107 } 108 109 rsp, err := cli.health.Watch(conn, cli.Service, cli.Timeout) 110 111 if err != nil { 112 const method = "Watch" 113 return nil, cli.HandlerError(conn, err, start, method) 114 } 115 return rsp, nil 116 } 117 118 // HandlerError "State":"IDLE","Status":"rpc error: code = Unknown desc = rpc error: code = DeadlineExceeded desc = context deadline exceeded"} 119 func (cli *Client) HandlerError(conn *grpc.ClientConn, err error, start time.Time, method string) error { 120 st := WithStatus(err) 121 if st != nil { 122 logger.E("rpc", "HandlerError connState= %s, status= %s", conn.GetState().String(), st.String()) 123 if IsNeedAlarm(st) { 124 cli.close(conn) 125 cli.Alarm(method, time.Now().Sub(start), err) 126 } 127 if IsDeadlineExceeded(st) { 128 return result.ErrCode(result.ErrDeadlineExceeded).WithError(err) 129 } 130 } 131 return err 132 } 133 134 func (cli *Client) Alarm(method string, latency time.Duration, err error) { 135 alarm.Fatal(context.Background(), -1, "rpc", cli.Title, fmt.Sprintf("method= '%s',url= '%s',latency= '%v'", method, cli.Server, latency), err) 136 }