How to create a generic GRPC server start function in Go - go

I'm trying to abstract the start of a GRPC server
The original main function is the following:
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterCollectionServer(s, &server.Server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
My goal is to have something like this:
func startService(sr func(*grpc.Server, interface{}), srv interface{}) error {
lis, err := net.Listen("tcp", port)
if err != nil {
return err
}
s := grpc.NewServer()
sr(s, srv)
reflection.Register(s)
return s.Serve(lis)
}
func main() {
err := startService(pb.RegisterCollectionServer, &server.Server{})
if err != nil {
log.Fatalf("failed to start Service: %v", err)
}
}
But this gives me the following error:
cannot use collection_api.RegisterCollectionServer (type func(*grpc.Server, collection_api.CollectionServer)) as type func(*grpc.Server, interface {}) in argument to startServicego
It seems that collection_api.CollectionServer is not a valid interface{} type.
Any idea how to make this work?

In your setup the function signature of collector_api.RegisterCollectionServer must match exactly func(*grpc.Server, interface{}), there is no "is-kind-of" in Go as there is in other languages.
If you want to keep the startService function independent of the collection_api types, you can use a anonymous function in main(). If you know that you what ever you are registering is always dependent on the implementation of the anonymous function (in this case &server.Server{}), then you can leave this detail out of the function signature of startService and put it into the anonymous function:
func startService(sr func(*grpc.Server)) error {
lis, err := net.Listen("tcp", port)
if err != nil {
return err
}
s := grpc.NewServer()
sr(s)
reflection.Register(s)
return s.Serve(lis)
}
func main() {
err := startService(func(grpcServer *grpc.Server) {
pb.RegisterCollectionServer(grpcServer, &server.Server{})
}, &server.Server{})
if err != nil {
log.Fatalf("failed to start Service: %v", err)
}
}

Related

grpc service does not implement reflection.GRPCServer

I am creating a grpc service in go, I set up my grpc as follows
func setupGrpc() {
lis, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := followerservice.UserServer{}
grpcServer := grpc.NewServer()
gen.RegisterUserServiceServer(grpcServer, &s)
// for ease of cli dev
reflection.Register(s) // this doesn't work!
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
}
My UserServer looks like this
type UserServer struct {
gen.UnimplementedUserServiceServer
}
func (s *UserServer) Create(ctx context.Context, in *gen.CreateUserRequest)(*gen.CreateUserResponse, error) {
log.Printf("Receive message body from client: %s", in.Email)
return &gen.CreateUserResponse{Id: "new id!"}, nil
}
At the line marked in func setupGrpc() I get a compile time error:
followerservice.UserServer does not implement reflection.GRPCServer (missing GetServiceInfo method)
but I can't find out how to implement it, I've been googling for hours now. It feels like protoc could generate this for me, but I'm kinda confused.
You have to reflection.Register the *grpc.Server that you got from the grpc.NewServer() constructor, not your own implementation.
grpcServer := grpc.NewServer()
gen.RegisterUserServiceServer(grpcServer, &s)
reflection.Register(grpcServer)

Create function that receives any function with specific amount of parameters

Say I have several different gRPC servers, for example x.Server, y.Server and z.Server, and in order to spin them up, I have a lot of repeated code inside their main function, e.g.:
func main() {
if err := config.EnsureArgLength(1); err != nil {
log.Fatalln(err.Error())
}
srv := &x.Server{}
if err := srv.ReadServerConfig(os.Args[1]); err != nil {
log.Fatalln(err.Error())
}
if err := srv.RegisterListener(); err != nil {
log.Fatalln(err.Error())
}
if err := srv.RegisterClients(); err != nil {
log.Fatalln(err.Error())
}
s := grpc.NewServer()
proto.RegisterXServer(s, srv)
if err := srv.Serve(s); err != nil {
log.Fatalf("failed to serve: %s", err.Error())
}
}
I would love to refactor this main function to make it one or two lines long, something like the following:
func main() {
srv := x.Server{}
if err := srv.RegisterAndServe(); err != nil {
log.Fatal("failed to serve: %s", err.Error())
}
}
But each server will have an auto-generated function proto.RegisterXServer which is not part of x.Server struct, and I'm also not able to modify the file which contains it, since it is auto generated. How should I proceed?
in regards to op changes, which was radical,
I can suggest using a reducer pattern like this.
package main
import (
"fmt"
)
func main() {
fail(reduce(sayHello(), sayGoodbye))
}
func sayHello() func() error {
return func() error { fmt.Println("Hello, playground"); return nil }
}
func sayGoodbye() error {
fmt.Println("Goodbye from the playground")
return nil
}
func reduce(h ...func() error) error {
for _, hh := range h {
if err := hh(); err != nil {
return err
}
}
return nil
}
func fail(err error) {
if err != nil {
panic(err)
}
}

Golang grpc: how to determine when the server has started listening?

So I have the following:
type Node struct {
Table map[string]string
thing.UnimplementedGreeterServer
address string
}
func (n *Node) Start() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
In my main function I'll spin up mulitple nodes like so:
func main() {
n :=Node{Table: map[string]string{}}
go n.Start()
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
The problem is, because I'm spinning up the node concurrently, there's a chance the dial up connection might not work because the node might not have been setup yet.
Ideally, I'd like a done channel that tells me when the grpc server has actually started listening. How do I accomplish this?
This is essntially the same problem as How to add hook on golang grpc server start? which doesn't have an answer
s.Serve(listener) blocks, so you can't achieve your purpose by having a done chan, instead you have to implement the healthcheck and readiness for your service, and check those before performing any request by the client.
The server should implement the following proto:
syntax = "proto3";
package grpc.health.v1;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
SERVICE_UNKNOWN = 3; // Used only by the Watch method.
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
For example, the envoy proxy grpc_health_check works with the above proto.
Read GRPC Health Checking Protocol for more information.
The server can be Dialed as soon as net.Listen returns a nil error. Dial will block until the server calls Accept (which will happen somewhere in s.Serve in this case).
Either move creation of the listener into the caller and pass it as an argument:
func (n *Node) Start(lis net.Listener) {
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
n := Node{Table: map[string]string{}}
go n.Start(lis)
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
Or signal that the listener is up after Listen returns:
func (n *Node) Start(up chan struct{}) {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
if up != nil {
close(up)
}
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
n := Node{Table: map[string]string{}}
up := make(chan struct{})
go n.Start(up)
<-up
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
For all those who are still looking for an answer to this, here is another simple way to do it. Start the server in a child routine. Here is a code snippet:
// Start the server in a child routine
go func() {
if err := s.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}()
fmt.Println("Server succesfully started on port :50051")
In my case I am using MongoDB as well, so when you run it, you get:
grpc-go-mongodb-cobra>go run server/main.go
Starting server on port :50051...
Connecting to MongoDB...
Connected to MongoDB
Server succesfully started on port :50051
I have also written a Blog post on this, with working code in GitHub. Here is the link: https://softwaredevelopercentral.blogspot.com/2021/03/golang-grpc-microservice.html

How to cache a TCP reverse proxy data transmission?

I've accomplished implementing TCP reverse proxy in GoLang. But unfortunately couldn't come up with implementing caching to a TCP reverse proxy. Is it possible to do so, if yes, is there any resource out there? Is caching possible on a TCP (Transport Layer of Network)?
Here's the simple TCP reverse proxy in Golang.
package main
import (
"io"
"log"
"net"
)
//Proxy struct
type Proxy struct {
laddr, raddr *net.TCPAddr
lconn, rconn io.ReadWriteCloser
errorSignal chan bool
}
// New Create a new Proxy instance.
func New(lconn *net.TCPConn, laddr, raddr *net.TCPAddr) *Proxy {
return &Proxy{
lconn: lconn,
laddr: laddr,
raddr: raddr,
errorSignal: make(chan bool),
}
}
//TCPAddressResolver resolves an address and returns to a struct having ip and port.
func TCPAddressResolver(addr string) (tcpAddress *net.TCPAddr, err error) {
tcpAddress, err = net.ResolveTCPAddr("tcp", addr)
return
}
func main() {
listenerAddress, err := TCPAddressResolver(":8080")
if err != nil {
log.Fatalf("Failed to resolve local address: %v", err)
}
remoteAddress, err := TCPAddressResolver(":3000")
if err != nil {
log.Fatalf("Failed to resolve remote address: %v", err)
}
listener, err := net.ListenTCP("tcp", listenerAddress)
if err != nil {
log.Fatalf("Failed to open local port to listen: %v", err)
}
log.Printf("Simple Proxy started on: %d and forwards to port %d", listenerAddress.Port, remoteAddress.Port)
for {
conn, err := listener.AcceptTCP()
if err != nil {
log.Fatalf("Failed to accept connection: %v", err)
continue
}
var p *Proxy
// HTTP is a stateless protocol thus a proxy needs to reinitiate the new next incoming call (conn)
// each time it finishes handling the previous one.
p = New(conn, listenerAddress, remoteAddress)
p.Start()
}
}
//Start initiates transmission of data to and from the remote to client side.
func (p *Proxy) Start() {
defer p.lconn.Close()
var err error
p.rconn, err = net.DialTCP("tcp", nil, p.raddr)
if err != nil {
log.Fatalf("Remote connection failure: %v", err)
}
defer p.rconn.Close()
go p.CopySrcDst(p.lconn, p.rconn)
go p.CopySrcDst(p.rconn, p.lconn)
//Wait for everything to close -- This one blocks the routine.
<-p.errorSignal
log.Printf("Closing Start routine \n")
}
func (p *Proxy) err(err error) {
if err != io.EOF {
log.Printf("Warning: %v: Setting error signal to true", err)
}
p.errorSignal <- true
}
//CopySrcDst copies data from src to dest
func (p *Proxy) CopySrcDst(src, dst io.ReadWriteCloser) {
buff := make([]byte, 1024)
for {
n, err := src.Read(buff)
if err != nil {
// Reading error.
p.err(err)
return
}
dataFromBuffer := buff[:n]
n, err = dst.Write(dataFromBuffer)
if err != nil {
// Writing error.
p.err(err)
return
}
}
}
You are asking how to save data read from an io.Reader. That's different from caching.
The easiest approach is to tee the reader into a buffer.
While you are at it, you might as well use io.Copy instead of the similar code in the question. The code in the question does not handle the case when read returns n > 0 and a non-nil error.
Use an error group to coordinate waiting for the goroutines and collecting error status.
var g errgroup.Group
var rbuf, lbuf bytes.Buffer
g.Go(func() error {
_, err := io.Copy(lconn, io.TeeReader(p.rconn, &rbuf))
return err
})
g.Go(func() error {
_, err := io.Copy(rconn, io.TeeReader(p.lconn, &lbuf))
return err
})
if err := g.Wait(); err != nil {
// handle error
}
// rbuf and lbuf have the contents of the two streams.
The name of the programming language is "Go", not "Golang" or "GoLang".

How to handle multiple endpoints via grpc-gateway?

I'm sure all the services are working properly.
I have the code below:
This snippet is used for registering two endpoints.
func RegisterEndpoints(ctx context.Context, c *utils.AppConfig, r resolver.Builder) (http.Handler, error) {
var err error
mux := runtime.NewServeMux()
dialOpts := []grpc.DialOption{grpc.WithBalancerName("round_robin"), grpc.WithInsecure()}
err = protos.RegisterUserCenterHandlerFromEndpoint(ctx, mux, r.Scheme()+"://author/user-center", dialOpts)
if err != nil {
return nil, err
}
err = protos.RegisterSsoHandlerFromEndpoint(ctx, mux, r.Scheme()+"://author/sso", dialOpts)
if err != nil {
return nil, err
}
return mux, nil
}
And in my main.go,I build a resolver to resolve name to address, then register the two endpoints and listen on port 8080.
func run() error {
c := utils.GetAppConfig()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
r := localresolver.NewResolver(fmt.Sprintf("%s:%d", c.Registry.Host, c.Registry.Port))
resolver.Register(r)
mux := http.NewServeMux()
// Register endpoints here
gw, err := routes.RegisterEndpoints(ctx, c, r)
if err != nil {
return err
}
mux.Handle("/", gw)
fmt.Println("Listening localhost:8080...")
return http.ListenAndServe(fmt.Sprintf("%s:%d", c.Gateway.Host, c.Gateway.Port), mux)
}
func main() {
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
But after I ran go run main.go, I found that only the last service I registered can be accessed, that is sso service (the err = protos.RegisterSsoHandlerFromEndpoint(ctx, mux, r.Scheme()+"://author/sso", dialOpts) line).
Can anyone show me an example of the correct way to register multiple endpoints via grpc-gateway? (make all the services registered with grpc-gateway can successfully be visited)
[2020-01-31] Need more help, now my code is like below:
Other code are same as before.
Additional, this is the result which name resolver shows:
There is no need to pass the ServeMux (gw) to mux var as handler, you can just ListenAndServe to the returned gw variable.
// Register endpoints here
gw, err := routes.RegisterEndpoints(ctx, c, r)
if err != nil {
return err
}
fmt.Println("Listening localhost:8080...")
return http.ListenAndServe(fmt.Sprintf("%s:%d", c.Gateway.Host, c.Gateway.Port), gw)
and in RegisterEndpoints function, the endpoint parameter should be your host:port, the api endpoint should be provided in the google api annotation in the proto file.
err = protos.RegisterUserCenterHandlerFromEndpoint(ctx, mux, fmt.Sprintf("%s:%d", c.Gateway.Host, c.Gateway.Port), dialOpts)
if err != nil {
return nil, err
}
err = protos.RegisterSsoHandlerFromEndpoint(ctx, mux, fmt.Sprintf("%s:%d", c.Gateway.Host, c.Gateway.Port), dialOpts)
if err != nil {
return nil, err
}
I appended grpc.WithBlock() to grpc.DialOption, then all services can be accessed via grpc-gateway now.
Like below:
dialOpts := []grpc.DialOption{grpc.WithBalancerName("round_robin"), grpc.WithInsecure(), grpc.WithBlock()}

Resources