golang开发一个简单的grpc

0.1、索引

https://waterflow.link/articles/1665674508275

1、什么是grpc

在 gRPC 中,客户端应用程序可以直接调用不同机器上的服务器应用程序上的方法,就像它是本地对象一样,使您更容易创建分布式应用程序和服务。 与许多 RPC 系统一样,gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。 在服务端,服务端实现这个接口并运行一个 gRPC 服务器来处理客户端调用。 在客户端,客户端有一个stub(在某些语言中仅称为客户端),它提供与服务器相同的方法。
http://image-1313007945.cos.ap-nanjing.myqcloud.com/image/1665674577.png

所以grpc是跨语言的。

2、什么是Protocol Buffers

Protocol Buffers提供了一种语言中立、平台中立、可扩展的机制,用于以向前兼容和向后兼容的方式序列化结构化数据。 它类似于 JSON,只是它更小更快,并且生成本地语言绑定。

可以通过 .proto定义数据结构,然后就可以使用Protocol Buffers编译器 protoc 从. proto 定义中生成我们喜欢的语言的数据访问类。 它们为每个字段提供简单的访问器,如 name() 和 set_name(),以及将整个结构序列化/解析到原始字节/从原始字节中提取的方法。

3、grpc服务端

1、首先我们需要下载go对应的protoc插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

然后把GOPATH放到PATH

export PATH="$PATH:$(go env GOPATH)/bin"

接着打印下看看有没有进去

echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$GOPATH/bin:/usr/local/go/bin

接着开新窗口,执行下面命令看下protoc是否安装成功

protoc --version
libprotoc 3.19.1

2、接着我们创建一个hello.proto

内容如下(不懂结构的可自行百度)

syntax = "proto3";

package helloservice;

option go_package = ".;helloservice"; // 指定包名

message String {
  string value = 1;
}

service HelloService {
  rpc Hello(String) returns (String); // 一元方法
  rpc Channel (stream String) returns (stream String); // 流式方法
}

目录结构如下

.
├── go.mod
├── go.sum
├── helloclient
│   └── main.go
├── helloservice
│   ├── hello.proto

3、接着命令行生成对应语言的类代码

cd helloservice
 protoc --go_out=./ --go-grpc_out=./ hello.proto

我们可以看下现在的目录结构(其他文件目录可忽略,后面会创建,现在只需要关注hello_grpc.pb.go)

.
├── go.mod
├── go.sum
├── helloclient
│   └── main.go
├── helloservice
│   ├── hello.pb.go
│   ├── hello.proto
│   ├── hello_grpc.pb.go
│   ├── hello_service.go
│   └── main
│       └── main.go

4、实现自己的hello service

在上面生成的hello_grpc.pb.go中我们可以看到这样的接口

// HelloServiceServer is the server API for HelloService service.
// All implementations must embed UnimplementedHelloServiceServer
// for forward compatibility
type HelloServiceServer interface {
    Hello(context.Context, *String) (*String, error)
    Channel(HelloService_ChannelServer) error
    mustEmbedUnimplementedHelloServiceServer()
}

翻译一下就是

// HelloServiceServer 是 HelloService 服务的服务端 API。
// 所有实现都必须嵌入 UnimplementedHelloServiceServer
// 为了向前兼容

所以我们在helloservice中创建一个hello_service.go文件,用来实现上面的接口

package helloservice

import (
    "context"
    "io"
    "time"
)

type HelloService struct {
}

func (h HelloService) mustEmbedUnimplementedHelloServiceServer() {
    panic("implement me")
}

func (h HelloService) Hello(ctx context.Context, args *String) (*String, error) {
    time.Sleep(time.Second)
    reply := &String{Value: "hello:" + args.GetValue()}
    return reply, nil
}

func (h HelloService) Channel(stream HelloService_ChannelServer) error {
    for {
        recv, err := stream.Recv()
        if err != nil {
            if err == io.EOF {
                return nil
            }
            return err
        }

        reply := &String{Value: "hello:" + recv.Value}
        err = stream.Send(reply)
        if err != nil {
            return err
        }
    }
}

上面的方法和简单,就是打印我们自定义的字符串。

然后我们在main中编写下服务启动的代码

package main

import (
    "google.golang.org/grpc"
    "grpcdemo/helloservice"
    "log"
    "net"
)

