Grpc application practice: (III) grpc four request modes

Time:2022-5-4

3.1 Preface

Grpc mainly has four request and response modes: simple RPC, server side streaming RPC and client side streaming
RPC), and bidirectional streaming RPC. In fact, many people can know relevant information as the name suggests:

  • Simple mode: also known as unary RPC. In the previous section, our example is the simple mode, which is similar to the conventional HTTP request. The client sends the request and the server responds to the request
  • Server streaming: the client sends a request to the server and gets a stream to read the returned message sequence. The client reads the returned stream until there is no message in it.
  • Client streaming: Contrary to the data flow mode of the server, this time the client sends a continuous stream of data to the server, and after sending, the server returns a response.
  • Bidirectional streaming: both parties use read-write streams to send a message sequence. The two streams operate independently, and both parties can send and receive at the same time.

Different calling methods often represent different application scenarios. Next, let’s practice the remaining three methods:

Warm tip: all the following codes are inhere, all Pb files are in the Pb package.

3.2 server side streaming RPC

Server side streaming RPC, that is, one-way flow, and refers to the server as stream and the client as ordinary unary RPC requests.

3.2.1 proto

In fact, the key is to add before the data returned by the serverstreamkeyword

//A service for serverside
service ServerSide {
  //A method of serversidehello
  rpc ServerSideHello (ServerSideRequest) returns (stream ServerSideResp) {}
}

Then runprotoc --go_out=plugins=grpc:. *.protoGenerate the corresponding code.

3.2.2 implementation of server code

3.2.2.1 define our services

First define our servicesServerSideServiceAnd realizeServerSideHellomethod.

type ServerSideService struct {
}

func (s *ServerSideService) ServerSideHello(request *pb.ServerSideRequest, server pb.ServerSide_ServerSideHelloServer) error {
    log.Println(request.Name)
    for n := 0; n < 5; n++ {
        //Send messages to the stream. By default, the maximum length of messages sent each time is ` math MaxInt32`bytes 
        err := server. Send (& PB. Serversideresp {message: "hello"})
        if err != nil {
            return err
        }
    }
    return nil
}

Then inServer packageRegister service in

    pb.RegisterServerSideServer(grpcServer, &services.ServerSideService{})

3.2.2.2 running our services

This is all the information of the server. This part of the information will not be repeated latergrpc.NewServer()Create a new grpc server, register the corresponding service, and callgrpc.NewServer()Blocking threads.

package main

import (
    "github.com/CodeFish-xiao/blogs/gRPCAction/code/grpc-3/pb"
    "github.com/CodeFish-xiao/blogs/gRPCAction/code/grpc-3/services"
    "google.golang.org/grpc"
    "log"
    "net"
)

const (
    //Address listening address
    Address string = ":8546"
    //Network communication protocol
    Network string = "tcp"
)

func main() {
    //Listen to local port
    listener, err := net.Listen(Network, Address)
    if err != nil {
        log.Panic("net.Listen err: %v", err)
    }
    log.Println(Address + " net.Listing...")
    //Create a new grpc server instance
    grpcServer := grpc.NewServer()
    //Register our service with grpc server
    pb.RegisterClientSideServer(grpcServer, &services.BidirectionalService{})
    pb.RegisterServerSideServer(grpcServer, &services.ServerSideService{})
    pb.RegisterBidirectionalServer(grpcServer, &services.ClientSideService{})
    //Use the server serve () method and our port information area to realize blocking and waiting until the process is killed or stop () is called
    err = grpcServer.Serve(listener)
    if err != nil {
        log.Panic("grpcServer.Serve err: %v", err)
    }
}

After operation:

Grpc application practice: (III) grpc four request modes

3.2.2 implementation of client code

The code is as follows:

const (
//Serveraddress connection address
ServerAddress string = ":8546"
)

func main() {
    ServerSide()
}

func ServerSide() {
//Connect server
    conn, err := grpc.Dial(ServerAddress, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("net.Connect err: %v", err)
    }
    defer conn.Close()

    //Establish grpc connection
    grpcClient := pb.NewServerSideClient(conn)
    //Create send structure
    req := pb.ServerSideRequest{
        Name: "I'll open you",
    }
    //Get stream
    stream, err := grpcClient.ServerSideHello(context.Background(), &req)
    if err != nil {
        log.Fatalf("Call SayHello err: %v", err)
    }
    for n := 0; n < 5; n++ {
        res, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf("Conversations get stream err: %v", err)
        }
        //Print return value
        log.Println(res.Message)
    }
}

Because it is the server stream mode, you need to obtain the stream from the server, that is, the link, and transmit data through the stream, and the client throughRecv()Get the information of the server and output it

3.2.3 service flow mode operation example

After writing the client code, the operation can be seen: after sending a request, collect the request information sent by the server

client:
Grpc application practice: (III) grpc four request modes

Server:

Send a message after receiving a request from the client.

Grpc application practice: (III) grpc four request modes

3.3 client side streaming RPC

Client streaming RPC is also a one-way flow, but the client sends streaming data.

3.3.1 proto

In fact, the key is to add before the data sent by the clientstreamkeyword

