跳到主要内容
版本:2.10.x(Latest)

基本介绍

GoFrame 框架从 v2.4 版本开始全面支持微服务模式开发,提供了常用的微服务组件、开发工具和开发教程,帮助开发团队快速实现微服务架构转型。

简单示例

GoFrame 的微服务组件采用低耦合和通用化设计,组件化的使用方式支持大部分的微服务通信协议。在官方文档中,我们以 HTTPgRPC 协议为示例,介绍微服务的开发以及组件工具的使用。由于 HTTP Web 开发已经有比较丰富完善的独立章节介绍,因此微服务章节主要以 gRPC 为例进行讲解。

HTTP 微服务示例

https://github.com/gogf/gf/tree/master/example/registry/file

server.go

package main

import (
"github.com/gogf/gf/contrib/registry/file/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/os/gfile"
)

func main() {
gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))

s := g.Server(`hello.svc`)
s.BindHandler("/", func(r *ghttp.Request) {
g.Log().Info(r.Context(), `request received`)
r.Response.Write(`Hello world`)
})
s.Run()
}

可以看到,一个 HTTP 微服务端和普通的 Web Server 基本一致,但是顶部多了一行代码:

gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))

这行代码用于启用并配置当前服务使用的服务注册发现组件。在该示例中使用的 file.New(gfile.Temp("gsvc")) 是基于本地文件系统的服务注册发现组件,其中的 gfile.Temp("gsvc") 指定了存放服务信息文件的路径。例如在 Linux/ MacOS 系统下,该路径指向 /tmp/gsvc 目录。

注意

基于文件系统的注册发现组件仅适用于本地开发和测试,不支持跨节点通信。

在生产环境中,我们需要使用分布式的服务注册发现组件,例如 etcdpolariszookeeper 等。框架的社区组件库中已经提供了这些常用服务注册发现组件的实现。

在该示例中,我们为 Server 设置了名称 hello.svc,这是该服务在微服务架构中的唯一标识。服务名称用于服务间的识别和通信,是微服务治理的基础。当启用服务注册组件后, HTTP Server 在运行时会自动将自己的访问地址(IP和端口)注册到服务注册中心,使得其他服务能够通过服务名称发现并访问该服务。

client.go

package main

import (
"time"

"github.com/gogf/gf/contrib/registry/file/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
)

func main() {
gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))

client := g.Client()
for i := 0; i < 10; i++ {
ctx := gctx.New()
res, err := client.Get(ctx, `http://hello.svc/`)
if err != nil {
panic(err)
}
g.Log().Debug(ctx, res.ReadAllString())
res.Close()
time.Sleep(time.Second)
}
}

客户端通过 g.Client() 创建一个 HTTP Client 对象,并通过 http://hello.svc/ 地址访问服务端。其中的 hello.svc 是之前服务端绑定的微服务名称。当客户端使用微服务名称进行访问时,服务注册发现组件会自动在底层进行服务发现,查询该服务名称对应的实际服务地址,然后建立连接进行通信。

执行结果

先执行 server.go 启动服务端,然后再执行 client.go 通过服务名称请求服务。

执行后,客户端输出:

$ go run client.go
2023-03-14 20:22:10.006 [DEBU] {8054f3a48c484c1760fb416bb3df20a4} Hello world
2023-03-14 20:22:11.007 [DEBU] {6831cae08c484c1761fb416b9d4df851} Hello world
2023-03-14 20:22:12.008 [DEBU] {9035761c8d484c1762fb416b1e648b81} Hello world
2023-03-14 20:22:13.011 [DEBU] {a05a32588d484c1763fb416bc19ff667} Hello world
2023-03-14 20:22:14.012 [DEBU] {40fdea938d484c1764fb416b8459fc43} Hello world
2023-03-14 20:22:15.014 [DEBU] {686c9acf8d484c1765fb416b3697d369} Hello world
2023-03-14 20:22:16.015 [DEBU] {906a470b8e484c1766fb416b85b9867e} Hello world
2023-03-14 20:22:17.017 [DEBU] {28c7fd468e484c1767fb416b86e5557f} Hello world
2023-03-14 20:22:18.018 [DEBU] {90d2ad828e484c1768fb416bfcde738f} Hello world
2023-03-14 20:22:19.019 [DEBU] {d05559be8e484c1769fb416baad06f23} Hello world

服务端输出:

$ go run server.go
2023-03-14 20:20:06.364 [INFO] pid[96421]: http server started listening on [:61589]
2023-03-14 20:20:06.364 [INFO] openapi specification is disabled
2023-03-14 20:20:06.364 [DEBU] service register: &{Head: Deployment: Namespace: Name:hello.svc Version: Endpoints:10.35.12.81:61589 Metadata:map[insecure:true protocol:http]}

SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
hello.svc | default | :61589 | ALL | / | main.main.func1 |
------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
hello.svc | default | :61589 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------

2023-03-14 20:22:10.006 [INFO] {8054f3a48c484c1760fb416bb3df20a4} request received
2023-03-14 20:22:11.007 [INFO] {6831cae08c484c1761fb416b9d4df851} request received
2023-03-14 20:22:12.008 [INFO] {9035761c8d484c1762fb416b1e648b81} request received
2023-03-14 20:22:13.010 [INFO] {a05a32588d484c1763fb416bc19ff667} request received
2023-03-14 20:22:14.012 [INFO] {40fdea938d484c1764fb416b8459fc43} request received
2023-03-14 20:22:15.013 [INFO] {686c9acf8d484c1765fb416b3697d369} request received
2023-03-14 20:22:16.015 [INFO] {906a470b8e484c1766fb416b85b9867e} request received
2023-03-14 20:22:17.016 [INFO] {28c7fd468e484c1767fb416b86e5557f} request received
2023-03-14 20:22:18.017 [INFO] {90d2ad828e484c1768fb416bfcde738f} request received
2023-03-14 20:22:19.019 [INFO] {d05559be8e484c1769fb416baad06f23} request received

GRPC 微服务示例

https://github.com/gogf/gf/tree/master/example/rpc/grpcx/basic

helloworld.proto

gRPCHTTP 协议的一个显著区别是, gRPC 需要通过 protobuf 来定义 API 接口和数据结构。 Protobuf 是一种语言无关、平台无关的可扩展序列化数据结构,相比 JSONXML 更小更快,是 gRPC 的核心特性。

syntax = "proto3";

package protobuf;

option go_package = "github.com/gogf/gf/grpc/example/helloworld/protobuf";

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}

以上 protobuf 文件通过以下命令执行编译(请提前安装 protoc 工具):

gf gen pb

将会生成对应的 proto go 数据结构文件以及 grpc 接口文件:

helloworld.pb.go
helloworld_grpc.pb.go

controller.go

控制器用于实现 proto 文件中定义的接口方法。如果使用框架的标准化工程目录结构,该控制器代码文件也可以由框架的 gf gen pb 工具自动生成,开发者只需要填充对应方法的具体实现逻辑即可。

type Controller struct {
protobuf.UnimplementedGreeterServer
}

func Register(s *grpcx.GrpcServer) {
protobuf.RegisterGreeterServer(s.Server, &Controller{})
}

// SayHello implements helloworld.GreeterServer
func (s *Controller) SayHello(ctx context.Context, in *protobuf.HelloRequest) (*protobuf.HelloReply, error) {
return &protobuf.HelloReply{Message: "Hello " + in.GetName()}, nil
}

config.yaml

服务端配置文件,在该配置文件中指定了微服务的名称为 demo。微服务名称是服务间通信的唯一标识符。当未显式配置服务端的监听端口时,服务端会自动监听一个可用的本地端口。在微服务模式下,由于使用服务名称进行通信,服务端端口通常不需要显式指定,采用随机监听即可。

grpc:
name: "demo"
logPath: "./log"
logStdout: true
errorLogEnabled: true
accessLogEnabled: true
errorStack: true

server.go

gRPC 服务端的启动代码。当未显式指定服务端使用的服务注册发现组件时,服务端默认使用基于文件系统的注册发现组件,该组件仅适用于单机测试。其中的 controller.Register(s) 调用了我们通过工具生成的控制器注册方法,将具体的接口实现注册到服务端中。

package main

import (
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"github.com/gogf/gf/example/rpc/grpcx/basic/controller"
)

func main() {
s := grpcx.Server.New()
controller.Register(s)
s.Run()
}

client.go

gRPC 客户端的调用代码。在创建连接时需要指定服务端服务的名称。这里的服务名称为 demo,对应的是上面配置文件中设置的微服务名称。当未显式指定客户端使用的服务注册发现组件时,客户端默认使用基于文件系统的注册发现组件,该组件仅适用于单机测试。

package main

import (
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
"github.com/gogf/gf/example/rpc/grpcx/basic/protobuf"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)

func main() {
var (
ctx = gctx.New()
conn = grpcx.Client.MustNewGrpcClientConn("demo")
client = protobuf.NewGreeterClient(conn)
)
res, err := client.SayHello(ctx, &protobuf.HelloRequest{Name: "World"})
if err != nil {
g.Log().Error(ctx, err)
return
}
g.Log().Debug(ctx, "Response:", res.Message)
}

执行结果

服务端输出:

可以看到,服务端输出了一些 DEBU 级别的调试信息,用于显示服务注册的详细信息。由于没有显式指定服务端的监听端口,这里随机监听了本地端口 64517

