基于 Go Module 管理依赖并将注册中心调整为 Etcd
前言
由于 Go Micro 框架去年年底将 Consul 从默认支持的注册中心调整为通过插件机制引入,导致很多同学反映按照基于 Go Micro 框架构建一个简单的微服务接口这篇教程遇到很多坑。另外,之前这篇教程是按照 GOPATH 方式管理项目依赖,很多人也会因为网络问题导致无法下载依赖包。尤其是对新手而言,有点无所适从,心态已然变成了从入门到想放弃。
所以学院君为此更新一篇教程,基于全新的 Go Module 方式管理依赖,使用 Go Module 的好处是可以配置代理来加速国内 Go 依赖包的下载,此外,还将使用最新版本的 Go Micro 并基于 Etcd 作为注册中心来演示如何构建第一个微服务接口。
注:本教程适用于 Go 1.11+ 版本,因为 Go 1.11 才正式引入了 Go Module 作为包管理器。
创建新项目
首先我们打开 GoLand,基于 Go Modules 创建一个新项目 hello
,这里我将项目保存到了 ~/Development/go/src/hello
这个路径,并且设置环境变量中的代理地址为 https://goproxy.io
,以便可以通过该地址加速依赖包下载:
按照上图所示设置完成后,点击「Create」按钮创建该项目,然后就可以在项目根目录下自动生成一个 go.mod
文件,你可以将其类比做 PHP 中的 composer.json
,或者 NPM 中的 package.json
,Go Module 基于该文件对项目依赖进行管理:
然后你可以在 GoLand 底部工具栏中点开「Terminal」,运行 go env
查看 Go Module 是否已启用以及代理地址设置是否成功:
至此,我们就基于 GoLand 提供的工具快速完成了微服务项目的初始化。
安装 Protobuf 相关工具
接下来,我们来安装基于 Protobuf 格式服务声明文件自动生成微服务原型代码所需要的一些工具。
安装 protoc-gen-micro
首先是 protoc-gen-micro
,该工具适用于在 Micro 框架中基于 Protobuf 文件生成服务代码,在项目根目录下运行如下 go get
命令安装:
go get -u github.com/micro/protoc-gen-micro
安装完成后,即可在 go.mod
的 require
中看到依赖声明:
module hello
go 1.13
require (
github.com/golang/protobuf v1.4.0 // indirect
github.com/micro/protoc-gen-micro v1.0.0 // indirect
)
系统默认会将 protoc-gen-micro
可执行文件安装到 GOPATH 路径的 bin
目录下,如果你不知道默认 GOPATH 路径在哪里,可以在 GoLand 的 Preferences 中看到:
安装 protoc
protoc-gen-micro
依赖 protoc
和 protoc-gen-go
,所以要使用 protoc-gen-micro
还要安装它们。
可以从这里 https://github.com/protocolbuffers/protobuf/releases 下载最新版的 protoc
:
选择与自己系统相匹配的压缩包,比如我的是 Mac 系统,则选择 osx 64 位下载,解压,然后将其移动到指定位置,并将 protoc
二进制可执行文件所在的 bin
目录放到系统路径中以便可以全局调用(以下是 Mac 系统指令,Windows 下请自行通过图形化界面设置系统路径):
mv ~/Downloads/protoc-3.11.4-osx-x86_64 ~/Development/tools
vi ~/.zshrc
export PATH="/Users/sunqiang/go/tools/protoc-3.8.0-osx-x86_64/bin:$PATH"
source ~/.zshrc
你可以运行 protoc --version
检测是否可以在任意位置调用 protoc
命令。
安装 protoc-gen-go
最后通过如下命令安装 protoc-gen-go
,该依赖包是 Protobuf 的 Go 语言实现:
go get -u github.com/golang/protobuf/protoc-gen-go
安装完成后,同样应该可以在 GOPATH 路径下的 bin
目录中看到 protoc-gen-go
。你需要将 $GOPATH/bin
目录也放到系统路径中,参照上面 protoc
的设置即可,以便可以全局调用 protoc-gen-go
和 protoc-gen-micro
可执行文件。
使用 Etcd 作为注册中心
由于 Go Micro 框架默认已经不能开箱支持 Consul,所以我们选择 Etcd 作为注册中心进行服务发现。你可以在这个页面 https://github.com/etcd-io/etcd/releases 下载最新版本的 Etcd:
选择自己系统对应的 zip 或者 tar.gz 包下载即可,以 Mac 为例,选择 etcd-v3.4.7-darwin-amd64.zip
下载,然后解压到本地指定目录:
mv ~/Downloads/etcd-v3.4.7-darwin-amd64.zip ~/Development/tools
cd ~/Development/tools
unzip etcd-v3.4.7-darwin-amd64.zip
进入该目录并启动 Etcd 服务器,然后不要关掉这个窗口,否则服务器会中止:
cd etcd-v3.4.7-darwin-amd64
./etcd
然后可以新开一个窗口,进入 etcd 所在目录,通过 etcdctl
指令进行客户端测试:
编写服务
至此,我们已经做好了所有外围的准备工作,接下来,可以正式通过 Go Micro 框架编写第一个微服务接口了。
服务接口声明
在 hello
目录下新建一个 proto
子目录,然后在 proto
目录下创建一个 Protobuf 格式的服务接口声明文件 greeter.proto
:
syntax = "proto3";
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 1;
}
如上述代码所示,我们定义了一个名为 Greeter
的服务,该服务中包含一个 Hello
方法,该方法接收一个 HelloRequest
对象,然后返回一个 HelloResponse
对象,这两个对象都只包含一个参数。
通过服务声明生成原型代码
接下来,我们就可以借助前面安装的 protoc
工具通过服务声明文件生成相应的服务原型代码(在 hello
项目根目录下运行):
protoc -I. --go_out=plugins=micro:. proto/greeter.proto
为了避免后续修改 greeter.proto
文件后需要频繁执行该指令,可以在项目根目录下创建一个 Makefile
文件,然后将该指令放到 build
构建步骤中:
build:
protoc -I. --go_out=plugins=micro:. proto/greeter.proto
这样,就可以通过运行 make build
执行该指令了,执行成功后,会在 hello/proto
目录下生成一个包含服务原型代码的 greeter.pb.go
文件:
如果在执行上述 protoc
命令出错,提示 micro
插件不存在:
可以将指令调整为下面这个重新执行(项目根目录下执行):
protoc --proto_path=. --micro_out=. --go_out=. proto/greeter.proto
编写服务实现代码
接下来,我们就可以在项目根目录下创建一个 main.go
,然后基于上述服务原型代码来编写第一个微服务服务端接口了:
package main
import (
"context"
"fmt"
"github.com/micro/go-micro"
proto "hello/proto"
)
type GreeterServiceHandler struct{}
func (g *GreeterServiceHandler) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = " 你好, " + req.Name
return nil
}
func main() {
// 创建新的服务
service := micro.NewService(
micro.Name("Greeter"),
)
// 初始化,会解析命令行参数
service.Init()
// 注册处理器,调用 Greeter 服务接口处理请求
proto.RegisterGreeterHandler(service.Server(), new(GreeterServiceHandler))
// 启动服务
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
这里的服务处理器类 GreeterServiceHandler
实现了服务原型代码中的 GreeterHandler
处理器接口,然后我们在入口函数 main
中初始化名为 Greeter
的微服务,并通过 proto.RegisterGreeterHandler
方法将 GreeterServiceHandler
处理器实现绑定到微服务的服务器中(默认是 rpc 服务器),最后通过 service.Run
启动服务,这样,就可以通过 GreeterServiceHandler
处理器方法处理客户端请求了。
在项目根目录下运行如下命令自动下载服务实现代码中的依赖:
go mod tidy
然后启动服务:
go run main.go --registry=etcd
注意,这里我们手动通过 --registry=etcd
指定注册中心为 etcd
,否则默认的注册中心是 mdns
(在 Windows 系统中默认不可用)。
当然,你也可以通过设置 GoLand 的 Go Modules 环境变量 MICRO_REGISTRY=etcd
来统一设置,这样,就不需要在启动服务时额外传入这个选项了(打开 GoLand 的 Preferences 界面即可完成设置):
后面我们都是基于这个系统环境设置进行演示,不再额外传入 --registry
指定注册中心,望知晓。
接下来,就可以通过客户端调用这个远程服务了。
客户端调用
我们来编写一段简单的客户端测试代码,在项目根目录下创建 client.go
并编写代码如下:
package main
import (
"context"
"fmt"
"github.com/micro/go-micro"
proto "hello/proto"
)
func main() {
// 创建一个新的服务
service := micro.NewService(micro.Name("Greeter.Client"))
// 初始化
service.Init()
// 创建 Greeter 客户端
greeter := proto.NewGreeterService("Greeter", service.Client())
// 远程调用 Greeter 服务的 Hello 方法
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "学院君"})
if err != nil {
fmt.Println(err)
}
// Print response
fmt.Println(rsp.Greeting)
}
我们通过原型代码中的 proto.NewGreeterClient
创建客户端,然后通过 RPC 远程调用 Hello
方法,并打印响应结果:
返回值符合预期,表明我们的微服务接口可以正常工作。至此,我们就基于 Go Micro 框架成功创建了第一个微服务接口,非常简单吧?
提供 HTTP 服务接口
上述客户端代码是以 RPC 方式请求服务接口的,能否通过 HTTP 请求的方式获取响应结果呢?
当然可以,Go Micro 框架为我们提供了一个 API 网关实现 —— Micro API,我们可以基于该组件快速对外提供 HTTP + JSON 格式的服务接口。
使用之前,需要先安装 micro
包:
go get github.com/micro/micro/v2
安装完成后,会在 $GOPATH/bin
目录下创建一个 micro
可执行文件,由于 $GOPATH/bin
已经包含在系统路径中,所以可以直接通过下面的指令启动:
该 API 网关通过 8080
端口对外提供服务,我们可以通过 http://localhost:8080/service/endponit
的方式发起对后端微服务接口的请求,API 网关会通过解析 URL 路径自动将请求路由给后端对应的微服务处理器方法。
注:这里注册中心通过
MICRO_REGISTRY
环境变量读取,如果没有设置需要手动指定--registry=etcd
。
这个时候通过 curl 测试服务接口的访问:
发现报错了,这是因为 Micro API 默认服务所在的命名空间是 go.micro.api
,而这里我们并没有设置任何命名空间,所以需要将命名空间指定为空字符串,然后设置处理器为 rpc
(尚未实现专门的 api
处理器方法):
这样一来,再次测试的话就可以正常访问了:
更多关于 Micro 的使用示例和底层实现,参考后续 API 网关相关教程。
友情提示:后续框架篇教程默认依然基于 GOPATH + Consul 进行介绍,你可以按照本篇教程进行代入和调整。
70 Comments
➜ hello micro api --namespace=' ' --handler=rpc
2021-03-02 00:14:50 file=api/api.go:220 level=info service=api Registering API RPC Handler at /
2021-03-02 00:14:50 file=http/http.go:90 level=info service=api HTTP API Listening on [::]:8080
2021-03-02 00:14:50 file=v2@v2.9.1/service.go:200 level=info service=api Starting [service] go.micro.api
2021-03-02 00:14:50 file=grpc/grpc.go:864 level=info service=api Server [grpc] Listening on [::]:53569
2021-03-02 00:14:50 file=grpc/grpc.go:697 level=info service=api Registry [etcd] Registering node: go.micro.api-2b9a2da3-8c24-4e67-8b70-886e56e51c92
::1 - - [02/Mar/2021:00:14:54 +0800] "GET /Greeter/Hello?name=test HTTP/1.1" 500 94 "" "curl/7.64.1"
看下了,一直是 500
按照文章的执行 报错 github.com/coreos/etcd/clientv3/balancer/resolver/endpoint undefined: resolver.BuildOption
然后执行了
get google.golang.org/grpc
成功解决问题执行 protoc -I. --go_out=plugins=micro:. proto/greeter.proto 报错:protoc-gen-go: unable to determine Go import path for "proto/greeter.proto"
protoc --version libprotoc 3.15.7;
是我哪里配置有问题吗?
在greeter.proto,添加:option go_package = "./proto"; 参考网址:https://blog.csdn.net/qq_44033530/article/details/115418377
C:\Users\Administrator\go\src\hello>go mod tidy
hello: no Go source files
hello/proto: no Go source files
没事了 在goroot目录src下我有一个同名的hello目录 删掉就好了
2021-10-20 01:37:41 file=grpc/grpc.go:1028 level=error service=api Server deregister error: {"Id":"go.micro.client","Code":500,"Detail":"connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:8000: connect: connection refused"","Status":"Internal Server Error"}
啊,没有启动micro服务,看了看官网。
此文档问题太多,无法进行,是否和micro版本更新有关
嗯 是的 后面打算基于Kratos重新写一版教程