service ClientSide {
  //A clientsidehello method
  rpc ClientSideHello (stream ClientSideRequest) returns (ClientSideResp) {}
}

Then runprotoc --go_out=plugins=grpc:. *.protoGenerate the corresponding code.

3.3.2 implementation of server code

The implementation code is similar to the client stream mode code.

3.3.2.1 define our services

First define our servicesClientSideServiceAnd realizeClientSideHellomethod.

type ClientSideService struct {
}

func (c *ClientSideService) ClientSideHello(server pb.ClientSide_ClientSideHelloServer) error {
    for i := 0; i < 5; i++ {
        recv, err := server.Recv()
        if err != nil {
            return err
        }
        log. Println ("client information:", recv)
    }
    //The last message sent by the server
    err := server. Sendandclose (& PB. Clientsideresp {message: "close"})
    if err != nil {
        return err
    }
    return nil
}

Then inServer packageRegister service in

    pb.RegisterClientSideServer(grpcServer, &services.ClientSideService{})

3.3.2.2 running our services

After operation:

Grpc application practice: (III) grpc four request modes

3.3.2 implementation of client code

The code is as follows:

func ClientSide() {
    //Connect server 
    conn, err := grpc.Dial(ServerAddress, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("net.Connect err: %v", err)
    }
    defer conn.Close()

    //Establish grpc connection 
    grpcClient := pb.NewClientSideClient(conn)
//Create send structure 
    res, err := grpcClient.ClientSideHello(context.Background())
    if err != nil {
        log.Fatalf("Call SayHello err: %v", err)
    }
    for i := 0; i < 5; i++ {
    //Send the stream information through the send method 
        Err = res.send (& PB. Clientsiderequest {Name: "client streaming"})
        if err != nil {
            return
        }
    }
    //Print return value 
    log.Println(res.CloseAndRecv())
}

3.3.3 example of client stream mode operation

After writing the client code, the operation shows that: the client sends the stream request, and then the server prints. After 5 times, the server sends the message of closing the stream, and the client receives the closing message and closes the stream:

client:

Grpc application practice: (III) grpc four request modes

Server:

Grpc application practice: (III) grpc four request modes

3.4 bidirectional streaming RPC

Both the client and the server use read-write streams to send a message sequence. The two streams operate independently, and both sides can send and receive at the same time.

3.4.1 proto

Add before the request value and return valuestreamkeyword

service Bidirectional {
  //A bidirectionalhello method
  rpc BidirectionalHello (stream BidirectionalRequest) returns (stream BidirectionalResp) {}
}

Then runprotoc --go_out=plugins=grpc:. *.protoGenerate the corresponding code.

3.4.2 implementation of server code

3.4.2.1 define our services

First define our servicesBidirectionalServiceAnd realizeBidirectionalHellomethod.

type BidirectionalService struct {
}

func (b *BidirectionalService) BidirectionalHello(server pb.Bidirectional_BidirectionalHelloServer) error {
    defer func() {
        log. Println ("client disconnected link")
    }()
    for  {
        //Get client information 
        recv, err := server.Recv()
        if err != nil {
            return err
        }
        log.Println(recv) 
        //Send server information 
        err = server. Send (& PB. Bidirectionalresp {message: "server information"})
        if err != nil {
            return err
        }
    }
}

Then inServer packageRegister service in

    pb.RegisterBidirectionalServer(grpcServer, &services.BidirectionalService{})

3.4.2.2 running our services

After operation:
Grpc application practice: (III) grpc four request modes

3.4.2 implementation of client code

The code is as follows:

func Bidirectional() {
    //Connect server 
    conn, err := grpc.Dial(ServerAddress, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("net.Connect err: %v", err)
    }
    defer conn.Close() 
    //Establish grpc connection 
    grpcClient := pb.NewBidirectionalClient(conn) 
    //Get stream information 
    stream, err := grpcClient.BidirectionalHello(context.Background())
    if err != nil {
        log.Fatalf("get BidirectionalHello stream err: %v", err)
    }

    for n := 0; n < 5; n++ {
        err := stream. Send (& PB. Bidirectionalrequest {Name: "bidirectional flow RPC" + strconv. Itoa (n)})
        if err != nil {
            log.Fatalf("stream request err: %v", err)
        }
        res, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf("Conversations get stream err: %v", err)
        }
        //Print return value 
        log.Println(res.Message) 
        }
}

In the two-way flow mode, the client needs to obtain the flow link from the server, and then both parties can transmit through the flow

3.4.3 two way flow mode operation example

Because grpc handles the processing after disconnecting the link, the defer code can run and output information after the client is disconnected.

client:
Grpc application practice: (III) grpc four request modes

Server:
Grpc application practice: (III) grpc four request modes

3.5 summary

The simple mode has been mentioned in the previous section. This time, I will explain several other interaction modes, which is basically enough for most business scenarios. However, in the actual development, we will need many more things: timeout control, load balancing, permission control, data verification and other functions. The follow-up will come slowly.

This work adoptsCC agreement, reprint must indicate the author and the link to this article

Code small miscellaneous fish