安装 proto
$cd /usr/local
$sudo wget https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protoc-3.20.1-linux-x86_64.zip
$sudo mkdir protoc
$cd protoc
$sudo unzip ../protoc-3.20.1-linux-x86_64.zip
$cd bin
$sudo cp protoc /xxx #复制到$GOPATH/bin目录下
$protoc --version
安装 protoc-gen-go
我是在 deepin 的linux系统里安装的,go 项目管理使用了 go mod。
按网络上找的教程
go get github.com/golang/protobuf/protoc-gen-go
执行上述命令就会在 $GOPATH/bin 目录下生成 protoc-gen-go 可执行文件。
我按上述操作一直无法生成 protoc-gen-go 可执行文件,只是把 github.com/golang/protobuf/protoc-gen-go 及所需的包 下载到了 $GOPATH/pkg/mod 目录下了。
后面一直百度查询与尝试,成功在 $GOPATH/bin 目录下生成 protoc-gen-go 可执行文件
- go get google.golang.org/protobuf/cmd/protoc-gen-go
- go install google.golang.org/protobuf/cmd/protoc-gen-go
上述两个命令 是在项目下执行的(项目下有 go.mod 文件)
安装 protoc-gen-micro
go get github.com/micro/micro/v2/cmd/protoc-gen-micro
go install github.com/micro/micro/v2/cmd/protoc-gen-micro
上述操作直接在 $GOPATH/bin 目录生成了 protoc-gen-micro 可执行文件,生成的 protoc-gen-micro 是v2版本的。
上述两个命令 是在项目下执行的(项目下有 go.mod 文件)
生成 v3 版本的
go install github.com/asim/go-micro/cmd/protoc-gen-micro/v3@latest
将 .proto 文件转换成 store.pb.go 文件
新增文件 store.proto ,在 目录 frame.service 下
syntax = "proto3";
package store.srv;
service Store{
rpc GetStoreDetail(StoreIdRequest) returns(StoreInfoResponse);
}
// 请求参数
message StoreIdRequest {
string id = 1;
}
// 返回参数
message StoreInfoResponse{
string id = 1;
}
}
protoc --proto_path=. --go_out=. store.proto
执行上述命令,报错:
protoc-gen-go: unable to determine Go import path for "store.proto"
Please specify either:
• a "go_package" option in the .proto source file, or
• a "M" argument on the command line.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.
--go_out: protoc-gen-go: Plugin failed with status code 1.
解决方式:
在 student.proto 文件内加上一行:
option go_package = “./proto”;
文件最终内容如下:
syntax = "proto3"; // 指定版本
package store.srv; // 指定包名
option go_package = "./proto"; // 指定生成位置
service Store{
rpc GetStoreDetail(StoreIdRequest) returns(StoreInfoResponse);
}
// 请求参数
message StoreIdRequest {
string id = 1;
}
// 返回参数
message StoreInfoResponse{
string id = 1;
}
protoc --proto_path=. --go_out=. store.proto 命令解析
protoc 命令详解
$ protoc
用法: protoc [OPTION] PROTO_FILES
解析proto文件并根据给定的选项生成输出:
-IPATH, --proto_path=PATH 指定搜索目录,可多次指定,默认为当前工作目录。
--version 显示版本信息并退出
-h, --help 显示帮助文档并退出
--encode=MESSAGE_TYPE 从标准输入读取给定类型的文本格式消息,从标准输出写入二进制文件。消息类型必须在原始文件或导入中定义。
--decode=MESSAGE_TYPE 从标准输入中读取给定类型的二进制消息,向标准输出中写入文本格式。消息类型必须定义在proto文件或其导入的文件中。
--decode_raw 从标准输入读取任意协议消息,向标准输出写入原始标记或文本格式的值。
--descriptor_set_in=FILES 指定文件分隔符列表,每个都包含了一个文件描述符集合。
-oFILE, 写入FileDescriptorSet
--include_imports 当使用--descriptor_set_out时, 同时包含输入文件的依赖项
--include_source_info 当使用--descriptor_set_out时无需剥离FileDescriptorProto中的SourceCodeInfo
--dependency_out=FILE 指定依赖输出文件
--error_format=FORMAT 设置打印错误格式,默认gcc,可选msvs。
--print_free_field_numbers 打印给定proto文件中消息定义的可用字段号
--plugin=EXECUTABLE 指定使用插件的可执行文件
--cpp_out=OUT_DIR 产生C++头文件和源文件
--csharp_out=OUT_DIR 产生C#源文件
--java_out=OUT_DIR 产生Java源文件
--javanano_out=OUT_DIR 产生Java Nano源文件
--js_out=OUT_DIR 产生JavaScript源文件
--objc_out=OUT_DIR 产生Objective C头文件和源文件
--php_out=OUT_DIR 产生PHP源文件
--python_out=OUT_DIR 产生Python源文件
--ruby_out=OUT_DIR 产生Ruby源文件
@<filename> 从文件中读取选项和文件名
–proto_path=PATH 指定搜索目录,可多次指定,默认为当前工作目录。
–go_out=. 产生GO源文件目录,默认为当前工作目录。
store.proto 转换执行的 proto 文件
生成 store.pb.micro.go 文件
protoc --go_out=. --micro_out=. store.proto
规则与上面生成 store.pb.go 一样。上述命令是 同时生成 store.pb.go,store.pb.micro.go 两个文件
安装 consul 服务发现软件
这个可以直接使用 docker 安装
docker pull consul
运行
docker run -itd --name consul -p 8500:8500 consul:latest
查询日志
docker logs 4e90f273e754
暴露 8500 端口,方便本地查看服务信息
127.0.0.1:8500 / 0.0.0.0:8500 访问,可以查看 consul 的服务信息
正式开启一个微服务项目
上面几步操作完成后 可以生成 store.pb.go,store.pb.micro.go 两个文件。
新建 store 微服务的 实现文件方法:store.service.go
package proto
import (
"context"
"fmt"
)
type StoreServiceDemo struct {}
func (s *StoreServiceDemo) GetStoreDetail(context.Context, *StoreIdRequest, *StoreInfoResponse) error {
fmt.Println("GetStoreDetail")
rep.Id = req.Id
rep.StoreName = "StoreName"
rep.Bindphone = "Bindphone"
rep.Email = "Email"
return nil
}
这里的 结构体可以随意命名,但结构体必须实现 store.proto 文件里的 GetStoreDetail 微服务方法。
之后就是写启动微服务的代码了,如下
package main
import (
"fmt"
"frame.service/proto"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-plugins/registry/consul/v2"
)
func main() {
//连接 consul
ConsulClient := consul.NewRegistry(registry.Addrs("127.0.0.1:8500"))
service := micro.NewService(
micro.Name("go.micro.srv.test"),
micro.Version("latest"),
micro.Registry(ConsulClient), // 服务注册到 consul
)
// Register Handler
_ = proto.RegisterStoreHandler(service.Server(), new(proto.StoreServiceDemo))
_ = service.Run()
}
上述代码分几步
- 连接 consul
- 创建 micro 服务:设定服务名称,版本,服务注册位置(这里是把服务注册到连接的 consul)
- 注册服务 Store
- 启动微服务
之后就可以在 consul 127.0.0.1:8500 上看到新注册的微服务了
微服务访问
这里我是用 gin 访问成功,新建目录 api,api 下生成 proto 的 store.pb.go 和 store.pb.micro.go 两个文件。代码如下
package main
import (
"context"
"encoding/json"
"log"
"net/http"
storeService "frame.service/api/proto"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/registry"
"github.com/micro/go-plugins/registry/consul/v2"
"github.com/gin-gonic/gin"
)
func main() {
// gin 路由
router := gin.Default()
router.GET("/", func(c *gin.Context) {
// consul
consulReg := consul.NewRegistry(func(options *registry.Options) {
options.Addrs = []string{"127.0.0.1:8500"}
})
service := micro.NewService(
micro.Registry(consulReg),
)
// 连接 go.micro.srv.test 微服务
mc := storeService.NewStoreService("go.micro.srv.test", service.Client())
// 请求 go.micro.srv.test 微服务 的 GetStoreDetail 方法
resp, err := mc.GetStoreDetail(context.TODO(), &storeService.StoreIdRequest{Id: "123465"})
if err != nil {
log.Println(err.Error())
c.String(http.StatusBadRequest, "hello")
return
}
// 输出结果
respStr, _ := json.Marshal(resp)
c.String(http.StatusOK, string(respStr))
})
// 启动路由
router.Run(":8848")
}
gin 访问结果如下: