# gRPC-go
ref:
- [AkhilSharma90 - FULL PROJECT - GO + GRPC](https://www.youtube.com/watch?v=a6G5-LUlFO4&ab_channel=AkhilSharma)
- [gRPC - Quick Start](https://grpc.io/docs/languages/go/quickstart/#update-and-run-the-application)
- [gRPC - Basics tutorial](https://grpc.io/docs/languages/go/basics/)
## Simply introduce types of gRPC (4 type)
### Unary
- Similar to traditional API, like RESTful
- **Client send request** and **Server send response**
### Server streaming
- Client send request
- Server send stream(data flow)
### Client streaming
- Client send stream(data flow)
- Server send request
### Bi-directional streaming
- Both client and server send stream
## Building Environment
* My notebook is windows 10
* libprotoc 3.19.4
* [Protocol Buffer Compiler Installation - Install pre-compiled binaries (any OS)](https://grpc.io/docs/protoc-installation/#install-pre-compiled-binaries-any-os)
* [Notes on installing GRPC development environment for Go on Windows](https://gist.github.com/jjeffery/6e3a4d18ffbe1fc715be403f6391c5f4)
* You can test by run `protoc --version`
* Success

* Install the protocol compiler plugins for Go using the following commands:
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
* run `go mod tidy`
## Creating proto files
```protobuf
syntax="proto3";
// Files will get created in thin path
option go_package = "./proto";
package greet_service;
service GreetService{
// Unary
rpc SayHello(NoParam) returns (HelloResponse);
// Server stream
rpc SayHelloServerStreaming(NamesList) returns (stream HelloResponse);
// Client stream
rpc SayHelloClientStreaming(stream HelloRequest) returns (MessagesLists);
// Bi-directional stream
rpc SayHelloBidirectionalStreaming(stream HelloRequest) returns (stream HelloResponse);
}
message NoParam{};
message HelloRequest{
string name = 1;
}
message HelloResponse{
string message = 1 ;
}
message NamesList{
repeated string names = 1;
}
message MessagesLists{
repeated string messages = 1;
}
```
- proto files will be created when you run
`protoc --go_out=. --go-grpc_out=. proto/greet.proto`
## main.go for Server and Client
### Server
```go
// server/main.go
package main
import (
"log"
"net"
pb "grpc-go/proto"
"google.golang.org/grpc"
)
const (
port = ":8080"
)
type helloServer struct {
pb.GreetServiceServer
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("Failed to start the server %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterGreetServiceServer(grpcServer, &helloServer{})
log.Printf("Server started at %v", lis.Addr())
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("Failed to start: %v", err)
}
}
```
### Client
```go
// client/main.go
package main
import (
pb "grpc-go/proto"
"log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const (
port = ":8080"
)
func main() {
conn, err := grpc.Dial("localhost"+port, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Did not connect: %v", err)
}
defer conn.Close()
client := pb.NewGreetServiceClient(conn)
// If testing Unary , comment out this
names := &pb.NamesList{
Names: []string{"Alice", "Bob", "Joe"},
}
// Discomment out to test
// Unary
// callSayHello(client)
// Server stream
// callSayHelloServerStream(client, names)
// Client stream
// callSayHelloClientStream(client, names)
// Bi-directional stream
// callSayHelloBidirectionalStream(client, names)
}
```
## Unary

### Server
```go
// server/unary.go
package main
import (
"context"
pb "grpc-go/proto"
)
func (s *helloServer) SayHello(ctx context.Context, req *pb.NoParam) (*pb.HelloResponse, error) {
// Send response
return &pb.HelloResponse{
Message: "Hello",
}, nil
}
```
### Client
```go
// client/unary.go
package main
import (
"context"
pb "grpc-go/proto"
"log"
"time"
)
func callSayHello(client pb.GreetServiceClient) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := client.SayHello(ctx, &pb.NoParam{})
if err != nil {
log.Fatalf("Could not greet: %v", err)
}
log.Printf("%s", res.Message)
}
```
## Server Stream

### Server
```go
// server/server_stream.go
package main
import (
"log"
"time"
pb "grpc-go/proto"
)
func (s *helloServer) SayHelloServerStreaming(req *pb.NamesList, stream pb.GreetService_SayHelloServerStreamingServer) error {
log.Printf("Got request with names: %v", req.Names)
// Send response stream
for _, name := range req.Names {
res := &pb.HelloResponse{
Message: "Hello " + name,
}
if err := stream.Send(res); err != nil {
return err
}
time.Sleep(2 * time.Second)
}
return nil
}
```
### Client
```go
// client/server_stream.go
package main
import (
"context"
"io"
"log"
pb "grpc-go/proto"
)
func callSayHelloServerStream(client pb.GreetServiceClient, names *pb.NamesList) {
log.Printf("Streaming Started")
// Send request
stream, err := client.SayHelloServerStreaming(context.Background(), names)
if err != nil {
log.Fatalf("Could not send names: %v", err)
}
for {
// Receive response stream
message, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Error while streaming %v", err)
}
log.Println(message)
}
log.Printf("Streaming finished.")
}
```
## Client Stream

### Server
```go
// server/client_stream.go
package main
import (
pb "grpc-go/proto"
"io"
"log"
)
func (s *helloServer) SayHelloClientStreaming(stream pb.GreetService_SayHelloClientStreamingServer) error {
var messages []string
for {
// Receive request stream
req, err := stream.Recv()
if err == io.EOF {
// Send response
return stream.SendAndClose(&pb.MessagesLists{Messages: messages})
}
if err != nil {
return err
}
log.Printf("Got request with name: %v", req.Name)
messages = append(messages, "Hello", req.Name)
}
}
```
### Client
```go
// client/client_stream.go
package main
import (
"context"
pb "grpc-go/proto"
"log"
"time"
)
func callSayHelloClientStream(client pb.GreetServiceClient, names *pb.NamesList) {
log.Printf("Client streaming started.")
stream, err := client.SayHelloClientStreaming(context.Background())
if err != nil {
log.Fatalf("Could not send names: %v", err)
}
// Send request stream
for _, name := range names.Names {
req := &pb.HelloRequest{
Name: name,
}
if err := stream.Send(req); err != nil {
log.Fatalf("Error while sending %v", err)
}
log.Printf("Sent the request with name: %s", name)
time.Sleep(2 * time.Second)
}
// Receive response
res, err := stream.CloseAndRecv()
log.Printf("Client streaming finished")
if err != nil {
log.Fatalf("Error while receiving %v", err)
}
log.Printf("%v", res.Messages)
}
```
## Bi-directional stream

### Server
```go
// server/bi_stream.go
package main
import (
pb "grpc-go/proto"
"io"
"log"
)
func (s *helloServer) SayHelloBidirectionalStreaming(stream pb.GreetService_SayHelloBidirectionalStreamingServer) error {
for {
// Receive request stream
req, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
log.Printf("Got request with name: %v", req.Name)
// Send response stream
res := &pb.HelloResponse{
Message: "Hello " + req.Name,
}
if err := stream.Send(res); err != nil {
return err
}
}
}
```
### Client
```go
// client/bi_stream.go
package main
import (
"context"
pb "grpc-go/proto"
"io"
"log"
"time"
)
func callSayHelloBidirectionalStream(client pb.GreetServiceClient, names *pb.NamesList) {
log.Printf("Bidirectional streaming started.")
stream, err := client.SayHelloBidirectionalStreaming(context.Background())
if err != nil {
log.Fatalf("Could not send names: %v", err)
}
// Receive response stream
go func() {
for {
message, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Error while streaming %v", err)
}
log.Println(message)
}
}()
// Send request stream
for _, name := range names.Names {
req := &pb.HelloRequest{
Name: name,
}
if err := stream.Send(req); err != nil {
log.Fatalf("Error while sending %v", err)
}
time.Sleep(2 * time.Second)
}
stream.CloseSend()
log.Printf("Bidirectional streaming finished.")
}
```