gRPC: Wo wohnst du? Was isst du?

Linda Hamilton
Freigeben: 2024-09-28 06:07:29
Original
997 Leute haben es durchsucht

Das erste Mal, dass ich von RPC hörte, war in einem Kurs über verteilte Systeme, als ich Informatik studierte. Ich fand es cool, aber ich erinnere mich, dass ich damals nicht genau verstand, warum ich beispielsweise RPC anstelle des REST-Standards verwenden würde. Die Zeit vergeht und ich arbeite in einem Unternehmen, in dem ein Teil des Altsystems SOAP verwendet. Ich erinnere mich, dass ich dachte: „Hmm, interessant! Es sieht aus wie RPC, geht aber über XML.“ Jahre später hörte ich zum ersten Mal von gRPC, aber ich verstand nie ganz, was es war, was es aß und wozu es diente.

Da mein Blog viele persönliche Dokumentationen enthält, dachte ich, es wäre cool, hier zu dokumentieren, was ich gelernt habe, angefangen bei RPC bis hin zu gRPC.

Komm schon, was ist RPC?

RPC ist ein Akronym für Remote Procedure Call. Mit anderen Worten: Sie senden Prozeduren/Befehle an einen Remote-Server. Einfach ausgedrückt ist dies RPC. Es funktioniert wie folgt:

gRPC: onde vive? o que come?

RPC funktioniert sowohl über UDP als auch über TCP. Es liegt an Ihnen, herauszufinden, was für Ihren Anwendungsfall sinnvoll ist! Wenn Ihnen eine mögliche Antwort oder sogar der Verlust von Paketen nichts ausmacht, UDP. Andernfalls verwenden Sie TCP. Wer gerne die RFCs liest, findet den Link hier!

OK, aber wie unterscheidet sich RPC beispielsweise von einem REST-Aufruf?

Bei beiden handelt es sich um Möglichkeiten zur Architektur von APIs. Allerdings verfügt die REST-Architektur über sehr klar definierte Prinzipien, die befolgt werden müssen, um eine vollständige REST-Architektur zu erhalten. RPC hat sogar Prinzipien, aber diese werden zwischen Client und Server definiert. Für den RPC-Client ist es so, als würde er eine lokale Prozedur aufrufen.

Ein weiterer wichtiger Punkt ist, dass es für RPC keine große Rolle spielt, ob die Verbindung TCP oder UDP ist. Was REST-APIs betrifft: Wenn Sie RESTfull folgen möchten, können Sie UDP nicht verwenden.

Für diejenigen, die mehr darüber erfahren möchten, empfehle ich diesen hervorragenden AWS-Leitfaden zu RPC x REST.

Und wie implementiert man einen RPC-Server mit Go?

Wir haben zwei Haupteinheiten, den Client und den Server.

Beginnend mit dem Server...

Der Server ist ein WEB-Server, der üblicherweise in jedem Microservice verwendet wird. Definieren wir dann den Verbindungstyp, den wir verwenden werden, in unserem Fall wurde TCP gewählt:

func main() {
  addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:52648")
  if err != nil {
    log.Fatal(err)
  }

  conn, err := net.ListenTCP("tcp", addr)
  if err != nil {
    log.Fatal(err)
  }
  defer conn.Close()

  // ...
}
Nach dem Login kopieren

Wenn unser Server instanziiert ist, benötigen wir einen Handler, d. h. unsere auszuführende Prozedur. Es ist wichtig zu sagen, dass wir immer definieren müssen, von welchen Argumenten wir kommen und worauf wir in unserer HTTP-Verbindung reagieren. Um unseren Proof of Concept zu vereinfachen, erhalten wir eine Argumentstruktur und antworten auf dieselbe Struktur:

type Args struct {
  Message string
}

type Handler int

func (h *Handler) Ping(args *Args, reply *Args) error {
  fmt.Println("Received message: ", args.Message)

  switch args.Message {
  case "ping", "Ping", "PING":
    reply.Message = "pong"
  default:
    reply.Message = "I don't understand"
  }

  fmt.Println("Sending message: ", reply.Message)
  return nil
}
Nach dem Login kopieren

Nachdem Sie unseren Prozessor erstellt haben, lassen Sie ihn nun nur noch Verbindungen akzeptieren:

