项目地址:https://github.com/gone-io/gone
原文地址:https://github.com/gone-io/goner/blob/main/gin/benchmark_test.md
Gone-Gin 是基于 Gin 框架的扩展,它提供了更加便捷的依赖注入和参数绑定功能,使得开发者可以更加专注于业务逻辑的实现,而不必关心请求参数的解析和绑定过程。本文档将介绍 Gone-Gin 的实现原理以及与原生 Gin 的性能对比。
HTTP 注入器 (HttpInjector)
代理 (Proxy)
响应处理器 (Responer)
gone:"http,xxx"
标签指定参数来源响应处理器(Responer)是 Gone-Gin 的核心组件之一,它负责将处理函数的返回值转换为 HTTP 响应。相比原生 Gin 需要手动构造响应,Gone-Gin 的响应处理机制更加智能和灵活:
自动类型识别:
统一响应格式:
{"code": 0, "msg": "", "data": ...}
错误处理机制:
高级功能支持:
goos: darwin
goarch: arm64
cpu: Apple M1 Pro
我们设计了四个基准测试用例来比较 Gone-Gin 和原生 Gin 的性能:
type Req struct {
A int `json:"a,omitempty"`
B int `json:"b,omitempty"`
C int `json:"c,omitempty"`
D int `json:"d,omitempty"`
E string `json:"e,omitempty"`
F string `json:"f,omitempty"`
}
// Gone-gin 的 http 处理函数
func (c *ctr) httpHandler(in struct {
req *Req `gone:"http,body"`
}) string {
return "ok"
}
// 原生 Gin 处理函数
func originHandler(c *gin.Context) {
var req Req
err := c.ShouldBindJSON(&req)
if err != nil {
c.JSON(http.StatusBadRequest, err.Error())
return
}
c.String(http.StatusOK, "ok")
}
// BenchmarkProcessRequestWithInject 测试 使用 gone-gin 处理请求
func BenchmarkProcessRequestWithInject(b *testing.B) {
_ = os.Setenv("GONE_SERVER_SYS-MIDDLEWARE_DISABLE", "true")
_ = os.Setenv("GONE_SERVER_RETURN_WRAPPED-DATA", "false")
gone.
NewApp(gin.Load, tracer.Load).
Load(&ctr{}).
Run(func(httpHandler http.Handler) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
b.StopTimer()
request := buildRequest()
response := buildResponse()
b.StartTimer()
httpHandler.ServeHTTP(response, request)
}
})
}
// BenchmarkProcessRequestWithOriGin 测试 使用 原生 gin 处理请求
func BenchmarkProcessRequestWithOriGin(b *testing.B) {
engine := gin.New()
engine.
POST("/api/test", originHandler)
b.ResetTimer()
for n := 0; n < b.N; n++ {
b.StopTimer()
request := buildRequest()
response := buildResponse()
b.StartTimer()
engine.ServeHTTP(response, request)
}
}
// HTTP 注入器的核心实现 - 处理 Body 参数注入
func (s *httpInjector) injectBody(kind, key string, field reflect.StructField) (fn BindFieldFunc, err error) {
if s.isInjectedBody {
return nil, cannotInjectBodyMoreThanOnce(field.Name)
}
t := field.Type
switch t.Kind() {
case reflect.Struct, reflect.Map, reflect.Slice:
return func(ctx *gin.Context, structVale reflect.Value) error {
v := fieldByIndexFromStructValue(structVale, field.Index, field.IsExported(), field.Type)
body := reflect.New(t).Interface()
if err := ctx.ShouldBind(body); err != nil {
return NewParameterError(err.Error())
}
v.Set(reflect.ValueOf(body).Elem())
return nil
}, nil
case reflect.Pointer:
return func(ctx *gin.Context, structVale reflect.Value) error {
v := fieldByIndexFromStructValue(structVale, field.Index, field.IsExported(), field.Type)
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
if err := ctx.ShouldBind(v.Interface()); err != nil {
return NewParameterError(err.Error())
}
return nil
}, nil
// ... 其他类型处理
default:
return nil, unsupportedAttributeType(field.Name)
}
}
// 代理机制的核心实现 - 构建代理函数
func (p *proxy) buildProxyFn(x HandlerFunc, funcName string, last bool) gin.HandlerFunc {
m := make(map[int]*bindStructFuncAndType)
args, err := p.funcInjector.InjectFuncParameters(
x,
func(pt reflect.Type, i int, injected bool) any {
switch pt {
case ctxPointType, ctxType, goneContextPointType, goneContextType:
return placeholder{
Type: pt,
}
}
p.injector.StartBindFuncs()
return nil
},
func(pt reflect.Type, i int, injected bool) any {
m[i] = &bindStructFuncAndType{
Fn: p.injector.BindFuncs(),
Type: pt,
}
return nil
},
)
// ... 错误处理
fv := reflect.ValueOf(x)
return func(context *gin.Context) {
// ... 性能统计
parameters := make([]reflect.Value, 0, len(args))
for i, arg := range args {
// ... 参数处理
if f, ok := m[i]; ok {
parameter, err := f.Fn(context, arg)
if err != nil {
p.responser.Failed(context, err)
return
}
parameters = append(parameters, parameter)
} else {
parameters = append(parameters, arg)
}
}
// 调用用户函数
values := fv.Call(parameters)
// 处理返回值
var results []any
for i := 0; i < len(values); i++ {
// ... 返回值处理
}
p.responser.ProcessResults(context, context.Writer, last, funcName, results...)
}
}
BenchmarkProcessRequestWithInject-8 261738 4124 ns/op
BenchmarkProcessRequestWithOriGin-8 370796 3166 ns/op
BenchmarkProxyGinHandlerFunc-8 278386 3942 ns/op
BenchmarkCallOriGinHandlerFunc-8 387363 3016 ns/op
完整请求处理性能:
处理函数性能:
从测试结果可以看出,Gone-Gin 相比原生 Gin 有一定的性能损耗,在同一个数量级上,这主要是由于以下原因:
虽然 Gone-Gin 相比原生 Gin 有一定的性能损耗,但在大多数业务场景下,这种性能差距是可以接受的,因为它带来了更好的开发体验和更高的开发效率。如果需要进一步优化性能,可以考虑以下方面:
Gone-Gin 通过提供更便捷的依赖注入和参数绑定功能,大大提高了开发效率,虽然相比原生 Gin 有一定的性能损耗,但在大多数业务场景下,这种性能差距是可以接受的。在选择使用 Gone-Gin 还是原生 Gin 时,需要根据实际业务需求和性能要求来权衡。
对于追求极致性能的场景,可以考虑使用原生 Gin;而对于注重开发效率和代码可维护性的场景,Gone-Gin 是一个更好的选择。