Advertisement

Goframe学习笔记(五)微服务

阅读量:

微服务

微服务是什么

微服务架构是一种软件设计模式,在现代应用开发中被广泛应用。它通过将一个复杂的应用系统分解为一组相互独立、功能明确的服务来实现系统的模块化设计与管理。每一项服务都专注于特定的功能模块,并能够独立运行以完成其任务。这一架构的主要目标是将复杂的系统分解为多个小型化且易于管理的服务单元

微服务如何实现

构建微服务时需要各服务之间能够实现通信。可以选择RPC(远程过程调用)、RESTful API以及消息队列等多种通信方式来实现。GoFrame支持基于开源GRPC框架来进行开发。GRPC是一种高性能的RPC框架,由Google开发,并基于Http/2标准结合Protocol Buffers序列化协议实现了高效的接口定义机制。

环境准备

1.Protocol Buffer 编译器安装

下载地址:发布版本 · protocolbuffers / protobuf (GitHub . com)

52cd84ae66b64451962462e1086a01a7.png

解压后将bin目录添加到环境变量

b137a32a5a9a46a6bb977aa0e513d9ff.png

查看是否成功安装

916bbe7e9a7f432faad0e356189d9ff7.png

2.Go协议编译插件安装,用于代码生成

复制代码
 go install google.golang.org/protobuf/cmd/protoc-gen-go@vlatest

    
 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

重要:将 %GOPATH%\bin 也添加到环境变量

注:若下载失败需设置代理

复制代码
    go env -w GOPROXY=https://goproxy.io,direct

后续操作基于模块支持

复制代码
    go env -w GO111MODULE=on

3.vscode安装protoc扩展

6a6947c2c7a24a51ae60bef45681a908.png

快速开始

使用命令gf init demo -u创建一个初始项目

manifest\config\config.yaml添加如下配置

复制代码
 grpc:

    
   name:             "demo"  # 服务名称
    
   address:          ":8000" # 自定义服务监听地址
    
   logPath:          "./log" # 日志存储目录路径
    
   logStdout:        true    # 日志是否输出到终端
    
   errorLogEnabled:  true    # 是否开启错误日志记录
    
   accessLogEnabled: true    # 是否开启访问日志记录
    
   errorStack:       true    # 当产生错误时,是否记录错误堆栈

manifest\protobuf下创建文件夹以及协议文件:user\v1\user.proto

目录结构如下:

复制代码

在协议文件user.proto中添加如下内容:

复制代码
 syntax = "proto3"; // 指定proto3语法编译

    
 ​
    
 package user; // 协议包名
    
 ​
    
 option go_package = "demo/api/user/v1"; // 生成go代码的文件的包名
    
 ​
    
 service User{
    
     rpc SayHello(HelloReq) returns (HelloRes) {} // rpc接口
    
 }
    
 ​
    
 // 请求体
    
 message HelloReq {
    
     string UserName = 1;
    
 }
    
 ​
    
 // 响应体
    
 message HelloRes {
    
     string Msg = 1;
    
 }

注:属性后的数字是在协议编译生成的二进制源码中的唯一标识符中使用以确保各属性的独特性。为了使数据量最小化建议尽可能小地设置这些数字并将它们控制在[1,15]范围内以避免不必要的复杂性和潜在冲突

在命令行输入如下指令根据协议文件生成Go代码:

复制代码
    gf gen pb

可以看到生成3个文件:

fba2d9eb8c05480a851ae5b1d4a9b9b9.png

爆红是因为不会自动导入mod,手动输入下面指令导入。

复制代码
    go mod tidy

controller\user\user.go 中添加SayHello方法的具体实现代码:

复制代码
 func (*Controller) SayHello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) {

    
     res = &v1.HelloRes{
    
     Msg: "Hello " +  req.UserName ,
    
     }
    
     return 
    
 }

cmd\cmd.go内容修改为:

复制代码
 package cmd

    
 ​
    
 import (
    
     "context"
    
     "demo/internal/controller/user"
    
 ​
    
     "github.com/gogf/gf/contrib/rpc/grpcx/v2"
    
 ​
    
     "github.com/gogf/gf/v2/os/gcmd"
    
 )
    
 ​
    
 var (
    
     Main = gcmd.Command{
    
     Name:  "main",
    
     Usage: "main",
    
     Brief: "start http server",
    
     Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
    
         
    
         s := grpcx.Server.New() // 创建grpc服务器
    
         user.Register(s) // 注册 user grpc服务
    
         s.Run() // 启动服务器
    
 ​
    
         return nil
    
     },
    
     }
    
 )

