使用http框架编写服务
当小草遇到强风,它随风而摇摆。
当水在杯子中,它是杯子形状。
当水在河里,它是河床的形状。
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
- 点赞
- 收藏
- 关注作者
评论(0)