手把手带你写一个 Web 框架
叶剑峰
腾讯高级工程师,前滴滴技术专家
22731 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 42 讲
特别放送 (1讲)
手把手带你写一个 Web 框架
15
15
1.0x
00:00/00:00
登录|注册

04|中间件:如何提高框架的可拓展性?

Get/Post/Put/Delete函数
Use函数
实现Next方法
修改Context结构
修改node结构
监听异常、超时信号
开启Goroutine执行业务逻辑
创建定时器Context
匿名函数实现ControllerHandler结构
注册Recovery中间件
实现Recovery中间件
注册控制器链路
控制器链路
TimeoutHandler
思考题
基本的中间件: Recovery
使用pipeline思想改造中间件
函数嵌套方式实现中间件
中间件:如何提高框架的可拓展性?

该思维导图由 AI 生成,仅供参考

你好,我是轩脉刃。
到目前为止我们已经完成了 Web 框架的基础部分,使用 net/http 启动了一个 Web 服务,并且定义了自己的 Context,可以控制请求超时。
之前在讲具体实现的时候,我们反复强调要注意代码的优化。那么如何优化呢?具体来说,很重要的一点就是封装。所以今天我们就回顾一下之前写的代码,看看如何通过封装来进一步提高代码扩展性。
在第二课,我们在业务文件夹中的 controller.go 的逻辑中设置了一个有超时时长的控制器:
func FooControllerHandler(c *framework.Context) error {
...
// 在业务逻辑处理前,创建有定时器功能的 context
durationCtx, cancel := context.WithTimeout(c.BaseContext(), time.Duration(1*time.Second))
defer cancel()
go func() {
...
// 执行具体的业务逻辑
time.Sleep(10 * time.Second)
// ...
finish <- struct{}{}
}()
// 在业务逻辑处理后,操作输出逻辑...
select {
...
case <-finish:
fmt.Println("finish")
...
}
return nil
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何通过中间件来提高框架的可拓展性。作者首先分析了现有代码中的优化点,强调了封装的重要性,并提出了通过中间件来实现代码扩展性的思路。接着,文章详细介绍了使用函数嵌套方式和pipeline思想分别实现中间件的方法。在使用函数嵌套方式时,作者通过封装核心业务逻辑和一层层添加装饰的方式实现了中间件的装饰器模式。然后,作者指出了函数嵌套方式存在的问题,并提出了使用pipeline思想改造中间件的方法。通过构建控制器链路的方式,实现了中间件和最终业务逻辑的结合,从而解决了函数嵌套方式存在的问题。文章通过改造node和Context数据结构,实现了控制器链路的注册和调用,同时提供了可变参数的设计,提高了注册入口的可用性。文章内容详实,逻辑清晰,对于想要深入了解中间件实现原理和优化方法的读者具有很高的参考价值。文章还演示了一个基本的中间件Recovery的实现,强调了中间件机制的重要性和应用价值。总的来说,本文通过具体的代码示例和思路分析,深入浅出地介绍了中间件的实现原理和优化方法,对于想要提高框架可拓展性的开发者具有一定的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《手把手带你写一个 Web 框架》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(25)

  • 最新
  • 精选
  • qinsi
    想到几个点: * 中间件的注册是有顺序的。比如最后才注册Recovery的话,pipeline中在Recovery前面的中间件如果panic了还是没法recover的 * 中间件需要显式调用ctx.Next(),如果写中间件时忘记了的话pipeline就断了。或许可以把中间件进一步拆成preRequest()和postRequest()两部分 * 中间件本质是装饰器模式,如果能像Java/Python里那样写装饰器标注的话可能意图更明显

    作者回复: 1 是的,中间件注册有顺序,所以recovery是需要放在第一个。 2 这个是可以考虑,但是分成两个函数和现在用一个函数区别就是有的变量是没有办法写到两个函数中的,参数传递比较麻烦,比如我要打印请求时长,有一个变量,start_time, 需要在preRequest中写,postRequest中读,现在的方式就比较简单。分成两个函数就需要在postRequest中用参数传递之类的。 preRequest() ctxParam postRequest(ctxParam) 3 注释来表示装饰器,然后运行的时候读取注释来按需加载。这种方式也是行的,就是具体实现的时候需要读取“注释”来映射中间件,这里存在一个反射的逻辑,可能会降低效率。所以在golang中这种实现不多见。

    2021-09-20
    4
    7
  • liyanfeng
    请教一下老师的UML图是用哪个软件画的哈?

    作者回复: 可用工具很多,draw.io 金山文档都能用

    2021-09-20
    3
  • The brain is a good thing
    这课程真的是,看一遍回本一次 - by 2023

    作者回复: 感谢

    2023-02-18归属地:广东
    2
  • 我看大家都说 allHandlers := append(c.middlewares, handlers...) 的写法有问题。其实没问题 因为每次扩容的时候 并没有赋值回去 即 :c.middwares := append(c.middlewares, handlers...) 所以每次都是拿未扩容的数组来 并不会出现覆盖的情况

    作者回复: 之前确实是有问题的,已经修改过来了

    2021-12-01
    2
  • liyanfeng
    这么好的课,大家快来买😄,熟悉加意外的感觉,真好

    作者回复: 感谢支持,希望能帮助到你

    2021-09-20
    2
  • 那些年
    支持!

    作者回复: 感谢!

    2021-12-13
    1
  • jayqiyoung
    如果每一节课后面的提问,下一节能够给些解答就好了

    编辑回复: 后面有篇加餐,统一整理了一些作业题的思路,你可以看看~

    2022-09-26
  • 我在睡觉
    提一个问题,这里面 gourp机构题里面封装一个Group类型的parent链表有什么用意,我不需要这个parent字段也完全实现了同样的功能。

    作者回复: 这个主要是使用上的方便,链式方式。 我可以多层嵌套group

    2021-12-21
    2
  • 我在睡觉
    core.Use( middleware.Test1()) 老师你好。问一个问题, 为什么此处的Test1一定要定义成返回ControllerHandler匿名函数的函数,我实际直接把Test1定义成ControllerHandler类型的的函数执行起来也没有任何问题。

    作者回复: 是这样的,这样设置的话,你可以在Test1的参数中带任何参数,然后在具体的ControllerHandler中使用这些参数,而如果你的Test1是ControllerHandler的话,参数就已经被固定了,后续扩展性就不是很好

    2021-12-14
    3
  • 老师请问 c.Next是可以捕获error的 在整个链路中如果一处地方抛出了error 但是在最顶层 ServeHTTP中的那个next如果返回nil 那么整条链路中的error会被忽略掉 我们只在 timeout中加入了 锁这个概念 其实这个其实这个应该可以抽出来 统一加上不允许重复写responseWriter

    作者回复: 1 确实这里更严谨的写法是在所有中间件的c.Next() 都处理error 2 使用锁保证 responseWriter 只写一次肯定是可以的,但是我觉得这种只限制写一次反而也会有问题,会不会有场景有的控制器写内容,有的中间件写header头这种。这种限制可以加,但是加的时候估计要思考很清楚

    2021-12-01
收起评论
显示
设置
留言
25
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部