准备工作

推荐使用kratos tool快速生成项目,如我们生成一个叫kratos-demo的项目。

快速开始

pb文件

创建项目成功后,进入api目录下可以看到api.protoapi.pb.gogenerate.go文件,其中:

  • api.proto 是gRPC server的描述文件
  • api.pb.go 是基于 api.proto 生成的代码文件
  • generate.go 是用于 kratos tool 执行 go generate 进行代码生成的临时文件

接下来可以将以上三个文件全部删除或者保留generate.go,之后编写自己的proto文件,确认proto无误后,进行代码生成:

  • 可直接执行 kratos tool kprotoc ,该命令会调用protoc工具生成 .pb.go 文件
  • generate.go 没删除,也可以执行 go generate 命令,将调用 kratos tool kprotoc 工具进行代码生成

注册server

进入internal/server/grpc目录,打开server.go文件,可以看到以下代码,只需要替换以下注释内容就可以启动一个gRPC服务。

package grpc

import (
    pb "kratos-demo/api"
    "kratos-demo/internal/service"
    "github.com/bilibili/kratos/pkg/conf/paladin"
    "github.com/bilibili/kratos/pkg/net/rpc/warden"
)

// New new a grpc server.
func New(svc *service.Service) *warden.Server {
    var rc struct {
        Server *warden.ServerConfig
    }
    if err := paladin.Get("grpc.toml").UnmarshalTOML(&rc); err != nil {
        if err != paladin.ErrNotExist {
            panic(err)
        }
    }
    ws := warden.NewServer(rc.Server)
    // 注意替换这里:
    // RegisterDemoServer方法是在"api"目录下代码生成的
    // 对应proto文件内自定义的service名字,请使用正确方法名替换
    pb.RegisterDemoServer(ws.Server(), svc)
    ws, err := ws.Start()
    if err != nil {
        panic(err)
    }
    return ws
}

注册注意

// SayHello grpc demo func.
func (s *Service) SayHello(ctx context.Context, req *pb.HelloReq) (reply *empty.Empty, err error) {
    reply = new(empty.Empty)
    fmt.Printf("hello %s", req.Name)
    return
}

请进入internal/service内找到SayHello方法,注意方法的入参和出参,都是按照gRPC的方法声明对应的:

  • 第一个参数必须是 context.Context ,第二个必须是proto内定义的 message 对应生成的结构体
  • 第一个返回值必须是proto内定义的 message 对应生成的结构体,第二个参数必须是 error
  • 在http框架bm中,如果共用proto文件生成bm代码,那么也可以直接使用该service方法

建议service严格按照此格式声明方法,使其能够在bm和warden内共用

client调用

请进入internal/dao方法内,一般对资源的处理都会在这一层封装。
对于client端,前提必须有对应proto文件生成的代码,那么有两种选择:

  • 拷贝proto文件到自己项目下并且执行代码生成
  • 直接import服务端的api package

PS:这也是业务代码我们加了一层internal的关系,服务对外暴露的只有接口

不管哪一种方式,以下初始化gRPC client的代码建议伴随生成的代码存放在统一目录下:

package dao

import (
    "context"

    "github.com/bilibili/kratos/pkg/net/rpc/warden"

    "google.golang.org/grpc"
)

// AppID unique app id for service discovery
const AppID = "your app id"

// NewClient new member grpc client
func NewClient(cfg *warden.ClientConfig, opts ...grpc.DialOption) (DemoClient, error) {
    client := warden.NewClient(cfg, opts...)
    conn, err := client.Dial(context.Background(), "discovery://default/"+AppID)
    if err != nil {
        return nil, err
    }
    // 注意替换这里:
    // NewDemoClient方法是在"api"目录下代码生成的
    // 对应proto文件内自定义的service名字,请使用正确方法名替换
    return NewDemoClient(conn), nil
}

其中,"discovery://default/"+AppID为gRPC target,提供给resolver用于discovery服务发现的,如果在使用其他服务发现组件,可以根据自己的实现情况传入。

有了初始化Client的代码,我们的Dao对象即可进行初始化和使用,以下以直接import服务端api包为例:

package dao

import(
    demoapi "kratos-demo/api"
    grpcempty "github.com/golang/protobuf/ptypes/empty"

    "github.com/pkg/errors"
)

type Dao struct{
    demoClient demoapi.DemoClient
}

// New account dao.
func New(c *conf.Config) (d *Dao) {
    d = &Dao{}
    var err error
    if d.demoClient, err = demoapi.NewClient(c.DemoRPC); err != nil { // NOTE: DemoRPC为warden包内的ClientConfig对象
        panic(err)
    }
    return
}

// SayHello say hello.
func (d *Dao) SayHello(c context.Context, req *demoapi.HelloReq) (resp *grpcempty.Empty, err error) {
    if resp, err = d.demoClient.SayHello(c, req); err != nil {
        err = errors.Wrapf(err, "%v", arg)
    }
    return
}

如此在internal/service层就可以进行资源的方法调用。

results matching ""

    No results matching ""