deepwzh的技术笔记
  • Golang
  • Python
  • Cpp
杂项
前端开发
  • 计算机网络
  • 操作系统
  • 数据库
云原生
Devops
AIGC
关于作者
  • Golang
  • Python
  • Cpp
杂项
前端开发
  • 计算机网络
  • 操作系统
  • 数据库
云原生
Devops
AIGC
关于作者
  • Golang
  • 工具
  • 最佳实践
  • 学习资料
  • 代码生成
  • 安装与配置

    • 安装与配置
    • GVM
    • 私有仓库拉取
    • 安装与配置
  • 库

    • 库
    • Grpc-go
    • 命令行参数
    • 依赖注入库 wire
  • 编译器

    • 编译器
    • clean
    • 逃逸分析
    • 编译过程
    • 构建选项
    • 编译优化

      • 编译优化
      • PGO
  • 语言

    • 语言
    • defer
    • go:embed
  • 微服务框架

    • 微服务框架
    • Kratos

      • Kratos
      • 集成三方 web 框架
      • 项目结构体系

Kratos

集成三方 Web 框架

Kratos 大乱炖 —— 整合其他 Web 框架:Gin、FastHttp、Hertz

Kratos 默认的 RPC 框架使用的是 gRPC,支持 REST 和 protobuf 两种通讯协议。其 API 都是使用 protobuf 定义的,REST 协议是通过 grpc-gateway 转译实现的。使用 protobuf 定义 API 是具有极大优点的,具有很强的可读性、可维护性,以及工程性。工程再大,人员再多,也不会乱。

一切看起来都是很美好的。那么,问题来了,我们现在使用的是其他的 Web 框架,迁移就会有成本,有风险,不可能一下子就把历史存在的代码一口气转换过来到 Kratos 框架。那我可以在 Kratos 中整合其他的 Web 框架做过渡吗?答案是:可以的。Kratos 是基于的插件化设计,万物皆可插。

我整合了主流的 Gin 和 FastHttp。顺便把字节跳动的 Hertz 也尝试着整合了一下。整合之后,使用起来毫无违和感。

Gin

Gin 是用 Go 编写的一个 Web 应用框架,对比其它主流的同类框架,他有更好的性能和更快的路由。由于其本身只是在官方 net/http 包的基础上做的完善,所以理解和上手很平滑。

封装的代码如下:

**package** gin

**import** (
        "context""crypto/tls""net/http""net/url""time""github.com/gin-gonic/gin""github.com/go-kratos/kratos/v2/errors""github.com/go-kratos/kratos/v2/log""github.com/go-kratos/kratos/v2/middleware""github.com/go-kratos/kratos/v2/transport"
        kHttp "github.com/go-kratos/kratos/v2/transport/http"
)

**var** (
        _ transport.Server     = (_Server)(__nil__)
__        _ transport.Endpointer = (_Server)(nil)
)

**type** Server **struct** {
        _gin.Engine
__        server _http.Server

        tlsConf  _tls.Config
__        endpoint _url.URL
        timeout  time.Duration
        addr     **string**

        err **error**

        filters []kHttp.FilterFunc
        ms      []middleware.Middleware
        dec     kHttp.DecodeRequestFunc
        enc     kHttp.EncodeResponseFunc
        ene     kHttp.EncodeErrorFunc
}

**func** **NewServer**(opts ...ServerOption) _Server {
        srv := &Server{
__                timeout: __1__ _ time.Second,
                dec:     kHttp.DefaultRequestDecoder,
                enc:     kHttp.DefaultResponseEncoder,
                ene:     kHttp.DefaultErrorEncoder,
        }

        srv.init(opts...)

        **return** srv
}

**func** (s _Server) init(opts ...ServerOption) {
        s.Engine = gin.Default()

__        _**for**_ _, o := _**range**_ opts {
                o(s)
        }

        s.server = &http.Server{
                Addr:      s.addr,
                Handler:   s.Engine,
                TLSConfig: s.tlsConf,
        }

        s.endpoint, _ = url.Parse(s.addr)
}

_**func**_ (s _Server) Endpoint() (_url.URL, _**error**_) {
__        _**return**_ s.endpoint, __nil__
}

_**func**_ (s _Server) Start(ctx context.Context) **error** {
        log.Infof("[GIN] server listening on: %s", s.addr)

        **var** err **error****if** s.tlsConf != nil {
                err = s.server.ListenAndServeTLS("", "")
        } **else** {
                err = s.server.ListenAndServe()
        }
        **if** !errors.Is(err, http.ErrServerClosed) {
                **return** err
        }

        **return** nil
}

**func** (s _Server) Stop(ctx context.Context) _**error**_ {
__        log.Info(__"[GIN] server stopping"__)
__        _**return**_ s.server.Shutdown(ctx)
}

_**func**_ (s _Server) ServeHTTP(res http.ResponseWriter, req *http.Request) {
        s.Engine.ServeHTTP(res, req)
}
复制代码

应用的代码如下:

**package** gin

**import** (
    "context""math/rand""strconv""github.com/gin-gonic/gin"
    transport "github.com/tx7do/kratos-transport/gin"
    
    api "github.com/tx7do/kratos-transport/_example/api/protobuf"
)

**func** **main**() {
    ctx := context.Background()
    
    srv := transport.NewServer(
        WithAddress(":8800"),
    )
    
    srv.Use(gin.Recovery())
    srv.Use(gin.Logger())
    
    srv.GET("/login/_param"__, _**func**_(c _gin.Context) {
        **if** len(c.Params.ByName("param")) > 1 {
            c.AbortWithStatus(404)
            **return**
        }
        c.String(200, "Hello World!")
    })
    
    srv.GET("/hygrothermograph", **func**(c *gin.Context) {
        **var** out api.Hygrothermograph
        out.Humidity = strconv.FormatInt(**int64**(rand.Intn(100)), 10)
        out.Temperature = strconv.FormatInt(**int64**(rand.Intn(100)), 10)
        c.JSON(200, &out)
    })
    
    **if** err := srv.Start(ctx); err != nil {
        panic(err)
    }
    
    **defer** **func**() {
        **if** err := srv.Stop(ctx); err != nil {
            t.Errorf("expected nil got %v", err)
        }
    }()
}
复制代码

FastHttp

FastHTTP 是 golang 下的一个 http 框架,顾名思义,与原生的 http 实现相比,它的特点在于快,按照官网的说法,它的客户端和服务端性能比原生有了十倍的提升。

它的高性能主要源自于“复用”,通过服务协程和内存变量的复用,节省了大量资源分配的成本。

封装的代码如下:

**package** fasthttp

**import** (
        "context""crypto/tls""net/http""net/url""time""github.com/fasthttp/router""github.com/valyala/fasthttp""github.com/go-kratos/kratos/v2/errors""github.com/go-kratos/kratos/v2/log""github.com/go-kratos/kratos/v2/middleware""github.com/go-kratos/kratos/v2/transport"
        kHttp "github.com/go-kratos/kratos/v2/transport/http"
)

**var** (
        _ transport.Server     = (_Server)(__nil__)
__        _ transport.Endpointer = (_Server)(nil)
)

**type** Server **struct** {
        _fasthttp.Server

__        tlsConf  _tls.Config
        endpoint _url.URL
        timeout  time.Duration
__        addr     _**string**_

__        err _**error**_

        filters []FilterFunc
        ms      []middleware.Middleware
        dec     kHttp.DecodeRequestFunc
        enc     kHttp.EncodeResponseFunc
        ene     kHttp.EncodeErrorFunc

__        strictSlash _**bool**_
__        router      _router.Router
}

**func** **NewServer**(opts ...ServerOption) _Server {
        srv := &Server{
__                timeout:     __1__ _ time.Second,
                dec:         kHttp.DefaultRequestDecoder,
                enc:         kHttp.DefaultResponseEncoder,
                ene:         kHttp.DefaultErrorEncoder,
                strictSlash: true,
                router:      router.New(),
        }

        srv.init(opts...)

        **return** srv
}

**func** (s _Server) init(opts ...ServerOption) {
__        _**for**_ _, o := _**range**_ opts {
                o(s)
        }

        s.Server = &fasthttp.Server{
                TLSConfig: s.tlsConf,
                Handler:   FilterChain(s.filters...)(s.router.Handler),
        }

        s.router.RedirectTrailingSlash = s.strictSlash

        s.endpoint, _ = url.Parse(s.addr)
}

_**func**_ (s _Server) Endpoint() (_url.URL, _**error**_) {
__        _**return**_ s.endpoint, __nil__
}

_**func**_ (s _Server) Start(ctx context.Context) **error** {
        log.Infof("[fasthttp] server listening on: %s", s.addr)

        **var** err **error****if** s.tlsConf != nil {
                err = s.Server.ListenAndServeTLS(s.addr, "", "")
        } **else** {
                err = s.Server.ListenAndServe(s.addr)
        }
        **if** !errors.Is(err, http.ErrServerClosed) {
                **return** err
        }

        **return** nil
}

**func** (s _Server) Stop(_ context.Context) _**error**_ {
__        log.Info(__"[fasthttp] server stopping"__)
__        _**return**_ s.Server.Shutdown()
}

_**func**_ (s _Server) Handle(method, path **string**, handler fasthttp.RequestHandler) {
        s.router.Handle(method, path, handler)
}

**func** (s _Server) GET(path _**string**_, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodGet, path, handler)
}

_**func**_ (s _Server) HEAD(path **string**, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodHead, path, handler)
}

**func** (s _Server) POST(path _**string**_, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodPost, path, handler)
}

_**func**_ (s _Server) PUT(path **string**, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodPut, path, handler)
}

**func** (s _Server) PATCH(path _**string**_, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodPatch, path, handler)
}

_**func**_ (s _Server) DELETE(path **string**, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodDelete, path, handler)
}

**func** (s _Server) CONNECT(path _**string**_, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodConnect, path, handler)
}

_**func**_ (s _Server) OPTIONS(path **string**, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodOptions, path, handler)
}

**func** (s *Server) TRACE(path **string**, handler fasthttp.RequestHandler) {
        s.Handle(fasthttp.MethodTrace, path, handler)
}
复制代码

应用的代码如下:

**package** fasthttp

**import** (
    "context""encoding/json""math/rand""strconv""github.com/valyala/fasthttp"
    transport "github.com/tx7do/kratos-transport/fasthttp"
    
    api "github.com/tx7do/kratos-transport/_example/api/protobuf"
)
    
**func** **main**() {
    ctx := context.Background()
    
    srv := transport.NewServer(
        WithAddress(":8800"),
    )
    
    srv.GET("/login/_param"__, _**func**_(c _fasthttp.RequestCtx) {
        _, _ = c.WriteString("Hello World!")
    })
    
    srv.GET("/hygrothermograph", **func**(c *fasthttp.RequestCtx) {
        **var** out api.Hygrothermograph
        out.Humidity = strconv.FormatInt(**int64**(rand.Intn(100)), 10)
        out.Temperature = strconv.FormatInt(**int64**(rand.Intn(100)), 10)
        _ = json.NewEncoder(c.Response.BodyWriter()).Encode(&out)
    })
    
    **if** err := srv.Start(ctx); err != nil {
        panic(err)
    }
    
    **defer** **func**() {
        **if** err := srv.Stop(ctx); err != nil {
            t.Errorf("expected nil got %v", err)
        }
    }()
}
复制代码

Hertz

Hertz[həːts] 是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttp、gin、echo 的优势, 并结合字节跳动内部的需求,使其具有高易用性、高性能、高扩展性等特点,目前在字节跳动内部已广泛使用。 如今越来越多的微服务选择使用 Golang,如果对微服务性能有要求,又希望框架能够充分满足内部的可定制化需求,Hertz 会是一个不错的选择。

封装的代码如下:

