Skip to content

Instantly share code, notes, and snippets.

@bmhatfield
Created August 11, 2020 12:44
Show Gist options
  • Save bmhatfield/dbff2a297d8bde57d29455e4a578a21e to your computer and use it in GitHub Desktop.
Save bmhatfield/dbff2a297d8bde57d29455e4a578a21e to your computer and use it in GitHub Desktop.
GRPC load balancing sample in Go
syntax = "proto3";
package clibalance;
service Balanceable {
rpc Relay(Ping) returns (Pong);
}
message Ping {
int64 id = 1;
}
message Pong {
int64 id = 1;
}
package main
import (
"context"
"fmt"
"log"
"net"
"os"
"strconv"
"time"
"client-balance/clibalance"
"github.com/sercand/kuberesolver"
"google.golang.org/grpc"
"google.golang.org/grpc/balancer/roundrobin"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
)
type Server struct {
Port int
}
func (s *Server) Relay(ctx context.Context, req *clibalance.Ping) (*clibalance.Pong, error) {
log.Printf("[%d] relaying ping as pong: %d", s.Port, req.GetId())
return &clibalance.Pong{Id: req.Id}, nil
}
func main() {
if len(os.Args) < 2 {
fmt.Println("need arg")
return
}
switch os.Args[1] {
case "client":
svc := clientSetup()
for i := int64(0); true; i++ {
client(svc, i)
time.Sleep(250 * time.Millisecond)
}
case "server":
if len(os.Args) < 3 {
fmt.Println("server must also have port")
return
}
port, err := strconv.Atoi(os.Args[2])
if err != nil {
fmt.Println("port must be numeric")
return
}
server(port)
default:
fmt.Println("unsupported mode; try 'client' or 'server'")
}
}
func clientSetup() clibalance.BalanceableClient {
res, _ := manual.GenerateAndRegisterManualResolver()
res.InitialState(resolver.State{Addresses: []resolver.Address{
resolver.Address{Addr: "127.0.0.1:8080", Type: resolver.Backend},
resolver.Address{Addr: "127.0.0.1:8081", Type: resolver.Backend},
resolver.Address{Addr: "127.0.0.1:8082", Type: resolver.Backend},
}})
resolver.SetDefaultScheme(res.Scheme())
kuberesolver.RegisterInCluster()
conn, err := grpc.Dial("", grpc.WithInsecure(), grpc.WithBalancerName(roundrobin.Name))
if err != nil {
log.Fatalf("failed to dial: %v", err)
}
return clibalance.NewBalanceableClient(conn)
}
func client(svc clibalance.BalanceableClient, num int64) {
p, err := svc.Relay(context.Background(), &clibalance.Ping{Id: num})
if err != nil {
log.Printf("failed to relay: %v", err)
return
}
log.Printf("Pong! (%d)", p.GetId())
}
func server(port int) {
log.Printf("starting server on port %d", port)
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
clibalance.RegisterBalanceableServer(grpcServer, &Server{Port: port})
grpcServer.Serve(lis)
}
@bmhatfield
Copy link
Author

To use this, you'll need to generate the protobuf file (using grpc-gen-go) into a clibalance sub-package so it can be imported.

You can then run multiple servers and a client, and then stop/start server processes to watch how the "pong" responses move around the "cluster".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment