微服务架构是用来替换传统的单体架构的。它使用细颗粒度的服务及其组合来完成一个业务系统的工作。服务间是以一种约定来进行通信。微服务架构会带来如下的好处:
当谈论微服务时,服务的编排和发现是非常重要的。例如Kubernetes就用于编排docker容器。通常来说一个微服务对应一个容器是一个比较好的实践。
服务发现是一个自动化发现一个微服务实例的IP地址的过程。服务发现避免了硬编码IP地址的问题。
Netflix’s Eureka和Spring Boot是非常有名的用于创建微服务的框架。 Go Micro提供了同样的能力。它是一个使用Golang创建微服务的工具集。它非常轻量化,这意味在开始使用时它比较容易上手,而且体积会比较小,但随着服务数量增加和业务的复杂化,它也可以变得很大。
它创建微服务的方式对于Golang开发者来说非常友好。Go Micro可以使用插件进行特性的扩充,Go Micro支持以下特性:
下面我们就进行具体的微服务开发工作,在这里我们的客户端和服务端通过gRPC进行通信
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/protoc-gen-micro
首先我们需要使用protocol buffers对我们的服务进行定义:
syntax = "proto3";
option go_package = "protofiles";
service Encrypter {
rpc Encrypt(Request) returns (Response) {}
rpc Decrypt(Request) returns (Response) {}
}
message Request {
string message = 1;
string key = 2;
}
message Response {
string result = 1;
}
在我们定义完服务后,就可以在项目根目录下使用如下命令进行编译:
protoc -I=. --micro_out=. --go_out=. protofiles/encryption.proto
参数说明
参数 | 说明 |
---|---|
-I | 指定项目的根目录 |
–go_out | 指定自动产生的golang代码的保存位置 |
–micro_out | 与go_out类似,指定的是自动产生的与go micro相关的代码文件的保存位置 |
protofiles/encryption.proto | 指定要编译的protocal buffers文件 |
在成功完成命令后,会在protofiles目录下生成新的代码文件:
在成功编译protocal buffers文件后,在新生成的文件encryption.micor.pb.go中有个比较重要的接口定义:
type EncrypterHandler interface {
Encrypt(context.Context, *Request, *Response) error
Decrypt(context.Context, *Request, *Response) error
}
我们的服务逻辑就在一个实现了EntrypterHandler接口的结构体中进行定义:
type Encrypter struct {}
func (g Encrypter) Encrypt(c context.Context, request *protofiles.Request, response *protofiles.Response) error {
result, err := utils.EncryptString(request.Key, request.Message)
response.Result = result
return err
}
func (g Encrypter) Decrypt(c context.Context, request *protofiles.Request, response *protofiles.Response) error {
result, err := utils.DecryptString(request.Key, request.Message)
response.Result = result
return err
}
在我们完成服务逻辑的开发后,我们会把这个服务注册到服务发现中心上,以便供其他服务或客户端使用:
package main
import (
micro "github.com/micro/go-micro"
"log"
"microservice/protofiles"
)
func main() {
service := micro.NewService(micro.Name("encrypter"))
service.Init()
protofiles.RegisterEncrypterHandler(service.Server(), new(Encrypter))
if err := service.Run(); err != nil {
log.Fatal(err.Error())
}
}
在这里我们创建了一个服务的实例,然后我们创建的服务实现的结构体注册到这个实例上。
在完成了服务注册后,我们就可以运行这个服务了。当终端显示如下信息时,就表示我们的服务已经启动成功,并可对外提供服务了:
在这里我们会看到由于我们没有指定特定的服务发现机制,所以Go Micro会默认使用mdns作为服务发现机制。
这样我们就完成了一个非常简单的服务,下面我们会编写一个客户端程序调用这个服务。
在服务端开发和客户端开发过程中,最大的区别是我们是使用proto.NewEncrypterService来创建服务实例,然后我们使用这个实例来进行服务调用。注意这个函数是protoc命令自动生成的。
为了开发客户端,我们要使用服务定义中的protocal buffers文件,经过编译形成客户端桩代码,另一种简单的方式就是直接拷贝服务端生成的代码直接拷贝到客户端开发项目中即可。
package main
import (
"context"
micro "github.com/micro/go-micro"
"log"
"microclient/protofiles"
)
func main() {
service := micro.NewService(micro.Name("encrypter.client"))
service.Init()
encrypter := protofiles.NewEncrypterService("encrypter", service.Client())
resp, err := encrypter.Encrypt(context.TODO(), &protofiles.Request{
Message: "hello",
Key: "111023043350789514532147",
})
if err != nil {
log.Fatal(err.Error())
}
log.Println(resp.Result)
// Call the decrypter
resp, err = encrypter.Decrypt(context.TODO(), &protofiles.Request{
Message: resp.Result,
Key: "111023043350789514532147",
})
if err != nil {
log.Println(err)
}
// Print response
log.Println(resp.Result)
}
这样我们就完成客户端的开发,在这里需要注意几点: