侧重记录在听课过程中自己的一些思考和学习路径
ep9 走进 HTTP 协议
ep9.1 Postman 14:01
熟悉操作
ep9.2 请求流程
ep10 HTTP 框架的设计与实现
ep10.1 分层设计 1:20(与ep9.2联系)
七层网络模型,也称为OSI(Open Systems Interconnection)模型,四层网络模型是一种简化版的网络模型,也称为TCP/IP模型或传输层/网络层模型。与七层网络模型相比,它将网络通信分为四个层次,更加精简,但仍能有效描述网络通信的基本过程。其中每一层分别有自己相对应的网络协议。
用户层-中间件层(预处理和后处理)-路由层-协议层-网络层
common层
层与层之间通过接口联系
下面详细介绍了各层
ep10.2 中间件设计 12:33
func main() {
// 创建一个新的服务器实例 h
h := server.New()
// 定义处理 "/login" 路由的处理器
h.POST("/login", func(c context.Context, ctx *app.RequestContext) {
// 打印收到的请求信息
logs.Infof("Received RawRequest: %s", ctx.Request.RawRequest())
// 执行一些业务逻辑
ctx.JSON(200, "OK")
// 打印响应信息
logs.Infof("Send RawResponse: %s", ctx.Response.RawResponse())
})
// 定义处理 "/logout" 路由的处理器
h.POST("/logout", func(c context.Context, ctx *app.RequestContext) {
// 打印收到的请求信息
logs.Infof("Received RawRequest: %s", ctx.Request.RawRequest())
// 执行一些业务逻辑
ctx.JSON(200, "OK")
// 打印响应信息
logs.Infof("Send RawResponse: %s", ctx.Response.RawResponse())
})
// 启动服务器
h.Spin()
}
func main() {
// 创建一个新的服务器实例 h
h := server.New()
// 定义处理 "/login" 路由的处理器
h.POST("/login", func(c context.Context, ctx *app.RequestContext) {
// 打印收到的请求信息
logs.Infof("Received RawRequest: %s", ctx.Request.RawRequest())
// 执行一些业务逻辑
ctx.JSON(200, "OK")
// 打印响应信息
logs.Infof("Send RawResponse: %s", ctx.Response.RawResponse())
})
// 定义处理 "/logout" 路由的处理器
h.POST("/logout", func(c context.Context, ctx *app.RequestContext) {
// 打印收到的请求信息
logs.Infof("Received RawRequest: %s", ctx.Request.RawRequest())
// 执行一些业务逻辑
ctx.JSON(200, "OK")
// 打印响应信息
logs.Infof("Send RawResponse: %s", ctx.Response.RawResponse())
})
// 启动服务器
h.Spin()
}
两段代码都实现了一个简单的应用程序,包含了两个路由处理器(login和logout)。区别在于第一段代码在每个处理器中都包含了日志打印代码,导致代码冗余较高;而第二段代码引入了中间件函数,将日志打印与业务逻辑分离,减少了代码冗余,使得代码结构更加清晰和灵活。使用中间件可以提高代码的可维护性和可扩展性,并将公共逻辑抽象出来,使得处理器中只关注业务逻辑,而不用关心其他非业务相关的代码。
ep11 性能修炼之道
课后作业
- 为什么HTTP框架要分层设计?
HTTP框架之所以采用分层设计,是为了将不同的功能和责任划分到不同的模块中,使得框架的设计更加清晰、灵活和可扩展。分层设计有助于降低代码的耦合性,提高代码的复用性和可维护性,同时也有利于团队合作开发。
优势:
- 模块化:分层设计将功能划分为不同的模块,使得每个模块的职责清晰,易于维护和扩展。
- 可复用性:不同的模块可以独立设计,使得某些功能可以在多个地方重复使用,提高了代码的复用性。
- 易于测试:分层设计将功能分散在不同模块中,可以更容易地对每个模块进行单元测试和集成测试。
- 团队协作:分层设计明确了每个模块的职责,使得团队成员可以独立开发不同的模块,提高了团队协作效率。
劣势:
- 复杂性:分层设计可能会导致框架的结构变得更加复杂,需要合理划分模块之间的依赖关系。
- 性能:分层设计可能会引入一定的性能开销,因为需要在不同模块之间进行通信和数据传递。
- 现有开源社区HTTP框架的优势与不足:
不同的开源社区HTTP框架具有不同的优势和不足,以下是一些常见的优势和不足:
优势:
- 高性能:一些HTTP框架专注于性能优化,使用高效的算法和数据结构,以提供卓越的性能。
- 简单易用:一些HTTP框架设计简洁,易于上手使用,适合快速开发和小型项目。
- 功能丰富:一些HTTP框架提供了丰富的功能和组件,涵盖了路由、中间件、模板引擎、ORM等,适用于构建复杂的Web应用。
- 社区支持:受欢迎的HTTP框架通常有活跃的开源社区,可以获得更多的支持和社区贡献。
不足:
- 学习曲线:一些HTTP框架可能有较大的学习曲线,需要较长时间来熟悉其设计和使用方式。
- 过度设计:一些HTTP框架可能过度设计,引入了许多不必要的复杂性和功能,增加了学习和使用的难度。
- 性能瓶颈:一些HTTP框架可能在高并发场景下存在性能瓶颈,需要进一步优化。
- 兼容性:一些HTTP框架可能对不同版本的Go语言或不同操作系统的兼容性存在一定问题。
- 中间件还有没有其他实现方式?可以用伪代码说明。
除了常见的基于函数调用的中间件实现方式外,还可以使用类似于装饰器模式的方式来实现中间件。在这种方式下,我们可以定义一个接口或函数类型,用于表示中间件的行为,然后通过装饰器模式将中间件逐层包装,从而实现对请求和响应的处理。
伪代码示例:
// 中间件接口
type Middleware interface {
Handle(next http.Handler) http.Handler
}
// 实现一个具体的中间件
type LoggingMiddleware struct{}
func (m LoggingMiddleware) Handle(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Received request:", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
log.Println("Sent response:", r.Method, r.URL.Path)
})
}
// 使用装饰器模式包装中间件
func UseMiddleware(h http.Handler, middleware ...Middleware) http.Handler {
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i].Handle(h)
}
return h
}
// 使用示例
func main() {
router := http.NewServeMux()
router.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
// 应用中间件
loggingMiddleware := LoggingMiddleware{}
handler := UseMiddleware(router, loggingMiddleware)
http.ListenAndServe(":8080", handler)
}
- 完成基于前缀路由树的注册与查找功能?可以用伪代码说明。
前缀路由树(Prefix Tree)是一种高效的路由匹配数据结构,它可以用于快速查找匹配某个URL路径的处理器。
伪代码示例:
// 路由节点
type RouteNode struct {
path string
handler http.HandlerFunc
children map[string]*RouteNode
}
// 创建路由节点
func NewRouteNode(path string, handler http.HandlerFunc) *RouteNode {
return &RouteNode{
path: path,
handler: handler,
children: make(map[string]*RouteNode),
}
}
// 注册路由
func (node *RouteNode) AddRoute(path string, handler http.HandlerFunc) {
if path == "" {
node.handler = handler
return
}
parts := strings.SplitN(path, "/", 2)
childPath := parts[0]
remainingPath := ""
if len(parts) > 1 {
remainingPath = parts[1]
}
child, ok := node.children[childPath]
if !ok {
child = NewRouteNode(childPath, nil)
node.children[childPath]
= child
}
child.AddRoute(remainingPath, handler)
}
// 查找路由
func (node *RouteNode) FindRoute(path string) (http.HandlerFunc, bool) {
if path == "" {
return node.handler, true
}
parts := strings.SplitN(path, "/", 2)
childPath := parts[0]
remainingPath := ""
if len(parts) > 1 {
remainingPath = parts[1]
}
child, ok := node.children[childPath]
if !ok {
return nil, false
}
return child.FindRoute(remainingPath)
}
// 使用示例
func main() {
router := NewRouteNode("", nil)
router.AddRoute("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
router.AddRoute("/about", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("This is the about page."))
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
handler, ok := router.FindRoute(r.URL.Path)
if !ok {
http.NotFound(w, r)
return
}
handler(w, r)
})
http.ListenAndServe(":8080", nil)
}
- 路由还有没有其他的实现方式?
除了基于前缀路由树的实现方式外,还有其他常见的路由实现方式,包括正则表达式路由、基于字典树的路由和基于哈希表的路由等。每种实现方式都有其特点和适用场景。选择合适的路由实现方式取决于具体的应用需求和性能要求。