go-grpc笔记1

本文介绍了如何在Go环境中安装protobuf编译器和gRPC插件,以及使用它们编译.proto文件生成Go代码。通过一个简单的helloworld示例展示了gRPC服务的创建,包括客户端和服务端的代码结构和调用逻辑。客户端连接服务器,发送SayHello和SayHelloAgain请求,服务器接收并返回响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

go-grpc Note1

参考链接:grpc.io

Note:记录这一部分也主要是因为过去资料大多失效,请注意版本时间(2023年7月10日)

Quick Start

Install Protocol Buffer Compiler

$ wget https://github.com/google/protobuf/releases/download/v3.11.2/protobuf-all-3.11.2.zip
$ unzip protobuf-all-3.11.2.zip && cd protobuf-3.11.2/
$ ./configure
$ make
$ make install

通过$ protoc --version检查protoc版本>3.x
其他安装方法 Install Proto Buffer Compiler

Install Go plugins for the protocol compiler

针对不同的语言,还需要不同的运行时的 protoc 插件,那么对应 Go 语言就是 protoc-gen-go 插件

$ 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

通过export PATH="$PATH:$(go env GOPATH)/bin"更新系统环境变量

go-rpc example

代码来源: 代码来源

名为helloworld的文件目录如下:

├── helloworld
│   ├── greeter_client
│   │   └── main.go
│   ├── greeter_server
│   │   └── main.go
│   ├── helloworld
│   │   ├── helloworld_grpc.pb.go   #由helloworld.proto生成
│   │   ├── helloworld.pb.go    #由helloworld.proto生成
│   │   └── helloworld.proto
│   └── README.md
helloworld.proto
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
// The response message containing the greetings
message HelloReply {
  string message = 1;
}
由helloworld.proto 生成.pb
$ protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    helloworld/helloworld.proto

此时应当在helloworld文件下生成_.pb.go和_grpc.pb.go文件

gretter_client/main.go
package main

import (
	"context"
	"flag"
	"log"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

const (
	defaultName = "world"
)

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
	name = flag.String("name", defaultName, "Name to greet")
)

func main() {
	flag.Parse()
	// Set up a connection to the server.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	// Contact the server and print out its response.
	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())
	r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: *name})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("Greeting: %s", r.GetMessage())
}
gretter_server/main.go
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

var (
	port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
	pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
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() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}

至此,我们应该有一个完整的示例文件。

RUN & Test

启动服务:
$ go run greeter_server/main.go

在另一个终端启动客户端:$ go run greeter_client/main.go --name=Alice
输出结果:

[root@xx helloworld] go run greeter_server/main.go
2023/07/11 15:26:24 server listening at [::]:50051
2023/07/11 15:26:30 Received: Alice
--------
[root@xx helloworld] go run greeter_client/main.go --name=Alice
2023/07/11 15:26:30 Greeting: Hello Alice
2023/07/11 15:26:30 Greeting: Hello again Alice

Understand

上面的步骤可以达成完整的grpc应用,那么如何理解Go Server&Client 利用ProtoBuffer工作(或者调用逻辑)的呢?(可能随版本更迭存在区别,@2023年7月11日)

About .proto

观察helloworld/helloworld.proto中服务声明部分代码:

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
... ... //The request & response message definition

包括两个rpc调用,即SayHelloSayHelloAgain,接收HelloRequest然后返回HelloReply消息(Message)

Ablout .go generated by .proto

观察生成的helloworld.pb.go
其定义了 HelloRequestHelloReply的消息结构体和GetName方法(以请求消息为例):

// The request message containing the user's name.
type HelloRequest struct {
	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
    ... ...
}
... //GetName method
func (m *HelloRequest) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

而另一个文件helloworld_grpc.pb.go则包括服务注册RegisterGreeterServer和New客户端NewGreeterClient方法。

Client部分

Client结构与实例化相关代码如下:

type GreeterClient interface {
	// Sends a greeting
	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
	// Sends another greeting
	SayHelloAgain(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}
... 
type greeterClient struct {
	cc grpc.ClientConnInterface
}
...
func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {
	return &greeterClient{cc}
}
... 

其中greeterClient结构体包括的cc是grpc.ClientConnInterface,这里给出grpc.ClientConnInterface的代码:

type ClientConnInterface interface {
	// Invoke performs a unary RPC and returns after the response is received into reply.
	Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...CallOption) error
	// NewStream begins a streaming RPC.
	NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error)
}

存在InvokeNewStream两种方法,分别对应一元rpc与流rpc,将在client中被调用。
而Client中包括SayHelloSayHelloAgain两个方法,以其一为例代码如下:

func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
	out := new(HelloReply)
	err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

调用了ClientConnInterface中的Invoke方法,其中ctx上下文由Client/main.go中提供示例,method方法名则对应"/helloworld.Greeter/SayHello"自动生成,in、out作为args、reply。

Server部分

Server结构与实例化相关代码如下:

type GreeterServer interface {
	// Sends a greeting
	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
	// Sends another greeting
	SayHelloAgain(context.Context, *HelloRequest) (*HelloReply, error)
	mustEmbedUnimplementedGreeterServer()
}
type UnsafeGreeterServer interface {
	mustEmbedUnimplementedGreeterServer()
}

func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
	s.RegisterService(&Greeter_ServiceDesc, srv)
}

其中mustEmbedUnimplementedGreeterServer()是为了前向兼容,不用过分在意,RegisterGreeterServer将用于注册Server于Server/main.go中。而SayHello方法与Client部分类似,不赘述。

About .go

Client部分

可以通过注释理解其中内容,主要调用有以下几点:

1.配置地址与安全相关信息:grpc.Dial

2.实例化Client:pb.NewGreeterClient(conn)

3.获取ctx:context.WithTimeout,注意这里的withtime是其中一种,也有withCancel之返回一个cancel.ctx

4.执行SayHello等动作

5.调用defer,结束调用,注意这里defer的顺序为:cancel->close,从栈顶弹出cancel再close

...
	pb "helloworld/helloworld" //常用别名

var (
	addr = flag.String("addr", "localhost:50051", "the address to connect to")
	name = flag.String("name", defaultName, "Name to greet")
)//flag接受参数
func main() {
	flag.Parse()
	// Set up a connection to the server.
	conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	//注意,这里rpc.WithTransportCredentials(...)禁用了安全配置,为简化入门程序
	if err != nil {...}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)//实例化了一个Client
	//给定一个处理超时配置,1s(个人理解)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})//发送携带Name的rpc Request 通过SayHello
...
}

Server部分

主要代码如下:

// server is used to implement helloworld.GreeterServer.
type server struct {
	pb.UnimplementedGreeterServer	//结构体封装为server
}
//implements helloworld.GreeterServer 供Client调用,也就是rpc中的p
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 (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}
func main() {
	flag.Parse()
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

以上存在个人理解,欢迎交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值