2

一、介绍

前几张我们已经学了kratos的基本框架。本章我们来深入剖析一下原理。
kratos框架之所以能够使用protbuf创建http服务器,多亏了框架自带的
protoc-gen-go-http插件。
那么我们是否可以在其他地方使用这个插件呢,答案是可以,今天我们就试一下。
本文章代码地址在 https://github.com/hisheng/kratos-http

1.1 准备目录

我们新建一个 kratos-http目录,并且go模块初始化。
创建目录:

mkdir kratos-http && cd kratos-http

go项目初始化:
我们在kratos-http根目录执行一下代码

go mod init github.com/hisheng/kratos-http

1.2 安装protoc-gen-go以及http扩展protoc-gen-go-http

我们在kratos-http根目录执行一下代码,安装扩展。

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest

二、创建protobuf文件

2.1 准备目录

我们在kratos-http根目录,创建一个api目录来存放我们的proto文件

mkdir api && cd api

2.2 创建user.proto

此时我们新建一个 user.proto 文件用来实现我们的http服务。

touch user.proto

我们user.proto的代码如下:

syntax = "proto3";
package api;
import "google/api/annotations.proto";

option go_package = "github/hisheng/kratos-http/api";

service User {
  rpc CreateUser (CreateUserRequest) returns (CreateUserReply){
    option (google.api.http) = {
      post: "/user",
      body: "*",
    };
  };
  rpc ListUser (ListUserRequest) returns (ListUserReply){
    option (google.api.http) = {
      get: "/users",
    };
  };
}
message CreateUserRequest {}
message CreateUserReply {}
message ListUserRequest {}
message ListUserReply {}

2.3 解决proto文件 cannot import "google/api/annotations.proto"

此时我们在proto文件里,引入了一个第三方的proto文件,此文件没有报错怎么办?
我们一般这类的第三方文件依赖发到third_party目录。

2.3.1 引入google proto库。

打开https://github.com/hisheng/kratos-http我们项目代码地址。
直接把third_party目录复制下来就可以了。
此时我们的目录如下:

➜  kratos-http git:(master) tree
.
├── api
│   └── user.proto
├── go.mod
└── third_party
    └── google
        ├── api
        │   ├── annotations.proto
        │   ├── client.proto
        │   ├── field_behavior.proto
        │   ├── http.proto
        │   └── httpbody.proto
        └── protobuf
            ├── any.proto
            ├── api.proto
            ├── compiler
            │   └── plugin.proto
            ├── descriptor.proto
            ├── duration.proto
            ......
6 directories, 19 files
2.3.2 配置goland,protobuf的第三方import地址

我们打开 goland的 “语言和框架”->"Protocol Buffers"
然后把我们自己的项目的 third_party目录加一下。
image.png
此时我们的proto文件显示正常了。

三、使用protoc生成pb对应的.go文件

我们把protoc命令放到Makefile里。
在kratos-http目录我们新建一个 Makefile文件

touch Makefile

对应的脚本为

.PHONY: api
# 由 proto 生成接口层代码
api:
    protoc --proto_path=./api \
           --proto_path=./third_party \
            --go_out=paths=source_relative:./api \
            --go-http_out=paths=source_relative:./api \
          user.proto

此时我们执行make命令make api

➜  kratos-http git:(master) ✗ make api

此时在api目录生成了user.pb.go以及user_http.pb.go文件

➜  api git:(master) ✗ tree
.
├── user.pb.go
├── user.proto
└── user_http.pb.go

0 directories, 3 files
➜  api git:(master) ✗ 

然后我再执行一下go mod tidy给新生成的go文件加载依赖

kratos-http git:(master) ✗  go mod tidy

四、实现服务接口

我们打开user_http.pb.go,发现有一个 UserHTTPServer 接口,我们需要实现。
在根目录,建立service文件夹,并且在service文件夹下建一个user.go

mkdir service && cd service && touch user.go

我们打开user.go 写入以下代码:

package service

import (
    "context"
    "github.com/hisheng/kratos-http/api"
)

type UserService struct {
}

func (svc UserService) ListUser(context.Context, *api.ListUserRequest) (*api.ListUserReply, error) {
    return &api.ListUserReply{Id: 100}, nil
}

func (svc UserService) CreateUser(context.Context, *api.CreateUserRequest) (*api.CreateUserReply, error) {
    return nil, nil
}

在这里我们定义了一个UserService,来实现user_http.pb.go里的UserHTTPServer接口。

五、创建http服务器,并注册路由。

我们在根目录,新建一个 server目录,并在server目录下新建一个main.go

mkdir server && cd server && touch main.go

我们打开main.go代码如下:

package main

import (
    "context"
    "github.com/go-kratos/kratos/v2/transport/http"
    "github.com/hisheng/kratos-http/api"
    "github.com/hisheng/kratos-http/service"
    "log"
    "net"
)

func main() {
    ln, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Fatal(err)
    }
    server := http.NewServer(http.Listener(ln))
    userService := service.UserService{}
    api.RegisterUserHTTPServer(server, userService)

    _ = server.Start(context.Background())
    // _ = server.Serve(ln) 这个也行,不过感觉上面的start更好,
                            // start会调用server.Serve
}

在这里,我们使用了kratos/v2/transport/http的NewServer()来创建服务器。
因为这个方法,兼容了protobuf,注册路由。


海生
104 声望32 粉丝

与黑夜里,追求那一抹萤火。