使用go micro开发微服务详解

初识微服务

微服务架构是用来替换传统的单体架构的。它使用细颗粒度的服务及其组合来完成一个业务系统的工作。服务间是以一种约定来进行通信。微服务架构会带来如下的好处:

  • 开发团队可以并行进行开发工作;
  • 新加入的工作人员更容易上手;
  • 可以在组成系统的组件级别上进行持续集成和持续交付;
  • 它是一个松散的架构,不同的组件更容易替换;
  • 微服务架构不依赖与特定的语言和技术;

当谈论微服务时,服务的编排和发现是非常重要的。例如Kubernetes就用于编排docker容器。通常来说一个微服务对应一个容器是一个比较好的实践。
服务发现是一个自动化发现一个微服务实例的IP地址的过程。服务发现避免了硬编码IP地址的问题。

Go Micro

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目录下生成新的代码文件:
使用go micro开发微服务详解_第1张图片

服务逻辑开发

在成功编译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)
}

这样我们就完成客户端的开发,在这里需要注意几点:

  1. 在我们创建服务实例时,我们指定的名字需要和服务注册的名字一致,否则会出现找不到服务的问题;
  2. 在客户端的过程中,我们只需要使用服务的名字即可,并不需要指定服务所在的IP地址,这个名称和地址的映射是由服务注册中心完成的。另外由于避免了IP地址硬编码的问题,提高了整体程序的灵活性。

你可能感兴趣的:(微服务)