$ go run server.go
2023-03-14 20:50:58.465 [DEBU] set default registry using file registry as no custom registry set
2023-03-14 20:50:58.466 [DEBU] service register: &{Head: Deployment: Namespace: Name:demo Version: Endpoints:10.35.12.81:64517 Metadata:map[protocol:grpc]}
2023-03-14 20:50:58.466 [INFO] pid[98982]: grpc server started listening on [:64517]
2023-03-14 20:52:37.059 {9898c809364a4c17da79e47f3e6c3b8f} /protobuf.Greeter/SayHello, 0.003ms, name:"World", message:"Hello World"

客户端输出:

客户端通过微服务名称访问,并成功接收到了服务端的响应。需要注意的是,客户端和服务端日志中的链路跟踪 ID( TraceID)是相同的( 9898c809364a4c17da79e47f3e6c3b8f),这表明它们属于同一个请求链路。 GoFrame 框架的微服务特性默认开启了链路跟踪能力,方便开发者进行请求链的追踪和问题排查。

$ go run client.go
2023-03-14 20:52:37.060 [DEBU] {9898c809364a4c17da79e47f3e6c3b8f} Response: Hello World

相关文档

📄️ 工程管理

详细介绍GoFrame框架微服务开发的工程管理规范,包括统一的工程目录结构设计、协议文件的组织管理、接口文件的生成和存放规则。系统讲解如何使用框架内置的gf gen pbentity命令自动生成数据表对应的protobuf协议文件,以及如何通过gf gen pb命令编译proto文件并自动生成接口文件和控制器文件。同时阐述微服务的启动控制机制、接口实现与注册的规范流程,以及标签自动注入和数据校验插件的集成使用方法,帮助开发者快速构建标准化的微服务应用。

📄️ 服务端配置

详细介绍GoFrame框架gRPC服务端的配置管理方法和最佳实践。系统讲解GrpcServerConfig配置对象的各个参数含义和使用方式,包括服务名称、监听地址、日志存储目录、错误日志记录、访问日志记录等核心配置项。提供完整的YAML格式配置模板示例,涵盖基础配置和日志组件扩展配置的详细参数说明。阐述配置文件的自动读取和映射机制,以及访问端口配置、服务注册地址配置等重要注意事项,帮助开发者实现灵活高效的gRPC服务端配置管理和日志管理。

📄️ 拦截器组件

详细介绍GoFrame框架中gRPC拦截器的使用方法和内置拦截器功能。讲解如何在服务端和客户端灵活配置和使用拦截器,包括使用grpcx.Server.ChainUnary添加服务端拦截器和grpcx.Client.ChainUnary添加客户端拦截器。系统介绍框架grpcx组件提供的一系列常用拦截器,涵盖链路跟踪支持、错误处理组件集成、服务端Panic自动捕获、nil响应对象支持、基于结构体标签的自动数据校验等功能,帮助开发者提高gRPC服务的可扩展性、稳定性和可维护性。

📄️ 服务注册发现

全面介绍GoFrame框架微服务架构中服务注册发现组件的使用方法和实现机制。详细讲解由gsvc组件管理的服务注册发现接口定义和社区实现,包括etcd、zookeeper、polaris、consul、nacos等多种主流注册中心的集成方式。通过HTTP和gRPC两种协议的完整示例,演示如何配置和使用服务注册发现功能,实现微服务的自动注册、服务发现和动态路由。阐述注册发现组件的启用方式、全局设置方法以及与框架各组件的协作机制,帮助开发者构建高可用、可扩展的分布式微服务系统。

📄️ 服务负载均衡

深入讲解GoFrame框架中客户端负载均衡组件gsel的使用方法和策略配置。详细介绍框架内置的四种负载均衡策略:LeastConnection(最小连接数)、Random(随机访问)、RoundRobin(轮询访问)和Weight(权重访问),阐述各策略的适用场景和特点。通过HTTP和gRPC两种协议的完整代码示例,演示如何在客户端配置和使用负载均衡策略,包括全局策略设置和请求级别策略选择。展示负载均衡在多服务端实例场景下的实际运行效果和请求分发机制,帮助开发者实现高可用、高性能的微服务架构。

📄️ 服务配置管理

全面介绍GoFrame框架的配置管理组件及其在微服务架构中的应用。详细讲解框架的解耦化和接口化设计,支持灵活对接多种第三方配置管理中心,包括Polaris、Apollo、Nacos、Consul等主流配置中心,以及Kubernetes ConfigMap容器编排配置。通过Polaris配置中心的完整实战示例,演示配置管理组件的启用方式、初始化配置、参数说明和使用方法。阐述包初始化的引入顺序要求、配置文件的读取机制、以及配置数据的获取和验证方法,帮助开发者实现微服务的集中化配置管理和动态配置更新能力。