一架梯子,一头程序猿,仰望星空!

go micro微服务框架入门教程


本节介绍如何使用micro微服务框架开发微服务。

我们以订单服务用户服务为例子,介绍如何开发微服务、微服务之间如何调用。

业务需求说明:

如果我们要根据订单id,查询订单信息(要求返回用户的地址和电话信息),但是订单信息仅保存了用户id,那么就需要通过用户id去用户服务查询用户信息。

下面一步步演示如何通过微服务完成这个需求。

1.依赖知识点

go micro依赖以下知识点,根据自己的情况,预先了解下面的知识点。

2.安装环境

安装protobuf编译器和对应的go语言插件

根据上面Grpc教程提示安装即可。

安装go micro微服务框架的编译器插件

go get github.com/micro/protoc-gen-micro

提示:记得将$GOPATH/bin目录添加到PATH环境变量中,这样才能直接找到我们安装的命令和插件,而不会提示找不到命令。

3.项目目录结构

为了方便演示,我们仅创建了一个项目,包含了用户服务和订单服务(实际项目中,一般一个微服务就是一个独立的项目)。

tizi/
├── go.mod     // go模块配置文件
├── orderservice.go   // 订单服务
├── proto // 保存微服务的接口定义文件
│   ├── order.proto   // 订单服务的接口定义
│   └── user.proto  // 用户服务的接口定义
└── userservice.go // 用户服务

初始化项目命令:

# 创建目录
mkdir tizi
cd tizi

# 创建接口定义目录
mkdir proto

# 初始化go模块, 我们模块名字叫:tizi365.com/tizi
go mod init tizi365.com/tizi

说明:上面是以Linux为例子介绍,windows也是类似的思路。

3.开发用户服务

用户服务,负责用户相关的业务,下面演示一个简单的用户服务。

实现一个微服务的步骤:

  1. 使用protobuf协议定义服务接口
  2. 使用protoc编译器根据我们定义的接口,生成go语言骨架代码
  3. 根据生成的骨架代码实现服务接口
  4. 初始化go micro微服务的服务端,注册微服务。

3.1.定义用户服务接口

下面使用protobuf协议定义用户服务的接口

文件:proto/user.proto

syntax = "proto3";

// 定义包名
package proto;

// 定义user服务的接口
service UserSrv {
    // 获取用户账号信息
    rpc GetAccount(GetAccountRequest) returns (Account) {}
}

// 定义获取账号信息的请求消息
message GetAccountRequest {
    int32 id = 1; // 用户id
}

// 定义用户账号消息
message Account {
	int32 id = 1;  // id
	string username = 2; // 账号
	string address = 3; // 地址
	string phone =4; // 电话
}

我们这里定义了一个GetAccount接口,用来读取用户信息。

3.2. 编译接口文件

上面定义了一个proto接口文件,我使用protoc编译器,生成go语言骨架代码。

# 切换到项目目录
cd tizi

# 编译user.proto接口定义文件
protoc --proto_path=proto:. --micro_out=proto/ --go_out=proto/ proto/user.proto

protoc参数说明:

  • --proto_path - proto文件目录
  • --micro_out - 生成的micro源码保存目录
  • --go_out - 生成的go源码保存目录
  • proto/user.proto - 最后面的参数就是我们要编译的proto文件

执行上面命令后,在proto目录下面生成了user.pb.go和user.micro.go两个文件,这两个文件包含了,我们定义的接口、请求和响应参数等等。

3.3. 实现用户服务

文件:userservice.go

package main

import (
	"context"
	"github.com/micro/go-micro"
	"tizi365.com/tizi/proto"
)

// 定义用户服务, 实现proto协议定义的接口
type UserService struct {
}

// 实现查询账号信息接口
// 需要注意的是,go micro微服务的rpc接口,请求参数和返回参数,都作为函数参数传入,而grpc的接口,返回参数作为函数返回值。
func (u *UserService) GetAccount (ctx context.Context, request *proto.GetAccountRequest, response *proto.Account) error  {
	// 作为演示这里直接返回账号信息
	response.Id = request.Id // 从request获取请求参数.
	response.Username = "tizi365"
	response.Address = "深圳市南山区西乡街道101号"
	response.Phone = "1300001111"

	return nil
}

func main() {
	// 定义一个微服务
	service := micro.NewService(
		micro.Name("go.micro.api.userservice"), // 定义用户服务的服务名
	)

	// 初始化
	service.Init()

	// 注册用户服务
	proto.RegisterUserSrvHandler(service.Server(), new(UserService))

	// 启动服务
	if err := service.Run(); err != nil {
		panic(err)
	}
}

提示:微服务的服务名,必须唯一,客户端通过服务名调用微服务的接口。

3.4. 运行用户服务

go run userservice.go

输出类似如下信息:

2019/10/18 00:08:40 Transport [http] Listening on [::]:51723
2019/10/18 00:08:40 Broker [http] Connected to [::]:51724
2019/10/18 00:08:40 Registry [mdns] Registering node: go.micro.api.userservice-84262902-0070-4820-8067-be76d85e7cf4

