Migrating from gRPC
Step-by-step guide to migrating existing gRPC services to Go Micro.
Why Migrate?
Go Micro adds:
- Built-in service discovery
- Client-side load balancing
- Pub/sub messaging
- Multiple transport options
- Unified tooling
You keep:
- Your proto definitions
- gRPC performance (via gRPC transport)
- Type safety
- Streaming support
Migration Strategy
Phase 1: Parallel Running
Run Go Micro alongside existing gRPC services
Phase 2: Gradual Migration
Migrate services one at a time
Phase 3: Complete Migration
All services on Go Micro
Step-by-Step Migration
1. Existing gRPC Service
// 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))
}
2. Generate Go Micro Code
Update your proto generation:
# Install protoc-gen-micro
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest
# 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
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)
3. Migrate Server to Go Micro
// Go Micro server
package main
import (
"context"
"go-micro.dev/v6"
"go-micro.dev/v6/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("greeter",
)
svc.Init()
pb.RegisterGreeterHandler(svc.Server(), new(Greeter))
if err := svc.Run(); err != nil {
log.Fatal(err)
}
}
Key differences:
- No manual port binding (Go Micro handles it)
- Automatic service registration
- Returns error, response via pointer parameter
4. Migrate Client
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("client")
svc.Init()
client := pb.NewGreeterService("greeter", svc.Client())
rsp, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: "John"})
Benefits:
- No hardcoded addresses
- Automatic service discovery
- Client-side load balancing
- Automatic retries
5. Keep gRPC Transport (Optional)
Use gRPC as the underlying transport:
import (
"go-micro.dev/v6"
"go-micro.dev/v6/client"
"go-micro.dev/v6/server"
grpcclient "go-micro.dev/v6/client/grpc"
grpcserver "go-micro.dev/v6/server/grpc"
)
svc := micro.NewService("greeter",
micro.Client(grpcclient.NewClient()),
micro.Server(grpcserver.NewServer()),
)
This gives you:
- gRPC performance
- Go Micro features (discovery, load balancing)
- Compatible with existing gRPC clients
Streaming Migration
Original gRPC Streaming
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})
}
}
Go Micro Streaming
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
}
}
}
Service Discovery Migration
Before (gRPC with Consul)
// 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")
After (Go Micro)
import "go-micro.dev/v6/registry/consul"
reg := consul.NewConsulRegistry()
svc := micro.NewService("greeter",
micro.Registry(reg),
)
// Registration automatic on Run()
// Deregistration automatic on shutdown
svc.Run()
Load Balancing Migration
Before (gRPC with custom LB)
// Need external load balancer or custom implementation
// Example: round-robin DNS, Envoy, nginx
After (Go Micro)
import "go-micro.dev/v6/selector"
// Client-side load balancing built-in
svc := micro.NewService("greeter",
micro.Selector(selector.NewSelector(
selector.SetStrategy(selector.RoundRobin),
)),
)
Gradual Migration Path
1. Start with New Services
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)
2. Migrate Read-Heavy Services First
Services with many clients benefit most from service discovery.
3. Migrate Services with Fewest Dependencies
Leaf services are easier to migrate.
4. Add Adapters if Needed
// 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})
Checklist
- Update proto generation to include
--micro_out - Convert handler signatures (response via pointer)
- Replace
grpc.Dialwith Go Micro client - Configure service discovery (Consul, Etcd, etc)
- Update deployment (remove hardcoded ports)
- Update monitoring (Go Micro metrics)
- Test service-to-service communication
- Update documentation
- Train team on Go Micro patterns
Common Issues
Port Already in Use
gRPC: Manual port management
lis, _ := net.Listen("tcp", ":50051")
Go Micro: Automatic or explicit
// Let Go Micro choose
svc := micro.NewService("greeter")
// Or specify
svc := micro.NewService("greeter",
micro.Address(":50051"),
)
Service Not Found
Check registry:
# Consul
curl http://localhost:8500/v1/catalog/services
# Or use micro CLI
micro services
Different Serialization
gRPC uses protobuf by default. Go Micro supports multiple codecs.
Ensure both use protobuf:
import "go-micro.dev/v6/codec/proto"
svc := micro.NewService("greeter",
micro.Codec("application/protobuf", proto.Marshaler{}),
)
Performance Comparison
| 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
Next Steps
- Read Go Micro Architecture
- Explore Plugin System
- Check Production Patterns
Need Help?
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.