阶段3(下):RAG、向量数据库专题;接入 BOSS直聘调研(research/、岗位对照和延伸技术栈);新增 cs 领域和 12 个技术栈骨架;面试题全部补齐追问并加入校验
This commit is contained in:
1 parent
7f52513ffc
commit
6650f7eb6d
52 files changed
+2777
-38
No files matched your search
@@ -20,6 +20,8 @@ week: [1, 2, 3, 16]
|
||||
|
||||
**参考答案**:Gin 的 `Engine` 实现了 `http.Handler` 接口,底层仍然由 `net/http` 负责监听和连接管理。Gin 增加了基于前缀树的高性能路由、`gin.Context`(封装请求和响应、参数获取、绑定、中间件间传值)、中间件链和常用工具(JSON 渲染、校验)。所以 Gin 可以和任何标准库中间件、`httptest` 一起使用。
|
||||
|
||||
**追问**:Gin 的 `*gin.Context` 和标准库的 `context.Context` 是什么关系?→ `gin.Context` 是 Gin 自己的请求上下文(参数、响应、中间件链),它也实现了 `context.Context` 接口;向下游传递取消信号时,用 `c.Request.Context()` 更明确。
|
||||
|
||||
### 【基础】Gin 怎么获取路径参数、查询参数和请求体?
|
||||
|
||||
**参考答案**:路径参数 `c.Param("id")`;查询参数 `c.Query("k")` / `c.DefaultQuery("k", "默认")`;请求体用绑定 `c.ShouldBindJSON(&req)`,查询参数也可以 `c.ShouldBindQuery(&q)` 绑定到结构体。绑定时根据结构体的 `binding` 标签做校验。
|
||||
@@ -30,10 +32,14 @@ week: [1, 2, 3, 16]
|
||||
|
||||
**参考答案**:按注册顺序依次执行,形成洋葱模型:`c.Next()` 之前的代码按顺序执行,之后的代码按相反顺序执行。`c.Next()` 立即执行后续的处理函数;`c.Abort()` 阻止后续处理函数执行,但当前函数会继续执行完,所以通常 `Abort` 后紧跟 `return`。
|
||||
|
||||
**追问**:在中间件里 `c.Abort()` 之后还需要 `return` 吗?→ 需要。`Abort` 只是阻止后续处理函数执行,当前函数后面的代码仍会继续执行。
|
||||
|
||||
### 【基础】RESTful 接口的状态码怎么选?
|
||||
|
||||
**参考答案**:查询成功 200、创建成功 201、删除成功 204、参数错误 400、未认证 401、已认证但无权限 403、不存在 404、状态冲突(如重复创建)409、请求过多 429、服务器错误 500、下游不可用 502/503。
|
||||
|
||||
**追问**:参数校验失败用 400 还是 422?→ 两种都常见,团队统一即可;关键是错误响应格式统一、带上具体的字段错误信息。
|
||||
|
||||
## 进阶
|
||||
|
||||
### 【进阶】JWT 和 Session 有什么区别?JWT 怎么实现退出登录?
|
||||
@@ -46,24 +52,36 @@ week: [1, 2, 3, 16]
|
||||
|
||||
**参考答案**:Gin 用对象池复用 `Context`,请求处理结束后它会被重置给下一个请求使用。如果后台 goroutine 还持有原来的 `c`,读到的可能是别的请求的数据。`c.Copy()` 生成只读副本。更好的做法是只把需要的值(用户 ID、请求 ID、`c.Request.Context()` 派生的 context)传给后台任务。
|
||||
|
||||
**追问**:goroutine 里需要请求的取消信号怎么办?→ 后台任务通常不应该跟随请求取消(请求结束就被取消),用 `context.WithoutCancel(c.Request.Context())` 或新的 context 并设置独立超时。
|
||||
|
||||
### 【进阶】如何设计统一的错误处理?
|
||||
|
||||
**参考答案**:定义业务错误类型(HTTP 状态 + 业务码 + 用户提示),处理函数通过 `c.Error(err)` 记录错误并返回,由一个错误中间件统一转换成标准响应;未知错误和 panic 统一返回通用提示,详细信息带请求 ID 写日志。详见本技术栈的场景文档。
|
||||
|
||||
**追问**:错误码和 HTTP 状态码是什么关系?→ HTTP 状态码表达大类(客户端错误 / 服务端错误),业务错误码表达具体原因,方便前端展示和排查;不要所有错误都返回 200。
|
||||
|
||||
### 【进阶】Gin 的路由为什么快?
|
||||
|
||||
**参考答案**:Gin 使用 httprouter 风格的前缀树(radix tree),每个 HTTP 方法一棵树,按路径公共前缀压缩存储,匹配时间与路径长度相关,基本不受路由数量影响;同时 `Context` 用对象池减少内存分配。
|
||||
|
||||
**追问**:路由冲突(例如 `/users/:id` 和 `/users/new`)会怎样?→ 新版本的 Gin 已经支持静态路由和参数路由共存,静态路由优先;旧版本会在注册时 panic。以使用的版本为准,写测试验证。
|
||||
|
||||
## 场景题
|
||||
|
||||
### 【场景题】接口被恶意刷,怎么在 Gin 里做限流?
|
||||
|
||||
**参考答案**:单实例用令牌桶(`golang.org/x/time/rate`),按用户 ID 或 IP 维护一个限流器 map(注意定期清理);多实例部署需要全局限流,用 Redis(固定窗口 `INCR+EXPIRE` 或滑动窗口 ZSet、Lua 脚本保证原子)。超限返回 429 并带 `Retry-After`。更外层可以在 Nginx 或网关做 IP 级限流,应用层做用户级、接口级限流。本计划第 3 周项目一实现过。
|
||||
|
||||
**追问**:多实例部署时限流怎么做?→ 单机限流(`x/time/rate`)只能限制单个实例;全局限流要用 Redis(滑动窗口 / 令牌桶 + Lua 原子执行)或在网关层统一做。
|
||||
|
||||
### 【场景题】一个上传接口偶尔导致服务内存暴涨,怎么处理?
|
||||
|
||||
**参考答案**:限制请求体大小(`http.MaxBytesReader` 或 Gin 的 `MaxMultipartMemory`,Nginx 的 `client_max_body_size`);大文件流式处理而不是一次读入内存;校验文件类型和大小;上传改成前端直传对象存储(预签名 URL),后端只接收回调。
|
||||
|
||||
**追问**:大文件上传怎么做更好?→ 前端直传对象存储(服务端签发预签名 URL),服务端只处理元数据;需要服务端处理时用流式读取,不要整个读进内存。
|
||||
|
||||
### 【场景题】怎么给 Gin 服务写接口测试?
|
||||
|
||||
**参考答案**:把路由组装放在一个函数里(如 `New(deps) *gin.Engine`),测试时注入内存版依赖;用 `httptest.NewRecorder()` 和 `r.ServeHTTP(w, req)` 直接调用,不启动端口;用表格驱动覆盖成功、参数错误、未登录、无权限、不存在、内部错误等情况;数据库相关的用集成测试单独跑。示例见 [examples/unified-api](examples/unified-api/)。
|
||||
|
||||
**追问**:测试需要数据库怎么办?→ 业务逻辑通过接口依赖存储层,单元测试用假实现;集成测试用真实数据库(测试库或容器),每个测试使用独立的数据并清理。
|
||||
Reference in new issue
Block a user