使用http框架编写服务

举报
码乐 发表于 2024/02/10 09:35:08 2024/02/10
【摘要】 当小草遇到强风,它随风而摇摆。当水在杯子中,它是杯子形状。当水在河里,它是河床的形状。 3 使用框架的http服务 和 路由在任何编程语言中,完全实现http的全部功能,都不是一件容易的事情。 3.1 框架简介这里不是专门介绍框架gin的,只对需要用的功能做介绍如下:框架性能评估指标 (1):在一定的时间内实现的总调用数,越高越好 (2):单次操作耗时(ns/op),越低越好 ...
当小草遇到强风,它随风而摇摆。
当水在杯子中,它是杯子形状。
当水在河里,它是河床的形状。

3 使用框架的http服务 和 路由

在任何编程语言中,完全实现http的全部功能,都不是一件容易的事情。

3.1 框架简介

  • 这里不是专门介绍框架gin的,只对需要用的功能做介绍如下:

框架性能评估指标

    (1):在一定的时间内实现的总调用数,越高越好
    (2):单次操作耗时(ns/op),越低越好
    (3):堆内存分配 (B/op), 越低越好
    (4):每次操作的平均内存分配次数(allocs/op),越低越好

性能横向对比链接,对比了tornado和gin的性能

    https://github.com/XFrankly/Comparative-performance-gin-vs-tornado

3.1.1 web框架使用步骤

    1 设计 API 端点。
    2 为您的代码创建一个文件夹。
    3 创建数据。
    4 编写一个处理程序以返回所有项目。
    5 编写一个处理程序来添加一个新项目。
    6 编写一个处理程序来返回一个特定的项目。

3.1.2 引擎介绍 gin.Engine

它使用GO的内置 http.Server 本质上是对 http server的封装,使用更便捷
gin.Default() 将创建一个默认的Engine对象,包括了Logger 和 Recovery

Logger 用于日志处理

Recovery确保单个请求发生panic时记录异常堆栈,输出统一错误信息

        Engine的定义
        func Default() *Engine {
            engine := New()
            engine.Use(Logger(), Recovery())  //使用两个中间件
            return engine
        }

而 gin.New() 将不使用默认的 Logger 和 Recovery
不使用默认的中间件

    使用
    r := gin.New()
    代替
    // Default 使用 Logger 和 Recovery 中间件
    r := gin.Default()

如下示例 //结构体 ServerGroup 服务和路由分组用到

    type ServerGroup struct {
        *gin.Engine
    }

创建一个引擎 并限制在写入磁盘之前多少内存占用 内存使用

 func MakeNewEngine() *gin.Engine {
        me := gin.New() 
        return me
    }

作为Server的构造器 GroupServer的构造器 最后用作 链式调用

   func NewServers(e *gin.Engine) *ServerGroup {
        s := &ServerGroup{Engine: MakeNewEngine()}
        if e != nil {
            s.Engine = e
        }
        return s
    }

3.1.4 路由和上下文处理

RouterGroup是对路由树的包装,所有路由规则最终都是由它管理,Engine结构体继承了RouterGroup,所以在Engine直接具备了RouterGroup所有的路由管理功能。
RouterGroup包括一个Engine指针

        type Engine struct {
            RouterGroup
            ....
        }
        type RouterGroup struct {
            ...
            engine *Engine
            ...
        }

RouterGroup 实现了IRouter接口,暴露了一系列路由方法,这些方法最终通过调用Engine.addRoute方法将请求处理器挂接到路由树

        GET(string, ...HandlerFunc) IRoutes
        POST...
        ...
        //匹配所有的HTTP Method
        Any(string, ...HandlerFunc) IRoutes

RouterGroup有一个前缀路径属性,它将所有子路径都加上前缀 再放入路由树。
有了这个前缀就可以实现URL分组功能。

Engine对象内嵌的RouterGroup的对象前缀路径是/ 它表示根路径
RouterGroup支持分组嵌套,使用Group方法就可让分钟下面再挂分组。

路由树,路由规则分成最多9棵前缀树,每个 HTTP Method对应一颗 前缀树

    树的节点按 URL中的/符号层级划分, 
    URL支持 :name形式的名称匹配,
    还支持 *subpath形式路径通配符
    path = /book/:id
        match  /book/13

Context负责处理请求的上下文信息。它是所有请求处理器的入口参数,负责处理请求中的URL参数,Cookie,Header等

