Go语言学习 - RPC篇:gin框架的基础能力剖析

简介: gin是非常流行的一款HTTP框架。相较于原生的HTTP server,gin有很多改进点,主要在于3点:1. 上手简单,开发思路与原生HTTP基本一致2. 引入多个工具库,提高了开发效率3. 生态丰富,有许多开源的组件围绕着gin框架,我们将展开今天的话题。

gin框架

gin是非常流行的一款HTTP框架。相较于原生的HTTP server,gin有很多改进点,主要在于3点:

  1. 上手简单,开发思路与原生HTTP基本一致
  2. 引入多个工具库,提高了开发效率
  3. 生态丰富,有许多开源的组件

围绕着gin框架,我们将展开今天的话题。

// 请求结构体
type MyRequest struct {
    MyInfo string `form:"my_info" json:"my_info"`
}

// 响应结构体
type MyResponse struct {
    Errno  int    `json:"errno"`
    Result string `json:"result"`
    MyInfo string `form:"my_info" json:"my_info"`
}

// handler
func GetData(c *gin.Context) {
    var b MyRequest
    err := c.Bind(&b)
    if err != nil {
        c.JSON(http.StatusOK, MyResponse{
            Errno: 1,
        })
        return
    }

    c.JSON(http.StatusOK, MyResponse{
        Result: "my result",
    })
}

func main() {
    // gin server
    r := gin.Default()
    // 中间件
    r.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
        if err, ok := recovered.(string); ok {
            c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
        }
        c.AbortWithStatus(http.StatusInternalServerError)
    }))

    r.GET("/data", GetData)
    r.Run()
}

关键函数分析

路由注册

func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes 

func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes

Gin支持不同HTTP方法的路由注册,这对RESTful风格的代码编写带来了很大帮助。对于阅读代码的同学,可以快速地通过路由注册的列表,如r.GET("/data", GetData),找到对应的方法。

Handler函数

type HandlerFunc func(*Context)

Handler函数相较于标准库,看似从2个参数RequestResponseWriter 转变成了一个参数 Context,简化了调用,但其实对调用者来说,复杂度并没有降低:

  1. Context 包含大量数据结构
  2. Context 包含了大量的方法

对于一名新手,在摸索出一条最佳实践路径前,学习成本不增反减。这主要是因为gin.Context过重。从编程角度来看,这个对象包含了过多信息,是个大而杂的工具集。

但不可否认的是,gin里提供了很多工具都比原生库好用,例如参数绑定、返回JSON数据。

绑定参数Bind

func (c *Context) Bind(obj any) error

Bind中引入了泛型中的any特性,但使用和原先的interface{}完全一致:

调用方可以填任意值。但实际上,Bind中必须为一个指针类型的数据结构,但由于interface{}对入参没有任何编译时的限制,导致传参问题在运行时才会报错。

例如:

var b MyRequest
// 正确
c.Bind(&b)
// 错误:编译正确,但运行时异常
c.Bind(b)
c.Bind(1)

返回JSON数据

func (c *Context) JSON(code int, obj any)

该方法是返回HTTP状态码为code,并且将obj数据进行JSON序列化。

它的问题同Bind函数,这里就不再赘述了。

middleware

gin框架提供了middleware的能力,它可以为整个Server提供一个公共能力的封装。有了middleware,整个server处理请求变成了:

middleware预处理 -> handler -> middleware后处理

  • 常见的预处理如

    • 参数校验
    • 用户认证
    • panic恢复
  • 常见的后处理则如

    • 定制HTTP状态码
    • 异常数据封装

总体来说,middleware能帮助用户减少重复性代码的编写,沉淀为公共能力,堪称web编程的一大利器。

gin能力剖析

我们先看看gin的改进点:

  1. mux支持RESTful风格的接口定义
  2. gin.Context提供了大量的工具,简化解析、返回的相关代码
  3. middleware可解决大量重复性的代码

这三点对开发者带来了不小的帮助。但是,我们在使用gin作为开发工具时,仍有一些问题:

  1. 大量的参数类型都是interface{}类型的数据结构,需要调用方自行保证
  2. gin.Context过大,学习和理解的成本很高
  3. 不少问题要在运行时才能发现,编译期无能为力

