【读书笔记二】:RPC和PROTOBUF

前言

RPC,远程过程调用,通俗的讲就是调用远程的一个函数;RPC可能会涉及到不同语言的函数,这时候就需要Protobuf来支持多种不同的语言,而且Protobuf本身就很方便描述服务的接口,因此非常适合作为RPC世界的接口交流语言。

RPC

通过一个的例子来实现简单的Rpc功能

// 服务端

package main

import (
	"log"
	"net"
	"net/rpc"
)

type HelloService struct {
     
}

//Go语言的Rpc规则:方法只能有2个可序列化的参数,第二个是指针类型,并且返回一个error类型
func (p *HelloService) Hello(request string, reply *string) error {
     
	*reply = "hello:" + request
	return nil
}
func main() {
     
	// 注册一个rpc服务
	// 会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放在HelloService服务的空间下
	rpc.RegisterName("HelloService", new(HelloService))
	listen, err := net.Listen("tcp", ":1234")
	if err != nil {
     
		log.Fatal("listen tcp fail", err)
	}
	accept, err := listen.Accept()
	if err != nil {
     
		log.Fatal("Accept fail:", err)

	}
	rpc.ServeConn(accept)

}



// 客户端
package main

import (
	"fmt"
	"log"
	"net/rpc"
)

func main() {
     
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
     
		log.Fatal("dialing error:",err)
	}
	var reply string
	// 第二第三是RPC方法的参数
	err = client.Call("HelloService.Hello", "hello", &reply)
	if err != nil {
     
		log.Fatal("call error:",err)
	}
	fmt.Println(reply)
}

  • 我们在涉及RPC开发的过程中,有三种角色:服务器端实现RPC方法,客户端调用RPC方法,制定服务端和客户端RPC接口规范。上面的例子中,我们把3种角色都耦合在一起,不利于维护。
  • 我们将RPC服务的接口规范分为3部分:服务的名字,服务要实现的详细方法列表,注册该类型服务的函数

跨语言的RPC

  • Go语言的RPC框架有2个比较有特色的设计
    • RPC数据打包时可以通过插件实现自定义编码和解码
    • RPC建立在抽象的io.ReadWriteCloser接口上,我们可以将RPC架设在不同的通信协议上,egnet/rpc/jsonrpc
      【读书笔记二】:RPC和PROTOBUF_第1张图片
      【读书笔记二】:RPC和PROTOBUF_第2张图片
      id为调用方维护的唯一的调用编号,Go语言的RPC支持异步调用,当返回结果的顺序和调用顺序不一致时,可以通过id来识别对应的调用
      在这里插入图片描述
      在这里插入图片描述
      无论采取何种语言,只要遵循同样的JSON格式,以同样的流程就可以和Go语言编写的RPC服务进行通信,这样就实现了跨语言的RPC

http的RPC


在这里插入图片描述

Protobuf

  • protobuf是谷歌开发的一种数据描述语言,通过附带工具生成代码并实现将结构化数据序列化功能,但是我们更关注Protobuf作为接口规范的描述下语言。
  • 第三版的protobuf对所有成员均采用类似Go语言中的零值初始化,取消了required特性。
  • Protobuf编码通过成员的唯一编码来绑定对应的数据,因此编码后数据的体积更小。

protoc

protobuf的核心工具集是c++开发的,要使用protoc工具集,可以通过

# 安装
go get github.com/golang/protobuf/protoc-gen-go
# 使用 生成go代码
protoc --go_out=. hello.proto

hello.proto

syntax= "proto3";
package main;
message String{
     
  string value = 1;
}

【读书笔记二】:RPC和PROTOBUF_第3张图片
生成一个hello.pb.go文件

/ Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.23.0
// 	protoc        v3.12.3
// source: hello.proto

package main

import (
	proto "github.com/golang/protobuf/proto"
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)
....
// s生成一个新的String类型,可以和rpc结合
type String struct {
     
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
}

func (x *String) Reset() {
     
	*x = String{
     }
	...
}

func (x *String) String() string {
     
	return protoimpl.X.MessageStringOf(x)
}

func (*String) ProtoMessage() {
     }

func (x *String) ProtoReflect() protoreflect.Message {
     
	mi := &file_hello_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
     
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
     
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use String.ProtoReflect.Descriptor instead.
func (*String) Descriptor() ([]byte, []int) {
     
	return file_hello_proto_rawDescGZIP(), []int{
     0}
}
// 为每一个类型都生成一个Get方法
func (x *String) GetValue() string {
     
	if x != nil {
     
		return x.Value
	}
	return ""
}
...

  • 如果在pb文件中定义rpc服务接口,
    【读书笔记二】:RPC和PROTOBUF_第4张图片
  • 重新生成go代码并没有变化,因为世界上rpc的语言有千万种,protoc编译器并不知道该如何为HelloService服务生成代码。
  • proto-gen-go内部集成了一个grpc插件
    【读书笔记二】:RPC和PROTOBUF_第5张图片
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface

...
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type HelloServiceClient interface {
     
	Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error)
}

type helloServiceClient struct {
     
	cc grpc.ClientConnInterface
}

func NewHelloServiceClient(cc grpc.ClientConnInterface) HelloServiceClient {
     
	return &helloServiceClient{
     cc}
}

func (c *helloServiceClient) Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) {
     
	out := new(String)
	err := c.cc.Invoke(ctx, "/main.HelloService/Hello", in, out, opts...)
	if err != nil {
     
		return nil, err
	}
	return out, nil
}

// HelloServiceServer is the server API for HelloService service.
type HelloServiceServer interface {
     
	Hello(context.Context, *String) (*String, error)
}

// UnimplementedHelloServiceServer can be embedded to have forward compatible implementations.
type UnimplementedHelloServiceServer struct {
     
}

func (*UnimplementedHelloServiceServer) Hello(context.Context, *String) (*String, error) {
     
	return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented")
}

func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) {
     
	s.RegisterService(&_HelloService_serviceDesc, srv)
}

func _HelloService_Hello_Handler(srv interface{
     }, ctx context.Context, dec func(interface{
     }) error, interceptor grpc.UnaryServerInterceptor) (interface{
     }, error) {
     
	in := new(String)
	if err := dec(in); err != nil {
     
		return nil, err
	}
	if interceptor == nil {
     
		return srv.(HelloServiceServer).Hello(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
     
		Server:     srv,
		FullMethod: "/main.HelloService/Hello",
	}
	handler := func(ctx context.Context, req interface{
     }) (interface{
     }, error) {
     
		return srv.(HelloServiceServer).Hello(ctx, req.(*String))
	}
	return interceptor(ctx, in, info, handler)
}

var _HelloService_serviceDesc = grpc.ServiceDesc{
     
	ServiceName: "main.HelloService",
	HandlerType: (*HelloServiceServer)(nil),
	Methods: []grpc.MethodDesc{
     
		{
     
			MethodName: "Hello",
			Handler:    _HelloService_Hello_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{
     },
	Metadata: "hello.proto",
}

你可能感兴趣的:(go,protobuf,rpc)