**package** hertz

**import** (
        "context""crypto/tls""net/url""time"

        hertz "github.com/cloudwego/hertz/pkg/app/server""github.com/go-kratos/kratos/v2/log""github.com/go-kratos/kratos/v2/middleware""github.com/go-kratos/kratos/v2/transport"
        kHttp "github.com/go-kratos/kratos/v2/transport/http"
)

**var** (
        _ transport.Server     = (_Server)(__nil__)
__        _ transport.Endpointer = (_Server)(nil)
)

**type** Server **struct** {
        _hertz.Hertz

__        tlsConf  _tls.Config
        endpoint _url.URL
        timeout  time.Duration
__        addr     _**string**_

__        err _**error**_

        filters []kHttp.FilterFunc
        ms      []middleware.Middleware
        dec     kHttp.DecodeRequestFunc
        enc     kHttp.EncodeResponseFunc
        ene     kHttp.EncodeErrorFunc
}

_**func**_ _**NewServer**_(opts ...ServerOption) _Server {
        srv := &Server{
                timeout: 1 * time.Second,
                dec:     kHttp.DefaultRequestDecoder,
                enc:     kHttp.DefaultResponseEncoder,
                ene:     kHttp.DefaultErrorEncoder,
        }

        srv.init(opts...)

        **return** srv
}

**func** (s _Server) init(opts ...ServerOption) {
__        _**for**_ _, o := _**range**_ opts {
                o(s)
        }

        s.Hertz = hertz.Default(hertz.WithHostPorts(s.addr), hertz.WithTLS(s.tlsConf))

        s.endpoint, _ = url.Parse(s.addr)
}

_**func**_ (s _Server) Endpoint() (_url.URL, _**error**_) {
__        _**return**_ s.endpoint, __nil__
}

_**func**_ (s _Server) Start(ctx context.Context) **error** {
        log.Infof("[hertz] server listening on: %s", s.addr)

        **return** s.Hertz.Run()
}

**func** (s *Server) Stop(ctx context.Context) **error** {
        log.Info("[hertz] server stopping")
        **return** s.Hertz.Shutdown(ctx)
}
复制代码

应用的代码如下:

**package** hertz

**import** (
    "context""math/rand""strconv""github.com/cloudwego/hertz/pkg/app"
    transport "github.com/tx7do/kratos-transport/hertz"
    
    api "github.com/tx7do/kratos-transport/_example/api/protobuf"
)

**func** **TestServer**(t _testing.T) {
    ctx := context.Background()
    
    srv := transport.NewServer(
__        WithAddress(__"127.0.0.1:8800"__),
    )
    
__    srv.GET(__"/login/_param", **func**(ctx context.Context, c _app.RequestContext) {
__        _**if**_ __len__(c.Params.ByName(__"param"__)) > __1__ {
__            c.AbortWithStatus(__404__)
__            _**return**_
        }
__        c.String(__200__, __"Hello World!"__)
    })
    
__    srv.GET(__"/hygrothermograph"__, _**func**_(ctx context.Context, c _app.RequestContext) {
        **var** out api.Hygrothermograph
        out.Humidity = strconv.FormatInt(**int64**(rand.Intn(100)), 10)
        out.Temperature = strconv.FormatInt(**int64**(rand.Intn(100)), 10)
        c.JSON(200, &out)
    })
    
    **if** err := srv.Start(ctx); err != nil {
        panic(err)
    }
    
    **defer** **func**() {
        **if** err := srv.Stop(ctx); err != nil {
            t.Errorf("expected nil got %v", err)
        }
    }()
}
复制代码

参考资料

  • GIN - Github
  • Gin - Website
  • FastHTTP Github
  • fasthttp:高性能背后的惨痛代价
  • fasthttp 性能真的比标准库 http 包好很多吗?一文告诉你真相!
  • Hertz - Github
  • Hertz - Docs

https://juejin.cn/post/7172470244009590815

子文档

  • 集成三方web框架
  • 项目结构体系
Next
集成三方 web 框架