Windows下使用Hertz以及用Hz搭配protobuf自动生成代码 | 青训营

454 阅读6分钟

前言

大项目作业我们团队打算使用Hertz + gorm的方案。然而之前并没有学习过Hertz,网上也没有什么视频资料,大多是零星的文字材料,唯一可依靠的只有官方文档。然而Hertz似乎因为历史原因,之前不支持Windows,连官方文档里都没有提到一些Windows下使用会出现的问题,网上的教程大多是Linux环境,完全没有提到Windows会出现的一些问题.本人也是踩坑了近两天,特开此文以记录,让大家能够脱坑。

准备工作

这个部分本来在你安装go的时候就应该做好了,但是网上众说纷纭,或许是因为版本的问题,这里我给出一个方案,这个方案可能有冗余,但一定不会出错。

  1. 安装Go,这个去官网就可以了。
  2. 配置环境变量,这个很重要。 有两个东西,一个叫GOROOT,这个就是你Go在你电脑上安装的地方。比如我的就是D:\own\idi_soft\idi_development\idi_cores\go。 还有一个叫做GOPATH,注意这个GOPATH可不是GOROOT下的bin目录,而是由你自己决定。也就是说是你以后GO的工作区。比如我的就设置在D:\own\idi_soft\idi_development\codes\projects\GoWorks。 然后,在GoWorks(以你自己名字为准)下再创建三个文件夹,分别叫做bin、pkg、src。以后你的项目正常情况下都要创建在src之下。 除此之外,你还要在PATH变量下创建 $GOPATH\bin。

Hertz快速开始

因为一些特殊原因,好像现在不容易看到Hertz的官方文档。所以我就搬运一下,在这一步可以直接跟着官方文档做,不会遇到问题(过了这一步后面就全是问题了) 创建一个项目,然后main.go:

package main

import (
    "context"

    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
    h := server.Default()

    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
            ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
    })

    h.Spin()
}

这里你会遇到一个问题对吧,你粘贴完import里面全是报错。正常的别着急 然后你需要初始化go模块 终端

go mod init

如果你是用Goland直接创建出来的项目那它就自带一个go.mod,你就不需要这一步,反正你就保证你的项目里要有一个可用的go.mod文件就可以了. 然后

go mod tidy

这个命令可厉害,会自动帮你把缺的东西都下载并且整合好. 这里也可能有一个问题,就是你下载不下来import里面那些东西. 解决办法:

打开设置,

image.png 把图中选项勾上,然后把你的GOPROXY填到环境里. go env 命令可以查看你的go环境,里面有GOPROXY.

然而这里又又分支出一个问题,如果你还是不行,就是你那个GOPROXY不行.给他设置成国内的

go env -w GOPROXY=https://goproxy.cn

然后启动你的项目就可以了.成功的话你访问 http://127.0.0.1:8888/ping 就可以得到一个返回信息.

Protobuf准备

  1. 你可能不知道什么是Protobuf,我也不知道,但是现在比较流行这个所以你就要会,我个人觉得可以粗浅的把他暂时当作是json的一个同事.之前json用很多对吧,前后端统一格式都传递json.然后这个protobuf就是牺牲一点可读性但是换取更高效率的一种数据交换格式.
  2. 你需要下载Protobuf,记住你下载在哪里了以后有用,比如我下载在D:\own\idi_soft\idi_development\idi_cores\protobuf\protoc-24.1-win64 然后你要把D:\own\idi_soft\idi_development\idi_cores\protobuf\protoc-24.1-win64\bin加到path里面.

Hz自动生成代码

Hz可以根据protobuf自动生成代码. 之前说过你的项目都在src之下,但如果你要用hz,那你项目就不要创建在/src下,你创建在\src\github.com\cloudwego下面,这点在官方文档里说过,如果你的代码在GOPATH下面你就需要额外创建两个文件夹.

基于 protobuf IDL 创建项目

