前端工程师后端快速手册
作为前端工程师,拥有一定的后端知识是非常重要的。在日益复杂的Web应用开发中,前端工程师需要更全面地了解后端领域,包括数据库管理、安全性、Web框架的使用、AOP编程思想以及常见语言和框架的特性。本手册将为你提供一个简要但全面的后端知识概览,帮助你更好地理解后端开发的关键要点。
数据库
1. 数据库选择
选择适合项目需求的数据库类型,包括关系型数据库(如MySQL、PostgreSQL)和NoSQL数据库(如MongoDB、Redis)。了解它们的特性、优势和劣势,确保选择符合项目需求的数据库。
名称 | 特性 | 优势 | 劣势 | 性能情况 |
---|---|---|---|---|
MySQL | - 关系型数据库- 支持 SQL 查询语言- ACID事务支持 | - 成熟的开源数据库,社区庞大- 良好的事务支持- 支持复杂查询优化 | - 难以扩展到大规模和高并发情景- 不适用于某些非关系型数据的场景- 读写性能相对较低 | 适用于传统的关系型数据库应用,如企业级应用 |
PostgreSQL | - 关系型数据库- 支持 SQL 查询语言- ACID事务支持 | - 高度的标准兼容性- 支持复杂查询和数据类型- 可扩展性强 | - 需要更多的硬件资源支持- 配置和优化较为复杂- 在大量写入和更新时性能相对较低 | 适用于复杂查询和需要高度标准兼容性的应用 |
MongoDB | - NoSQL数据库,文档存储方式 | - 高度的灵活性和扩展性- 支持复杂的数据结构和嵌套文档- 适用于大量读取操作 | - 不支持事务(在某些场景下)- 写入操作性能相对较差- 空间占用较大 | 适用于大规模文档型数据存储,如日志、用户信息等 |
Redis | - NoSQL数据库,键值存储方式 | - 读写性能极高- 支持多种数据结构,如字符串、列表、集合等- 内存中存储,快速的读写操作 | - 数据持久性相对较差,对断电较为敏感- 不支持复杂查询- 内存占用较高 | 适用于高速读写操作,如缓存、会话存储等 |
2. 数据库设计
学会进行合理的数据库设计,包括表的规范化、索引的优化以及关系的建模。良好的数据库设计对于应用性能和扩展性至关重要。
范式 | 例子 | 建议 |
---|---|---|
第一范式 (1NF) | 每个字段都是不可再分的原子值,无重复列。 | - 在每个表中,确保每列包含的是单一的原子值。- 避免使用重复的列,使用关联表的方式处理重复数据。 |
第二范式 (2NF) | 满足1NF,并且非主键列完全依赖于主键。 | - 将非主键列与主键相关联,确保非主键列完全依赖于整个主键,而非部分依赖。- 使用适当的联合主键。 |
第三范式 (3NF) | 满足2NF,并且非主键列之间没有传递依赖。 | - 避免非主键列之间的传递依赖关系。- 将非主键列直接与主键关联,而非通过其他非主键列关联。 |
Boyce-Codd范式 | 满足3NF,并且每一个非主键列都直接依赖于候选键(超码)。 | - 将表设计成每个非主键列都直接依赖于超码,不允许存在非主键列对超码的部分依赖。- 通常用于关系数据库。 |
第四范式 (4NF) | 满足BCNF,并且没有多值依赖。 | - 将表设计成不存在多值依赖,确保每个非主键列都只依赖于超码的某一部分。- 通常用于关系数据库。 |
第五范式 (5NF) | 满足4NF,并且没有连接依赖。 | - 确保表中不存在连接依赖,即通过一个非主键列无法推导其他非主键列。- 通常用于关系数据库。 |
通常情况下,保持到第三范式是一个不错的基准。这有助于确保数据结构的清晰性和避免冗余。如果项目对性能有较高的要求,可能需要考虑适度的冗余以减少联接操作。在这种情况下,可以考虑到达到 Boyce-Codd 范式 (BCNF) 或更高的范式。
3. 数据库优化
拿MongoDB的索引来说,在性能优化方面发挥着重要的作用,能够加速数据查询、提高数据检索效率。以下是MongoDB索引带来的性能优化方面的一些主要优点:
索引优点 | 例子 | 缺点或注意事项 |
---|---|---|
快速数据检索 | 创建字段上的单字段索引 | 写入操作可能变慢,索引需要占用存储空间 |
加速排序操作 | 创建排序字段的单字段索引 | 大量索引可能导致内存消耗增加,需要平衡查询和写入性能 |
支持唯一约束 | 创建唯一索引 | 创建索引会占用额外的磁盘空间,维护唯一性可能影响写入性能 |
提高聚合操作性能 | 创建聚合操作字段的单字段索引 | 过多的索引可能导致性能下降,需要谨慎选择索引字段 |
优化范围查询 | 创建范围查询字段的单字段索引 | 需要根据查询模式选择合适的索引类型,不是所有查询都需要索引 |
加速数据更新和删除 | 创建更新或删除字段的单字段索引 | 过多的索引可能导致写入性能下降,不是所有字段都适合创建索引 |
支持覆盖查询 | 查询字段在索引中时,执行覆盖查询 | 需要根据查询需求决定是否创建复合索引 |
缓存索引数据 | 利用内存中的索引数据进行查询 | 需要合理配置内存和索引大小,过大的索引可能导致缓存效果降低 |
4. 硬件
存储介质的读写速度比较受多种因素影响,包括硬件类型、接口标准、缓存大小等。以下是一些常见存储介质的读写速度比较:
介质 | 读速度 | 写速度 | 注意事项 |
---|---|---|---|
SSD(固态硬盘) | 非常快,高达几千 MB/s | 非常快,高达几千 MB/s | 寿命受写入次数限制,不宜长时间满负荷写入 |
SATA硬盘 | 相对较慢,一般为几百 MB/s | 相对较慢,一般为几百 MB/s | 机械结构,受机械运动限制,适用于大容量存储 |
内存 | 极快,高达几十 GB/s | 极快,高达几十 GB/s | 临时存储,断电后数据丢失,适用于高性能临时计算 |
了解了硬件的速度量级,可以帮助我们在设计数据库时更好地平衡读写性能和存储空间。比如,可以将热数据存储在SSD上,将冷数据存储在SATA硬盘上,以达到性能和成本的平衡。
安全
后端是最后一道防线,需要保证数据的安全性。在设计后端应用时,需要考虑到安全性的方方面面,包括数据的加密、用户的认证、权限的控制等。了解一些常见的安全问题和解决方案,可以帮助我们更好地设计安全的后端应用。
1. 数据加密
常见的加密算法在Web上的使用主要涉及数据传输的加密和存储的哈希加密。以下是一些常见的加密算法及其在Web上的应用:
算法或协议 | 类型 | 应用 |
---|---|---|
TLS/SSL 加密算法 | 对称、非对称、哈希 | 保护网络通信的安全,HTTPS 协议使用 TLS/SSL 加密算法对数据进行加密传输,确保通信的机密性和完整性。 |
AES (Advanced Encryption Standard) | 对称 | 用于对敏感数据进行加密,例如用户密码在数据库中的存储,或在客户端与服务器之间的加密通信。 |
RSA | 非对称 | 主要用于数据的数字签名和密钥交换。在Web中,常用于对传输中的对称密钥进行安全地交换,从而保障对称加密的安全性。 |
SHA-256 | 哈希 | 用于生成数据的摘要,常用于密码存储、数字签名等场景。在Web中,常见于存储密码的哈希过程,确保用户密码的安全性。 |
BCrypt | 哈希 | 专门用于密码哈希加密,通过加盐(salt)和多次迭代的方式提高密码的安全性。在Web中,常用于用户密码的存储。 |
JWT (JSON Web Token) | HMAC SHA-256 | 用于生成具有签名的令牌,以便在客户端和服务器之间安全地传输信息,例如用户认证信息。在Web开发中,常用于身份验证和信息传递。 |
OAuth | HMAC SHA-256 | 用于在不同应用之间进行安全的用户身份验证,通过令牌进行授权。在Web开发中,常用于第三方登录和授权。 |
2. 常见攻击
在设计后端应用时,需要考虑到常见的攻击手段,包括SQL注入、XSS攻击、CSRF攻击等。以下是一些常见的攻击手段及其防范措施:
攻击类型 | 防范办法 |
---|---|
SQL 注入 | 使用参数化查询或预编译语句来防止恶意注入数据库的 SQL 代码。确保用户输入数据经过正确的验证和转义。 |
跨站脚本攻击 (XSS) | 对用户输入进行合适的转义,避免直接插入到页面中。使用 Content Security Policy (CSP) 防止非法脚本的执行。 |
跨站请求伪造 (CSRF) | 使用随机生成的 CSRF Token,并将其嵌入到表单中。验证请求中的 Token 是否有效,确保只处理来自合法来源的请求。 |
命令注入 | 不信任用户输入,对用户输入进行验证和过滤。使用安全的 API 或库来执行系统命令,而不是拼接用户输入。 |
未经授权的访问 | 使用强密码,并定期更改密码。实施身份验证和授权机制,限制用户访问权限。使用 HTTPS 加密传输敏感数据。 |
敏感数据泄露 | 加密存储在数据库中的敏感数据,使用适当的密钥管理。定期审查和更新访问权限。 |
文件上传漏洞 | 对文件上传进行严格的限制,仅允许上传特定类型和大小的文件。确保在保存或提供用户上传的文件之前进行充分验证和处理。 |
拒绝服务攻击 (DDoS) | 使用防火墙、反向代理和负载均衡器来分散和过滤流量。定期进行容量规划和性能优化。 |
中间人攻击 | 使用 HTTPS 进行数据传输,确保数据在传输过程中加密。实施合适的身份验证和授权机制。 |
信息泄露与错误处理 | 不要将详细的错误信息直接返回给客户端。在生产环境中,将错误信息记录到日志中,并给予用户友好的错误提示。 |
Web框架
1. 选择合适的后端框架
了解主流的后端框架,如Express(Node.js)、Django(Python)、Spring Boot(Java),并选择适合项目需求的框架。
名称 | 语言 | 特性 |
---|---|---|
Express.js | JavaScript | 简洁、灵活的Node.js Web 应用框架,适用于构建 RESTful API 和单页面应用。 |
Django | Python | Python 高级 Web 框架,强调快速开发和 DRY(Don’t Repeat Yourself)原则。 |
Flask | Python | 轻量级的 Python Web 框架,简单易用,适用于小型和中型应用。 |
Spring Boot | Java | 面向 Java 开发者的快速构建生产级别的 Spring 应用的框架,集成了各种 Spring 生态组件。 |
Ruby on Rails | Ruby | Ruby 的全栈 Web 框架,注重开发者友好性和约定大于配置的理念,适用于快速开发 Web 应用。 |
Laravel | PHP | PHP Web 框架,提供了优雅的语法、模块化、ORM 和其他工具,适用于构建现代 PHP 应用。 |
ASP.NET Core | C# | 跨平台的 .NET 框架,用于构建高性能、现代化、云原生的应用,支持 Web、云、移动和 IoT。 |
Nest.js | TypeScript | 基于 TypeScript 的渐进式 Node.js 框架,结合了 OOP、FP 和 FRP 的元素,用于构建可伸缩的服务器端应用。 |
FastAPI | Python | 基于 Python 的现代、快速(通过使用 Starlette 和 Pydantic)的 Web 框架,用于构建高性能的 API。 |
2. Restful API
Restful API是一种设计风格,用于构建可伸缩的Web服务。以下是一些常见的Restful API设计原则:
原则 | 例子 | 说明 |
---|---|---|
资源标识符(URI) | /users/{id} |
每个资源都有唯一的标识符,通过 URI 进行访问和操作。 |
统一接口(Uniform Interface) | HTTP 方法(GET、POST、PUT、DELETE) | 使用一致的接口,包括 URI、HTTP 方法、表示形式,以简化系统架构和提高可见性。 |
状态无关(Stateless) | 无 | 每个请求都包含足够的信息,服务器不存储客户端的状态。 |
资源的自描述性(Resource Representation) | JSON、XML | 资源的表示形式应该包含足够的信息,使得客户端能够理解如何处理资源。 |
超媒体驱动(HATEOAS) | 嵌入链接在资源表示中 | 资源的表示应该包含相关操作的链接,以引导客户端进行进一步的状态转移。 |
可缓存性(Cacheability) | Cache-Control 头 | 提供可缓存性,以提高性能和减轻服务器负载。 |
一个简单的办法判断api的设计是否Restful,可以简单地看一下url中是否存在动词,如果存在,那么就不是Restful的。
3. ORM
ORM(Object Relational Mapping)是一种编程技术,用于将对象模型和关系型数据库之间进行转换,其作用是在关系型数据库和面向对象编程语言之间建立映射关系,使得开发者可以使用面向对象的方式操作数据库,而不必直接处理 SQL 查询和数据库表结构。以下是一些常见的ORM框架:
名称 | 语言 | 特性 |
---|---|---|
Django ORM | Python | 1. 集成于 Django 框架中,用于与数据库交互。2. 提供模型类,用于定义数据库表结构。3. 支持多种数据库后端。 |
Hibernate | Java | 1. 针对 Java 编程语言的 ORM 框架,广泛用于 Java EE 项目。2. 支持关系数据库的映射。3. 提供对象和数据库的映射。 |
Entity Framework | C# | 1. Microsoft 提供的 .NET 平台上的 ORM 框架。2. 可与各种数据库进行集成。3. 提供 LINQ 查询语言支持。 |
SQLAlchemy | Python | 1. Python 编程语言中的 ORM 框架,支持多种数据库后端。2. 提供高度灵活的查询语言。3. 支持事务管理。 |
Sequelize | JavaScript | 1. 面向 Node.js 环境的 JavaScript ORM 框架。2. 支持多种数据库,如 MySQL、PostgreSQL、SQLite。3. 使用 Promise 进行异步操作。 |
Beanie | Python | 1. 面向 MongoDB 的 Python 异步 ORM 框架。2. 基于 Pydantic 模型定义数据库文档结构。3. 支持异步操作和 MongoDB 特性。 |
Prisma | TypeScript | 1. 面向 Node.js 和 TypeScript 的数据库访问工具。2. 自动生成类型安全的查询 API。3. 支持多种数据库后端,如 MySQL、PostgreSQL。 |
AOP
以上介绍的主要是CRUD的操作,但是在实际的开发中,我们还需要考虑到日志、缓存、事务等方面的问题。这些问题都可以通过AOP来解决。
AOP 是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)与主要业务逻辑进行分离,提高代码的模块性和可维护性。横切关注点指的是在应用程序中散布的与核心关注点(如业务逻辑)无关的功能,例如日志记录、事务管理、安全性等。
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from typing import List
app = FastAPI()
# Sample JWT token validation function
async def get_current_user(token: str = Depends(OAuth2PasswordBearer(tokenUrl="token"))):
credentials_exception = HTTPException(
status_code=401,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, "secret_key", algorithms=["HS256"])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return username
# AOP-style logging aspect
def log_request(endpoint):
async def wrapper(*args, **kwargs):
print(f"Received request to {endpoint.__name__}")
response = await endpoint(*args, **kwargs)
print(f"Request to {endpoint.__name__} completed")
return response
return wrapper
# Applying AOP aspect to an endpoint
@app.get("/items/", response_model=List[str])
@log_request
async def read_items(current_user: str = Depends(get_current_user)):
return [{"item": "Item 1"}, {"item": "Item 2"}]
在上面的示例中,log_request 函数充当一个 AOP 切面,负责记录请求的开始和结束。这个切面被应用到 /items/ 路由上,通过 @log_request 装饰器实现。这样,我们可以在不修改核心业务逻辑的情况下,添加日志记录等横切关注点。
同步异步和多线程线程池
同步与异步
特征 | 同步 | 异步 |
---|---|---|
执行方式 | 顺序执行,一个操作完成后才执行下一个操作 | 不按顺序执行,通过回调函数、事件驱动等处理 |
阻塞与非阻塞 | 阻塞,操作执行时会阻塞程序,程序等待操作完成 | 非阻塞,一个操作的开始不会等待上一个操作的完成 |
效率与性能 | 可能会导致等待时间过长,影响效率和性能 | 可以提高效率和性能,因为可以并发执行多个操作 |
例子 | 传统的阻塞 I/O 操作,如读取文件 | 异步 I/O 操作、回调函数、事件驱动,如在网页上进行异步加载资源 |
编程风格 | 编码相对简单,但可能导致程序无法执行其他任务 | 编码可能更复杂,需要处理回调函数、事件监听等,但更灵活地处理多个任务 |
Javascript异步编程举例
回调函数:
function fetchData(callback) {
// 模拟异步操作
setTimeout(() => {
const data = '异步数据';
callback(data);
}, 1000);
}
fetchData((result) => {
console.log(result); // 在回调函数中处理获取的数据
});
Promise:
function fetchData() {
return new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
const data = '异步数据';
resolve(data);
// 或者 reject(new Error('异步操作失败'));
}, 1000);
});
}
fetchData()
.then((result) => {
console.log(result); // 在Promise的then方法中处理获取的数据
})
.catch((error) => {
console.error(error); // 处理Promise中的错误
});
async/await:
async function fetchData() {
return new Promise((resolve) => {
// 模拟异步操作
setTimeout(() => {
const data = '异步数据';
resolve(data);
}, 1000);
});
}
async function getData() {
try {
const result = await fetchData();
console.log(result); // 在async/await中处理获取的数据
} catch (error) {
console.error(error); // 处理异步操作的错误
}
}
getData();
多线程与线程池
线程:
线程是操作系统能够进行运算调度的最小单位,它由线程 ID、程序计数器、寄存器集合和堆栈组成。在多线程环境中,多个线程共享相同的资源,如代码段、数据段等,但每个线程有自己的寄存器和堆栈,用于存储线程私有的数据。
线程池:
线程池是一种管理和复用线程的机制,用于提高线程的利用率和减少线程创建和销毁的开销。线程池中包含一定数量的线程,它们等待分配任务并执行。当有任务到达时,线程池分配一个线程来执行任务,执行完毕后线程不被销毁,而是重新放入线程池,等待下一个任务。这样可以避免频繁创建和销毁线程的开销,提高系统的性能和响应速度。
语言 | 支持情况 |
---|---|
Java | 内置支持线程和线程池,通过 java.lang.Thread 和 java.util.concurrent 包实现。 |
Python | 使用 threading 和 concurrent.futures 模块来支持线程,但由于 GIL 的存在,多线程效果受限。 |
C++ | 支持原生线程库,如 <thread> 和 <future> ,也可使用第三方库,如 Boost 等。 |
JavaScript | 支持异步编程,通过事件循环和回调函数实现,Web Workers 支持在独立的线程中执行脚本。 |
C# | 内置支持线程和线程池,通过 System.Threading 命名空间和 Task 类实现。 |
TypeScript | 类似于 JavaScript,支持异步编程,但不直接操作线程,依赖于事件循环和回调机制。 |
Docker原理
Docker 利用了 Linux 内核的一些特性,如命名空间(Namespaces)和控制组(Cgroups)。命名空间隔离了进程、网络、文件系统等,而控制组用于限制和监控资源的使用,如 CPU、内存等。Docker 使用联合文件系统(UnionFS)来实现容器镜像的分层。它是一种轻量级、可移植的容器化平台,旨在简化应用程序的部署、管理和扩展。
Docker 的核心概念包括镜像(Image)、容器(Container)、仓库(Repository)和服务(Service)。
镜像: Docker 镜像是容器的基础,它包含了运行应用程序所需的所有文件、库和依赖项。镜像是不可变的,确保在不同环境中具有相同的运行时环境。
容器: 容器是 Docker 镜像的运行实例。每个容器都是相互隔离的,拥有自己的文件系统、进程空间和网络接口。容器提供了一种轻量级、可移植的运行环境。
Dockerfile: Dockerfile 是用于定义 Docker 镜像构建过程的文本文件。通过 Dockerfile,开发人员可以指定应用程序的环境和依赖项,以及如何运行应用程序。
网络和端口映射: Docker 允许容器与主机和其他容器通信。每个容器都有自己的网络命名空间,Docker 提供了网络驱动程序,如 bridge、host 和 overlay,以便配置容器之间的通信。Docker 还支持端口映射,将容器内部的端口映射到主机上的端口,以便外部访问。
容器编排: Docker 提供了容器编排工具,如 Docker Compose 和 Kubernetes,用于管理和协调多个容器的部署、伸缩和更新。其中,Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个单独的 docker-compose.yml 文件,用户可以配置应用程序的服务、网络、卷等,并使用一条命令启动整个应用。一个简单的 docker-compose.yml 文件如下所示:
version: '3'
services:
web:
image: nginx
ports:
- "8080:80"
db:
image: postgres
Docker 仓库是用于存储和管理 Docker 镜像的中央仓库。它允许用户上传和下载 Docker 镜像,方便在不同的环境中分享和部署应用。Docker 官方提供了公共 Docker Hub 仓库,用户可以在其中找到大量的官方和社区维护的镜像。此外,用户也可以搭建自己的私有 Docker 仓库,以满足特定需求或提高安全性。以下是一些常用的仓库。
名称 | 说明 | 地址 |
---|---|---|
Docker Hub | Docker 官方提供的公共仓库,包含官方和社区维护的镜像。 | Docker Hub |
Amazon ECR | 亚马逊提供的云服务,用于存储、管理和部署 Docker 镜像。 | Amazon ECR |
Google Container Registry | Google Cloud 提供的 Docker 镜像仓库服务,与 Google Cloud Platform 集成。 | GCR |
Azure Container Registry | 微软 Azure 提供的 Docker 镜像仓库服务,与 Azure 云平台集成。 | ACR |
Docker 服务是在 Docker 中用于定义和运行分布式应用程序的抽象层。服务可以包括多个容器实例,这些实例可以在多个节点上运行,从而实现负载均衡和高可用性。Docker 服务通常与 Docker Compose 结合使用,通过 docker-compose.yml 文件定义服务的配置。
References