• 云原生 | go-micro学习与实践



    微服务已成为市⾯流⾏且成熟的框架。备受众多企业⻘睐。
    以下是对⼀个主流的golang微服务框架go-micro的简单应⽤。


    组件介绍

    在这里插入图片描述

    组件定义

    安装官⽅的定义,go-micro组件⼀般可以分为:

    • Auth:服务调⽤之间的认证组件。默认不使⽤认证,需要在服务处理逻辑中⾃⾏实现。
    • Broker:消息通知组件。消息发布⽅会向所有的消息订阅⽅推送消息。默认情况下,订阅端会发布⼀个broker的
      http请求地址,并注册到registry,发布端通过registry获取到所有订阅的服务节点,然后通过http请求推送消息。
    • Cache:服务的缓存组件。默认使⽤本地内存缓存。
    • Cmd:命令⾏⼯具组件。可通过命令⾏传参的⽅式来发布微服务。执⾏service.Init()时,会使⽤命令⾏中的参数会覆
      盖服务中的初始配置。
    • Config:配置中⼼组件。默认储存在本地内存中。
    • Client:客户端。⽤于发起微服务请求。默认使⽤官⽅原⽣的mucp协议。
    • Server:服务端。⽤于发布微服务,接收微服务请求并处理。默认使⽤官⽅原⽣的mucp协议。
    • Store:数据存储组件。默认使⽤本地内存存储。
    • Registry:注册中⼼。存放微服务的注册信息。默认使⽤mdns协议。
    • Runtime:服务运⾏时组件。可以管理其他微服务的启停状态等。
    • Transport:微服务通信协议。默认采⽤http。
    • Profile:调试组件。可显示信息包括默认不配置。

    项⽬实践

    项⽬架构示意图

    在这里插入图片描述

    • web:⻚⾯及路由微服务。负责与外部系统交互。接收所有的请求,并分发到对应的微服务。
    • user:⽤户功能微服务。处理⽤户相关功能。
    • system:管理员功能微服务。处理管理相关功能。
    • etcd:服务注册中⼼
    • redis:数据存储中⼼
    • config:动态配置中⼼

    使⽤脚⼿架创建微服务

    使⽤官⽅脚⼿架可以⽣成模板代码。不同的参数,可⽣成不同环境代码,可选参数如下:
    在这里插入图片描述
    下载脚⼿架:

    go install github.com/go-micro/cli/cmd/go-micro@v1.1.1
    
    • 1

    创建微服务:

     go-micro new service system
     go-micro new service user
     go-micro new service web
    
    • 1
    • 2
    • 3

    创建⼯作区

    go work init system user web
    
    • 1

    ⾃动⽣成好的⽬录结构如下:
    在这里插入图片描述


    微服务配置

    简单配置

    go-micro框架提供了很多默认配置。有时只需要简单对配置就可以发布⼀个服务。

    func main() {
    // Create service
    srv := micro.NewService(
    micro.Name(service),
    micro.Version(version),
     )
    srv.Init()
    // Register handler
    pb.RegisterSystemHandler(srv.Server(), new(handler.System))
    // Run service
    if err := srv.Run(); err != nil {
    log.Fatal(err)
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    全量配置

    内容较多,因此单独拎出来了,可点击【go-micro全量配置】查看。

    配置执⾏顺序

    由上述配置可以看到有许多重复的配置。go-micro采⽤的是循环加载配置。即放在前⾯的配置会先执⾏,后⾯的配
    置后执⾏,后执⾏的配置会覆盖先执⾏的配置。
    ⽐如:micro.NewService(micro.Server(server.Registry(r1)), micro.Registry(r2)),后配置的注册中⼼r2,会覆盖
    服务中先配置的注册中⼼r1。


    功能开发

    以认证功能为例
    请添加图片描述

    定义接⼝拦截器

    web微服务采⽤gin框架作为web路由。采⽤中间件来进⾏统⼀拦截处理。

    router.Use(func(c *gin.Context) {
     // ⽩名单放⾏
     for _, s := range micro.Service.Options().Config.Get("auth",
    "whiteList").StringSlice(make([]string, 0)) {
     if c.Request.URL.Path == s {
     c.Next()
     return
     }
     }
    // 通过Auth组件对token进⾏认证
     token := c.GetHeader(micro.AuthHeader)
     if token == "" {
     token, _ = c.GetQuery("token")
     }
     account, err := micro.Service.Options().Auth.Inspect(token)
     expireAt, _ := strconv.ParseInt(account.Metadata["expireAt"], 10, 0)
     if expireAt-time.Now().Unix() < int64(micro.Service.Options().Config.Get("auth",
    "refreshTime").Int(0)) {
     // token要到期时,⾃动续约
    // 从Config组件中获取token相关配置
     expireTime := micro.Service.Options().Config.Get("auth", "expireTime").Int(0)
     newAccount, err := micro.Service.Options().Auth.Generate(account.ID,
    auth.WithType("user"),
     auth.WithMetadata(map[string]string{
     "createAt": strconv.FormatInt(time.Now().Unix(), 10),
     "expireAt":
    strconv.FormatInt(time.Now().Add(time.Second*time.Duration(expireTime)).Unix(), 10),
     }))
     if err != nil {
     c.Redirect(http.StatusMovedPermanently, "/login")
     c.Abort()
     }
     newToken, err :=
    micro.Service.Options().Auth.Token(auth.WithExpiry(time.Second*time.Duration(expireTime
    )), auth.WithCredentials(newAccount.ID, newAccount.Secret))
     if err != nil {
     c.Redirect(http.StatusMovedPermanently, "/login")
     c.Abort()
     }
     token = newToken.AccessToken
     }
     c.Set(micro.AuthHeader, token)
    // 认证失败,跳转到登录⻚⾯
     if err != nil {
     c.Redirect(http.StatusMovedPermanently, "/login")
     c.Abort()
     }
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    定义接⼝路由

    在gin路由中注册/login路由。然后在路由处理函数中,使⽤Client调⽤user微服务,完成注册

    protobuf消息通信

    protocol buffers是⼀个消息传输协议。⽤于微服务之前的消息通信。

    • protobuf定义

    定义proto⽂件

    option go_package = "./;user";
    service User { // 服务名称
    rpc Login(LoginRequest) returns (LoginResponse) {} // ⽅法名称
    rpc Info(InfoRequest) returns (InfoResponse) {}
    }
    message LoginRequest {// ⽅法请求参数
    string username = 1;
    string password = 2;
    }
    message LoginResponse {// ⽅法响应参数
    string token = 1;
    }
    message InfoRequest {
    }
    message InfoResponse {
    string Id = 1;
    string Username = 2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    根据proto⽂件⽣成go代码
    执⾏Makefile⾥⾯的init和proto
    init:安装⽣成代码的必要软件。
    proto:根据proto⽂件地址,来⽣成代码。
    在这里插入图片描述

    .PHONY: init
    init:
     @go get -u google.golang.org/protobuf/proto
     @go install github.com/golang/protobuf/protoc-gen-go@latest
     @go install github.com/asim/go-micro/cmd/protoc-gen-micro/v4@latest
    .PHONY: proto
    proto:
     @protoc --proto_path=. --micro_out=./proto/user --go_out=:./proto/user
    proto/user/user.proto
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 服务端处理

    实现接⼝具体处理逻辑
    实现xx.pb.mico.go⽂件中的"{服务名称}Handler"接⼝

    type UserHandler interface {
     Login(context.Context, *LoginRequest, *LoginResponse) error
     Info(context.Context, *InfoRequest, *InfoResponse) error
    }
    
    • 1
    • 2
    • 3
    • 4

    将服务注册到go-micro中进⾏发布
    在main⽅法中,调⽤xxx.pb.micro.go⽂件中的"Register{服务名称}Handler"⽅法。“server.Server"参数为定义的
    微服务,”{服务名称}Handler"参数为"{服务名称}Handler"接⼝具体实现类。

    func RegisterUserHandler(s server.Server, hdlr UserHandler, opts
    ...server.HandlerOption) error {
     type user interface {
     Login(ctx context.Context, in *LoginRequest, out *LoginResponse) error
     Info(ctx context.Context, in *InfoRequest, out *InfoResponse) error
     }
     type User struct {
     user
     }
     h := &userHandler{hdlr}
     return s.Handle(s.NewHandler(&User{h}, opts...))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 客户端处理

    客户端通过Client.Call对对应的微服务发起请求。

    loginRsp := &user.LoginResponse{}
     if err := micro.Service.Options().Client.Call(context.Background(),
    micro.Service.Options().Client.NewRequest("user", "User.Login",
    &user.LoginRequest{Username: username, Password: password}), loginRsp); err != nil {
     ctx.HTML(422, "user/login.html", gin.H{
     "message": err.Error(),
     })
     return
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在下期内容中,我们会从源码角度深入分析go-micro,敬请期待吧~

  • 相关阅读:
    SpringBoot+Jwt+Redis
    ELK Stack日志采集架构详解
    SSM注解大全
    从入门到精通:深入了解CSS中的Grid网格布局技巧和应用!
    实习项目总结-Pandroid仿真数据管理平台
    容器批量计算调度引擎Volcano v1.2版本后的资源预留
    python中pdf转图片的操作方法二
    基于JavaSwing开发教学软件(在线考试测评)+报告+说明书 大作业 毕业设计
    STM32CubeMX 下载和安装 详细教程
    LeetCode 169. 多数元素
  • 原文地址:https://blog.csdn.net/CBGCampus/article/details/127420785