grpc 示例

本文档详细介绍了如何使用gRPC从头开始创建一个示例应用,包括下载protobuf工具,编写.proto文件,生成Go代码,以及实现服务端和客户端的代码。涉及了单项、服务端流式、客户端流式和双向流式四种不同类型的RPC方法。

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

1

go get -u github.com/golang/protobuf/proto

go get -u github.com/golang/protobuf/protoc-gen-go

2 下载

https://github.com/google/protobuf/releases
将bin目录添加到path目录下。

3 编写proto文件rpctest.proto

// 使用方法:protoc -I ./rpctest/ ./rpctest/rpctest.proto --go_out=plugins=grpc:rpctest
syntax = "proto3";
package serverpb;

service funcname
{
    rpc FuncName(MessReq) returns (MessRes);
};

message MessReq{
    int32 NameId = 1 [ json_name="nameid" ];
};

message MessRes{
    int32 result = 1;
};

//
执行方法:protoc -I ./rpctest/ ./rpctest/rpctest.proto –go_out=plugins=grpc:rpctest

路径根据自己的路径来处理

4 生成go文件。

就会获取到

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: funname.proto

package serverpb

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import (
    context "golang.org/x/net/context"
    grpc "google.golang.org/grpc"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

type MessReq struct {
    NameId int32 `protobuf:"varint,1,opt,name=NameId,json=nameid" json:"NameId,omitempty"`
}

func (m *MessReq) Reset()                    { *m = MessReq{} }
func (m *MessReq) String() string            { return proto.CompactTextString(m) }
func (*MessReq) ProtoMessage()               {}
func (*MessReq) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} }

func (m *MessReq) GetNameId() int32 {
    if m != nil {
        return m.NameId
    }
    return 0
}

type MessRes struct {
    Result int32 `protobuf:"varint,1,opt,name=result" json:"result,omitempty"`
}

func (m *MessRes) Reset()                    { *m = MessRes{} }
func (m *MessRes) String() string            { return proto.CompactTextString(m) }
func (*MessRes) ProtoMessage()               {}
func (*MessRes) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{1} }

func (m *MessRes) GetResult() int32 {
    if m != nil {
        return m.Result
    }
    return 0
}

func init() {
    proto.RegisterType((*MessReq)(nil), "serverpb.MessReq")
    proto.RegisterType((*MessRes)(nil), "serverpb.MessRes")
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4

// Client API for Funcname service

type FuncnameClient interface {
    FuncName(ctx context.Context, in *MessReq, opts ...grpc.CallOption) (*MessRes, error)
}

type funcnameClient struct {
    cc *grpc.ClientConn
}

func NewFuncnameClient(cc *grpc.ClientConn) FuncnameClient {
    return &funcnameClient{cc}
}

func (c *funcnameClient) FuncName(ctx context.Context, in *MessReq, opts ...grpc.CallOption) (*MessRes, error) {
    out := new(MessRes)
    err := grpc.Invoke(ctx, "/serverpb.funcname/FuncName", in, out, c.cc, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

// Server API for Funcname service

type FuncnameServer interface {
    FuncName(context.Context, *MessReq) (*MessRes, error)
}

func RegisterFuncnameServer(s *grpc.Server, srv FuncnameServer) {
    s.RegisterService(&_Funcname_serviceDesc, srv)
}

func _Funcname_FuncName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    in := new(MessReq)
    if err := dec(in); err != nil {
        return nil, err
    }
    if interceptor == nil {
        return srv.(FuncnameServer).FuncName(ctx, in)
    }
    info := &grpc.UnaryServerInfo{
        Server:     srv,
        FullMethod: "/serverpb.funcname/FuncName",
    }
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(FuncnameServer).FuncName(ctx, req.(*MessReq))
    }
    return interceptor(ctx, in, info, handler)
}

var _Funcname_serviceDesc = grpc.ServiceDesc{
    ServiceName: "serverpb.funcname",
    HandlerType: (*FuncnameServer)(nil),
    Methods: []grpc.MethodDesc{
        {
            MethodName: "FuncName",
            Handler:    _Funcname_FuncName_Handler,
        },
    },
    Streams:  []grpc.StreamDesc{},
    Metadata: "funname.proto",
}

func init() { proto.RegisterFile("funname.proto", fileDescriptor3) }

var fileDescriptor3 = []byte{
    // 134 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4d, 0x2b, 0xcd, 0xcb,
    0x4b, 0xcc, 0x4d, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x28, 0x4e, 0x2d, 0x2a, 0x4b,
    0x2d, 0x2a, 0x48, 0x52, 0x52, 0xe4, 0x62, 0xf7, 0x4d, 0x2d, 0x2e, 0x0e, 0x4a, 0x2d, 0x14, 0x12,
    0xe3, 0x62, 0xf3, 0x4b, 0xcc, 0x4d, 0xf5, 0x4c, 0x91, 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0d, 0x62,
    0x03, 0x69, 0xc8, 0x4c, 0x41, 0x28, 0x29, 0x06, 0x29, 0x29, 0x4a, 0x2d, 0x2e, 0xcd, 0x29, 0x81,
    0x29, 0x81, 0xf0, 0x8c, 0x6c, 0xb8, 0x38, 0xd2, 0x4a, 0xf3, 0x92, 0x41, 0x1a, 0x84, 0x0c, 0xb8,
    0x38, 0xdc, 0x4a, 0xf3, 0x92, 0x41, 0x46, 0x09, 0x09, 0xea, 0xc1, 0x2c, 0xd2, 0x83, 0xda, 0x22,
    0x85, 0x21, 0x54, 0x9c, 0xc4, 0x06, 0x76, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x35,
    0x1b, 0xe8, 0xa5, 0x00, 0x00, 0x00,
}

5 编写服务端代码

根据.go文件编写服务端代码

package main

import (
    "context"
    "fmt"
    "net"
    proto "rpctest"

    "google.golang.org/grpc"
)

type server struct {
}

func (s *server) FuncName(c context.Context, in *proto.MessReq) (*proto.MessRes, error) {
    return &proto.MessRes{Result :in.NameId }, nil
}

func main() {
    listen, err := net.Listen("tcp", ":50051")
    if err != nil {
        fmt.Println("listen failed err:", err)
        return
    }
    s := grpc.NewServer()
    proto.RegisterGreeterServer(s, &server{})
    s.Serve(listen)
}

6 编写客户端代码

package main

import (
    "context"
    "log"
    proto "rpctest"
    "google.golang.org/grpc"
)

func main() {
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    if err != nil {
        log.Fatal("new dial failed err:", err)
    }

    defer conn.Close()
    c := proto.NewGreeterClient(conn)
    r, err := c.FuncName(context.Background(), &proto.MessReq{NameId : 1})
    if err != nil {
        log.Fatal("get reply failed err:", err)
    }
    log.Print(r.GetMessage())
}

其他服务方法:

(1) 单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。

rpc SayHello(HelloRequest) returns (HelloResponse){
}

(2) 服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}

(3)客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}

(4) 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}

连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值