文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

利用Go传统RPC和gRPC框架分别实现一个RPC服务端

2024-11-30 02:46

关注

RPC(Remote Procedure Call, 远程过程调用) 是一种计算机通信协议, 它允许程序调用另一个地址空间(通常是远程机器上的)的过程或函数, 就像本地调用一样, 而不需要显示地处理网络通信的细节。RPC使得分布式系统中的不同模块能够相互通信, 而对开发者而言, 这种通信就像是本地调用一样简单。其调用原理图如下:

图片

上面的步骤看起来很复杂, 实际上, 在当前的主流RPC框架, 例如:grpc、thrift, 只需要关心第1步和最后1步即可, 中间过程已经由框架进行了封装。在这篇文章中, 将从学习的角度自己来实现一个RPC的服务端全流程。

2.实现RPC服务端

这里模拟了用户信息的注册管理流程, 在服务端, 保存有多个用户信息, 并提供两个远程调用接口, 一个是通过ID获取对应的用户信息接口, 另一个是添加新的用户。参考代码如下:

// 1. 定义要远程调用的方法
type MathService struct {}

func (m *MathService) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

// 2. 定义请求和响应的数据结构
type Args struct {
    A, B int
}

在上面的代码中, 我们定义了一个MathService结构体,其中包含了一个Multiply方法,该方法用于实现两个整数相乘的远程调用。接下来我们需要完成服务端的服务端口监听和连接建立, 参考代码如下:

func main() {
    mathService := new(MathService)
    rpc.Register(mathService)

    listener, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal("Listen error:", err)
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Fatal("Accept error:", err)
        }
        go rpc.ServeConn(conn)
    }
}

在上面的main函数中, 我们注册了一个MathService服务,并在本地监听1234端口,当接收到客户端连接后, 使用rpc.ServeConn来处理RPC请求。

3.实现客户端连接

客户端连接服务端的参考代码如下:

type Args struct {
  A, B int
}

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

    args := &Args{7, 8}
    var reply int

    err = client.Call("MathService.Multiply", args, &reply)
    if err != nil {
        log.Fatal("MathService.Multiply error:", err)
    }

    fmt.Printf("MathService.Multiply: %d * %d = %d\n", args.A, args.B, reply)
}

先编译服务端代码并启动, 然后执行客户端程序,结果如下:

图片

客户端成功调用了服务端的远程函数并收到结果。

4.利用gRPC框架实现服务端

gRPC(gRPC Remote Procedure Calls)是由Google开发的开源RPC(Remote Procedure Call,远程过程调用)框架,其目标是在跨网络的服务之间实现高效的通信。gRPC使用Protocol Buffers(protobuf)作为其接口描述语言,并支持多种编程语言,包括C++, Java, Python, Go, Node.js等。

首先需要安装gRPC相关的包,通过以下命令安装:

go get -u google.golang.org/grpc

接着需要安装Protocol Buffers工具, 可以从这里直接下载最新版, 根据自己的操作系统类型选择:

https://github.com/protocolbuffers/protobuf/releases

接下来需要安装两个包:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

紧接着创建proto文件, 内容如下:

syntax = "proto3";

package grpcsample;
option go_package = ".";

message User {
  string id = 1;
  string name = 2;
  int32 age = 3;
}

service UserService {
  rpc GetUserById (UserRequest) returns (User);
  rpc AddUser (User) returns (User);
}

message UserRequest {
  string id = 1;
}

将上面的代码生成文件, 文件名为: user.proto。

我这里将protoc二进制程序放到工程根目录gosample下, 接着在命令行下输入以下命令:

./protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./grpcsample/user.proto

该命令将把.proto文件内容生成对应的grpc Go代码, 生成后将在grpcsample目录下生成user.pb.go和user_grpc.pb.go文件,如图:

图片

在grpcsample目录下新建工程文件userservice.go, 加入以下代码:

import (
  "context"
  "fmt"
)

var users = map[string]User{
  "1": {Id: "1", Name: "John Doe", Age: 30},
  "2": {Id: "2", Name: "Jane Doe", Age: 25},
}

type UserServer struct {
  UnimplementedUserServiceServer
}

func (s *UserServer) GetUserById(ctx context.Context, req *UserRequest) (*User, error) {
  user, exists := users[req.Id]
  if exists {
    return &user, nil
  }
  return nil, fmt.Errorf("User with ID %s not found", req.Id)
}

func (s *UserServer) AddUser(ctx context.Context, user *User) (*User, error) {
  users[user.Id] = *user
  return user, nil
}

上面的代码提供了两个RPC方法, GetUserById支持通过ID查询对应的用户信息, AddUser支持添加一个新的用户。

接着添加服务端的主程序代码:

import (
  "google.golang.org/grpc"
  pb "gosample/grpcsample"
)

func main() {
    listener, err := net.Listen("tcp", ":50051")
  if err != nil {
    log.Fatalf("Failed to listen: %v", err)
  }

  server := grpc.NewServer()
  pb.RegisterUserServiceServer(server, &pb.UserServer{})

  log.Println("gRPC server is running on port 50051")
  if err := server.Serve(listener); err != nil {
    log.Fatalf("Failed to serve: %v", err)
  }
}

在服务端主程序代码中, 我们调用了grpcsample中的RegisterUserServiceServer方法注册了一个服务,并在本地的50051端口监听客户端连接。

5.生成gRPC客户端

同样的方式, 新打开一个工程, 按照服务端生成gRPC的方式生成客户端的代码, 如图:

图片

在客户端的主程序中利用如下代码进行服务端方法调用:

package main

import (
  "context"
  "fmt"
  "log"

  "google.golang.org/grpc"
  "google.golang.org/grpc/credentials/insecure"

  pb "sampleclient/grpcsample"
)

func main() {
   conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
  if err != nil {
    log.Fatalf("Failed to connect: %v", err)
  }
  defer conn.Close()

  client := pb.NewUserServiceClient(conn)

  // 通过ID查询用户
  user, err := client.GetUserById(context.Background(), &pb.UserRequest{Id: "1"})
  if err != nil {
    log.Fatalf("Error getting user: %v", err)
  }
  fmt.Printf("User: %+v\n", user)

  // 添加一个新用户
  newUser := &pb.User{Id: "3", Name: "Alice", Age: 28}
  addedUser, err := client.AddUser(context.Background(), newUser)
  if err != nil {
    log.Fatalf("Error adding user: %v", err)
  }
  fmt.Printf("Added User: %+v\n", addedUser)
}

在上面的代码中, 首先通过grpc包中的Dial函数连接到本地50051端口, 并调用gRPC的方法NewUserServiceClient新建一个客户端连接, 接着远程调用了服务端的两个方法。首先开启服务端, 查看客户端调用方法后的返回,如图:

图片

可以看到,成功获取到远程的两个方法返回的结果。

来源:二进制空间安全内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