RPC,远程过程调用,通俗的讲就是调用远程的一个函数;RPC可能会涉及到不同语言的函数,这时候就需要Protobuf来支持多种不同的语言,而且Protobuf本身就很方便描述服务的接口,因此非常适合作为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)
}
required
特性。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;
}
/ 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 ""
}
...
// 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",
}