Go框架三件套详解 | 青训营笔记(下)

简介: Go框架三件套详解 | 青训营笔记(下)
编译运行

kitex 工具已经帮我们生成好了编译和运行所需的脚本:

编译:

$ sh build.sh

执行上述命令后,会生成一个 output 目录,里面含有我们的编译产物。

运行:
///

$ sh output/bootstrap.sh

执行上述命令后,Echo 服务就开始运行了。

编写客户端

有了服务端后,接下来就编写一个客户端用于调用刚刚运行起来的服务端。

首先,同样的,先创建一个目录用于存放我们的客户端代码:

$ mkdir client

进入目录:

$ cd client

创建一个 main.go 文件,然后就开始编写客户端代码了。

首先让我们创建一个调用所需的 client

import "example/kitex_gen/api/echo"
import "github.com/cloudwego/kitex/client"
...
c, err := echo.NewClient("example", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
  log.Fatal(err)
}

上述代码中,echo.NewClient 用于创建 client,其第一个参数为调用的 服务名(用于微服务中的服务发现),第二个参数为 options,用于传入参数, 此处的 client.WithHostPorts 用于指定服务端的地址。

发起调用
import "example/kitex_gen/api"
...
req := &api.Request{Message: "my request"}
resp, err := c.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
if err != nil {
  log.Fatal(err)
}
log.Println(resp)

上述代码中,我们首先创建了一个请求 req , 然后通过 c.Echo 发起了调用。

其第一个参数为 context.Context,通过通常用其传递信息或者控制本次调用的一些行为,你可以在后续章节中找到如何使用它。

其第二个参数为本次调用的请求。

其第三个参数为本次调用的 options ,Kitex 提供了一种 callopt 机制,顾名思义——调用参数 ,有别于创建 client 时传入的参数,这里传入的参数仅对此次生效。 此处的 callopt.WithRPCTimeout 用于指定此次调用的超时(通常不需要指定,此处仅作演示之用)。

在编写完一个简单的客户端后,我们终于可以发起调用了。

可以通过下述命令来完成这一步骤:

$ go run main.go

如果不出意外,可以看到类似如下输出:

2023/01/26 07:23:35 Response({Message:my request})
至此成功编写了一个 Kitex 的服务端和客户端,并完成了一次调用!

Hertz

安装命令行工具hz

首先,我们需要安装使用demo所需要的命令行工具 hz:

  • 确保 GOPATH 环境变量已经被正确地定义(例如 export GOPATH=~/go)并且将$GOPATH/bin添加到 PATH 环境变量之中(例如 export PATH=$GOPATH/bin:$PATH);请勿将 GOPATH 设置为当前用户没有读写权限的目录
  • 安装 hz:go install github.com/cloudwego/hertz/cmd/hz@latest

确定代码放置位置

  • 若将代码放置于$GOPATH/src下,需在$GOPATH/src下创建额外目录,进入该目录后再获取代码:

shell

复制代码

  $ mkdir -p $(go env GOPATH)/src/github.com/cloudwego
  $ cd $(go env GOPATH)/src/github.com/cloudwego
  • 若将代码放置于 GOPATH 之外,可直接获取

编写示例代码

  • 在当前目录下创建 hertz_demo 文件夹,进入该目录中
  • 创建 main.go 文件
  • main.go 文件中添加以下代码
package main
import (
    "context"
    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main() {
    h := server.Default()
    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
            ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
    })
    h.Spin()
}
  • 生成go.mod 文件


$ go mod init hertz_demo
  • 整理 & 拉取依赖
$ go mod tidy

运行示例代码

完成以上操作后,我们可以直接编译并启动 Server

$ go build -o hertz_demo && ./hertz_demo

如果成功启动,将看到以下信息

2023/01/28 21:47:09.626332 engine.go:567: [Debug] HERTZ: Method=GET    absolutePath=/ping   --> handlerName=main.main.func1 (num=2 handlers)
2023/01/28 21:47:09.629874 transport.go:84: [Info] HERTZ: HTTP server listening on address=[::]:8888

接下来,我们可以对接口进行测试

$ curl http://127.0.0.1:8888/ping
如果不出意外,可以看到类似如下输出
ruby
复制代码
$ {"message":"pong"}

到现在,我们已经成功启动了 Hertz Server,并完成了一次调用!

Hertz路由优先级

Hertz提供了参数路由和通配路由,路由的优先级为:静态路由>命名路由>通配路由

Hertz中间件

Hertz中间件的种类是多种多样的,简单分为两大类:

  • 服务端中间件
  • 客户端中间件
服务端中间件

image.png

中间件可以在请求更深入地传递到业务逻辑之前或之后执行:

  • 中间件可以在请求到达业务逻辑之前执行,比如执行身份认证和权限认证,当中间件只有初始化(pre-handle)相关逻辑,且没有和 real handler 在一个函数调用栈中的需求时,中间件中可以省略掉最后的.Next,如图中的中间件 B。
  • 中间件也可以在执行过业务逻辑之后执行,比如记录响应时间和从异常中恢复。如果在业务 handler 处理之后有其它处理逻辑( post-handle ),或对函数调用链(栈)有强需求,则必须显式调用.Next,如图中的中间件 C。
