Go中的gRPC入门教程详解

Go中的gRPC入门教程详解

什么是gRPC?

gRPC是一种高性能、通用的开源框架,用于构建分布式系统。它由Google公司推出,基于Protocol Buffers(一种高效的序列化技术)开发,支持多种语言(如Go、Java、Python等)。gRPC主要解决了分布式系统中服务间通信的问题,极大地简化了开发人员的工作量。

基本概念

在了解gRPC的使用之前,我们需要了解一些基本概念:

  • 服务定义:定义服务的接口和方法。
  • 消息:通过Protocol Buffers定义的数据结构,用于在客户端和服务端之间传输数据。
  • 客户端:调用远程服务的一方。
  • 服务端:提供服务的一方。

安装

使用Go语言开发gRPC应用,需要安装以下两个库:

  • protoc:Protocol Buffers编译器。
  • gRPC:Go中的gRPC库。

安装方法可以参考这个网站:https://grpc.io/docs/languages/go/quickstart/

编写服务定义文件

服务定义文件使用Protocol Buffers语言编写,包含服务和消息的定义内容。以下是一个服务定义文件的示例:

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

上述文件定义了一个Greeter服务,包括一个SayHello方法和两个消息类型:HelloRequest和HelloReply。SayHello方法接收一个HelloRequest消息,并返回一个HelloReply消息。

编写服务端

服务端代码主要负责注册服务、实现服务接口的具体方法。以下是一个gRPC服务端的示例代码:

package main

import (
    "context"
    "log"
    "net"

    pb "github.com/your_username/your_project/your_proto_file_directory"
    "google.golang.org/grpc"
)

const (
    port = ":50051"
)

type server struct{}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.GetName())
    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

上述代码定义了一个名为Greeter的服务,包含一个SayHello方法。通过pb.RegisterGreeterServer(s, &server{})将服务注册到grpc服务器中。

编写客户端

客户端代码主要负责调用服务端提供的接口。以下是一个gRPC客户端的示例代码:

package main

import (
    "context"
    "log"
    "os"
    "time"

    pb "github.com/your_username/your_project/your_proto_file_directory"
    "google.golang.org/grpc"
)

const (
    address     = "localhost:50051"
    defaultName = "world"
)

func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.GetMessage())
}

上述代码定义了一个Greeter客户端,通过pb.NewGreeterClient(conn)创建一个客户端实例。然后调用该实例的SayHello方法,向服务端发送一个HelloRequest消息,并接收其返回的HelloReply消息。

示例说明

以下是两个示例说明:

示例一

一个简单的计算器服务,实现add和multiply两个方法。

服务定义文件calculator.proto:

syntax = "proto3";

package calculator;

service Calculator {
    rpc Add (Numbers) returns (Number) {}
    rpc Multiply (Numbers) returns (Number) {}
}

message Numbers {
    int32 num1 = 1;
    int32 num2 = 2;
}

message Number {
    int32 result = 1;
}

服务端代码:

package main

import (
    "context"
    "log"
    "net"

    pb "github.com/your_username/calculator"
    "google.golang.org/grpc"
)

const (
    port = ":50051"
)

type server struct{}

func (s *server) Add(ctx context.Context, in *pb.Numbers) (*pb.Number, error) {
    result := in.Num1 + in.Num2
    return &pb.Number{Result: result}, nil
}

func (s *server) Multiply(ctx context.Context, in *pb.Numbers) (*pb.Number, error) {
    result := in.Num1 * in.Num2
    return &pb.Number{Result: result}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterCalculatorServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

客户端代码:

package main

import (
    "context"
    "log"
    "os"
    "time"

    pb "github.com/your_username/calculator"
    "google.golang.org/grpc"
)

const (
    address     = "localhost:50051"
    defaultNum1 = 2
    defaultNum2 = 3
)

func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewCalculatorClient(conn)

    num1 := defaultNum1
    num2 := defaultNum2
    if len(os.Args) > 1 {
        num1 = int32(os.Args[1])
        num2 = int32(os.Args[2])
    }
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    r1, err := c.Add(ctx, &pb.Numbers{Num1: num1, Num2: num2})
    if err != nil {
        log.Fatalf("could not add: %v", err)
    }
    r2, err := c.Multiply(ctx, &pb.Numbers{Num1: num1, Num2: num2})
    if err != nil {
        log.Fatalf("could not multiply: %v", err)
    }
    log.Printf("Add result: %d", r1.GetResult())
    log.Printf("Multiply result: %d", r2.GetResult())
}

示例二

一个简单的文件传输服务,实现Upload和Download两个方法。

服务定义文件file.proto:

syntax = "proto3";

package file;

service File {
    rpc Upload (FileInfo) returns (Response) {}
    rpc Download (Request) returns (FileInfo) {}
}

message Request {
    string name = 1;
}

message FileInfo {
    bytes content = 1;
    string name = 2;
}

message Response {
    string message = 1;
}

服务端代码:

package main

import (
    "context"
    "io/ioutil"
    "log"
    "net"

    pb "github.com/your_username/file"
    "google.golang.org/grpc"
)

const (
    port = ":50051"
)

type server struct{}

func (s *server) Upload(ctx context.Context, in *pb.FileInfo) (*pb.Response, error) {
    err := ioutil.WriteFile(in.GetName(), in.GetContent(), 0644)
    if err != nil {
        log.Fatalf("Failed to save file: %v", err)
    }
    return &pb.Response{Message: "File uploaded successfully!"}, nil
}

