Skip to content

Commit 00a1194

Browse files
Copilotasim
andauthored
Add documentation for native gRPC compatibility vs transport (#2819)
* Initial plan * Add documentation for native gRPC compatibility with grpc client/server packages Co-authored-by: asim <[email protected]> * Fix go_package path in proto example to use relative path Co-authored-by: asim <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: asim <[email protected]>
1 parent 3e25be9 commit 00a1194

File tree

4 files changed

+347
-3
lines changed

4 files changed

+347
-3
lines changed

internal/website/docs/guides/comparison.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,21 @@ func main() {
133133

134134
### Integration
135135

136-
You can use gRPC with Go Micro:
136+
You can use gRPC with Go Micro for native gRPC compatibility:
137137
```go
138-
import "go-micro.dev/v5/client/grpc"
138+
import (
139+
grpcServer "go-micro.dev/v5/server/grpc"
140+
grpcClient "go-micro.dev/v5/client/grpc"
141+
)
139142

140143
svc := micro.NewService(
141-
micro.Client(grpc.NewClient()),
144+
micro.Server(grpcServer.NewServer()),
145+
micro.Client(grpcClient.NewClient()),
142146
)
143147
```
144148

149+
See [Native gRPC Compatibility](grpc-compatibility.md) for a complete guide.
150+
145151
## vs Dapr
146152

147153
### Dapr Approach
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
---
2+
layout: default
3+
---
4+
5+
# Native gRPC Compatibility
6+
7+
This guide explains how to make your Go Micro services compatible with native gRPC clients like `grpcurl`, `grpcui`, or clients generated by the standard `protoc` gRPC plugin in any language.
8+
9+
## Understanding Transport vs Server
10+
11+
Go Micro has two different gRPC-related concepts that are often confused:
12+
13+
### gRPC Transport (`go-micro.dev/v5/transport/grpc`)
14+
15+
The gRPC **transport** uses the gRPC protocol as a communication layer, similar to how you might use NATS, RabbitMQ, or HTTP. It does **not** guarantee compatibility with native gRPC clients.
16+
17+
```go
18+
// This uses gRPC as transport but is NOT compatible with native gRPC clients
19+
import "go-micro.dev/v5/transport/grpc"
20+
21+
t := grpc.NewTransport()
22+
service := micro.NewService(
23+
micro.Name("helloworld"),
24+
micro.Transport(t),
25+
)
26+
```
27+
28+
When using the gRPC transport:
29+
- Communication between Go Micro services works fine
30+
- Native gRPC clients (grpcurl, etc.) will fail with "Unimplemented" errors
31+
- The protocol is used like a message bus, not as a standard gRPC server
32+
33+
### gRPC Server/Client (`go-micro.dev/v5/server/grpc` and `go-micro.dev/v5/client/grpc`)
34+
35+
The gRPC **server** and **client** provide native gRPC compatibility. These implement a proper gRPC server that any gRPC client can communicate with.
36+
37+
```go
38+
// This IS compatible with native gRPC clients
39+
import (
40+
"go-micro.dev/v5"
41+
grpcServer "go-micro.dev/v5/server/grpc"
42+
grpcClient "go-micro.dev/v5/client/grpc"
43+
)
44+
45+
service := micro.NewService(
46+
micro.Name("helloworld"),
47+
micro.Server(grpcServer.NewServer()),
48+
micro.Client(grpcClient.NewClient()),
49+
)
50+
```
51+
52+
## When to Use Which
53+
54+
| Use Case | Solution |
55+
|----------|----------|
56+
| Need native gRPC client compatibility | Use gRPC server/client |
57+
| Need to call service with `grpcurl` | Use gRPC server |
58+
| Need polyglot gRPC clients (Python, Java, etc.) | Use gRPC server |
59+
| Only Go Micro services communicating | Either works |
60+
| Want gRPC as a message protocol (like NATS) | Use gRPC transport |
61+
62+
## Complete Example: Native gRPC Compatible Service
63+
64+
### Proto Definition
65+
66+
```protobuf
67+
syntax = "proto3";
68+
69+
package helloworld;
70+
option go_package = "./proto;helloworld";
71+
72+
service Say {
73+
rpc Hello(Request) returns (Response) {}
74+
}
75+
76+
message Request {
77+
string name = 1;
78+
}
79+
80+
message Response {
81+
string message = 1;
82+
}
83+
```
84+
85+
### Generate Code
86+
87+
```bash
88+
# Install protoc-gen-micro
89+
go install go-micro.dev/v5/cmd/protoc-gen-micro@latest
90+
91+
# Generate Go code
92+
protoc --proto_path=. \
93+
--go_out=. --go_opt=paths=source_relative \
94+
--micro_out=. --micro_opt=paths=source_relative \
95+
proto/helloworld.proto
96+
```
97+
98+
### Server Implementation
99+
100+
```go
101+
package main
102+
103+
import (
104+
"context"
105+
"log"
106+
107+
"go-micro.dev/v5"
108+
grpcServer "go-micro.dev/v5/server/grpc"
109+
pb "example.com/helloworld/proto"
110+
)
111+
112+
type Say struct{}
113+
114+
func (s *Say) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
115+
rsp.Message = "Hello " + req.Name
116+
return nil
117+
}
118+
119+
func main() {
120+
// Create service with gRPC server for native gRPC compatibility
121+
service := micro.NewService(
122+
micro.Name("helloworld"),
123+
micro.Address(":8080"),
124+
micro.Server(grpcServer.NewServer()),
125+
)
126+
127+
service.Init()
128+
129+
// Register handler
130+
pb.RegisterSayHandler(service.Server(), &Say{})
131+
132+
// Run service
133+
if err := service.Run(); err != nil {
134+
log.Fatal(err)
135+
}
136+
}
137+
```
138+
139+
### Client Implementation (Go Micro)
140+
141+
```go
142+
package main
143+
144+
import (
145+
"context"
146+
"fmt"
147+
"log"
148+
149+
"go-micro.dev/v5"
150+
grpcClient "go-micro.dev/v5/client/grpc"
151+
pb "example.com/helloworld/proto"
152+
)
153+
154+
func main() {
155+
// Create service with gRPC client
156+
service := micro.NewService(
157+
micro.Name("helloworld.client"),
158+
micro.Client(grpcClient.NewClient()),
159+
)
160+
service.Init()
161+
162+
// Create client
163+
sayService := pb.NewSayService("helloworld", service.Client())
164+
165+
// Call service
166+
rsp, err := sayService.Hello(context.Background(), &pb.Request{Name: "Alice"})
167+
if err != nil {
168+
log.Fatal(err)
169+
}
170+
171+
fmt.Println(rsp.Message) // Output: Hello Alice
172+
}
173+
```
174+
175+
### Testing with grpcurl
176+
177+
Once your service is running with the gRPC server, you can use `grpcurl`:
178+
179+
```bash
180+
# List available services
181+
grpcurl -plaintext localhost:8080 list
182+
183+
# Call the Hello method
184+
grpcurl -proto ./proto/helloworld.proto \
185+
-plaintext \
186+
-d '{"name":"Alice"}' \
187+
localhost:8080 helloworld.Say.Hello
188+
```
189+
190+
## Using Both gRPC Server and Client Together
191+
192+
For full native gRPC compatibility (both inbound and outbound), use both:
193+
194+
```go
195+
package main
196+
197+
import (
198+
"go-micro.dev/v5"
199+
grpcClient "go-micro.dev/v5/client/grpc"
200+
grpcServer "go-micro.dev/v5/server/grpc"
201+
)
202+
203+
func main() {
204+
service := micro.NewService(
205+
micro.Name("helloworld"),
206+
micro.Address(":8080"),
207+
micro.Server(grpcServer.NewServer()),
208+
micro.Client(grpcClient.NewClient()),
209+
)
210+
211+
service.Init()
212+
// ... register handlers
213+
service.Run()
214+
}
215+
```
216+
217+
## Common Errors
218+
219+
### "unknown service" Error with grpcurl
220+
221+
If you see this error:
222+
223+
```
224+
ERROR:
225+
Code: Unimplemented
226+
Message: unknown service helloworld.Say
227+
```
228+
229+
**Cause**: You're using the gRPC transport instead of the gRPC server.
230+
231+
**Solution**: Change from:
232+
233+
```go
234+
// Wrong - uses transport
235+
t := grpc.NewTransport()
236+
service := micro.NewService(
237+
micro.Transport(t),
238+
)
239+
```
240+
241+
To:
242+
243+
```go
244+
// Correct - uses server
245+
import grpcServer "go-micro.dev/v5/server/grpc"
246+
247+
service := micro.NewService(
248+
micro.Server(grpcServer.NewServer()),
249+
)
250+
```
251+
252+
### Import Path Confusion
253+
254+
Note the different import paths:
255+
256+
```go
257+
// Transport (NOT native gRPC compatible)
258+
import "go-micro.dev/v5/transport/grpc"
259+
260+
// Server (native gRPC compatible)
261+
import "go-micro.dev/v5/server/grpc"
262+
263+
// Client (native gRPC compatible)
264+
import "go-micro.dev/v5/client/grpc"
265+
```
266+
267+
## Environment Variable Configuration
268+
269+
You can also configure the server and client via environment variables:
270+
271+
```bash
272+
# Use gRPC server
273+
MICRO_SERVER=grpc go run main.go
274+
275+
# Use gRPC client
276+
MICRO_CLIENT=grpc go run main.go
277+
```
278+
279+
## Summary
280+
281+
| Component | Import Path | Native gRPC Compatible |
282+
|-----------|-------------|----------------------|
283+
| Transport | `go-micro.dev/v5/transport/grpc` | ❌ No |
284+
| Server | `go-micro.dev/v5/server/grpc` | ✅ Yes |
285+
| Client | `go-micro.dev/v5/client/grpc` | ✅ Yes |
286+
287+
For native gRPC compatibility with tools like `grpcurl` or polyglot clients, always use the gRPC **server** and **client** packages, not the transport.
288+
289+
## Related Documentation
290+
291+
- [Transport](../transport.md) - Understanding transports in Go Micro
292+
- [Plugins](../plugins.md) - Available plugins including gRPC
293+
- [Migration from gRPC](migration/from-grpc.md) - Migrating existing gRPC services

internal/website/docs/plugins.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Common interfaces and locations:
1010
- Registry: `go-micro.dev/v5/registry/*` (e.g. `consul`, `etcd`, `nats`, `mdns`)
1111
- Broker: `go-micro.dev/v5/broker/*` (e.g. `nats`, `rabbitmq`, `http`, `memory`)
1212
- Transport: `go-micro.dev/v5/transport/*` (e.g. `nats`, default `http`)
13+
- Server: `go-micro.dev/v5/server/*` (e.g. `grpc` for native gRPC compatibility)
14+
- Client: `go-micro.dev/v5/client/*` (e.g. `grpc` for native gRPC compatibility)
1315
- Store: `go-micro.dev/v5/store/*` (e.g. `postgres`, `mysql`, `nats-js-kv`, `memory`)
1416
- Auth, Cache, etc. follow the same pattern under their respective directories.
1517

@@ -94,6 +96,29 @@ func main() {
9496
}
9597
```
9698

99+
## gRPC Server/Client (Native gRPC Compatibility)
100+
101+
For native gRPC compatibility (required for `grpcurl`, polyglot gRPC clients, etc.), use the gRPC server and client plugins. Note: This is different from the gRPC transport.
102+
103+
```go
104+
import (
105+
"go-micro.dev/v5"
106+
grpcServer "go-micro.dev/v5/server/grpc"
107+
grpcClient "go-micro.dev/v5/client/grpc"
108+
)
109+
110+
func main() {
111+
svc := micro.NewService(
112+
micro.Server(grpcServer.NewServer()),
113+
micro.Client(grpcClient.NewClient()),
114+
)
115+
svc.Init()
116+
svc.Run()
117+
}
118+
```
119+
120+
See [Native gRPC Compatibility](guides/grpc-compatibility.md) for a complete guide.
121+
97122
## Store Examples
98123

99124
Postgres:

internal/website/docs/transport.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,26 @@ Supported transports include:
1717
- gRPC (`go-micro.dev/v5/transport/grpc`)
1818
- Memory (`go-micro.dev/v5/transport/memory`)
1919

20+
## Important: Transport vs Native gRPC
21+
22+
The gRPC **transport** uses gRPC as an underlying communication protocol, similar to how NATS or RabbitMQ might be used. It does **not** provide native gRPC compatibility with tools like `grpcurl` or standard gRPC clients generated by `protoc`.
23+
24+
If you need native gRPC compatibility (to use `grpcurl`, polyglot gRPC clients, etc.), you must use the gRPC **server** and **client** packages instead:
25+
26+
```go
27+
import (
28+
grpcServer "go-micro.dev/v5/server/grpc"
29+
grpcClient "go-micro.dev/v5/client/grpc"
30+
)
31+
32+
service := micro.NewService(
33+
micro.Server(grpcServer.NewServer()),
34+
micro.Client(grpcClient.NewClient()),
35+
)
36+
```
37+
38+
See [Native gRPC Compatibility](guides/grpc-compatibility.md) for a complete guide.
39+
2040
Plugins are scoped under `go-micro.dev/v5/transport/<plugin>`.
2141

2242
You can specify the transport when initializing your service or via env vars.

0 commit comments

Comments
 (0)