到这步说明用户服务启动成功了,下面我们看看订单服务,怎么调用用户服务的接口。

4.开发订单服务

订单服务负责订单相关的业务,我们这里这里仅开发一个查询订单信息的接口

4.1.定义订单服务接口

文件:proto/order.proto

syntax = "proto3";

// 定义包名
package proto;

// 定义订单服务的接口
service OrderSrv {
    // 获取订单信息
	rpc GetOrder(GetOrderRequest) returns (Order) {}
}

// 定义获取订单的请求消息
message GetOrderRequest {
    int32 id = 1; // 订单id
}

// 定义订单消息
message Order {
	int32 id = 1;  // 订单id
	string name = 2; // 商品名
	double price = 3; // 价格
	string username = 4; //用户
	string address = 5; // 用户地址
	string phone =6; // 联系电话
	string createTime = 7; //创建订单时间
}

4.2.编译接口文件

上面定义了一个proto接口文件,我使用protoc编译器,生成go语言骨架代码。

# 切换到项目目录
cd tizi

# 编译order.proto接口定义文件
protoc --proto_path=proto:. --micro_out=proto/ --go_out=proto/ proto/order.proto

执行命令后,在proto目录生成了order.pb.go和order.micro.go两个go语言的定义文件。

4.3.实现订单服务

文件:orderservice.go

package main

import (
	"context"
	"github.com/micro/go-micro"
	"time"
	"tizi365.com/tizi/proto"
)

// 定义用户服务, 实现proto协议定义的接口
type OrderService struct {
}

// 实现查询订单信息的接口
func (u *OrderService) GetOrder (ctx context.Context, request *proto.GetOrderRequest, response *proto.Order) error  {
	// 通常我们的订单信息都保存了用户的id, 例如用户id如下
	userId := 1312

	// 初始化用户服务对象
	// 初始化用户服务,需要用户服务的名字,这是一个初始化用户服务的时候定义的唯一标识
	userSrv := proto.NewUserSrvService("go.micro.api.userservice", service.Client())

	// 调用用户服务,查询用户信息,获取用户的电话和地址
	user, err := userSrv.GetAccount(context.TODO(), &proto.GetAccountRequest{Id:int32(userId)})
	if err == nil {
		// 调用成功, 初始化订单返回值
		response.Username = user.Username
		response.Address = user.Address
		response.Phone = user.Phone
	}

	response.Name = "大瓶可乐"
	response.Price = 6.5
	response.Id = request.Id // 从request获取请求参数.
	// 订单创建时间
	response.CreateTime = time.Now().Format(time.RFC3339)
	return nil
}

// 声明服务对象
var service micro.Service

func main() {
	// 定义一个微服务
	service = micro.NewService(
		micro.Name("go.micro.api.orderservice"), // 定义订单服务的服务名
	)

	// 初始化
	service.Init()

	// 注册订单服务
	proto.RegisterOrderSrvHandler(service.Server(), new(OrderService))

	// 启动服务
	if err := service.Run(); err != nil {
		panic(err)
	}
}

4.4.运行订单服务

另外打开一个命令窗口,输入运行命令:

go run orderservice.go

输出类似启动信息:

2019/10/18 00:17:50 Transport [http] Listening on [::]:51839
2019/10/18 00:17:50 Broker [http] Connected to [::]:51840
2019/10/18 00:17:50 Registry [mdns] Registering node: go.micro.api.orderservice-9f30f8d8-3c5a-47d7-ac61-5f0766140507

到目前为止,订单服务也成功启动,我们调用订单服务的GetOrder接口,GetOrder接口会去调用用户服务查询用户信息。

那么不写代码的情况下,怎么调用服务的接口呢? 总不能在开发一个客户端调用订单服务的接口。

go micro微服务框架为我们准备可视化的web后台,可以查询所有的注册的微服务,调试微服务接口,下面一节会介绍如何调试微服务接口。

5.调试微服务接口

go micro框架为我们提供了两种调试微服务接口的方法:

  • 通过micro命令工具调试服务接口
  • 通过micro web后台调试服务接口

我们首先安装micro运行时组件

go get github.com/micro/micro

5.1.通过命令调试

调用服务接口命令语法:

micro call 服务名 接口 参数

参数以json的形式提交。

例子:

调用订单服务:go.micro.api.orderservice 的OrderSrv.GetOrder接口,以json的形式传入了一个参数id

micro call go.micro.api.orderservice OrderSrv.GetOrder '{"id":1}'

提示:服务名是我们启动微服务定义的唯一标识,接口的格式是:proto文件定义的service名字 + rpc接口名

输出:

{
	"id": 1,
	"name": "大瓶可乐",
	"price": 6.5,
	"username": "tizi365",
	"address": "深圳市南山区西乡街道101号",
	"phone": "1300001111",
	"createTime": "2019-10-18T00:26:28+08:00"
}

结果以json的形式打印出来了。

5.2.通过micro web调试

启动micro web后台

 micro web

启动后,通过http://localhost:8082/访问后台。

接口测试页面:

遗留问题:客户端通过服务名为什么可以调用微服务,微服务部署在不同的机器怎么通信?后面的教程会解答这个问题。