loading...

gRPC quick start in Go

dannypsnl profile image 林子篆 Originally published at dannypsnl.github.io on ・2 min read

What is RPC? RPC means “remote procedure call”. The concept is called a remote function as a local function.

Then gRPC? It is a framework that helps you create RPC.

Traditional RPC has some problem making it hard to use. For example, how do you know, what type of message you get?

Usually, we use JSON or others format. But marshal & unmarshal quickly became a big issue. Because as time goes on, we don’t actually know which service use which field, thus we can’t reduce anything.

And server & client will get further farther for the same reason.

These all, won’t be an issue in gRPC.

In gRPC, you define a *.proto file for your service.

At here, we will create one named UserService

syntax = "proto3";

package user;

service UserService {
  rpc GetUser (Empty) returns (User) {}
}

// define message type
message Empty {}
message User {
  string name = 1;
  int32 age = 2;
}

To generate code, I usually use go generate ./...

So let’s have a file gen.go, just leave a comment about what command you want to execute.

//go:generate sh -c "protoc -I./user --go_out=plugins=grpc:./user ./user/*.proto"

Implement service:

import "path/to/grpc_generated/user"

type UserService struct{}

func (us *UserService) GetUser(ctx context.Context, u *user.Empty) (*user.User, error) {
    return &user.UserName{
        Name: "Danny",
        Age: 21,
    }, nil
}

Create server:

import (
    "net"
    "path/to/grpc_generated/user"
    "google.golang.org/grpc"
)

func main() {
    l, err := net.Listen("tcp", ":50051")
    // handle err
    server := grpc.NewServer()

    service := user.UserServiceServer(&UserService{})
    user.RegisterUserServiceServer(server, service)

    err = server.Serve(l)
    // handle err
}

Final is our client:

import (
    "fmt"
    "net"
    "path/to/grpc_generated/user"
    "google.golang.org/grpc"
)

func main() {
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsercure())
    // handle err
    defer conn.Close()
    client := user.NewUserServiceClient(conn)
    u, err := client.GetUser(context.Background(), &user.Empty{})
    // handle err
    fmt.Printf("Name: %s, Age: %d", u.Name, u.Age)
}

After that, run go generate ./... from project root dir.

Then go run server.go, open another terminal, go run client.go

I usually won’t commit generated code(unless commit it makes sense), so I usually will write *.pb.go in file .gitignore

More info:

Discussion

pic
Editor guide
Collapse
ernsheong profile image
Jonathan ES Lin

What's interesting to me is that when I create the GRPC client in another package (outside main) and try to use it elsewhere... the GRPC calls fails with:

rpc error: code = Canceled desc = grpc: the client connection is closing

Any thoughts on this peculiarity?

Collapse
dannypsnl profile image
林子篆 Author

Not sure what happened on your computer, could you provide a sample for reproducing?

Collapse
ernsheong profile image
Jonathan ES Lin

Thanks for the reply. I figured it out. Gotta be careful with defer conn.Close() if calling outside main function... After function ends it will close the connection and the client cannot be used anymore.

Thread Thread
dannypsnl profile image
林子篆 Author

Good to hear that; yes, better to using context if you want to manage the lifecycle. It provides a not bad abstraction for stopping a goroutine.

Collapse
bimbimprasetyoafif profile image
Bimo Prasetyo Afif

Hey, i already have golang backend with rest api implemented. can i use grpc without make .proto file ? because i already have service, interface, repo, etc

Collapse
dannypsnl profile image
林子篆 Author

A short answer is it's possible, but that also means you are using HTTP2 directly.
That would make the gRPC became useless. Or you have to implement client/server-side abstraction for RPC.
Therefore I guess you probably can use .proto, just redirect to existed services.

Collapse
bimbimprasetyoafif profile image
Bimo Prasetyo Afif

maybe last option helpful for me. okay i will try it. thanks for suggest me :)