之前的文章我们大致了解了微服务,同时我们还准备好了一台搭载CentOS系统的云服务器,这篇文章我们正式开始微服务的实践之旅。
一提到微服务,首先想到的就是Spring Cloud,阿里的Dubbo等。与go语言相比,java的一个巨大优势就是生态系统非常成熟,有很多成熟的商业级框架可供使用,go相对而言比较新,因此生态上还不如java那么完善,但是这并不妨碍我们学习微服务,在github上已经有一些开源的go微服务框架可以使用,比如go micro,go kit,go chassis等,这里我们选择go micro,在写这篇文章的时候,这个开源微服务框架在github上已经有多达12.9K的Star数,一个微服务框架该有的东西基本上全都具备了,是入门微服务非常不错的一个开源项目。
1、认识micro微服务框架
如果想深入学习,最好的方法是先荡一份源码研究,go micro的主页:
https://github.com/micro/go-micro
初学者可以先不急着一上来就去挖原理,先学会如何使用,然后在循序渐进,建议初学者先点开下面的链接仔细看一遍文档:
https://micro.mu/docs/framework.html
micro满足分布式系统开发的核心需求,在设计上采用插件化的方式,一些核心功能诸如消息中间件、服务注册及发现等都是可插拔的,micro提供默认实现,但是开发者可以根据自己的需求随意替换。
我们来看看micro的主要特点:
(1) 服务发现:micro支持自动化的服务发现和域名解析。服务发现在微服务开发中处于核心地位,正如我们在第一篇文章所说,当微服务独立部署在不同机器上的时候,服务A想要和服务B通信就必须要知道服务B所在的机器。micro提供了基于mDNS的服务发现的默认实现,当然你可以使用其他插件甚至自己编写插件来满足自己的需求。
(2) 负载均衡:通常一个商业级的应用,因为用户量较大,一些核心服务往往会部署在多台机器上,这就涉及到负载均衡的问题,大量的请求不能落地到某一台机器上,而是用某种负载均衡策略分摊到所有的机器上去。micro提供了基于随机哈希的负载均衡策略,当发现某台机器有问题时会选择另外一台机器进行重试。
(3) RPC:micro对同步通信进行了抽象,客户端到服务端的请求会被自动解析并做负载均衡,默认采用gRPC做为RPC通道。
(4) 异步消息:micro支持消息的发布-订阅,这个也是可插拔的,你可以用Kafka,RabbitMQ等任何你熟悉的消息中间件发送异步消息。
(5) 插件化:诸如服务发现、负载均衡、消息中间件等这些分布式系统开发中的核心组件,micro都做了抽象,可以根据自己的需求实现接口去替换,当然社区也已经有很多现成的插件供开发者使用。
2、Hello,Micro
这一节,我们就用经典的Hello world的方式来看看如何编写一个最简单的微服务。
2.1 开发环境
我们假定读者已经是go语言的开发者,这个系列的文章我们不会介绍go语法相关的知识。我们以GoLand IDE为例。
go版本1.14,GoLand版本2020.1.2
首先,新建一个工程,建好之后在GoLand中开启go module,并且添加环境变量:GOPROXY = https://goproxy.cn,在Mac上可以通过Preferences -> Go Modules来设置:
2.2 初始化模块
我们在工程中新建一个模块micro,新建一个文件夹micro,然后命令行:
go mod init micro
然后我们会在项目根目录下看到一个go.mod的文件文件。
2.3 编写协议文件
micro是基于google protobuf的,因此我们需要先编写.proto的协议文件:
syntax = "proto3";
package pb;
// 定义微服务对外提供的接口
service Greeter {
rpc Hello(Request) returns (Response) {}
}
// 请求
message Request {
string name = 1;
}
// 响应
message Response {
string msg = 1;
}
是不是很简单,你的服务想提供什么能力,就用接口定义出来(包括参数等),我们在项目中新建一个proto目录,将协议文件放在这里,目录结构如下,其中pbfile目录存放.proto,pb目录则保存生成的代码:
接下来我们需要根据这份.proto协议生成代码,为了生成micro特定的代码,需要先在开发机上安装protobuf以及一些相关工具:
brew install protobuf
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v2/cmd/protoc-gen-micro@master
所有必须的工具都安装好之后,在命令行敲入命令来生成代码:
protoc --proto_path=$GOPATH/src:./proto/pbfile --go_out=./proto/pb --micro_out=./proto/pb hello.proto
--proto_path指定了要在哪些目录中寻找.proto协议文件,--go_out和--micro_out指定了文件的输出目录。
2.4 编写微服务
终于到了手撕第一个微服务的时刻了,因为功能非常简单,我们直接上代码,相信大家都能秒懂实在不懂的也没关系,复制到IDE里先运行起来也可以。在项目中新建server/main目录,在此目录下新建main.go,把以下代码复制进去:
package main
import (
"context"
"fmt"
"github.com/micro/go-micro/v2"
"micro/proto/pb"
)
type Greeter struct {}
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
//把客户端的请求回射给客户端
rsp.Msg = req.Name
return nil
}
func main() {
// 新创建一个服务,服务名为greeter,服务注册中心会用这个名字来发现服务
service := micro.NewService(
micro.Name("greeter"),
)
// 初始化
service.Init()
// 注册处理器
pb.RegisterGreeterHandler(service.Server(), new(Greeter))
// 启动服务运行
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
总结一下主要的点:
(1) 定义结构体,实现协议文件中定义的接口;
(2) 使用micro.NewService创建一个微服务,必须要指定一个服务名,如果一个系统有多个微服务,请注意不要重名,这个名字是用来做服务发现用的;
(3) 将我们实现了协议接口的结构体注册到micro;
(4) 最后启动服务运行;
现在,我们运行一下试试,在终端使用go run server/main/main.go运行程序,可以看到以下输出:
从控制台输出的日志中可以看到服务使用grpc,在54836端口上监听请求,同时可以看到我们在mdns上注册了一个节点,这样客户端就能通过mdns发现我们的微服务并调用接口。此前我们说过micro的服务发现与注册中心是可以通过插件化的方式替换的,在后续的文章中我们会用kubernetes做为服务注册中心,替换micro默认的mdns。
2.5 编写客户端
最后我们写个客户端程序来调用上面的微服务,在项目中新建client/main目录,在此目录下新建main.go文件,把以下代码拷贝过去:
package main
import (
"context"
"fmt"
"github.com/micro/go-micro/v2"
"micro/proto/pb"
)
func main() {
// 创建一个服务
service := micro.NewService(micro.Name("greeter.client"))
// 初始化
service.Init()
// 创建一个微服务的客户端
greeter := pb.NewGreeterService("greeter", service.Client())
// 调用微服务
rsp, err := greeter.Hello(context.TODO(), &pb.Request{Name: "Hello Micro"})
if err != nil {
fmt.Println(err)
}
fmt.Println(rsp.Msg)
}
同样我们先创建并初始化一个客户端服务,注意名字与服务端不同,然后调用服务接口,并在控制台打印服务的响应,在终端运行该程序,在控制台上看到以下输出:
如果你顺利完成了这一步,那么恭喜,你已经开发出了自己的第一个微服务,虽然它几乎什么也没有做。
2.6 使用micro来运行微服务
前面我们在自己的开发机上本地编译运行微服务,我们还可以用micro工具集来启动微服务,micro有一套完备的工具集供我们使用,我们先安装一下:
go install github.com/micro/micro/v2
安装完成以后,我们来尝试运行一下刚才的服务:
在终端运行:
micro server
然后,在打开一个终端,运行:
micro run server/main/
指定我们的微服务代码所在的目录即可,此时可以在终端看到以下输出:
我们用micro工具集一样运行起了我们的微服务。
然后我们利用工具来查看一下有哪些服务,在终端敲以下命令:
micro list services
输出如下:
可以看到默认已经启动了不少的服务,我们的greeter服务位于最下面。
现在查看一下这个服务的详细信息,在终端键入下面的命令:
micro get service greeter
看到以下输出:
详细信息一目了然。
最后咱们试着调用一下:
这次我们得到了JSON格式的输出。
3、总结
这篇文章我们实操了第一个微服务例子,并用不同的方式运行了它,完整的跑通这个简单的示例后,相信你对微服务的认识又进了一步。