Te-ai gândit vreodată să returnezi mai multe răspunsuri de pe server folosind o singură conexiune? Da, despre asta este vorba în acest articol.

Astăzi vă voi arăta cum să implementați streaming gRPC pe partea serverului cu Go.

Cum se configureaza Streaming gRPC Server Side cu Go
Este în regulă, nu va mușca

Înainte de a merge direct la implementare, să acoperim ceea ce vom învăța. Dacă ați făcut clic pe acest articol, este posibil să știți deja despre gRPC. Dar, dacă ai dat clic din curiozitate, nu te îngrijora – îți voi da o scurtă introducere la gRPC într-un pic.

Și dacă nu știi prea multe despre streamingul de pe server, este ok. Voi acoperi și asta mai jos.

În cele din urmă, dacă vă întrebați ce este Go, răspunsul rapid este că este un limbaj de programare. Nu voi acoperi acest lucru aici, dar puteți citi mai multe despre Go in documentele sale oficiale aici înainte de a începe.

În acest articol, voi începe prin a descrie gRPC și streamingul de pe server.
Dacă aveți deja o idee despre ceea ce sunt ambele, nu ezitați să ignorați primele două secțiuni de mai jos.

Ce este gRPC?

1612025827 548 Cum se configureaza Streaming gRPC Server Side cu Go

Ați visat vreodată să apelați o cerere de server cu un apel funcțional? În loc să utilizați un apel HTTP cu o anumită adresă URL? Ei bine, asta există deja de ceva timp – și noi îl numim Apel de procedură la distanță.

Și în 2015, Google a introdus ceva numit gRPC, care este practic un apel de procedură la distanță pe steroizi.

Funcționează aproape la fel ca un apel de procedură la distanță tradițional. Dar Google a introdus utilizarea HTTP / 2 ca protocol de comunicare și protobuf ca contract de comunicare între server și client.

HTTP / 2 a fost, de asemenea, creat de Google și permite comunicarea să fie mult mai performantă. De asemenea, permite multiplexarea, despre care voi vorbi mai târziu.

Protobuf este practic contractul utilizat pentru a permite comunicarea între server și partea clientului printr-un apel funcțional.

Bine, aceasta este o prezentare generală de bază a gRPC. Dacă sunteți încă interesat și doriți să vă scufundați mai adânc, puteți citi mai multe detalii despre el aici.

Ce este streamingul pe server?

Pârâu spumos mărginit de copaci
Fotografie de Jon Flobrant / Unsplash

Și acum, ce zici de streamingul de pe server?

Prin design, gRPC folosește HTTP / 2 și acceptă ceva numit multiplexare. Nu voi intra într-o grămadă de detalii aici, dar permite unei singure cereri să aibă mai multe răspunsuri și invers.

Acest mecanism este implementat în gRPC și se numește streaming.

Există 3 tipuri de streaming:

  • Stream în partea clientului: în cazul în care clientul va avea mai multe solicitări și serverul va returna un singur răspuns.
  • Streaming bidirecțional: în cazul în care atât clientul, cât și serverul pot avea mai multe cereri și răspunsuri împreună într-o singură conexiune.
  • Streaming pe partea de server: în cazul în care clientul trimite o singură cerere și serverul poate returna mai multe răspunsuri împreună. Acesta este cel pe care vi-l voi arăta cum să implementați astăzi.

Cum se implementează Streaming pe partea de server

Sărind la răsăritul soarelui
Fotografie de Cam Adams / Unsplash

Este timpul de implementare! Dacă citiți această secțiune, presupun că știți deja despre aceste 3 lucruri:

  • gRPC
  • Streaming pe partea de server
  • Merge

Transmiterea de pe server este utilă mai ales dacă serverul dvs. trebuie să returneze o sarcină utilă voluminoasă.

Utilizând streaming, puteți împărți acele răspunsuri și le puteți returna unul câte unul. Clientul va putea să taie răspunsurile neutilizate dacă are deja răspunsuri suficiente sau dacă a așteptat prea mult timp pentru unele răspunsuri.

Bine, acum să sărim direct în cod.

Creați fișierul Proto

Pentru început, va trebui să ne definim fișierul protobuf care va fi utilizat de client și de partea serverului. Să facem una simplă aici, ca aceasta:

syntax = "proto3";

package protobuf;

service StreamService {
  rpc FetchResponse (Request) returns (stream Response) {}
}

message Request {
  int32 id = 1;
}

message Response {
  string result = 1;
}
date.proto

Acest fișier proto conține în esență o singură funcție de apel cu un parametru Request și returnează un flux de Response .

Înainte de a continua, trebuie, de asemenea, să ne generăm pb fișier care va fi utilizat de programul nostru Go. Fiecare limbaj de programare va avea un mod diferit de a genera fișierul tampon de protocol. În Go vom folosi protoc bibliotecă.

Dacă nu l-ați instalat încă, Google oferă ghid de instalare pentru asta aici.

Să generăm fișierul tampon de protocol executând următoarea comandă:
protoc --go_out=plugins=grpc:. *.proto

Și acum avem data.pb.go gata de utilizare.

Creați fișierul client

Pentru pasul următor, puteți crea fie fișierul client, fie fișierul server, în orice ordine. Dar în acest exemplu voi face mai întâi fișierul client.

package main

import (
	"context"
	"io"
	"log"

	pb "github.com/pramonow/go-grpc-server-streaming-example/src/proto"

	"google.golang.org/grpc"
)

func main() {
	// dial server
	conn, err := grpc.Dial(":50005", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("can not connect with server %v", err)
	}

	// create stream
	client := pb.NewStreamServiceClient(conn)
	in := &pb.Request{Id: 1}
	stream, err := client.FetchResponse(context.Background(), in)
	if err != nil {
		log.Fatalf("open stream error %v", err)
	}

	done := make(chan bool)

	go func() {
		for {
			resp, err := stream.Recv()
			if err == io.EOF {
				done <- true //means stream is finished
				return
			}
			if err != nil {
				log.Fatalf("cannot receive %v", err)
			}
			log.Printf("Resp received: %s", resp.Result)
		}
	}()

	<-done //we will wait until all response is received
	log.Printf("finished")
}
client.go

Clientul va fi practic cel care trimite cererea. De asemenea, va fi cel care va primi mai multe răspunsuri.

Clientul va apela metoda gRPC FetchResponse și așteptați toate răspunsurile. Folosesc un goroutine aici pentru a arăta posibilitatea concurenței. Și o folosesc channel pentru a aștepta până la finalizarea tuturor proceselor înainte de a ieși din program.

Creați fișierul server

Pentru al treilea și ultimul fișier, vom crea fișierul server. Acest fișier va primi răspunsul de la client și, la rândul său, va trimite un flux de răspunsuri clientului.

package main

import (
	"fmt"
	"log"
	"net"
	"sync"
	"time"

	pb "github.com/pramonow/go-grpc-server-streaming-example/src/proto"

	"google.golang.org/grpc"
)

type server struct{}

func (s server) FetchResponse(in *pb.Request, srv pb.StreamService_FetchResponseServer) error {

	log.Printf("fetch response for id : %d", in.Id)

  	//use wait group to allow process to be concurrent
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(count int64) {
			defer wg.Done()
      
      			//time sleep to simulate server process time
			time.Sleep(time.Duration(count) * time.Second)
			resp := pb.Response{Result: fmt.Sprintf("Request #%d For Id:%d", count, in.Id)}
			if err := srv.Send(&resp); err != nil {
				log.Printf("send error %v", err)
			}
			log.Printf("finishing request number : %d", count)
		}(int64(i))
	}

	wg.Wait()
	return nil
}

func main() {
	// create listiner
	lis, err := net.Listen("tcp", ":50005")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	// create grpc server
	s := grpc.NewServer()
	pb.RegisterStreamServiceServer(s, server{})

	log.Println("start server")
	// and start...
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}

}
server.go

În fișierul server, folosesc și un goroutine pentru a simula un proces concurent.
Pentru fiecare dintre solicitări, voi transmite înapoi cinci cereri către partea clientului. Fiecare va avea, de asemenea, un timp de proces diferit pentru a simula diferitele perioade de procesare pe care le-ați avea într-un scenariu din viața reală.

Ieșirea

Acum vine partea interesantă. Să construim atât fișierul nostru client, cât și serverul cu go build pentru a obține fișierul nostru binar. Apoi vom deschide două comenzi separate de consolă pentru al rula.

Doar o notă rapidă: ar trebui să porniți serverul mai întâi înainte de client, deoarece acesta va invoca direct metoda serverului.

Deci, să intrăm în directorul fiecăruia dintre fișierele noastre binare și să le rulăm pe amândouă cu /.server și ./client .

Clientul dvs. va afișa acest lucru:

2020/11/10 22:26:11 Resp received: Request #0 For Id:1
2020/11/10 22:26:12 Resp received: Request #1 For Id:1
2020/11/10 22:26:13 Resp received: Request #2 For Id:1
2020/11/10 22:26:14 Resp received: Request #3 For Id:1
2020/11/10 22:26:15 Resp received: Request #4 For Id:1
2020/11/10 22:26:15 finished

Și serverul va afișa acest lucru:

2020/11/10 22:26:09 start server
2020/11/10 22:26:11 fetch response for id : 1
2020/11/10 22:26:11 finishing request number : 0
2020/11/10 22:26:12 finishing request number : 1
2020/11/10 22:26:13 finishing request number : 2
2020/11/10 22:26:14 finishing request number : 3
2020/11/10 22:26:15 finishing request number : 4

Dacă totul este în regulă, v-ați construit cu succes un serviciu de streaming gRPC pe partea serverului cu Go! Dacă aveți nevoie de codul GitHub pentru întregul exemplu, îl puteți găsi Aici.

Încheierea

Sper că acest exemplu al modului de implementare a streaming-ului pe serverul gRPC cu Go te-a ajutat să înțelegi procesul.

Este posibil ca această implementare să nu fie foarte obișnuită și este posibil să nu aveți nevoie chiar de acest tip de implementare complexă în proiectul dvs. Dar gândiți-vă la el ca la un instrument pentru a vă ridica și mai mult proiectul.

Dacă doriți să aflați mai multe, consultați aceste alte concepte interesante, cum ar fi transmiterea pe partea clientului sau chiar transmisia bidirecțională Am găsit exemplul Aici a fi destul de bun.

Vă mulțumim că ați citit articolul meu până la capăt! Sper cu adevărat că ați învățat ceva nou și util astăzi. După cum am spus, s-ar putea să nu aveți cu adevărat nevoie de el, dar de ce nu îl încercați?

Nu așteptați schimbarea, luați inițiativa și fiți catalizatorul schimbării.