在 %GOPATH%\src 下创建文件夹及文件:client\client.go 并输入一下内容:

复制代码
 package main

    
 ​
    
 import (
    
     v1 "client/user/v1"
    
 ​
    
     "github.com/gogf/gf/contrib/rpc/grpcx/v2"
    
     
    
     "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 = v1.NewUserClient(conn)
    
     )
    
     res, err := client.SayHello(ctx, &v1.HelloReq{UserName: "World"})
    
     if err != nil {
    
     g.Log().Error(ctx, err)
    
     return
    
     }
    
     g.Log().Debug(ctx, "Response:", res.Msg)
    
 }

将之前协议编译生成的api文件复制进文件夹:

d027b5460a5e4af6a6a9068cfaeb6fd6.png

然后输入指令:

复制代码
 go mod init

    
 go mod tidy

运行服务端:

a1da1f81e405421a81a30d7dedb80bec.png

运行客户端:

aa0160ce872a4f689e89113ba9597bd3.png

可以看到回应:Hello World

注册发现

注冊是一種網路架構模式的存在,在於幫助各服務建立聯絡並實現通訊連線。在微服務架構中則扮演著關鍵角色,在此架構下服務通常會分佈於不同的主机、容器或虛拟機中,并且它們的位置與IP可能会發生 frequent 變化。Goframe社區提供的丰富的一階组件支援註冊功能。其中 most prominente is the widely known open-source 分布式 key-value storage system EtCD(EtCD)。EtCD is a versatile system 幾乎被廣泛應用於註冊 discovery 領域,并提供配置管理等多種功能场景;以下將示范如何使用 EtCD来进行註冊 discovery:

安装etcd:

访问途径:Releases(滑动至底部查看更多)

dcc0e584fc1e47ea96083e5b3b36c8b4.png

将下面这个目录添加到环境变量

e2857909554a463ca0adcd425fcbf703.png

验证是否安装成功:

复制代码
    etcd --version
8fcb3a157f444a8ca8db3bcc1fcb0e1d.png

在命令行输入如下指令启动etcd(etcd的默认地址为127.0.0.1:2379):

复制代码
    etcd

下载Goframe的etcd支持:

复制代码
    go get github.com/gogf/gf/contrib/registry/etcd/v2

cmd\cmd.go

复制代码
 package cmd

    
 ​
    
 import (
    
     "github.com/gogf/gf/contrib/registry/etcd/v2" // 导入etcd包
    
 ​
    
     "context"
    
     "demo/internal/controller/user"
    
 ​
    
     "github.com/gogf/gf/contrib/rpc/grpcx/v2"
    
 ​
    
     "github.com/gogf/gf/v2/os/gcmd"
    
 )
    
 ​
    
 var (
    
     Main = gcmd.Command{
    
     Name:  "main",
    
     Usage: "main",
    
     Brief: "start http server",
    
     Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
    
     
    
         grpcx.Resolver.Register(etcd.New("127.0.0.1:2379")) // 注册etcd
    
 ​
    
         s := grpcx.Server.New()
    
         user.Register(s)
    
         s.Run()
    
 ​
    
         return nil
    
     },
    
     }
    
 )
    
 ​

client\client.go

复制代码
 package main

    
 ​
    
 import (
    
     "github.com/gogf/gf/contrib/registry/etcd/v2" // 导包
    
 ​
    
     v1 "client/user/v1"
    
 ​
    
     "github.com/gogf/gf/contrib/rpc/grpcx/v2"
    
     
    
     "github.com/gogf/gf/v2/frame/g"
    
     "github.com/gogf/gf/v2/os/gctx"
    
 )
    
 ​
    
 func main() {
    
     grpcx.Resolver.Register(etcd.New("127.0.0.1:2379")) // 注册etcd
    
 ​
    
     var (
    
     ctx    = gctx.New()
    
         conn   = grpcx.Client.MustNewGrpcClientConn("demo")
    
         client = v1.NewUserClient(conn)
    
     )
    
     res, err := client.SayHello(ctx, &v1.HelloReq{UserName: "World"})
    
     if err != nil {
    
     g.Log().Error(ctx, err)
    
     return
    
     }
    
     g.Log().Debug(ctx, "Response:", res.Msg)
    
 }

