首页>教程>工单中心>Dapr-SDK

需要支持?

如果通过文档没办法解决您的问题,请提交工单获取我们的支持!

Dapr-SDK

Go SDK的proto代码生成

准备工作

准备protoc

安装Protoc,目前 daprd 要求的版本是 v3.14.0

具体见 Daprd Proto代码生成

sdk 和 daprd保持一致,但实际目前go sdk采用的是 protoc 3.11.2。

将proto生成源码

现在 go sdk的make file 提供了 protos 命令来从proto生成go代码:

$ make protos
# 下载安装gogoreplace
go install github.com/gogo/protobuf/gogoreplace
go: finding module for package github.com/gogo/protobuf/gogoreplace
go: found github.com/gogo/protobuf/gogoreplace in github.com/gogo/protobuf v1.3.2
# 删除本地的proto文件
rm -f ./dapr/proto/common/v1/*
rm -f ./dapr/proto/runtime/v1/*
# 从 dapr/dapr 仓库下载 common.proto 文件到本地
wget -q https://raw.githubusercontent.com/dapr/dapr/master/dapr/proto//common/v1/common.proto -O ./dapr/proto/common/v1/common.proto
# 使用 gogoreplace 工具替换 common.proto 文件中的 go_package
gogoreplace 'option go_package = "github.com/dapr/dapr/pkg/proto/common/v1;common";' \
        'option go_package = "github.com/dapr/go-sdk/dapr/proto/common/v1;common";' \
        ./dapr/proto/common/v1/common.proto
wget -q https://raw.githubusercontent.com/dapr/dapr/master/dapr/proto//runtime/v1/appcallback.proto -O ./dapr/proto/runtime/v1/appcallback.proto
gogoreplace 'option go_package = "github.com/dapr/dapr/pkg/proto/runtime/v1;runtime";' \
        'option go_package = "github.com/dapr/go-sdk/dapr/proto/runtime/v1;runtime";' \
        ./dapr/proto/runtime/v1/appcallback.proto
wget -q https://raw.githubusercontent.com/dapr/dapr/master/dapr/proto//runtime/v1/dapr.proto -O ./dapr/proto/runtime/v1/dapr.proto
gogoreplace 'option go_package = "github.com/dapr/dapr/pkg/proto/runtime/v1;runtime";' \
        'option go_package = "github.com/dapr/go-sdk/dapr/proto/runtime/v1;runtime";' \
        ./dapr/proto/runtime/v1/dapr.proto
# 从proto文件生成go代码
protoc --go_out=. --go_opt=paths=source_relative \
       --go-grpc_out=. --go-grpc_opt=paths=source_relative \
           dapr/proto/common/v1/common.proto
protoc --go_out=. --go_opt=paths=source_relative \
           --go-grpc_out=. --go-grpc_opt=paths=source_relative \
           dapr/proto/runtime/v1/*.proto
# 删除刚才下载到本地的proto
rm -f ./dapr/proto/common/v1/*.proto
rm -f ./dapr/proto/runtime/v1/*.proto

遇到的问题

gogoreplace的安装

不知为何我遇到下面的报错:

$ make protos
go install github.com/gogo/protobuf/gogoreplace
cannot find package "." in:
        /home/sky/work/code/skyao/go-sdk/vendor/github.com/gogo/protobuf/gogoreplace
make: *** [Makefile:48:protos] 错误 1

单独执行这个 go install 命令也是同样错误:

$ go install github.com/gogo/protobuf/gogoreplace
cannot find package "." in:
        /home/sky/work/code/skyao/go-sdk/vendor/github.com/gogo/protobuf/gogoreplace

解决的方法是进入上一级目录(不要在go-sdk目录下)进行安装:

$ cd ..
$ go install github.com/gogo/protobuf/gogoreplace
go install: version is required when current directory is not in a module
        Try 'go install github.com/gogo/protobuf/gogoreplace@latest' to install the latest version

$ go install github.com/gogo/protobuf/gogoreplace@latest

然后临时注释掉这一行:

#go install github.com/gogo/protobuf/gogoreplace

方法有点恶心,主要是没有找到问题所在。

cgo报错stdlib.h找不到

在ubuntu 20.04新系统上遇到这个错误:

$ make test
go mod tidy
go test -count=1 \
                -race \
                -coverprofile=coverage.txt \
                -covermode=atomic \
                ./...
# runtime/cgo
_cgo_export.c:3:10: fatal error: stdlib.h: 没有那个文件或目录
    3 | #include <stdlib.h>
      |          ^~~~~~~~~~
compilation terminated.

这是因为 ubuntu 默认没有C和C++编译环境,执行以下命令安装即可:

$ sudo aptitude install build-essential

后记:更新protoc版本

发现 go sdk 使用的protoc 版本是 v3.11.2,而之前我为了满足 dapr/dapr 仓库的要求安装的是 protoc v3.14.0。两个版本生成的代码会有一些细微的差异,也就造成了生成的代码会合git 仓库中的现有代码不同。

在不同仓库之间切换使用不同的 protoc 版本实在是太不方便了,最简单的方法还是统一到同一个版本。

提交了issue和PR 给到社区,希望可以升级 go sdk 的 protoc 版本到 v3.14.0:

https://github.com/dapr/go-sdk/pull/141

Go SDK的proto代码生成(旧版)

旧版的proto代码生成,非常麻烦,新版改进之后就不要这么折腾了。

本文归档备用。

准备工作

安装特定版本的相关文件

如果直接用brew命令安装,默认是安装当前最新版本:

brew install protobuf  						//protobuf-3.14.0
brew install grpc 								// grpc-1.33.2_1
brew install protoc-gen-go-grpc 	// protoc-gen-go-grpc-1.0.1_1

但是生成的go文件会和目前go-sdk上已有的文件有非常大的差异,原因应该是上面几个工具的版本造成的。

在go-sdk中未能找到具体的版本要求,但是在darp/dapr项目下的 dapr/proto/READEME.md 文件中有如下说明:

Prerequsites

Because of etcd dependency issue, contributor needs to use the below verisons of tools to generate gRPC protobuf clients.

这个README文件中有如何安装上述特定版本的方式,参考原文操作如下:

  1. 清理上面已安装的版本brew remove grpc protoc-gen-go-grpc protobuf Copy
  2. 手工安装 protoc v3.11.0
    • 下载对应版本: mac
    • 解压缩之后复制二进制文件到Path路径下,一般习惯放到 /usr/local/bin 下
    • 将include目录的内容复制到 /usr/local/include
      • cp -r protoc-3.11.0-osx-x86_64/include/ /usr/local/include
      • 如果这一步没有执行,则后面会报错: Import "google/protobuf/empty.proto" was not found or had errors
    • 执行protoc --version 验证一下版本
  3. 手工构建 protoc-gen-go : 参考README中的详细步骤

将proto生成源码

执行命令:

protoc -I . ./dapr/proto/runtime/v1/dapr.proto --go_out=plugins=grpc:.

生成的代码存放在当前目录下的 github.com/dapr/dapr/pkg/proto/runtime/v1/dapr.pb.go

将其复制到现有dapr项目存放生成文件的目录 pkg/proto/runtime/v1/

这个方式生成的go源码可以做到和dapr/dapr项目下提交的go源码完全一致。

但和 go-sdk项目中的源码差异非常大,再次猜测是版本问题,

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.25.0
// 	protoc        v3.13.0
// source: dapr/proto/runtime/v1/dapr.proto

将protoc换成对应的版本之后,代码生成基本一致了。

备注:给dapr提了建议,希望后续能把dapr/dapr和dapr/go-sdk中的版本升级并保持一致,已经被接受,后续应该不用这么麻烦了。

其他

期间的特殊错误处理

运行make protos时,遇到过一个莫名其妙的报错:

 % make protos
go install github.com/gogo/protobuf/gogoreplace
cannot find package "." in:
        /Users/sky/work/code/skyao/go-sdk/vendor/github.com/gogo/protobuf/gogoreplace
make: *** [protos] Error 1

没找到合理的解决方案,只好用了一个很土的办法:

  1. 修改 go.mod 文件,加入依赖require github.com/gogo/protobuf v1.3.1
  2. 为了拉取文件,加入一个go文件,内容只有一行:import _ "github.com/gogo/protobuf/gogoreplace"不要管ide的报错,这两个事情的目的只是为了能拉取gogo/protobuf/gogoreplace的文件到本地,避免上面的报错而已。完成之后,回滚上面的改动。

Java SDK的proto代码生成

从 dapr java sdk 仓库 clone下来代码之后,直接用IDE 如 IntelliJ 导入,是会报错的:有部分 dapr 的类找不到。这是因为这些代码是需要从 dapr 的 proto 代码中临时生成然后被项目装载。因此如果要进行 dapr java sdk 项目的开发或者代码阅读,需要从proto文件中生成这些类。

备注:

不同语言的sdk对生成代码的处理方式不同:

  • java sdk:采用的是不保存生成的代码到git仓库,每次需要的时候从proto文件生成,然后以 generated-sources 的方式被挂载到 mvn 项目中。
  • go sdk:有些语言的sdk如 go sdk,是将生成的代码直接提交到git仓库,这样如果不需要重新生成代码,是可以clone下来仓库就直接可以使用和阅读的。

准备protoc等工具

安装Protoc,目前 daprd 要求的版本是 v3.14.0。sdk 和 daprd保持一致。

具体见 Daprd Proto代码生成

从proto文件生成Java代码

在 dapr java sdk 项目下,有名为 sdk-augogen的子项目,在这里执行 generate-sources 命令就可以生成对应的java类:

$ cd sdk-augogen
$ mvn generate-sources

代码生成的过程有几个主要步骤:

步骤1: 下载proto文件

从命令输出中能看到,maven会下载三个proto文件: common.proto, dapr.proto,appcallback.proto:

[INFO] --- download-maven-plugin:1.6.0:wget (getCommonProto) @ dapr-sdk-autogen ---
[INFO] Downloading: https://raw.githubusercontent.com/dapr/dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto/common/v1/common.proto
[INFO] Downloading: https://raw.githubusercontent.com/dapr/dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto/runtime/v1/dapr.proto
[INFO] Downloading: https://raw.githubusercontent.com/dapr/dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto/runtime/v1/appcallback.proto

对照 sdk-autogen 子项目下的 pom.xml 文件,可以找到相关的maven插件配置:

<plugin>
        <groupId>com.googlecode.maven-download-plugin</groupId>
        <artifactId>download-maven-plugin</artifactId>
        <version>1.6.0</version>
        <executions>
          <execution>
            <id>getCommonProto</id>
            <!-- the wget goal actually binds itself to this phase by default -->
            <phase>initialize</phase>
            <goals>
              <goal>wget</goal>
            </goals>
            <configuration>
              <url>${dapr.proto.baseurl}/common/v1/common.proto</url>
              <outputFileName>common.proto</outputFileName>
              <!-- default target location, just to demonstrate the parameter -->
              <outputDirectory>${protobuf.input.directory}/dapr/proto/common/v1</outputDirectory>
            </configuration>
          </execution>
          <execution>
            <id>getDaprProto</id>
            <!-- the wget goal actually binds itself to this phase by default -->
            <phase>initialize</phase>
            <goals>
              <goal>wget</goal>
            </goals>
            <configuration>
              <url>${dapr.proto.baseurl}/runtime/v1/dapr.proto</url>
              <outputFileName>dapr.proto</outputFileName>
              <!-- default target location, just to demonstrate the parameter -->
              <outputDirectory>${protobuf.input.directory}</outputDirectory>
            </configuration>
          </execution>
          <execution>
            <id>getDaprClientProto</id>
            <!-- the wget goal actually binds itself to this phase by default -->
            <phase>initialize</phase>
            <goals>
              <goal>wget</goal>
            </goals>
            <configuration>
              <url>${dapr.proto.baseurl}/runtime/v1/appcallback.proto</url>
              <outputFileName>appcallback.proto</outputFileName>
              <!-- default target location, just to demonstrate the parameter -->
              <outputDirectory>${protobuf.input.directory}</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>

在这里特别要注意的是 ${dapr.proto.baseurl},这个属性在根目录下的 pom.xml 中设置,比如我当前版本的值是:

  <dapr.proto.baseurl>
https://raw.githubusercontent.com/dapr/
dapr/4a6369caaba9cf46eae9bfa4fa6e76b474854c89/dapr/proto
  </dapr.proto.baseurl>

这意味着如果要针对某个版本的 proto 文件进行代码生成,则需要修改这里的 ${dapr.proto.baseurl} 的值,比如指向某个分支/tag或者如上面指向某个commit。

步骤2: 生成代码

protoc-jar-maven-plugin 插件负责将前面下载的proto文件生成java 代码,生成之后的代码存在的 target/generated-sources 目录下:

[INFO] --- protoc-jar-maven-plugin:3.11.4:run (default) @ dapr-sdk-autogen ---
[INFO] Protoc version: 3.13.0
protoc-jar: protoc version: 3.13.0, detected platform: osx-x86_64 (mac os x/x86_64)
protoc-jar: cached: /var/folders/vr/8yl21p696dd41c776p67qlkm0000gp/T/protocjar.webcache/com/google/protobuf/protoc/maven-metadata.xml
protoc-jar: cached: /var/folders/vr/8yl21p696dd41c776p67qlkm0000gp/T/protocjar.webcache/com/google/protobuf/protoc/3.13.0/protoc-3.13.0-osx-x86_64.exe
protoc-jar: executing: [/var/folders/vr/8yl21p696dd41c776p67qlkm0000gp/T/protocjar852218627845177615/bin/protoc.exe, --version]
libprotoc 3.13.0

[INFO]     Processing (grpc-java): dapr.proto
[INFO]     Processing (grpc-java): common.proto
[INFO]     Processing (grpc-java): appcallback.proto

[INFO] Adding generated sources (java): /Users/sky/work/code/skyao/java-sdk/sdk-autogen/target/generated-sources
[INFO] Adding generated sources (grpc-java): /Users/sky/work/code/skyao/java-sdk/sdk-autogen/target/generated-sources

[INFO] BUILD SUCCESS

刷新项目,比如 idea 下可以在 java-sdk 根目录下pom.xml上右键 maven -> reload project 。

相关的文件路径小结

一目了然:

有坑小心:proto文件缓存

有一个比较坑的事情是,download-maven-plugin 插件在下载文件时,不是每次都去请求,有时会从maven本地缓存中拿文件。

比如我某次被坑的时候,就发现有如下日志:

[INFO] --- download-maven-plugin:1.6.0:wget 
[INFO] Got from cache: /Users/sky/.m2/repository/.cache/download-maven-plugin/common.proto_d701b2bbc1c789dfa25b99c95a8cd8c2
[INFO] Got from cache: /Users/sky/.m2/repository/.cache/download-maven-plugin/dapr.proto_372181342afa932d4684925872c96d22
[INFO] Got from cache: /Users/sky/.m2/repository/.cache/download-maven-plugin/appcallback.proto_e110e7ac8ee53213ecf133574e146d64
[INFO] 

这个缓存会导致拿到的 proto 文件和原来期望的proto文件内容可能不一样,因此后面生成的代码也会有偏差,某些情况下就会出现代码编译不过,错误可能是某个类或者某个方法找不到。

这个时候最简单的办法就是删除这个缓存目录,让 maven 去远程下载,然后再重新生成:

rm -rf ~/.m2/repository/.cache/download-maven-plugin/

mvn generate-sources
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索