e071084ebe
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
305 lines
7.3 KiB
Markdown
305 lines
7.3 KiB
Markdown
---
|
|
layout: default
|
|
---
|
|
|
|
# Native gRPC Compatibility
|
|
|
|
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.
|
|
|
|
## Understanding Transport vs Server
|
|
|
|
Go Micro has two different gRPC-related concepts that are often confused:
|
|
|
|
### gRPC Transport (`go-micro.dev/v6/transport/grpc`)
|
|
|
|
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.
|
|
|
|
```go
|
|
// This uses gRPC as transport but is NOT compatible with native gRPC clients
|
|
import "go-micro.dev/v6/transport/grpc"
|
|
|
|
t := grpc.NewTransport()
|
|
service := micro.NewService("helloworld",
|
|
micro.Transport(t),
|
|
)
|
|
```
|
|
|
|
When using the gRPC transport:
|
|
- Communication between Go Micro services works fine
|
|
- Native gRPC clients (grpcurl, etc.) will fail with "Unimplemented" errors
|
|
- The protocol is used like a message bus, not as a standard gRPC server
|
|
|
|
### gRPC Server/Client (`go-micro.dev/v6/server/grpc` and `go-micro.dev/v6/client/grpc`)
|
|
|
|
The gRPC **server** and **client** provide native gRPC compatibility. These implement a proper gRPC server that any gRPC client can communicate with.
|
|
|
|
```go
|
|
// This IS compatible with native gRPC clients
|
|
import (
|
|
"go-micro.dev/v6"
|
|
grpcServer "go-micro.dev/v6/server/grpc"
|
|
grpcClient "go-micro.dev/v6/client/grpc"
|
|
)
|
|
|
|
service := micro.NewService("helloworld",
|
|
micro.Server(grpcServer.NewServer()),
|
|
micro.Client(grpcClient.NewClient()),
|
|
)
|
|
```
|
|
|
|
## When to Use Which
|
|
|
|
| Use Case | Solution |
|
|
|----------|----------|
|
|
| Need native gRPC client compatibility | Use gRPC server/client |
|
|
| Need to call service with `grpcurl` | Use gRPC server |
|
|
| Need polyglot gRPC clients (Python, Java, etc.) | Use gRPC server |
|
|
| Only Go Micro services communicating | Either works |
|
|
| Want gRPC as a message protocol (like NATS) | Use gRPC transport |
|
|
|
|
## Complete Example: Native gRPC Compatible Service
|
|
|
|
### Proto Definition
|
|
|
|
```protobuf
|
|
syntax = "proto3";
|
|
|
|
package helloworld;
|
|
option go_package = "./proto;helloworld";
|
|
|
|
service Say {
|
|
rpc Hello(Request) returns (Response) {}
|
|
}
|
|
|
|
message Request {
|
|
string name = 1;
|
|
}
|
|
|
|
message Response {
|
|
string message = 1;
|
|
}
|
|
```
|
|
|
|
### Generate Code
|
|
|
|
```bash
|
|
# Install protoc-gen-micro
|
|
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest
|
|
|
|
# Generate Go code
|
|
protoc --proto_path=. \
|
|
--go_out=. --go_opt=paths=source_relative \
|
|
--micro_out=. --micro_opt=paths=source_relative \
|
|
proto/helloworld.proto
|
|
```
|
|
|
|
### Server Implementation
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
|
|
"go-micro.dev/v6"
|
|
grpcServer "go-micro.dev/v6/server/grpc"
|
|
pb "example.com/helloworld/proto"
|
|
)
|
|
|
|
type Say struct{}
|
|
|
|
func (s *Say) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
|
|
rsp.Message = "Hello " + req.Name
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
// Create service with gRPC server for native gRPC compatibility
|
|
// Note: Server must be set before Name to ensure the name is applied to the gRPC server
|
|
service := micro.NewService("helloworld",
|
|
micro.Server(grpcServer.NewServer()),
|
|
micro.Address(":8080"),
|
|
)
|
|
|
|
service.Init()
|
|
|
|
// Register handler
|
|
pb.RegisterSayHandler(service.Server(), &Say{})
|
|
|
|
// Run service
|
|
if err := service.Run(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Client Implementation (Go Micro)
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"go-micro.dev/v6"
|
|
grpcClient "go-micro.dev/v6/client/grpc"
|
|
pb "example.com/helloworld/proto"
|
|
)
|
|
|
|
func main() {
|
|
// Create service with gRPC client
|
|
service := micro.NewService("helloworld.client",
|
|
micro.Client(grpcClient.NewClient()),
|
|
)
|
|
service.Init()
|
|
|
|
// Create client - use the service name "helloworld" (not the proto package name)
|
|
// Go Micro uses this name for registry lookup, which may differ from the package name
|
|
sayService := pb.NewSayService("helloworld", service.Client())
|
|
|
|
// Call service
|
|
rsp, err := sayService.Hello(context.Background(), &pb.Request{Name: "Alice"})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Println(rsp.Message) // Output: Hello Alice
|
|
}
|
|
```
|
|
|
|
### Testing with grpcurl
|
|
|
|
Once your service is running with the gRPC server, you can use `grpcurl`:
|
|
|
|
```bash
|
|
# List available services
|
|
grpcurl -plaintext localhost:8080 list
|
|
|
|
# Call the Hello method
|
|
grpcurl -proto ./proto/helloworld.proto \
|
|
-plaintext \
|
|
-d '{"name":"Alice"}' \
|
|
localhost:8080 helloworld.Say.Hello
|
|
```
|
|
|
|
## Using Both gRPC Server and Client Together
|
|
|
|
For full native gRPC compatibility (both inbound and outbound), use both:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"go-micro.dev/v6"
|
|
grpcClient "go-micro.dev/v6/client/grpc"
|
|
grpcServer "go-micro.dev/v6/server/grpc"
|
|
)
|
|
|
|
func main() {
|
|
service := micro.NewService("helloworld",
|
|
micro.Server(grpcServer.NewServer()),
|
|
micro.Client(grpcClient.NewClient()),
|
|
micro.Address(":8080"),
|
|
)
|
|
|
|
service.Init()
|
|
// ... register handlers
|
|
service.Run()
|
|
}
|
|
```
|
|
|
|
## Common Errors
|
|
|
|
### "unknown service" Error with grpcurl
|
|
|
|
If you see this error:
|
|
|
|
```
|
|
ERROR:
|
|
Code: Unimplemented
|
|
Message: unknown service helloworld.Say
|
|
```
|
|
|
|
**Cause**: You're using the gRPC transport instead of the gRPC server.
|
|
|
|
**Solution**: Change from:
|
|
|
|
```go
|
|
// Wrong - uses transport
|
|
t := grpc.NewTransport()
|
|
service := micro.NewService("helloworld",
|
|
micro.Transport(t),
|
|
)
|
|
```
|
|
|
|
To:
|
|
|
|
```go
|
|
// Correct - uses server
|
|
import grpcServer "go-micro.dev/v6/server/grpc"
|
|
|
|
service := micro.NewService("helloworld",
|
|
micro.Server(grpcServer.NewServer()),
|
|
)
|
|
```
|
|
|
|
### Import Path Confusion
|
|
|
|
Note the different import paths:
|
|
|
|
```go
|
|
// Transport (NOT native gRPC compatible)
|
|
import "go-micro.dev/v6/transport/grpc"
|
|
|
|
// Server (native gRPC compatible)
|
|
import "go-micro.dev/v6/server/grpc"
|
|
|
|
// Client (native gRPC compatible)
|
|
import "go-micro.dev/v6/client/grpc"
|
|
```
|
|
|
|
### Service Name vs Package Name
|
|
|
|
When creating a client to call another service, use the **service name** passed to `micro.NewService`, not the proto package name:
|
|
|
|
```go
|
|
// If the server was started with micro.NewService("helloworld", ...)
|
|
sayService := pb.NewSayService("helloworld", service.Client()) // Use service name
|
|
|
|
// NOT the package name from the proto file
|
|
// sayService := pb.NewSayService("helloworld.Say", service.Client()) // Wrong!
|
|
```
|
|
|
|
Go Micro uses the service name for registry lookup, which may differ from the proto package name.
|
|
|
|
## Environment Variable Configuration
|
|
|
|
You can also configure the server and client via environment variables:
|
|
|
|
```bash
|
|
# Use gRPC server
|
|
MICRO_SERVER=grpc go run main.go
|
|
|
|
# Use gRPC client
|
|
MICRO_CLIENT=grpc go run main.go
|
|
```
|
|
|
|
## Summary
|
|
|
|
| Component | Import Path | Native gRPC Compatible |
|
|
|-----------|-------------|----------------------|
|
|
| Transport | `go-micro.dev/v6/transport/grpc` | ❌ No |
|
|
| Server | `go-micro.dev/v6/server/grpc` | ✅ Yes |
|
|
| Client | `go-micro.dev/v6/client/grpc` | ✅ Yes |
|
|
|
|
For native gRPC compatibility with tools like `grpcurl` or polyglot clients, always use the gRPC **server** and **client** packages, not the transport.
|
|
|
|
## Related Documentation
|
|
|
|
- [Transport](../transport.md) - Understanding transports in Go Micro
|
|
- [Plugins](../plugins.md) - Available plugins including gRPC
|
|
- [Migration from gRPC](migration/from-grpc.md) - Migrating existing gRPC services
|