运行服务端:

3d7ce7a556d843b2979cd784b6b486c5.png

运行客户端:

7002ab303e4f41c7b96bae946f7d57e2.png

可以看到回应Hello World

负载均衡

作为计算机网络技术的一种,在实际应用中实现资源的有效分配是其核心功能之一。当一个网络系统面临高流量和高负载的挑战时,在客户端端引入负载均衡技术能够有效地将用户的请求均匀分配至多台服务器上,在这种情况下不仅能够提升响应速度还能有效缓解单个服务器的压力并保证系统的整体可靠性

在Goframe框架中,提供了如下几种负载均衡策略:

负载均衡调度策略(Round Robin):通过依次循环地发送客户端请求至服务集群中的各个节点,并均匀地分担请求量

随机访问(Random):将客户端请求依次随机分发给服务集群中的某个节点。

最少连接数(LeastConnection)定义为将客户端请求按顺序分配至当前服务集群中并行度最低的节点。

权重访问机制(Weight):在服务注册阶段配置权重值;客户端根据配置的权重值进行请求转发;从而实现资源优化分配。

示例:

由于同一台设备上的单个IP地址只能绑定给一个服务进程,因此若要在一台服务器上运行多个服务程序,则需要对GRPC服务器监听的本地IP地址进行相应设置

注意:如果需要进一步了解此功能的具体实现细节,请参考官方文档或技术手册

复制代码
 grpc:

    
   name:             "demo"  # 服务名称
    
   address:          ""      # 自定义服务监听地址 ------ 修改为空
    
   logPath:          "./log" # 日志存储目录路径
    
   logStdout:        true    # 日志是否输出到终端
    
   errorLogEnabled:  true    # 是否开启错误日志记录
    
   accessLogEnabled: true    # 是否开启访问日志记录
    
   errorStack:       true    # 当产生错误时,是否记录错误堆栈

cmd\cmd.go修改为:

复制代码
 package cmd

    
 ​
    
 import (
    
 ​
    
     "context"
    
     "demo/internal/controller/user"
    
 ​
    
     "github.com/gogf/gf/contrib/rpc/grpcx/v2"
    
 ​
    
     "github.com/gogf/gf/v2/os/gcmd"
    
 )
    
 ​
    
 var (
    
     Main = gcmd.Command{
    
     Name:  "main",
    
     Usage: "main",
    
     Brief: "start http server",
    
     Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
    
 ​
    
         s := grpcx.Server.New()
    
         user.Register(s)
    
         s.Run()
    
 ​
    
         return nil
    
     },
    
     }
    
 )

使用如下命令启动Server1:

复制代码
    gf run main.go -p 8000

注:-p 用于指定网络服务监听端口(区别于grpc服务监听端口)

新开一个终端并使用如下命令启动Server2:

复制代码
    gf run main.go -p 8001

client\client.go修改为:

复制代码
 package main

    
 ​
    
 import (
    
 ​
    
 ​
    
     v1 "client/user/v1"
    
 ​
    
     "github.com/gogf/gf/contrib/rpc/grpcx/v2"
    
     
    
     "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", grpcx.Balancer.WithRandom())
    
         // 轮询
    
         //conn   = grpcx.Client.MustNewGrpcClientConn("demo", grpcx.Balancer.WithRoundRobin())
    
         // 最小连接数
    
         //conn   = grpcx.Client.MustNewGrpcClientConn("demo", grpcx.Balancer.WithLeastConnection())
    
         // 权重访问,服务注册时需要设置Weight参数。
    
         //conn   = grpcx.Client.MustNewGrpcClientConn("demo", grpcx.Balancer.WithWeight())
    
         client = v1.NewUserClient(conn)
    
     )
    
     for i := 0; i <10; i++ {
    
     res, err := client.SayHello(ctx, &v1.HelloReq{UserName: "World"})
    
     if err != nil {
    
         g.Log().Error(ctx, err)
    
         return
    
     }
    
     g.Log().Debug(ctx, "Response:", res.Msg)
    
     }
    
     
    
 }

可以看到在10次随机访问时:Serve1接收到7次请求,Serve2接收到3次请求

2e42fba1b7254c00bd432aad45deed3b.png
b9494fc84074420d96d69a687cc1f2fd.png

全部评论 (0)

还没有任何评论哟~