这些弊端汇总起来,依旧是和handler的函数定义相关:没有充分地利用Go强类型、编译检查的特点,来提高程序的质量、降低开发者的学习成本

更简单的Handler框架

那么,什么样的Handler框架对用户来说效果更好呢?我这边给出一个函数签名:

 func BetterHandler(ctx context.Context, req *MyRequest) (rsp *MyResponse, err error)

我们依次看一下这些参数及其使用场景:

  1. ctx - 上下文,传递公共参数以及超时控制
  2. req - 请求的参数结构
  3. rsp - 响应的参数结构
  4. err - 错误信息

从整个RPC框架来看,它重点做了2件事:

  1. 自动将http参数解析到ctx和req中

    1. 解析规则按标准约定,如HTTP RESTful
    2. 一般是将Header里的信息放到ctx中,将URL+Body里的信息匹配到req结构体
  2. 自动将rsp和err对应到HTTP响应中

    1. err=nil时,认为请求成功,将rsp序列化后、填入到HTTP Body中
    2. err!=nil时,认为请求去失败,返回约定的协议(如异常状态码、异常HTTP的Body)

BetterHandler是一个很棒的编程体验:

  1. 无需关心解析参数与返回响应这两步的具体实现,统一由框架封装
  2. 函数的输入和输出都是强类型的,开发者有了一个明确的“模板”
  3. 将handler中的业务逻辑与RPC框架中协议部分解耦

也许你一下子无法快速理解,但反复对比下,你会逐渐体会到其中的精妙。但是,使用这个框架前,我们要解决以下两个问题:

  1. URL与Handler的匹配逻辑
  2. 怎么约定解析请求和返回响应的协议

小结

今天,我们一起看了gin框架的相关示例,编程体验比原生http库有了明显提升。gin的生态也给出了不少的优化方案或者插件,但由于框架本身限制,很难治本。

下一讲,我们将来看一个我最为推荐的RPC框架,分析一下其相关利弊。

目录
相关文章
|
8月前
|
安全 Java 编译器
对比Java学习Go——基础理论篇
本章介绍了Java开发者学习Go语言的必要性。Go语言以简单、高效、并发为核心设计哲学,摒弃了传统的类继承和异常机制,采用组合、接口和多返回值错误处理,提升了代码清晰度与开发效率。Go直接编译为静态二进制文件,启动迅速、部署简便,其基于Goroutine和Channel的并发模型相较Java的线程与锁机制更轻量安全。此外,Go Modules简化了依赖管理,与Java的Maven/Gradle形成鲜明对比,提升了构建与部署效率。
554 1
|
8月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
440 86
|
7月前
|
JavaScript 前端开发 Java
【GoWails】Go做桌面应用开发?本篇文章带你上手Wails框架!一步步带你玩明白前后端双端的数据绑定!
wails是一个可以让你使用Go和Web技术编写桌面应用的项目 可以将它看作Go的快并且轻量级的Electron替代品。可以使用Go的功能,并结合现代化UI完成桌面应用程序的开发
1333 5
|
7月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
367 2
|
7月前
|
开发框架 前端开发 Go
【GoGin】(0)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
Gin:Go语言编写的Web框架,以更好的性能实现类似Martini框架的APInet/http、Beego:开源的高性能Go语言Web框架、Iris:最快的Go语言Web框架,完备的MVC支持。
599 1
|
8月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
238 4
|
8月前
|
存储 Java 编译器
对比Java学习Go——程序结构与变量
本节对比了Java与Go语言的基础结构,包括“Hello, World!”程序、代码组织方式、入口函数定义、基本数据类型及变量声明方式。Java强调严格的面向对象结构,所有代码需置于类中,入口方法需严格符合`public static void main(String[] args)`格式;而Go语言结构更简洁,使用包和函数组织代码,入口函数为`func main()`。两种语言在变量声明、常量定义、类型系统等方面也存在显著差异,体现了各自的设计哲学。
293 0
|
9月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
561 0
|
9月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
392 0
|
9月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
441 0