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