Skip to content

Commit a4086f3

Browse files
committed
Add grpc plugin
1 parent 01b8394 commit a4086f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5582
-0
lines changed

client/grpc/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# GRPC Client
2+
3+
The grpc client is a [micro.Client](https://godoc.org/github.com/micro/go-micro/client#Client) compatible client.
4+
5+
## Overview
6+
7+
The client makes use of the [google.golang.org/grpc](google.golang.org/grpc) framework for the underlying communication mechanism.
8+
9+
## Usage
10+
11+
Specify the client to your micro service
12+
13+
```go
14+
import (
15+
"github.com/micro/go-micro"
16+
"github.com/micro/go-plugins/client/grpc"
17+
)
18+
19+
func main() {
20+
service := micro.NewService(
21+
micro.Name("greeter"),
22+
micro.Client(grpc.NewClient()),
23+
)
24+
}
25+
```

client/grpc/codec.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package grpc
2+
3+
import (
4+
b "bytes"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
9+
"go-micro.dev/v5/codec"
10+
"go-micro.dev/v5/codec/bytes"
11+
"google.golang.org/grpc"
12+
"google.golang.org/grpc/encoding"
13+
"google.golang.org/protobuf/encoding/protojson"
14+
"google.golang.org/protobuf/proto"
15+
"google.golang.org/protobuf/runtime/protoiface"
16+
"google.golang.org/protobuf/runtime/protoimpl"
17+
)
18+
19+
type jsonCodec struct{}
20+
type protoCodec struct{}
21+
type bytesCodec struct{}
22+
type wrapCodec struct{ encoding.Codec }
23+
24+
var useNumber bool
25+
26+
var (
27+
defaultGRPCCodecs = map[string]encoding.Codec{
28+
"application/json": jsonCodec{},
29+
"application/proto": protoCodec{},
30+
"application/protobuf": protoCodec{},
31+
"application/octet-stream": protoCodec{},
32+
"application/grpc": protoCodec{},
33+
"application/grpc+json": jsonCodec{},
34+
"application/grpc+proto": protoCodec{},
35+
"application/grpc+bytes": bytesCodec{},
36+
}
37+
)
38+
39+
// UseNumber fix unmarshal Number(8234567890123456789) to interface(8.234567890123457e+18).
40+
func UseNumber() {
41+
useNumber = true
42+
}
43+
44+
func (w wrapCodec) String() string {
45+
return w.Codec.Name()
46+
}
47+
48+
func (w wrapCodec) Marshal(v interface{}) ([]byte, error) {
49+
b, ok := v.(*bytes.Frame)
50+
if ok {
51+
return b.Data, nil
52+
}
53+
return w.Codec.Marshal(v)
54+
}
55+
56+
func (w wrapCodec) Unmarshal(data []byte, v interface{}) error {
57+
b, ok := v.(*bytes.Frame)
58+
if ok {
59+
b.Data = data
60+
return nil
61+
}
62+
return w.Codec.Unmarshal(data, v)
63+
}
64+
65+
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
66+
switch m := v.(type) {
67+
case *bytes.Frame:
68+
return m.Data, nil
69+
case proto.Message:
70+
return proto.Marshal(m)
71+
case protoiface.MessageV1:
72+
// #2333 compatible with etcd legacy proto.Message
73+
m2 := protoimpl.X.ProtoMessageV2Of(m)
74+
return proto.Marshal(m2)
75+
}
76+
return nil, fmt.Errorf("failed to marshal: %v is not type of *bytes.Frame or proto.Message", v)
77+
}
78+
79+
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
80+
switch m := v.(type) {
81+
case proto.Message:
82+
return proto.Unmarshal(data, m)
83+
case protoiface.MessageV1:
84+
// #2333 compatible with etcd legacy proto.Message
85+
m2 := protoimpl.X.ProtoMessageV2Of(m)
86+
return proto.Unmarshal(data, m2)
87+
}
88+
return fmt.Errorf("failed to unmarshal: %v is not type of proto.Message", v)
89+
}
90+
91+
func (protoCodec) Name() string {
92+
return "proto"
93+
}
94+
95+
func (bytesCodec) Marshal(v interface{}) ([]byte, error) {
96+
b, ok := v.(*[]byte)
97+
if !ok {
98+
return nil, fmt.Errorf("failed to marshal: %v is not type of *[]byte", v)
99+
}
100+
return *b, nil
101+
}
102+
103+
func (bytesCodec) Unmarshal(data []byte, v interface{}) error {
104+
b, ok := v.(*[]byte)
105+
if !ok {
106+
return fmt.Errorf("failed to unmarshal: %v is not type of *[]byte", v)
107+
}
108+
*b = data
109+
return nil
110+
}
111+
112+
func (bytesCodec) Name() string {
113+
return "bytes"
114+
}
115+
116+
func (jsonCodec) Marshal(v interface{}) ([]byte, error) {
117+
if b, ok := v.(*bytes.Frame); ok {
118+
return b.Data, nil
119+
}
120+
121+
if pb, ok := v.(proto.Message); ok {
122+
bytes, err := protojson.Marshal(pb)
123+
if err != nil {
124+
return nil, err
125+
}
126+
return bytes, nil
127+
}
128+
129+
return json.Marshal(v)
130+
}
131+
132+
func (jsonCodec) Unmarshal(data []byte, v interface{}) error {
133+
if len(data) == 0 {
134+
return nil
135+
}
136+
if b, ok := v.(*bytes.Frame); ok {
137+
b.Data = data
138+
return nil
139+
}
140+
if pb, ok := v.(proto.Message); ok {
141+
return protojson.Unmarshal(data, pb)
142+
}
143+
144+
dec := json.NewDecoder(b.NewReader(data))
145+
if useNumber {
146+
dec.UseNumber()
147+
}
148+
return dec.Decode(v)
149+
}
150+
151+
func (jsonCodec) Name() string {
152+
return "json"
153+
}
154+
155+
type grpcCodec struct {
156+
// headers
157+
id string
158+
target string
159+
method string
160+
endpoint string
161+
162+
s grpc.ClientStream
163+
c encoding.Codec
164+
}
165+
166+
func (g *grpcCodec) ReadHeader(m *codec.Message, mt codec.MessageType) error {
167+
md, err := g.s.Header()
168+
if err != nil {
169+
return err
170+
}
171+
if m == nil {
172+
m = new(codec.Message)
173+
}
174+
if m.Header == nil {
175+
m.Header = make(map[string]string, len(md))
176+
}
177+
for k, v := range md {
178+
m.Header[k] = strings.Join(v, ",")
179+
}
180+
m.Id = g.id
181+
m.Target = g.target
182+
m.Method = g.method
183+
m.Endpoint = g.endpoint
184+
return nil
185+
}
186+
187+
func (g *grpcCodec) ReadBody(v interface{}) error {
188+
if f, ok := v.(*bytes.Frame); ok {
189+
return g.s.RecvMsg(f)
190+
}
191+
return g.s.RecvMsg(v)
192+
}
193+
194+
func (g *grpcCodec) Write(m *codec.Message, v interface{}) error {
195+
// if we don't have a body
196+
if v != nil {
197+
return g.s.SendMsg(v)
198+
}
199+
// write the body using the framing codec
200+
return g.s.SendMsg(&bytes.Frame{Data: m.Body})
201+
}
202+
203+
func (g *grpcCodec) Close() error {
204+
return g.s.CloseSend()
205+
}
206+
207+
func (g *grpcCodec) String() string {
208+
return g.c.Name()
209+
}