func (s *server) Download(ctx context.Context, in *pb.Request) (*pb.FileInfo, error) {
    content, err := ioutil.ReadFile(in.GetName())
    if err != nil {
        log.Fatalf("Failed to read file: %v", err)
    }
    return &pb.FileInfo{Content: content, Name: in.GetName()}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterFileServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

客户端代码:

package main

import (
    "context"
    "io/ioutil"
    "log"
    "os"

    pb "github.com/your_username/file"
    "google.golang.org/grpc"
)

const (
    address     = "localhost:50051"
    defaultFile = "test.txt"
)

func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewFileClient(conn)

    file := defaultFile
    if len(os.Args) > 1 {
        file = os.Args[1]
    }
    content, err := ioutil.ReadFile(file)
    if err != nil {
        log.Fatalf("Failed to read file: %v", err)
    }
    ctx := context.Background()

    _, err = c.Upload(ctx, &pb.FileInfo{Content: content, Name: file})
    if err != nil {
        log.Fatalf("Failed to upload file: %v", err)
    }
    log.Printf("File uploaded successfully!")

    r, err := c.Download(ctx, &pb.Request{Name: file})
    if err != nil {
        log.Fatalf("Failed to download file: %v", err)
    }
    err = ioutil.WriteFile(file, r.GetContent(), 0644)
    if err != nil {
        log.Fatalf("Failed to save file: %v", err)
    }
    log.Printf("File downloaded successfully!")
}

以上就是Go中gRPC入门教程的详解,希望能对您有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Go中的gRPC入门教程详解 - Python技术站

(0)
上一篇 2023年6月3日
下一篇 2023年6月3日

相关文章

  • C#泛型方法在lua中表示的一种设计详解

    C#泛型方法在lua中表示的一种设计详解 在C#中我们可以使用泛型方法来实现更加灵活的代码设计,而在将C#代码转换为Lua代码时,如何正确地将泛型方法转换为Lua代码也是一项必需的技能。本文将介绍一种C#泛型方法在Lua中表示的设计方法。 泛型方法的基本语法 C#中泛型方法的基本语法如下: public void Method<T>(T inpu…

    C# 2023年5月15日
    00
  • C#遍历集合与移除元素的方法

    关于C#遍历集合与移除元素的方法,我来给大家讲解一下。主要分为两个部分:遍历集合和移除元素。下面将介绍两种常用的方法。 遍历集合 方法一:foreach循环 遍历集合最常用的方式之一就是使用foreach循环。语法如下: foreach (var item in collection) { // 执行代码 } 其中,item代表集合中的每个元素,collec…

    C# 2023年6月7日
    00
  • C#使用System.Buffer以字节数组Byte[]操作基元类型数据

    操作基元类型数据时,我们通常需要进行数据类型转换,进行字节序列转换。而C#中的System.Buffer类提供了方便的功能,可以以字节数组Byte[]的形式对基本类型进行操作。以下是C#使用System.Buffer以字节数组Byte[]操作基元类型数据的完整攻略: 1. 引入命名空间 为了使用System.Buffer类,必须在代码中引入该命名空间。在代码…

    C# 2023年6月8日
    00
  • C# winform打印excel的方法

    下面是关于如何使用C# WinForm打印Excel的完整攻略,包含以下几个步骤: 1. 引用Excel Interop 要打印Excel,需要使用Microsoft Excel Interop库。这个库需要先引用才能在程序中使用。下面是引用Excel Interop的具体步骤: 在Visual Studio的工具栏中选择“项目”。 在项目中选择“添加引用”…

    C# 2023年6月7日
    00
  • Asp.Mvc 2.0用户客户端验证实例讲解(3)

    Asp.Mvc 2.0用户客户端验证实例讲解是一篇教程文章,介绍了如何使用Asp.Mvc 2.0实现用户客户端验证。下面是Asp.Mvc 2.0用户客户端验证实例讲解的完整攻略。 1. 概述 本文将介绍如何使用Asp.Mvc 2.0实现用户客户端验证,在前后端分离开发中,用户客户端验证是非常重要的,可以在一定程度上减少请求次数,提高用户体验,同时还可以避免一…

    C# 2023年5月31日
    00
  • asp.net 数字签名实例代码

    为了实现数字签名,我们需要使用ASP.NET自带的RSACryptoServiceProvider类。该类可以生成密钥对,对消息进行数字签名,以及验证数字签名。下面是实现数字签名的详细步骤: 步骤一:生成密钥对 首先,我们需要生成一个RSA密钥对,用于数字签名。我们可以使用以下代码生成密钥对: // 创建一个RSA实例 using (var rsa = ne…

    C# 2023年5月31日
    00
  • 轻松学习C#的ArrayList类

    轻松学习C#的ArrayList类 介绍 ArrayList是C#中的一个动态数组类,可以自动调整大小,支持任意数据类型的添加和移除。本文将提供一个完整的攻略,帮助你轻松学习并应用ArrayList。 创建ArrayList对象 你可以使用以下方法创建一个ArrayList对象: ArrayList list = new ArrayList(); 添加元素 …

    C# 2023年5月31日
    00
  • C#实现将应用程序设置为开机启动的方法

    下面我会详细讲解如何用 C# 实现将应用程序设置为开机启动的方法。 方法一:使用注册表 Windows 操作系统允许我们通过修改注册表的方式来设置开机启动程序。下面是具体的步骤: 打开注册表编辑器。在 Windows 搜索框中键入 “regedit” 并回车即可打开。 找到以下注册表路径:HKEY_CURRENT_USER\SOFTWARE\Microsof…

    C# 2023年6月7日
    00
合作推广
合作推广
分享本页
返回顶部