RPC基础 | 青训营笔记

136 阅读5分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。

1 RPC基础

1.1 RPC入门

RPC是远程过程调用的简称,是分步式系统不同节点间流行的通信方式。而只有满足RPC规则的函数才能注册为RPC函数:

1.方法只能有两个可序列化参数

2.其中第二个参数是指针类型,函数返回error类型

3.必须是公开方法


//服务端
func main(){
    rpc.RegisterName("方法的名字", new(初始化函数))//它会将方法的所有函数注册
    listener, err := net.Listen("tcp", "localhost:1234")
    conn, err := listener.Accept()
    rpc.ServeConn(conn)
}
//客户端
func main(){
    client, err := rpc.Dail("tcp", "localhost:1234")
    err = client.Call("包含绝对路径的服务名.函数名",函数参数1, 函数参数2)
}

1.2 更安全的RPC接口

//服务端的规范
const HelloServiceName = "path/to/pkg.Helloservice"

//只有满足HelloServiceInterface接口的类才能调用注册的函数
type HelloserviceInterface = interface{
    Hello(request string, reply *string) error
}

func RegisterHelloService(svc HelloServiceInterface) error {
    return rpc.RegisterName(HelloserviceName, svc)
}
//客户端的接口规范
type HelloeServiceClient struct {
    //增加其他的判断操作相关的函数
    *rpc.Client
}

//类型判断,倘若HelloServiceClient不满足HelloServiceInterface的话,此句报错
var _ HelloServiceInterface = (*HelloServiceClient)(nil)

//同时我们可以包装Dail函数和访问函数,更加安全
func DailHelloService(netswork, address string) (*HelloServiceClient, error){
    c, err := rpc.Dail(network, address)
    return &HelloServiceClient{Client:c}, nil
}

func(p *HelloServieClient) Hello(request string, reply *string) error {
    return p.Client.Call(HelloServiceName+"Hello", request, reply)
}

1.3 跨语言的RPC

其实就是使用JSON传递信息

在最后启动的时候改为:

type serverRequest struct {
    //和clientRequest基本相同
    Method 	string			`json: "method"`
    Paeams 	*json.RawMessage	`json: "params"`
    id 		*json.RawMessage	`json: "id"`
}

//启动的更改
go rpc.ServeCode(jsonrpc.NewServerCode(conn))

//信息接收
type serverResponse struct {
    Id		*json.RawMessage	`json: "id"`
    Result	interface{}		`json: "result"`
    Error	interface{}		`json: "error"`
}
//数据的绑定
type clietnRequest struct{
    Method 	string			`json: "method"`
    Paeams 	[1]interface{}	        `json: "params"`
    id 		uint64			`json: "id"`
}

//启动模块
client := rpc.NewClientWithCode(jsonrpc.NewClientCode(conn))

//信息接收
type clientResponse struct {
    Id		uint64			`json: "id"`
    Result	*json.RawMessage	`json: "result"`
    Error	interface{}	        `json: "error"`
}

同样的,如果注册服务后可以借用http的中间件开始RPC服务,因为HTTP服务采用的是Gob协议,并没有采用其他的协议接口,所以其他语言同样无法访问。

2 Protobuf基础

2.1 protobuf基础

每次需要获取的依赖:

go get -u google.golang.org/protobuf

为文件增加依赖:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
//永久性获取

在主文件夹下新建“pb”文件夹,再在其中建立相关接口分包,包中的文件以“.proto”结尾

syntax = "proto3";						//表明编译的版本,只有proto3支持gRPC

package student;						//生成的包名

option go_package = "./需要生成到的文件夹";//一般是./proto

message Student {						//message相当于struct,支持嵌套
  string name = 1;
  bool male = 2;
  repeated int32 scores = 3;
}

enum PhoneType {					        //枚举类型,值必须从0开始
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }
  
repeated PhoneNumber phones = 4;

  google.protobuf.Timestamp last_updated = 5;
}

message AddressBook {
  repeated Person people = 1;
}
//map字段对应map类型, 声明格式map<string, int64>对应map[string]int64

完成编辑后在终端输入以下代码实现编译(首先要cd到pb包中,在进行操作):

//格式
protoc --proto_path=IMPORT_PATH --go_out=OUT_DIR  --go_opt=paths=source_relative
//注意你的GOOS不能错误

//生成编译完成的包
protoc --go_out=. ./源文件名.proto

解释:

1.–proto_path:指定 import 路径可以指定多个参数,编译时按顺序查找,省略时默认查找当前目录

2.*.proto 文件中也可以引入其他 *.proto 文件,这里主要用于指定被引入文件的位置。

3.–go_out:golang编译支持,指定输出文件路径(其他语言则替换即可,比如 --java_out 等等)。

4.–go_opt:指定参数,比如--go_opt=paths=source_relative就是表明生成文件输出使用相对路径。

2.2字段

2.2.1 保留字段(Reserved field)

更新消息的时候,某些字段或者标识符会被删除,升级时,需要对一些字段或者标识符进行保留,保证它不被复用,需要关键字reservrd:

message Foo{
    reserved 2, 15, 9 to 11;
    reserved "foo", "bar";
}

2.2.2 标量类型

proto类型go类型备注
doublefloat 64
floatfloat32
int32int32
int64int64
uint32uint32
uint64uint64适合负数
sint32int32适合负数
fixed32uint32固长编码适合大于2^28的值
fixed64uint64固长编码适合大于2^56的值
sfixed32int32固长编码
sfixed64int64固长编码
boolbool
stringstringUTF8编码,长度不超过2^32
bytes[]byte任意字节序列,长度不超过2^32

注意: 如果标量类型没有被赋值,不会被序列化,解析时会被赋予默认值。(bool默认false

2.2.3 枚举类型

1.关键词enum,规定枚举类型的第一个选项的标识符必须是0。

2.使用语句 option allow_alias = true表示可以使用别名。

2.2.4 其他消息类型

1.result:一种消息类型,在SearchResponse作为一个消息字段类型使用。

2.Any:表示任意类型,可以表示不在.proto中定义的任意内置类型。

3 oneof:某消息包含多个字段,但这些字段同一时间最多只允许一个被设置时,可以通过oneof来保证这样的行为,对oneof中任意一个字段设值,都会将其他字段清空。

4.map:声明方式map<string, int32> 变量名,它的key不能是double, float, bytes类型。

2.2.5定义服务

如果消息类型是用来远程通信的,可以在 .proto 文件中定义 RPC 服务接口。例如我们定义了一个名为 SearchService 的 RPC 服务,提供了 Search 接口,入参是 SearchRequest 类型,返回类型是 SearchResponse

2.2.6 补充

在编写的文件中,每个字段都有一个数字,= 1 这个不是赋值,而是编号。一个 message 中,每个字段都有唯一的编号,这些数字用于标识二进制格式的字段(数据传输时会被压缩等),当编号范围是 1-15 时,存储编号需要一个字节,简单来说一个message中最好不要超过15个编号。

PS.写的时间比较久了,只是对网上的博客的总结(个人使用)