new: 创建一个新项目

  1. 在当前目录下创建 protobuf idl 文件

    1. 为在 protobuf 中支持 api 注解,请在使用了注解的 proto 文件中,import 下面的文件
    2. 如果想自行拓展注解的使用,请不要以"5"作为序号的开头,避免出现冲突。例如 “optional string xxx = 77777;”
    // idl/api.proto; 注解拓展
    syntax = "proto2";
    
    package api;
    
    import "google/protobuf/descriptor.proto";
    
    option go_package = "/api";
    
    extend google.protobuf.FieldOptions {
    optional string raw_body = 50101;
    optional string query = 50102;
    optional string header = 50103;
    optional string cookie = 50104;
    optional string body = 50105;
    optional string path = 50106;
    optional string vd = 50107;
    optional string form = 50108;
    optional string go_tag = 51001;
    optional string js_conv = 50109;
    }
    
    extend google.protobuf.MethodOptions {
    optional string get = 50201;
    optional string post = 50202;
    optional string put = 50203;
    optional string delete = 50204;
    optional string patch = 50205;
    optional string options = 50206;
    optional string head = 50207;
    optional string any = 50208;
    optional string gen_path = 50301;
    optional string api_version = 50302;
    optional string tag = 50303;
    optional string name = 50304;
    optional string api_level = 50305;
    optional string serializer = 50306;
    optional string param = 50307;
    optional string baseurl = 50308;
    }
    
    extend google.protobuf.EnumValueOptions {
    optional int32 http_code = 50401;
    }
    

    Copy

    主 idl 定义:

    // idl/hello/hello.proto
    syntax = "proto3";
    
    package hello;
    
    option go_package = "hertz/hello"; // 这个名字按你自己的来,你的项目名叫douyin,你就改成douyin/hello
    
    import "api.proto";
    
    message HelloReq {
    string Name = 1[(api.query)="name"];
    }
    
    message HelloResp {
    string RespBody = 1;
    }
    
    service HelloService {
    rpc Method1(HelloReq) returns(HelloResp) {
        option (api.get) = "/hello";
    }
    }
    

    Copy

  2. 创建新项目

    # 在 GOPATH 外执行,需要指定 go mod 名,如果主 IDL 的依赖和主 IDL 不在同一路径下,需要加入 "-I" 选项,其含义为 IDL 搜索路径,等同于 protoc 的 "-I" 命令
    
    hz new -module example.com/m -I idl -idl idl/hello/hello.proto
    
    # 整理 & 拉取依赖
    go mod tidy
    

    Copy

    # GOPATH 下执行,如果主 IDL 的依赖和主 IDL 不在同一路径下,需要加入 "-I" 选项,其含义为 IDL 搜索路径,等同于 protoc 的 "-I" 命令
    hz new -I idl -idl idl/hello/hello.proto
    
    go mod init
    
    # 整理 & 拉取依赖
    go mod tidy
    

    Copy

  3. 修改 handler,添加自己的逻辑

    // handler path: biz/handler/hello/hello_service.go
    // 其中 "/hello" 是 protobuf idl 中 go_package 的最后一级
    // "hello_service.go" 是 protobuf idl 中 service 的名字,所有 service 定义的方法都会生成在这个文件中
    
    // Method1 .
    // @router /hello [GET]
    func Method1(ctx context.Context, c *app.RequestContext) {
    var err error
    var req hello.HelloReq
    err = c.BindAndValidate(&req)
    if err != nil {
        c.String(400, err.Error())
        return
    }
    
    resp := new(hello.HelloResp)
    
    // 你可以修改整个函数的逻辑,而不仅仅局限于当前模板
    resp.RespBody = "hello," + req.Name // 添加的逻辑
    
    c.JSON(200, resp)
    }
    

    Copy

  4. 编译项目

    go build
    

    Copy

  5. 运行项目并测试

    运行项目:

    ./{{your binary}}
    

    Copy

    测试:

    curl --location --request GET 'http://127.0.0.1:8888/hello?name=hertz'
    

    Copy

    如果返回{"RespBody":"hello,hertz"},说明接口调通。

    以上都是官方文档内容我就直接抄过来了,但是你跟着做绝对做不成功. 有几个你可能会碰到的问题:

  6. 提示你说什么google的包找不到之类的,mod tidy不成功, 参考这个解决办法 (protoc_gen_hertz returns error: exit status 1 · Issue #919 · cloudwego/hertz · GitHub))

  7. 说你router什么的找不到.你刚创建项目的时候先hz new一下,然后再做他的东西,把 hz new -I idl -idl idl/hello/hello.proto改成hz update ..... 就可以