Appearance
常见技术栈
打开任何一个稍微复杂的 Web 系统的技术架构图,你会看到一堆名字:Vue、React、FastAPI、Spring Boot、MySQL、Redis、Kafka、Nginx、Prometheus、Grafana……第一次看很容易懵——这么多东西都是干嘛的?其实它们不是平铺在一起的工具清单,而是各自占着系统的某一层:页面、接口、数据、入口、发布、观测。
理解技术栈的关键是先看每个组件在系统里站哪一层——页面谁来渲染、接口谁来提供、数据放在哪里、异步任务怎么跑、外部请求从哪里进来、出问题去哪里看。把这些位置关系理清楚,再看到陌生技术栈就不会晕。
一、页面层(前端)
前端负责页面结构、样式、交互和接口调用。浏览器最终加载的东西就是 HTML、CSS、JavaScript、图片、字体这些。
| 类型 | 主流选择 | 在什么位置 |
|---|---|---|
| 基础能力 | HTML、CSS、JavaScript | 浏览器原生支持,所有前端最终都跑这三样 |
| 框架 | Vue、React、Angular | 复杂交互、组件化开发,简化 DOM 操作 |
| 构建工具 | Vite、Webpack | 把源码(Vue/JSX)编译成浏览器能跑的纯 JS |
| UI 组件库 | Element Plus、Ant Design、Naive UI | 表单、表格、弹窗、导航这些现成组件 |
国内内部平台 Vue 用得多一些(上手快、生态完善),React 在大厂和海外项目里更主流。页面特别简单的时候,原生 HTML/CSS/JS 也够用,不一定非要上框架。前端构建之后产出的静态文件,可以放在 Nginx、对象存储(OSS/S3)或者 CDN 上。
前端和后端的连接点是 HTTP API。前端代码通过 fetch、axios 这种工具请求 /api/users/me、/api/tasks 这类接口,后端返回 JSON,前端拿到 JSON 渲染页面。这就是前后端分离的核心交互模式。
二、接口层(后端)
后端负责 HTTP 接口、业务逻辑、认证权限、数据库读写、调用外部系统。用什么语言写都行,常见的几种组合:
| 语言 | 主流框架 | 适合什么场景 |
|---|---|---|
| Java | Spring Boot、Spring Cloud | 企业业务系统、大型微服务体系(国内最主流) |
| Python | FastAPI、Django、Flask | 运维平台、内部工具、数据处理、AI 应用 |
| Go | 标准库 net/http、Gin、Fiber | 云原生组件、API 网关、运维工具(性能好) |
| Node.js | Express、NestJS | BFF(服务于前端的后端)、轻量 API |
语言选择往往跟团队背景关系最大,而不是技术本身好坏。Java 生态最完整、招人也容易,所以大公司核心业务基本都用 Java;Python 写内部工具和数据处理特别快,运维平台和 AI 应用多用 Python;Go 编译出来是单个二进制文件,部署简单、性能好,云原生组件(K8s、Docker、Prometheus 全家桶)几乎都是 Go 写的;Node.js 跟前端 JavaScript 同语言,前端团队能顺手写后端。
注意一个事:一个后端服务可不只是几个路由函数。真正生产级的后端还要处理配置管理、数据库连接池、HTTP 客户端超时、统一错误返回、权限拦截、日志中间件、优雅关闭、健康检查、metrics 暴露……这些"非业务"代码往往比业务代码还多。
三、数据层
数据库保存系统的所有状态——用户、订单、权限、审计、配置、任务记录,都要落到某种存储里。根据数据特性选不同的存储:
| 类型 | 主流选择 | 适合存什么 |
|---|---|---|
| 关系型数据库 | MySQL、PostgreSQL | 用户、订单、权限、审计、配置这种结构化数据 |
| 文档数据库 | MongoDB | 字段变化多、结构不固定的文档型数据 |
| 缓存/键值 | Redis | 热点数据、会话、计数器、分布式锁 |
| 搜索引擎 | Elasticsearch、OpenSearch | 日志检索、全文搜索、海量数据分析 |
国内传统 Web 和大部分业务系统 MySQL 用得最多——稳定、生态成熟、运维资料多。PostgreSQL 在复杂 SQL、JSON 字段、GIS 地理数据、扩展能力上比 MySQL 强,最近几年用得越来越多。MongoDB 适合"文档型"数据——比如一篇文章有标题、正文、标签、评论这些,字段可能动态变化,用关系型数据库建表很麻烦,用 MongoDB 直接存 JSON 文档就特别顺手。Redis 几乎是缓存的标准选择,也经常顺手用来做会话存储、限流计数、分布式锁。
数据库选型不只看"能不能存",还要看备份恢复方案、迁移工具、索引能力、权限模型、监控生态、团队熟悉程度。一个团队没人会运维的数据库,再先进也不能上生产。
四、缓存和消息队列
缓存放在数据库前面,挡住重复查询。Redis 是最主流的缓存组件,除了缓存也经常顺手做会话存储、计数器、限流、分布式锁——一个 Redis 能解决一堆小问题,所以在技术栈里几乎必出现。
消息队列放在服务之间,主要解决两个问题:让耗时任务异步执行(用户不用等)、把突发流量缓冲下来(削峰填谷)。
| 消息系统 | 适合什么场景 |
|---|---|
| RabbitMQ | 任务分发、可靠投递、传统业务队列(AMQP 协议) |
| Kafka | 日志流、事件流、大吞吐数据管道 |
| Redis Stream/List | 内部小任务、轻量队列(不想到处装新组件) |
一个典型场景:用户点击"导出报表",如果同步等接口生成,可能要等几十秒到几分钟,用户体验极差。改成异步——接口立刻返回任务 ID,实际生成工作扔到消息队列里后台慢慢跑,生成完了再通知用户。这种模式下要区分两个状态:"请求已接收"(接口返回成功)不等于"任务已完成"(报表真生成出来了)。排查这类问题时,要看任务状态机、队列有没有积压、消费失败和重试记录。
五、文件存储
上传的文件(头像、附件、图片)、备份包、日志归档、构建产物——这些都不适合长期绑在某台应用服务器的本地目录上。
对象存储是主流方案,按 bucket(桶)和 object key(对象键)组织文件,通过 HTTP API 访问。云厂商的对象存储(阿里云 OSS、腾讯云 COS、AWS S3)、自建的 MinIO 都属于这一类。
后端数据库里一般只保存文件元数据,不直接存文件本身:
| 字段 | 干什么 |
|---|---|
| 文件名 | 展示给用户的原始名字 |
| 大小、类型 | 用于下载、预览、校验 |
| 归属对象 | 这个文件属于哪个用户、任务、业务单据 |
| object key | 对象存储里的真实路径(数据库存这个,不存文件本身) |
为什么不能放本地目录?应用一旦扩成多实例就出问题——用户上传头像时请求落到 app-01,文件保存到 app-01:/data/uploads/;下次访问头像时请求被分到 app-02,app-02 本地没这个文件,直接 404。对象存储让多个实例访问同一份文件,从根上解决这个问题。
六、入口层
入口层负责接收外部请求,再送到正确的内部服务。这一层组件不少:
| 组件 | 主要职责 |
|---|---|
| Nginx | 静态资源服务、反向代理、TLS 卸载、限流 |
| HAProxy | 高性能代理和负载均衡(四层/七层都行) |
| 云负载均衡(SLB/ALB) | 公网入口、四层/七层转发 |
| Ingress / Gateway | Kubernetes HTTP/HTTPS 入口 |
| API 网关(Kong/APISIX) | 认证、路由、限流、审计、协议转换 |
用户报 502、504、404、证书错误、跨域错误这些事,入口层是第一处检查点。入口日志能看到请求有没有进来、转发到了哪个后端、后端返回了什么状态、耗时花在哪里。基本上 80% 的"用户访问异常"问题,从入口日志开始查最快。
七、观测和发布
可观测组件负责"看见"系统状态,出了问题能定位:
| 能力 | 主流选择 |
|---|---|
| 指标采集和存储 | Prometheus、VictoriaMetrics |
| 指标看板 | Grafana(几乎是标准) |
| 告警 | Alertmanager、Nightingale(夜莺) |
| 日志收集和查询 | Loki、ELK、OpenSearch |
| 链路追踪 | OpenTelemetry、Jaeger、Tempo |
发布组件负责把代码变成线上版本,管的是构建、测试、镜像、部署:
| 能力 | 主流选择 |
|---|---|
| 代码仓库 | GitLab、GitHub、Gitea |
| CI(持续集成) | GitLab CI、Jenkins、GitHub Actions |
| 镜像仓库 | Harbor、云厂商镜像仓库 |
| CD/GitOps(持续部署) | Argo CD、Flux |
把前面所有层串起来,一套典型的内部技术栈大概长这样:前端用 Vue 或 React → 后端用 FastAPI/Spring Boot/Go → 数据库用 MySQL 或 PostgreSQL → 缓存用 Redis → 文件放对象存储 → 入口用 Nginx 或 Ingress → 指标进 Prometheus → 日志进 Loki 或 OpenSearch → CI 用 GitLab CI 或 GitHub Actions → 镜像放 Harbor → 部署到 Kubernetes。
这不是固定答案,只是一种容易理解的位置关系。换技术栈时(比如把 Vue 换 Svelte、把 MySQL 换 TiDB),先确认每一层都有对应的组件和排查入口——能跑起来是一回事,出问题能排查才是更重要的另一回事。