实现一个中间件
// 方式一
func MyMiddleware() app.HandlerFunc {
  return func(ctx context.Context, c *app.RequestContext) {
    // pre-handle
    // ...
    c.Next(ctx)
  }
}
// 方式二
func MyMiddleware() app.HandlerFunc {
  return func(ctx context.Context, c *app.RequestContext) {
    c.Next(ctx) // call the next middleware(handler)
    // post-handle
    // ...
  }
}

中间件会按定义的先后顺序依次执行,如果想快速终止中间件调用,可以使用以下方法,注意当前中间件仍将执行

  • Abort():终止后续调用
  • AbortWithMsg(msg string, statusCode int):终止后续调用,并设置 response中body,和状态码
  • AbortWithStatus(code int):终止后续调用,并设置状态码
Server级别中间件

Server级别中间件会对整个server的路由生效

h := server.Default()
h.Use(GlobalMiddleware())
路由组级别中间件

路由组级别中间件对当前路由组下的路径生效

h := server.Default()
group := h.Group("/group")
group.Use(GroupMiddleware())

或者

package main
import (
    "context"
    "fmt"
    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
)
func GroupMiddleware() []app.HandlerFunc {
    return []app.HandlerFunc{func(ctx context.Context, c *app.RequestContext) {
        fmt.Println("group middleware")
        c.Next(ctx)
    }}
}
func main() {
    h := server.Default(server.WithHostPorts("127.0.0.1:8888"))
    group := h.Group("/group", append(GroupMiddleware(),
        func(ctx context.Context, c *app.RequestContext) {
            fmt.Println("group middleware 2")
            c.Next(ctx)
        })...)
    // ...
    h.Spin()
}
使用默认中间件

Hertz 框架已经预置了常用的 recover 中间件,使用 server.Default() 默认可以注册该中间件

客户端中间件

客户端中间件可以在请求发出之前或获取响应之后执行:

  • 中间件可以在请求发出之前执行,比如统一为请求添加签名或其他字段。
  • 中间件也可以在收到响应之后执行,比如统一修改响应结果适配业务逻辑。
实现一个中间件

客户端中间件实现和服务端中间件不同。Client 侧无法拿到中间件 index 实现递增,因此 Client 中间件采用提前构建嵌套函数的形式实现,在实现一个中间件时,可以参考下面的代码。


func MyMiddleware(next client.Endpoint) client.Endpoint {
  return func(ctx context.Context, req *protocol.Request, resp *protocol.Response) (err error) {
    // pre-handle
    // ...
    err = next(ctx, req, resp)
    if err != nil {
      return
    }
    // post-handle
    // ...
  }
}
注册一个中间件
package main
import (
    "context"
    "fmt"
    "github.com/cloudwego/hertz/pkg/app/client"
    "github.com/cloudwego/hertz/pkg/protocol"
)
func MyMiddleware(next client.Endpoint) client.Endpoint {
    return func(ctx context.Context, req *protocol.Request, resp *protocol.Response) (err error) {
        // pre-handle
        // ...
        fmt.Println("before request")
        req.AppendBodyString("k1=v1&")
        err = next(ctx, req, resp)
        if err != nil {
            return
        }
        // post-handle
        // ...
        fmt.Println("after request")
        return nil
    }
}
func main() {
    client, _ := client.NewClient()
    client.Use(MyMiddleware)
    statusCode, body, err := client.Post(context.Background(),
        []byte{},
        "http://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fpost&status_code=302",
        &protocol.Args{})
    fmt.Printf("%d, %s, %s", statusCode, body, err)
}

总结

  • 了解Gorm/Kitex/Hertz是什么
  • 熟悉Gorm/Kitex/Hertz的基础使用
  • 通过实战案例分析将三个框架的使用串联起来


目录
相关文章
|
30天前
|
JSON 自然语言处理 网络协议
【字节跳动青训营】后端笔记整理-2 | Go实践记录:猜谜游戏,在线词典,Socks5代理服务器
猜数字游戏也算是入门一门编程语言必写的程序了。通过这个程序,我们可以熟悉Go语言中的输入输出、流程控制与随机函数的调用。
37 2
|
8天前
|
Go 数据库
Go语言之GORM框架(四)——预加载,关联标签与多态关联,自定义数据类型与事务(完结篇)
Go语言之GORM框架(四)——预加载,关联标签与多态关联,自定义数据类型与事务(完结篇)
|
8天前
|
SQL Go
Go语言之GORM框架(三)——Hook(钩子)与Gorm的高级查询
Go语言之GORM框架(三)——Hook(钩子)与Gorm的高级查询
|
8天前
|
SQL Go 数据库
Go语言之GORM框架(二) ——GORM的单表操作
Go语言之GORM框架(二) ——GORM的单表操作
|
8天前
|
SQL 关系型数据库 MySQL
Go语言之Gorm框架(一) ——初窥Gorm框架
Go语言之Gorm框架(一) ——初窥Gorm框架
|
30天前
|
存储 关系型数据库 MySQL
|
5天前
|
安全 测试技术 Go
Go语言在高并发场景下的应用
在当今互联网高速发展的时代,高并发已成为众多应用系统面临的核心问题。本文探讨了Go语言在高并发场景下的优势,并通过具体实例展示了其在实际应用中的效果和性能表现。
|
2天前
|
Go
go语言map、实现set
go语言map、实现set
9 0
|
2天前
|
Go
go语言数组与切片
go语言数组与切片
10 0