func main() {
  // NewServer 创建一个 gRPC 服务器,它没有注册服务,也没有开始接受请求。
    grpcServer := grpc.NewServer()
  // 注册服务
    helloservice.RegisterHelloServiceServer(grpcServer, new(helloservice.HelloService))

  // 开启一个tcp监听
    listen, err := net.Listen("tcp", ":1234")
    if err != nil {
        log.Fatal(err)
    }
    log.Println("server started...")
  // 在监听器 listen 上接受传入的连接,创建一个新的ServerTransport 和 service goroutine。 服务 goroutine读取 gRPC 请求,然后调用注册的处理程序来回复它们。
    log.Fatal(grpcServer.Serve(listen))
}

然后我们启动下看下效果

go run helloservice/main/main.go
2022/10/13 23:07:46 server started...

4、grpc客户端

接着我们编写客户端的代码helloclient/main.go

package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "grpcdemo/helloservice"
    "io"
    "log"
    "time"
)

func main() {
    // 连接grpc服务端
    conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

  // 一元rpc
    unaryRpc(conn)
  // 流式rpc
    streamRpc(conn)

}


func unaryRpc(conn *grpc.ClientConn) {
  // 创建grpc客户端
    client := helloservice.NewHelloServiceClient(conn)
  // 发送请求
    reply, err := client.Hello(context.Background(), &helloservice.String{Value: "hello"})
    if err != nil {
        log.Fatal(err)
    }
    log.Println("unaryRpc recv: ", reply.Value)
}

func streamRpc(conn *grpc.ClientConn) {
  // 创建grpc客户端
    client := helloservice.NewHelloServiceClient(conn)
  // 生成ClientStream
    stream, err := client.Channel(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    go func() {
        for {
      // 发送消息
            if err := stream.Send(&helloservice.String{Value: "hi"}); err != nil {
                log.Fatal(err)
            }
            time.Sleep(time.Second)
        }
    }()

    for {
    // 接收消息
        recv, err := stream.Recv()
        if err != nil {
            if err == io.EOF {
                break
            }
            log.Fatal(err)
        }

        fmt.Println("streamRpc recv: ", recv.Value)

    }
}
15 声望
0 粉丝
0 条评论
推荐阅读
golang中的错误处理
0.1、索引[链接]1、panic当我们执行panic的时候会结束下面的流程: {代码...} {代码...} 但是panic也是可以捕获的,我们可以使用defer和recover实现: {代码...} {代码...} 那什么时候适合panic呢?在 Go 中,pan...

liuyuede阅读 648

封面图
写给go开发者的gRPC教程-通信模式
本篇为【写给go开发者的gRPC教程系列】第二篇第一篇:protobuf基础第二篇:通信模式上一篇介绍了如何编写 protobuf 的 idl,并使用 idl 生成了 gRPC 的代码,现在来看看如何编写客户端和服务端的代码Simple RPC (...

liangwt2阅读 1k

封面图
写给go开发者的gRPC教程-protobuf基础
序列化协议。gRPC使用protobuf,首先使用protobuf定义服务,然后使用这个文件来生成客户端和服务端的代码。因为pb是跨语言的,因此即使服务端和客户端语言并不一致也是可以互相序列化和反序列化的

liangwt1阅读 990评论 1

封面图
protocol-buffers namespace conflict
在运行grpc服务,加载*.pb.go时可能会报冲突错误,如文件名命名冲突:其实针对文件名冲突的错误处理开发者有移除过"文件冲突检测":[链接]后来发现有问题又加上了"文件冲突检测":[链接]

AVOli阅读 833

写给go开发者的gRPC教程-拦截器
本篇为【写给go开发者的gRPC教程】系列第三篇第一篇:protobuf基础第二篇:通信模式第三篇:拦截器gRPC的拦截器和其他框架的拦截器(也称middleware)作用是一样的。利用拦截器我们可以在不侵入业务逻辑的前提下...

liangwt阅读 574

封面图
《go入门grpc》第五章:protoc生成的.pb.go文件解读
在第三章,以及第四章,我们学习了,如何把proto生产go文件。《go入门grpc》第三章:从 proto 文件自动生成go代码《go入门grpc》第四章:使用Makefile优化protoc命令本章我们学习下protoc --go_out命令 生成的.pb...

海生阅读 474

Grpc使用buf.build 快速编译
本文通过实例来讲解使用buf来快速的编译proto文件,不需要再用protoc命令加各种参数来编译proto文件。事先需要安装buf, 安装方法请参考官网installation我们先建立目录结构auth.proto {代码...} 进入proto文件夹...

这个名字好长阅读 469

15 声望
0 粉丝
宣传栏