go-micro.dev/v5@v5.12.0/internal/website/docs/guides/grpc-compatibility.md (about) 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.Server(grpcServer.NewServer()), // Server must come before Name 47 micro.Client(grpcClient.NewClient()), 48 micro.Name("helloworld"), 49 ) 50 ``` 51 52 > **Important**: The `micro.Server()` option must be specified **before** `micro.Name()`. This is because `micro.Name()` sets the name on the current server, and if `micro.Server()` comes after, it replaces the server with a new one that has no name set. 53 54 ## When to Use Which 55 56 | Use Case | Solution | 57 |----------|----------| 58 | Need native gRPC client compatibility | Use gRPC server/client | 59 | Need to call service with `grpcurl` | Use gRPC server | 60 | Need polyglot gRPC clients (Python, Java, etc.) | Use gRPC server | 61 | Only Go Micro services communicating | Either works | 62 | Want gRPC as a message protocol (like NATS) | Use gRPC transport | 63 64 ## Complete Example: Native gRPC Compatible Service 65 66 ### Proto Definition 67 68 ```protobuf 69 syntax = "proto3"; 70 71 package helloworld; 72 option go_package = "./proto;helloworld"; 73 74 service Say { 75 rpc Hello(Request) returns (Response) {} 76 } 77 78 message Request { 79 string name = 1; 80 } 81 82 message Response { 83 string message = 1; 84 } 85 ``` 86 87 ### Generate Code 88 89 ```bash 90 # Install protoc-gen-micro 91 go install go-micro.dev/v5/cmd/protoc-gen-micro@latest 92 93 # Generate Go code 94 protoc --proto_path=. \ 95 --go_out=. --go_opt=paths=source_relative \ 96 --micro_out=. --micro_opt=paths=source_relative \ 97 proto/helloworld.proto 98 ``` 99 100 ### Server Implementation 101 102 ```go 103 package main 104 105 import ( 106 "context" 107 "log" 108 109 "go-micro.dev/v5" 110 grpcServer "go-micro.dev/v5/server/grpc" 111 pb "example.com/helloworld/proto" 112 ) 113 114 type Say struct{} 115 116 func (s *Say) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { 117 rsp.Message = "Hello " + req.Name 118 return nil 119 } 120 121 func main() { 122 // Create service with gRPC server for native gRPC compatibility 123 // Note: Server must be set before Name to ensure the name is applied to the gRPC server 124 service := micro.NewService( 125 micro.Server(grpcServer.NewServer()), 126 micro.Name("helloworld"), 127 micro.Address(":8080"), 128 ) 129 130 service.Init() 131 132 // Register handler 133 pb.RegisterSayHandler(service.Server(), &Say{}) 134 135 // Run service 136 if err := service.Run(); err != nil { 137 log.Fatal(err) 138 } 139 } 140 ``` 141 142 ### Client Implementation (Go Micro) 143 144 ```go 145 package main 146 147 import ( 148 "context" 149 "fmt" 150 "log" 151 152 "go-micro.dev/v5" 153 grpcClient "go-micro.dev/v5/client/grpc" 154 pb "example.com/helloworld/proto" 155 ) 156 157 func main() { 158 // Create service with gRPC client 159 service := micro.NewService( 160 micro.Client(grpcClient.NewClient()), 161 micro.Name("helloworld.client"), 162 ) 163 service.Init() 164 165 // Create client - use the service name "helloworld" (not the proto package name) 166 // Go Micro uses this name for registry lookup, which may differ from the package name 167 sayService := pb.NewSayService("helloworld", service.Client()) 168 169 // Call service 170 rsp, err := sayService.Hello(context.Background(), &pb.Request{Name: "Alice"}) 171 if err != nil { 172 log.Fatal(err) 173 } 174 175 fmt.Println(rsp.Message) // Output: Hello Alice 176 } 177 ``` 178 179 ### Testing with grpcurl 180 181 Once your service is running with the gRPC server, you can use `grpcurl`: 182 183 ```bash 184 # List available services 185 grpcurl -plaintext localhost:8080 list 186 187 # Call the Hello method 188 grpcurl -proto ./proto/helloworld.proto \ 189 -plaintext \ 190 -d '{"name":"Alice"}' \ 191 localhost:8080 helloworld.Say.Hello 192 ``` 193 194 ## Using Both gRPC Server and Client Together 195 196 For full native gRPC compatibility (both inbound and outbound), use both: 197 198 ```go 199 package main 200 201 import ( 202 "go-micro.dev/v5" 203 grpcClient "go-micro.dev/v5/client/grpc" 204 grpcServer "go-micro.dev/v5/server/grpc" 205 ) 206 207 func main() { 208 service := micro.NewService( 209 micro.Server(grpcServer.NewServer()), // Server first 210 micro.Client(grpcClient.NewClient()), 211 micro.Name("helloworld"), // Name after Server 212 micro.Address(":8080"), 213 ) 214 215 service.Init() 216 // ... register handlers 217 service.Run() 218 } 219 ``` 220 221 ## Common Errors 222 223 ### "unknown service" Error with grpcurl 224 225 If you see this error: 226 227 ``` 228 ERROR: 229 Code: Unimplemented 230 Message: unknown service helloworld.Say 231 ``` 232 233 **Cause**: You're using the gRPC transport instead of the gRPC server. 234 235 **Solution**: Change from: 236 237 ```go 238 // Wrong - uses transport 239 t := grpc.NewTransport() 240 service := micro.NewService( 241 micro.Transport(t), 242 ) 243 ``` 244 245 To: 246 247 ```go 248 // Correct - uses server 249 import grpcServer "go-micro.dev/v5/server/grpc" 250 251 service := micro.NewService( 252 micro.Server(grpcServer.NewServer()), 253 ) 254 ``` 255 256 ### Import Path Confusion 257 258 Note the different import paths: 259 260 ```go 261 // Transport (NOT native gRPC compatible) 262 import "go-micro.dev/v5/transport/grpc" 263 264 // Server (native gRPC compatible) 265 import "go-micro.dev/v5/server/grpc" 266 267 // Client (native gRPC compatible) 268 import "go-micro.dev/v5/client/grpc" 269 ``` 270 271 ### Option Ordering Issue 272 273 If the gRPC server is working but your service has no name or is not being found in the registry: 274 275 **Cause**: The `micro.Server()` option is specified **after** `micro.Name()`. 276 277 When options are processed, `micro.Name()` sets the name on the current server. If `micro.Server()` comes later, it replaces the server with a new one that doesn't have the name set. 278 279 **Solution**: Always specify `micro.Server()` **before** `micro.Name()`: 280 281 ```go 282 // Wrong - server replaces the one with the name set 283 service := micro.NewService( 284 micro.Name("helloworld"), // Sets name on default server 285 micro.Server(grpcServer.NewServer()), // Replaces server, name is lost! 286 ) 287 288 // Correct - name is set on the gRPC server 289 service := micro.NewService( 290 micro.Server(grpcServer.NewServer()), // Set server first 291 micro.Name("helloworld"), // Name is now applied to gRPC server 292 ) 293 ``` 294 295 ### Service Name vs Package Name 296 297 When creating a client to call another service, use the **service name** (set via `micro.Name()`), not the proto package name: 298 299 ```go 300 // If the server was started with micro.Name("helloworld") 301 sayService := pb.NewSayService("helloworld", service.Client()) // Use service name 302 303 // NOT the package name from the proto file 304 // sayService := pb.NewSayService("helloworld.Say", service.Client()) // Wrong! 305 ``` 306 307 Go Micro uses the service name for registry lookup, which may differ from the proto package name. 308 309 ## Environment Variable Configuration 310 311 You can also configure the server and client via environment variables: 312 313 ```bash 314 # Use gRPC server 315 MICRO_SERVER=grpc go run main.go 316 317 # Use gRPC client 318 MICRO_CLIENT=grpc go run main.go 319 ``` 320 321 ## Summary 322 323 | Component | Import Path | Native gRPC Compatible | 324 |-----------|-------------|----------------------| 325 | Transport | `go-micro.dev/v5/transport/grpc` | ❌ No | 326 | Server | `go-micro.dev/v5/server/grpc` | ✅ Yes | 327 | Client | `go-micro.dev/v5/client/grpc` | ✅ Yes | 328 329 For native gRPC compatibility with tools like `grpcurl` or polyglot clients, always use the gRPC **server** and **client** packages, not the transport. 330 331 ## Related Documentation 332 333 - [Transport](../transport.md) - Understanding transports in Go Micro 334 - [Plugins](../plugins.md) - Available plugins including gRPC 335 - [Migration from gRPC](migration/from-grpc.md) - Migrating existing gRPC services