internal/website/docs/guides/migration/from-grpc.md
Step-by-step guide to migrating existing gRPC services to Go Micro.
Go Micro adds:
You keep:
Run Go Micro alongside existing gRPC services
Migrate services one at a time
All services on Go Micro
// proto/hello.proto
syntax = "proto3";
package hello;
option go_package = "./proto;hello";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
// Original gRPC server
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "myapp/proto"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + req.Name}, nil
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Fatal(s.Serve(lis))
}
Update your proto generation:
# Install protoc-gen-micro
go install go-micro.dev/v5/cmd/[email protected]
# Generate both gRPC and Go Micro code
protoc --proto_path=. \
--go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
--micro_out=. --micro_opt=paths=source_relative \
proto/hello.proto
Note: Use a specific version instead of
@latestto avoid module path conflicts. See releases for the latest version.
This generates:
hello.pb.go - Protocol Buffers typeshello_grpc.pb.go - gRPC client/server (keep for compatibility)hello.pb.micro.go - Go Micro client/server (new)// Go Micro server
package main
import (
"context"
"go-micro.dev/v5"
"go-micro.dev/v5/server"
pb "myapp/proto"
)
type Greeter struct{}
func (s *Greeter) SayHello(ctx context.Context, req *pb.HelloRequest, rsp *pb.HelloReply) error {
rsp.Message = "Hello " + req.Name
return nil
}
func main() {
svc := micro.NewService(
micro.Name("greeter"),
)
svc.Init()
pb.RegisterGreeterHandler(svc.Server(), new(Greeter))
if err := svc.Run(); err != nil {
log.Fatal(err)
}
}
Key differences:
Original gRPC client:
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
defer conn.Close()
client := pb.NewGreeterClient(conn)
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
Go Micro client:
svc := micro.NewService(micro.Name("client"))
svc.Init()
client := pb.NewGreeterService("greeter", svc.Client())
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
Benefits:
Use gRPC as the underlying transport:
import (
"go-micro.dev/v5"
"go-micro.dev/v5/client"
"go-micro.dev/v5/server"
grpcclient "go-micro.dev/v5/client/grpc"
grpcserver "go-micro.dev/v5/server/grpc"
)
svc := micro.NewService(
micro.Name("greeter"),
micro.Client(grpcclient.NewClient()),
micro.Server(grpcserver.NewServer()),
)
This gives you:
service Greeter {
rpc StreamHellos (stream HelloRequest) returns (stream HelloReply) {}
}
func (s *server) StreamHellos(stream pb.Greeter_StreamHellosServer) error {
for {
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
stream.Send(&pb.HelloReply{Message: "Hello " + req.Name})
}
}
func (s *Greeter) StreamHellos(ctx context.Context, stream server.Stream) error {
for {
var req pb.HelloRequest
if err := stream.Recv(&req); err != nil {
return err
}
if err := stream.Send(&pb.HelloReply{Message: "Hello " + req.Name}); err != nil {
return err
}
}
}
// Manually register with Consul
config := api.DefaultConfig()
config.Address = "consul:8500"
client, _ := api.NewClient(config)
reg := &api.AgentServiceRegistration{
ID: "greeter-1",
Name: "greeter",
Address: "localhost",
Port: 50051,
}
client.Agent().ServiceRegister(reg)
// Cleanup on shutdown
defer client.Agent().ServiceDeregister("greeter-1")
import "go-micro.dev/v5/registry/consul"
reg := consul.NewConsulRegistry()
svc := micro.NewService(
micro.Name("greeter"),
micro.Registry(reg),
)
// Registration automatic on Run()
// Deregistration automatic on shutdown
svc.Run()
// Need external load balancer or custom implementation
// Example: round-robin DNS, Envoy, nginx
import "go-micro.dev/v5/selector"
// Client-side load balancing built-in
svc := micro.NewService(
micro.Selector(selector.NewSelector(
selector.SetStrategy(selector.RoundRobin),
)),
)
New services use Go Micro, existing services stay on gRPC.
// New Go Micro service can call gRPC services
// Configure gRPC endpoints directly
grpcConn, _ := grpc.Dial("old-service:50051", grpc.WithInsecure())
oldClient := pb.NewOldServiceClient(grpcConn)
Services with many clients benefit most from service discovery.
Leaf services are easier to migrate.
// gRPC adapter for Go Micro service
type GRPCAdapter struct {
microClient pb.GreeterService
}
func (a *GRPCAdapter) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return a.microClient.SayHello(ctx, req)
}
// Register adapter as gRPC server
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &GRPCAdapter{microClient: microClient})
--micro_outgrpc.Dial with Go Micro clientgRPC: Manual port management
lis, _ := net.Listen("tcp", ":50051")
Go Micro: Automatic or explicit
// Let Go Micro choose
svc := micro.NewService(micro.Name("greeter"))
// Or specify
svc := micro.NewService(
micro.Name("greeter"),
micro.Address(":50051"),
)
Check registry:
# Consul
curl http://localhost:8500/v1/catalog/services
# Or use micro CLI
micro services
gRPC uses protobuf by default. Go Micro supports multiple codecs.
Ensure both use protobuf:
import "go-micro.dev/v5/codec/proto"
svc := micro.NewService(
micro.Codec("application/protobuf", proto.Marshaler{}),
)
| Scenario | gRPC | Go Micro (HTTP) | Go Micro (gRPC) |
|---|---|---|---|
| Simple RPC | ~25k req/s | ~20k req/s | ~24k req/s |
| With Discovery | N/A | ~18k req/s | ~22k req/s |
| Streaming | ~30k msg/s | ~15k msg/s | ~28k msg/s |
Go Micro with gRPC transport performs similarly to pure gRPC