// 路由注册,组名称 查询列表 具体信息

    func SetupRouters(sg *gin.RouterGroup) {

        lives := sg.Group("/resource")
        lives.GET("/", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{
                "message": "success", "data": "welcome!", "code": 200,
            })
        }) 
        lives.GET("/list", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{
                "message": "success", "data": vdata, "code": 200,
            })
        })

        lives.GET("/:id", func(c *gin.Context) {
            id := c.Param("id")
            var data map[string]string
            for _, v := range vdata {
                if id == v["id"] {
                    data = v
                }
            }
            c.JSON(http.StatusOK, gin.H{
                "message": "success", "data": data, "code": 200,
            })
        })

    }

绑定路由到引擎

    func NewRouter() http.Handler {
        RS.Routers()
        SetupRouters(Router)

        return RS
    }

3.1.5 Json响应

Gin 使用 encoding/json 作为默认的 json 包,但是你可以在编译中使用标签将其修改为 jsoniter。

    go build -tags=jsoniter .

使用内置的响应方式

    c.JSON(http.StatusOK, gin.H{
        "message": "success", "data": "welcome!", "code": 200,
    }

3.2 后端API模块一般采用下述划分方法:

协议处理 https的请求交互处理层,可支持多种协议 thrift,gRPC,http/https 中间件开发

中间件有两部分:

            第一部分是在初始化中间件时执行一次。这就是你设置所有全局对象、逻辑等的地方。每个应用程序生命周期发生一次的所有事情。

            第二部分是对每个请求执行的。例如,您只需将“全局”数据库对象注入上下文中的数据库中间件。一旦它在上下文中,您可以从其他中间件和您的处理程序函数中检索它。

Controller,

        与上述类似,服务入口,负责处理路由,参数校验,请求转发。

Logic/Service,

        逻辑(服务)层,一般是业务逻辑的入口,可以认为从这里开始,所有的请求参数一定是合法的。业务逻辑和业务流程也都在这一层中。常见的设计中会将该层称为 Business Rules。

DAO/Repository,

        这一层主要负责和数据、存储打交道。将下层存储以更简单的函数、接口形式暴露给 Logic 层来使用。负责数据的持久化工作。

Routers 内置服务路由,用于健康检查的ping 和 内置记录器 Recovery将 recover任何panic,如果有panic 将写入500

    func (that *ServerGroup) Routers() {

        that.Use(gin.Logger())
        that.Use(gin.Recovery())

        that.GET("/", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{
                "message": "pong",
            })
        })

    }

3.3 服务封装: 使用 默认的路由封装和寻址

这些服务通过使用定义良好的API 相互通信。即使是单个业务操作也可能导致 所有服务直接多个点对点调用。一个常见通信模式是使用充当协调器的集中式服务。

它确认所有传入的请求并将操作委托给相应服务。 它还管理整个业务交易的工作流程,每个服务只是完成一个操作,并不了解整个流程。

创建服务 HTTPServer,并处理可能的超时,绑定路由到服务引擎

func NewHttpServer(handler http.Handler) *http.Server {

    return &http.Server{
        Addr:           Ports,
        Handler:        handler,
        ReadTimeout:    500 * time.Second,
        WriteTimeout:   100 * time.Second,
        MaxHeaderBytes: 1000 << 20,
    }
}

启动服务

func Start() {
    var Servers = NewHttpServer(NewRouter())
    log.Fatalf(Servers.ListenAndServe().Error())
}

func main() {
    Start()
}

4 使用该服务

主界面

     http://127.0.0.1:3040/
        
        message "pong"

欢迎界面

     http://127.0.0.1:3040/resource

        "message": "success", 
        "data": "welcome!", 
        "code": 200,

查询列表

    http://127.0.0.1:3040/resource/list
    
    {"code":200,
    "data":[
                {"id":"en00029","name":"infinsh war III","path":"video.asdaliyun.com/oss/usubda","times":"180min"},
                {"id":"en00028","name":"infinsh war II","path":"video.asdaliyun.com/oss/usubde","times":"170min"}],
    "message":"success"}

具体信息

     http://127.0.0.1:3040/resource/en00029
     
    {"code":200,
    "data":
        {"id":"en00029","name":"infinsh war III","path":"video.asdaliyun.com/oss/usubda","times":"180min"},
    "message":"success"}

5 小结

使用框架的方式将帮助我们处理很多事情,比如序列号返回,传入参数绑定,它更有利于CS架构的交互。这将在下一节中得到体现。

我们从下一节正式开始完整的服务旅程。

本节代码地址

         https://github.com/hahamx/examples/blob/main/tcps/2_with_http_handler/http_gin.go
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。