client/grpc/error.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package grpc
2+
3+
import (
4+
"net/http"
5+
6+
"go-micro.dev/v5/errors"
7+
"google.golang.org/grpc/codes"
8+
"google.golang.org/grpc/status"
9+
)
10+
11+
func microError(err error) error {
12+
// no error
13+
switch err {
14+
case nil:
15+
return nil
16+
}
17+
18+
if verr, ok := err.(*errors.Error); ok {
19+
return verr
20+
}
21+
22+
// grpc error
23+
s, ok := status.FromError(err)
24+
if !ok {
25+
return err
26+
}
27+
28+
// return first error from details
29+
if details := s.Details(); len(details) > 0 {
30+
return microError(details[0].(error))
31+
}
32+
33+
// try to decode micro *errors.Error
34+
if e := errors.Parse(s.Message()); e.Code > 0 {
35+
return e // actually a micro error
36+
}
37+
38+
// fallback
39+
return errors.New("go.micro.client", s.Message(), microStatusFromGrpcCode(s.Code()))
40+
}
41+
42+
func microStatusFromGrpcCode(code codes.Code) int32 {
43+
switch code {
44+
case codes.OK:
45+
return http.StatusOK
46+
case codes.InvalidArgument:
47+
return http.StatusBadRequest
48+
case codes.DeadlineExceeded:
49+
return http.StatusRequestTimeout
50+
case codes.NotFound:
51+
return http.StatusNotFound
52+
case codes.AlreadyExists:
53+
return http.StatusConflict
54+
case codes.PermissionDenied:
55+
return http.StatusForbidden
56+
case codes.Unauthenticated:
57+
return http.StatusUnauthorized
58+
case codes.FailedPrecondition:
59+
return http.StatusPreconditionFailed
60+
case codes.Unimplemented:
61+
return http.StatusNotImplemented
62+
case codes.Internal:
63+
return http.StatusInternalServerError
64+
case codes.Unavailable:
65+
return http.StatusServiceUnavailable
66+
}
67+
68+
return http.StatusInternalServerError
69+
}

client/grpc/go.mod

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module go-micro.dev/v5/client/grpc
2+
3+
go 1.24
4+
5+
require (
6+
go-micro.dev/v5 v5.5.1-0.20250507183911-01b8394c8119
7+
google.golang.org/grpc v1.53.0
8+
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e
9+
google.golang.org/protobuf v1.33.0
10+
)
11+
12+
require (
13+
github.com/bitly/go-simplejson v0.5.0 // indirect
14+
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
15+
github.com/fsnotify/fsnotify v1.6.0 // indirect
16+
github.com/golang/protobuf v1.5.4 // indirect
17+
github.com/google/uuid v1.3.0 // indirect
18+
github.com/imdario/mergo v0.3.13 // indirect
19+
github.com/miekg/dns v1.1.50 // indirect
20+
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
21+
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
22+
github.com/pkg/errors v0.9.1 // indirect
23+
github.com/rogpeppe/go-internal v1.12.0 // indirect
24+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
25+
github.com/urfave/cli/v2 v2.25.7 // indirect
26+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
27+
go.etcd.io/bbolt v1.4.0 // indirect
28+
golang.org/x/mod v0.9.0 // indirect
29+
golang.org/x/net v0.23.0 // indirect
30+
golang.org/x/sync v0.10.0 // indirect
31+
golang.org/x/sys v0.29.0 // indirect
32+
golang.org/x/text v0.14.0 // indirect
33+
golang.org/x/tools v0.7.0 // indirect
34+
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
35+
)

0 commit comments

Comments
 (0)