timzaak / blog

8 stars 1 forks source link

理解Web框架大致思路 #104

Closed timzaak closed 11 months ago

timzaak commented 11 months ago

Web 框架主要解决的问题:屏蔽性能相关底层代码,提供统一的接口,方便用户以贴近直觉的方式编写 Http API / Websocket / HtmlTemplate。 PS: 一些轻量级的框架会去除 Websocket、HtmlTemplate,只支持 Http 1.1 API。

对用户的基本要求

  1. 了解最基本的 HTTP 协议概念。
  2. 对 Socket 编程有一定了解,知道 TCP/UDP。
  3. 知道长链接/短链接。
  4. 了解 JSON 序列化、反序列化

以上只是预备知识点,先弄清楚了,后续才能事半功倍。

Web框架的组成

先以 HTTP 1.1 为例,以处理短链接、无状态请求串联起整个Web 框架。 该 Web 框架基于 BlockingIO + 多线程来承接HTTP请求。至于 WebSoket(长链接)、异步(NIO/AIO),则有时间再讲。

过滤/路由/转换(Filter / Collect / Map)

HTTP 分为报头、报体两部分,报头包含这个请求的基本信息,例如:

  1. 路由 POST a\b\c?parm=1
  2. 报体类型 Content-Type
  3. 长度、协议版本等常规 Protocol 信息

在进入对请求参数、报体处理之前,会要先做过滤、鉴权、路由。过滤主要是拦截非法请求,鉴权则是识别该请求来源于哪个用户,并将此信息以上下文的形式向下一步骤传递, 路由则是依据HTTP Method+ uri,执行特定过滤和鉴权,并将请求传递到具体业务处理函数/方法, 注意,过滤和鉴权是多次的,往往会依据路由而配置不同的过滤和鉴权规则,举例:

  1. 根据IP黑白名单,过滤非法IP请求
  2. 根据路由,选择是否鉴定用户是谁
  3. 根据用户是谁和路由信息,判定用户是否有权限服务,无权限则拒绝请求

以上完成后,再对报体进行反序列化,一般是将Json字符串 反序列化 对象/struct 等方便寻址的实例。在反序列化的过程中往往会补上Json Validation, 校验报体中的参数是否是合规的,不合规,则返回 InvalidParam。若合规,则进入和数据库、缓存打交道的环节。

数据源(缓存、数据库)

数据库

我们在使用数据库的时候,可简单抽象成:SQL语句+表格+特殊索引。 SQL语句:用来和数据库打交道的语言。 很多的HTTP请求响应,可以简单的看做多SQL语句的组装和执行。一些特殊的数据库会有特别的交互语句,例如 Cyper,可以简单的看成 SQL方言。 表格:指数据库的存储格式:行式存储,至于特定领域的列式存储、Bson 存储、矩阵存储等,用到时再说。 特殊索引:关系型数据库都是基于B+树索引,目的是有序和快速查找的目标记录。除此之外还有数组索引、图索引、倒排序等,这个对于建表结构和性能至关重要。主要解决的问题点是:查询的时候尽量顺序读并读得少。

缓存

缓存是数据库的延伸,在性能极端要求的情况下,缓存用于绕过数据库获取数据,减轻数据库压力。其业界的常用方案是:Redis。 另外,Redis本身提供丰富的数据结构和脚本能力,除了作为缓存,也可用来做消息队列、锁结构等。建议最好通读其源码。

代码组织结构与运行时代码片段加载

代码结构

Controller、Service、Dao 三层是目前Web开发的常规结构,若是Web项目复杂了,会再补上 Module 层,这一层主要是为了快速查找代码。其余三层的含义: Controller:Web框架主要提供的API就在这一层,解决先前所说的 过滤/路由/转换。 Service:这一层简单来讲,就是去混合多数据库表、多模块的代码,用来处理业务逻辑。很多外包团队,会抛弃这一层,直接在Controller层写业务逻辑,调用Dao层。 Dao:和数据库打交道的一层,企业级开发时,会使用 ORM 框架来抽象这一层,较少代码量。

以上三层,最多变的是 Service 层,不同团队、不同语言、不同框架都会有不同的编写范式,我个人原则是:若是自己写,尽量少写,若是在大型团队里,则按照团队写法来写。

代码加载(DI 注入)

Web框架在开始运行时,要先将代码加载完毕,才能对外提供HTTP服务。代码加载在Java中一般是树形加载,先通过遍历 class 文件,梳理出依赖关系,然后依次加载,针对循环引用的,可通过代理、懒加载、多线程同步来解决。 有一本专门讲解 编程锁 的书,可以好好看看,但找不到书名。

无法忽视的工具链

接口文档自动化生成

接口文档目前已发展成前端可以据此自动化生成Api Client。 甚至渗透到交互协议中,例如GraphQL。

timzaak commented 11 months ago

以下都是统记,方便查询,不方便阅读。 缓存问题介绍: #9 数据库: #42 权限设计 #6 代码项目工程化实施 #102 程序员进步阶梯 #65

timzaak commented 7 months ago

Scala 的世界里,Tapir 这种声明式 web route 语法糖为中间层会有很多好处,Swagger之类的说明文档会被强制编写出来。也方便针对 Route 做测试和Client端导出。