func main() {
  // ...

  h := new(Handler)
  log.Printf("Server listening at %v", conn.Addr())
  s := rpc.NewServer()
  s.Register(h)
  s.Accept(conn)
}
Nach dem Login kopieren

Den Kunden definieren...

Da Client und Server derselben definierten Struktur folgen müssen, definieren wir hier die Argumentstruktur neu, die von unserem Client gesendet werden soll:

type Args struct {
  Message string
}
Nach dem Login kopieren

Um es einfacher zu machen, erstellen wir einen interaktiven Client: Er liest Einträge in STDIN und sendet ihn, wenn er einen neuen Eintrag erhält, an unseren Server. Zu Bildungszwecken werden wir die erhaltene Antwort aufschreiben.

func main() {
  client, err := rpc.Dial("tcp", "localhost:52648")
  if err != nil {
    log.Fatal(err)
  }

  for {
    log.Println("Please, inform the message:")

    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()

    args := Args{Message: scanner.Text()}
    log.Println("Sent message:", args.Message)
    reply := &Args{}
    err = client.Call("Handler.Ping", args, reply)
    if err != nil {
      log.Fatal(err)
    }

    log.Println("Received message:", reply.Message)
    log.Println("-------------------------")
  }
}
Nach dem Login kopieren

Sie können sehen, dass wir die Adresse angeben müssen, unter der der Server läuft, und welchen Handler (Prozedur) wir ausführen möchten.

Ein wichtiger Zusatz ist, dass wir Binärdaten transportieren und Go standardmäßig Encoding/Gob verwendet. Wenn Sie einen anderen Standard wie JSON verwenden möchten, müssen Sie Ihren Server anweisen, diesen neuen Codec zu akzeptieren.

Für diejenigen, die den vollständigen Code sehen möchten, greifen Sie einfach auf den PoC zu.

Und was ist gRPC?

gRPC ist ein Framework zum Schreiben von Anwendungen mit RPC! Dieses Framework wird derzeit von der CNCF gepflegt und wurde laut offizieller Dokumentation von Google erstellt:

gRPC wurde ursprünglich von Google entwickelt, das seit über einem Jahrzehnt eine einzige universelle RPC-Infrastruktur namens Stubby verwendet, um die große Anzahl von Mikrodiensten zu verbinden, die in und zwischen seinen Rechenzentren ausgeführt werden. Im März 2015 beschloss Google, die nächste Version von Stubby zu entwickeln und als Open Source bereitzustellen. Das Ergebnis war gRPC, das heute in vielen Organisationen außerhalb von Google verwendet wird, um Anwendungsfälle von Microservices bis zur „letzten Meile“ der Datenverarbeitung (mobil, Web und Internet der Dinge) voranzutreiben.

gRPC funktioniert nicht nur auf unterschiedlichen Betriebssystemen und auf unterschiedlichen Architekturen, sondern bietet auch folgende Vorteile:

  • Bibliotecas idiomáticas em 11 linguagens;
  • Framework simples para definição do seu serviço e extremamente performático.
  • Fluxo bi-direcional de dados utilizando http/2 para transporte;
  • Funcionalidades extensíveis como autenticação, tracing, balanceador de carga e verificador de saúde.

E como utilizar o gRPC com Go?

Para nossa sorte, Go é uma das 11 linguagens que tem bibliotecas oficiais para o gRPC! É importante falar que esse framework usa o Protocol Buffer para serializar a mensagem. O primeiro passo então é instalar o protobuf de forma local e os plugins para Go:

brew install protobuf
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Nach dem Login kopieren

E adicionar os plugins ao seu PATH:

export PATH="$PATH:$(go env GOPATH)/bin"
Nach dem Login kopieren

A mágica do protobuf...

Vamos então criar nossos arquivos .proto! Nesse arquivo vamos definir nosso serviço, quais os handlers que ele possui e para cada handler, qual a requisição e qual resposta esperadas.

syntax = "proto3";

option go_package = "github.com/mfbmina/poc_grpc/proto";

package ping_pong;

service PingPong {
  rpc Ping (PingRequest) returns (PingResponse) {}
}

message PingRequest {
  string message = 1;
}

message PingResponse {
  string message = 1;
}
Nach dem Login kopieren

Com o arquivo .proto, vamos fazer a mágica do gRPC + protobuf acontecer. Os plugins instalados acima, conseguem gerar tudo o que for necessário para um servidor ou cliente gRPC com o seguinte comando:

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative proto/ping_pong.proto
Nach dem Login kopieren

Esse comando vai gerar dois arquivos: ping_pong.pb.go e ping_pong_grpc.pb.go. Recomendo dar uma olhada nesses arquivos para entender melhor a estrutura do servidor e do cliente. Com isso, podemos então construir o servidor:

Construindo o servidor...

Para conseguir comparar com o RPC comum, vamos utilizar a mesma lógica: recebemos PING e respondemos PONG. Aqui definimos um servidor e um handler para a requisição e usamos as definições vindas do protobuf para a requisição e resposta. Depois, é só iniciar o servidor:

type server struct {
  pb.UnimplementedPingPongServer
}

func (s *server) Ping(_ context.Context, in *pb.PingRequest) (*pb.PingResponse, error) {
  r := &pb.PingResponse{}
  m := in.GetMessage()
  log.Println("Received message:", m)

  switch m {
  case "ping", "Ping", "PING":
    r.Message = "pong"
  default:
    r.Message = "I don't understand"
  }

  log.Println("Sending message:", r.Message)

  return r, nil
}

func main() {
  l, err := net.Listen("tcp", ":50051")
  if err != nil {
    log.Fatal(err)
  }

  s := grpc.NewServer()
  pb.RegisterPingPongServer(s, &server{})
  log.Printf("Server listening at %v", l.Addr())

  err = s.Serve(l)
  if err != nil {
    log.Fatal(err)
  }
}
Nach dem Login kopieren

E o cliente...

Para consumir o nosso servidor, precisamos de um cliente. o cliente é bem simples também. A biblioteca do gRPC já implementa basicamente tudo que precisamos, então inicializamos um client e só chamamos o método RPC que queremos usar, no caso o Ping. Tudo vem importado do código gerado via plugins do protobuf.

func main() {
    conn, err := grpc.NewClient("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    c := pb.NewPingPongClient(conn)

    for {
        log.Println("Enter text: ")
        scanner := bufio.NewScanner(os.Stdin)
        scanner.Scan()
        msg := scanner.Text()
        log.Printf("Sending message: %s", msg)

        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()
        r, err := c.Ping(ctx, &pb.PingRequest{Message: msg})
        if err != nil {
            log.Fatal(err)
        }

        log.Printf("Received message: %s", r.GetMessage())
        log.Println("-------------------------")
    }
}
Nach dem Login kopieren

Quem tiver interesse para ver o código completo, pode acessar a PoC gRPC.

Considerações finais

O gRPC não é nada mais que uma abstração em cima do RPC convencional utilizando o protobuf como serializador e o protocolo http/2. Existem algumas considerações de performance ao se utilizar o http/2 e em alguns cenários, como em requisições com o corpo simples, o http/1 se mostra mais performático que o http/2. Recomendo a leitura deste benchmark e desta issue aberta no golang/go sobre o http/2. Contudo, em requisições de corpo complexo, como grande parte das que resolvemos dia a dia, gRPC se torna uma solução extremamente atraente devido ao serializador do protobuf, que é extremamente mais rápido que JSON. O Elton Minetto fez um blog post explicando melhor essas alternativas e realizando um benchmark. Um consideração também é o protobuf consegue resolver o problema de inconsistência de contratos entre servidor e cliente, contudo é necessário uma maneira fácil de distribuir os arquivos .proto.

Por fim, minha recomendação é use gRPC se sua equipe tiver a necessidade e a maturidade necessária para tal. Hoje, grande parte das aplicações web não necessitam da performance que gRPC visa propor e nem todos já trabalharam com essa tecnologia, o que pode causar uma menor velocidade e qualidade nas entregas. Como nessa postagem eu citei muitos links, decidi listar todas as referências abaixo:

  • RPC
  • RPC RFC
  • RPC x REST
  • PoC RPC
  • net/rpc
  • encoding/gob
  • CNCF - Cloud Native Computing Foundation
  • gRPC
  • Protocol Buffer
  • PoC gRPC
  • http/1 x http/2 x gRPC
  • http/2 issue
  • JSON x Protobuffers X Flatbuffers

Espero que vocês tenham gostado do tema e obrigado!

Das obige ist der detaillierte Inhalt vongRPC: Wo wohnst du? Was isst du?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage