乐闻世界logo
搜索文章和话题

服务端面试题手册

Koa 与 Express 框架的详细对比和选择建议

Koa 与 Express 是两个流行的 Node.js Web 框架,它们各有特点和适用场景。理解它们的差异有助于在实际项目中做出正确的选择。1. 核心设计理念:Express:内置大量功能(路由、中间件、模板引擎等)提供开箱即用的解决方案采用传统的回调函数模式中间件链式调用Koa:极简核心,只提供最基础的功能通过中间件扩展功能采用现代 async/await 模式洋葱模型中间件机制2. 中间件机制对比:Express 中间件:const express = require('express');const app = express();app.use((req, res, next) => { console.log('Middleware 1'); next(); console.log('Middleware 1 after');});app.use((req, res, next) => { console.log('Middleware 2'); res.send('Hello Express');});// 执行顺序:Middleware 1 -> Middleware 2 -> Middleware 1 afterKoa 中间件:const Koa = require('koa');const app = new Koa();app.use(async (ctx, next) => { console.log('Middleware 1 before'); await next(); console.log('Middleware 1 after');});app.use(async (ctx, next) => { console.log('Middleware 2 before'); await next(); console.log('Middleware 2 after'); ctx.body = 'Hello Koa';});// 执行顺序:Middleware 1 before -> Middleware 2 before -> // Middleware 2 after -> Middleware 1 after3. 代码风格对比:Express 回调风格:app.get('/users/:id', (req, res, next) => { User.findById(req.params.id, (err, user) => { if (err) return next(err); Post.findByUserId(user.id, (err, posts) => { if (err) return next(err); res.json({ user, posts }); }); });});Koa async/await 风格:app.get('/users/:id', async (ctx) => { const user = await User.findById(ctx.params.id); const posts = await Post.findByUserId(user.id); ctx.body = { user, posts };});4. 请求/响应处理对比:Express:app.get('/', (req, res) => { // 请求信息 const url = req.url; const method = req.method; const query = req.query; const body = req.body; // 响应设置 res.status(200); res.json({ message: 'Hello' }); // 或 res.send('Hello'); // 或 res.render('index', { title: 'Hello' });});Koa:app.get('/', async (ctx) => { // 请求信息 const url = ctx.url; const method = ctx.method; const query = ctx.query; const body = ctx.request.body; // 响应设置 ctx.status = 200; ctx.body = { message: 'Hello' }; // 或 ctx.type = 'text/html'; ctx.body = '<h1>Hello</h1>';});5. 错误处理对比:Express 错误处理:app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ error: err.message });});// 抛出错误app.get('/error', (req, res, next) => { const err = new Error('Something went wrong'); err.status = 500; next(err);});Koa 错误处理:app.use(async (ctx, next) => { try { await next(); } catch (err) { ctx.status = err.status || 500; ctx.body = { error: err.message }; ctx.app.emit('error', err, ctx); }});// 抛出错误app.get('/error', async (ctx) => { ctx.throw(500, 'Something went wrong');});6. 路由功能对比:Express 内置路由:const express = require('express');const router = express.Router();router.get('/users', getUsers);router.post('/users', createUser);router.get('/users/:id', getUser);router.put('/users/:id', updateUser);router.delete('/users/:id', deleteUser);app.use('/api', router);Koa 需要路由中间件:const Router = require('@koa/router');const router = new Router();router.get('/users', getUsers);router.post('/users', createUser);router.get('/users/:id', getUser);router.put('/users/:id', updateUser);router.delete('/users/:id', deleteUser);app.use(router.routes());app.use(router.allowedMethods());7. 性能对比:Express:成熟稳定,经过大量生产环境验证中间件链式调用,性能相对较低回调函数,可能存在回调地狱内存占用相对较高Koa:更轻量级,核心只有约 2KBasync/await,代码更简洁洋葱模型,中间件控制更灵活内存占用相对较低8. 学习曲线对比:Express:文档丰富,社区活跃学习曲线平缓大量教程和示例适合初学者Koa:需要理解 async/await需要理解洋葱模型需要选择合适的中间件适合有一定经验的开发者9. 适用场景对比:Express 适合:快速开发原型传统 Web 应用需要大量内置功能的项目团队成员对 async/await 不熟悉需要稳定成熟的框架Koa 适合:现代 Web 应用需要精细控制中间件的项目追求代码简洁和可维护性团队熟悉现代 JavaScript需要更好的错误处理10. 迁移建议:从 Express 迁移到 Koa:// Expressapp.get('/users/:id', async (req, res, next) => { try { const user = await User.findById(req.params.id); res.json(user); } catch (err) { next(err); }});// Koaapp.get('/users/:id', async (ctx) => { const user = await User.findById(ctx.params.id); ctx.body = user;});总结:| 特性 | Express | Koa ||------|---------|-----|| 核心大小 | 较大 | 极小(2KB) || 中间件模式 | 链式调用 | 洋葱模型 || 异步处理 | 回调函数 | async/await || 路由 | 内置 | 需要中间件 || 学习曲线 | 平缓 | 较陡 || 社区生态 | 成熟 | 快速发展 || 性能 | 良好 | 优秀 || 适用场景 | 传统应用 | 现代应用 |选择建议:如果追求快速开发和稳定性,选择 Express如果追求代码质量和现代化,选择 Koa如果团队熟悉 async/await,优先选择 Koa如果需要大量内置功能,选择 Express
阅读 0·2月21日 15:54

Kubernetes Ingress 是什么?它如何实现外部访问集群内服务?

Kubernetes Ingress 是一种 API 对象,用于管理外部访问集群内服务的规则,通常是 HTTP 和 HTTPS 路由。Ingress 提供了基于域名和路径的路由、TLS 终止等功能。Ingress 的作用路由规则:根据域名和路径将流量路由到不同的 Service负载均衡:在多个 Service 实例之间分发流量SSL/TLS 终止:在 Ingress 层面处理 HTTPS,简化后端配置基于名称的虚拟主机:支持多个域名指向同一个集群路径重写:支持 URL 路径重写Ingress ControllerIngress Controller 是实现 Ingress 功能的组件,它监听 Ingress 资源的变化并配置负载均衡器。常见的 Ingress ControllerNGINX Ingress Controller:最流行的 Ingress Controller基于 NGINX/OpenResty功能丰富,性能优秀支持高级路由、限流、认证等Traefik:云原生设计自动服务发现支持 Let's Encrypt 自动证书配置简单HAProxy Ingress:基于 HAProxy高性能支持高级负载均衡算法Istio Gateway:服务网格的一部分支持高级流量管理集成 mTLS、流量镜像等AWS ALB Ingress Controller:专门为 AWS 设计使用 AWS Application Load Balancer原生集成 AWS 服务Ingress 资源示例基本路由apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: simple-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /spec: rules: - host: example.com http: paths: - path: /app1 pathType: Prefix backend: service: name: app1-service port: number: 80 - path: /app2 pathType: Prefix backend: service: name: app2-service port: number: 80TLS 配置apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: tls-ingressspec: tls: - hosts: - secure.example.com secretName: tls-secret rules: - host: secure.example.com http: paths: - path: / pathType: Prefix backend: service: name: secure-service port: number: 443默认后端apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: default-backend-ingressspec: defaultBackend: service: name: default-service port: number: 80 rules: - host: example.com http: paths: - path: /api pathType: Prefix backend: service: name: api-service port: number: 80Ingress 注解(Annotations)注解用于配置 Ingress Controller 的特定行为。NGINX Ingress Controller 常用注解重写路径:nginx.ingress.kubernetes.io/rewrite-target: /$2启用 SSL 重定向:nginx.ingress.kubernetes.io/ssl-redirect: "true"限流配置:nginx.ingress.kubernetes.io/limit-rps: "10"nginx.ingress.kubernetes.io/limit-connections: "5"CORS 配置:nginx.ingress.kubernetes.io/enable-cors: "true"nginx.ingress.kubernetes.io/cors-allow-origin: "*"认证配置:nginx.ingress.kubernetes.io/auth-type: basicnginx.ingress.kubernetes.io/auth-secret: basic-auth自定义错误页面:nginx.ingress.kubernetes.io/custom-http-errors: "404,503"Ingress vs Service| 特性 | Ingress | Service ||------|---------|---------|| 协议 | HTTP/HTTPS | TCP/UDP || 路由 | 基于域名和路径 | 基于端口 || 负载均衡 | L7(应用层) | L4(传输层) || SSL 终止 | 支持 | 不支持 || 使用场景 | Web 应用 | 通用服务 |Ingress vs LoadBalancer| 特性 | Ingress | LoadBalancer ||------|---------|--------------|| 成本 | 低(共享 IP) | 高(每个 Service 一个 IP) || 路由能力 | 强(域名、路径) | 弱(仅端口) || SSL 终止 | 支持 | 部分支持 || 适用场景 | 多个 HTTP/HTTPS 服务 | 少量服务或非 HTTP 服务 |部署 Ingress Controller部署 NGINX Ingress Controller# 添加 Helm 仓库helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginxhelm repo update# 安装helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --create-namespace验证安装kubectl get pods -n ingress-nginxkubectl get svc -n ingress-nginx最佳实践使用命名空间隔离:将 Ingress Controller 部署在独立的命名空间配置资源限制:为 Ingress Controller 设置合理的 CPU 和内存限制启用监控:监控 Ingress Controller 的性能指标使用 TLS:为生产环境配置 TLS 证书配置健康检查:确保后端 Service 的健康检查正常使用注解优化:根据应用需求配置合适的注解备份配置:定期备份 Ingress 配置版本管理:跟踪 Ingress Controller 的版本更新故障排查查看 Ingress 状态:kubectl get ingresskubectl describe ingress <ingress-name>查看 Ingress Controller 日志:kubectl logs -n ingress-nginx <pod-name>测试 DNS 解析:nslookup example.com检查 Service 和 Endpoint:kubectl get svckubectl get endpoints验证证书:kubectl get secret tls-secret -o yaml
阅读 0·2月21日 15:53

Kubernetes Deployment 的作用是什么?它如何实现滚动更新和回滚?

Kubernetes Deployment 是用于管理 Pod 和 ReplicaSet 的声明式更新控制器,它提供了声明式的应用部署和更新能力。Deployment 的核心功能声明式管理:通过 YAML 文件定义期望状态,Kubernetes 自动实现当前状态到期望状态的转换。滚动更新:支持零停机时间的应用更新,逐步替换旧版本的 Pod。回滚能力:可以轻松回滚到之前的版本,支持查看更新历史。自动扩缩容:支持手动或自动调整 Pod 的副本数量。自愈能力:当 Pod 故障或被删除时,自动创建新的 Pod 以维持期望的副本数。Deployment 的工作原理Deployment 通过 ReplicaSet 管理 Pod:ReplicaSet:确保指定数量的 Pod 副本始终运行。Pod 模板:定义 Pod 的规格,包括容器镜像、资源限制等。更新策略:控制滚动更新的行为,如最大不可用 Pod 数量、最大激增 Pod 数量。Deployment 的 YAML 示例apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-deploymentspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80更新策略Deployment 支持两种更新策略:RollingUpdate(默认):逐步替换 Pod,确保始终有可用 Pod可配置 maxUnavailable 和 maxSurge适用于大多数应用Recreate:先删除所有旧 Pod,再创建新 Pod会导致短暂的服务中断适用于无法同时运行新旧版本的应用滚动更新参数maxUnavailable:更新过程中最多允许多少个 Pod 不可用(默认 25%)maxSurge:更新过程中最多允许多少个额外的 Pod(默认 25%)回滚操作查看更新历史:kubectl rollout history deployment/nginx-deployment回滚到上一个版本:kubectl rollout undo deployment/nginx-deployment回滚到指定版本:kubectl rollout undo deployment/nginx-deployment --to-revision=2扩缩容手动扩缩容:kubectl scale deployment/nginx-deployment --replicas=5自动扩缩容(HPA):apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: nginx-hpaspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx-deployment minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 50Deployment 与其他控制器的区别vs ReplicaSet:Deployment 是 ReplicaSet 的上层控制器Deployment 提供更新和回滚能力,ReplicaSet 只能维持副本数vs StatefulSet:Deployment 适用于无状态应用StatefulSet 适用于有状态应用,提供稳定的网络标识和持久化存储vs DaemonSet:Deployment 可以在任意节点运行指定数量的 PodDaemonSet 确保在每个节点上运行一个 Pod 副本最佳实践使用声明式配置:始终使用 YAML 文件定义 Deployment,而不是命令式命令。设置合理的资源限制:为容器设置 CPU 和内存的 requests 和 limits。配置健康检查:使用 livenessProbe 和 readinessProbe 确保应用健康。使用多阶段构建:优化镜像大小,提高部署速度。设置适当的更新策略:根据应用特性选择 RollingUpdate 或 Recreate。使用标签和注解:为 Deployment 添加有意义的标签和注解,便于管理和追踪。监控更新过程:使用 kubectl rollout status 监控更新进度。
阅读 0·2月21日 15:53

Kubernetes ConfigMap 和 Secret 的区别是什么?如何使用它们管理应用配置?

Kubernetes ConfigMap 和 Secret 是用于管理配置数据的两种重要资源,它们允许将配置与容器镜像分离,提高应用的可移植性和安全性。ConfigMapConfigMap 用于存储非敏感的配置数据,如应用配置文件、命令行参数、环境变量等。ConfigMap 的创建方式从字面值创建:kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2从文件创建:kubectl create configmap my-config --from-file=config.properties从目录创建:kubectl create configmap my-config --from-file=./config-dir/从 YAML 文件创建:apiVersion: v1kind: ConfigMapmetadata: name: my-configdata: key1: value1 key2: value2 app.properties: | server.port=8080 database.url=jdbc:mysql://localhost:3306/mydbConfigMap 的使用方式作为环境变量:apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: nginx env: - name: KEY1 valueFrom: configMapKeyRef: name: my-config key: key1作为命令行参数:apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: nginx command: ["/bin/sh", "-c"] args: ["echo $(KEY1)"] env: - name: KEY1 valueFrom: configMapKeyRef: name: my-config key: key1挂载为文件:apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: nginx volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: my-configSecretSecret 用于存储敏感信息,如密码、OAuth 令牌、SSH 密钥、证书等。Secret 的类型Opaque:默认类型,用于存储任意用户数据kubernetes.io/service-account-token:用于存储 Service Account 令牌kubernetes.io/dockercfg:用于存储 Docker registry 凭据kubernetes.io/dockerconfigjson:用于存储 Docker registry JSON 配置kubernetes.io/basic-auth:用于存储基本认证凭据kubernetes.io/ssh-auth:用于存储 SSH 认证凭据kubernetes.io/tls:用于存储 TLS 证书Secret 的创建方式从字面值创建:kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=secret123从文件创建:kubectl create secret generic my-secret --from-file=./username.txt --from-file=./password.txt从 YAML 文件创建:apiVersion: v1kind: Secretmetadata: name: my-secrettype: Opaquedata: username: YWRtaW4= password: c2VjcmV0MTIz注意:Secret 的 data 字段中的值必须是 Base64 编码的。使用 stringData:apiVersion: v1kind: Secretmetadata: name: my-secrettype: OpaquestringData: username: admin password: secret123stringData 会自动进行 Base64 编码。Secret 的使用方式Secret 的使用方式与 ConfigMap 类似:作为环境变量:apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: nginx env: - name: USERNAME valueFrom: secretKeyRef: name: my-secret key: username挂载为文件:apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: nginx volumeMounts: - name: secret-volume mountPath: /etc/secrets volumes: - name: secret-volume secret: secretName: my-secret拉取镜像:apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: my-private-registry/my-image imagePullSecrets: - name: registry-secretConfigMap 与 Secret 的区别| 特性 | ConfigMap | Secret ||------|-----------|--------|| 数据类型 | 非敏感数据 | 敏感数据 || 存储方式 | 明文存储 | Base64 编码(非加密) || 访问控制 | 普通 RBAC | 更严格的 RBAC || 大小限制 | 1 MiB | 1 MiB || 挂载方式 | 文件、环境变量 | 文件、环境变量 |安全最佳实践使用 Secret 存储敏感数据:永远不要将密码、密钥等敏感信息存储在 ConfigMap 中。启用 Secret 加密:使用 KMS(Key Management Service)对 etcd 中的 Secret 进行加密。限制 Secret 访问:使用 RBAC 限制对 Secret 的访问权限。使用临时文件:将 Secret 挂载为临时文件(tmpfs),避免持久化到磁盘。定期轮换密钥:定期更新 Secret 中的敏感信息。使用外部密钥管理:对于高安全性要求,考虑使用外部密钥管理系统(如 HashiCorp Vault)。审计 Secret 访问:启用审计日志,记录对 Secret 的访问。注意事项Base64 不是加密:Secret 中的数据只是 Base64 编码,不是加密,需要额外的安全措施。大小限制:ConfigMap 和 Secret 都有 1 MiB 的大小限制,超出限制需要拆分。版本管理:ConfigMap 和 Secret 的更新不会自动触发 Pod 重启,需要使用 Deployment 的滚动更新或手动重启。不可变性:可以将 ConfigMap 和 Secret 设置为不可变(immutable),提高性能和安全性。
阅读 0·2月21日 15:53

Koa 洋葱模型的执行机制和实际应用场景有哪些

Koa 的洋葱模型(Onion Model)是其最核心的设计特性,通过 async/await 实现中间件的执行流程控制。在洋葱模型中,中间件按照注册顺序执行,形成类似洋葱的层级结构。执行流程如下:请求从外层中间件进入,逐层向内传递每个中间件在 await next() 之前执行"前置逻辑"执行 await next() 进入下一层中间件到达最内层后,开始逐层向外返回每个中间件在 await next() 之后执行"后置逻辑"最终响应从最外层中间件返回客户端代码示例:const Koa = require('koa');const app = new Koa();app.use(async (ctx, next) => { console.log('1 - 前置'); await next(); console.log('1 - 后置');});app.use(async (ctx, next) => { console.log('2 - 前置'); await next(); console.log('2 - 后置');});app.use(async (ctx) => { console.log('3 - 核心处理'); ctx.body = 'Hello Koa';});// 执行顺序:1-前置 -> 2-前置 -> 3-核心处理 -> 2-后置 -> 1-后置洋葱模型的优势:清晰的执行顺序:前置和后置逻辑分离,代码结构清晰灵活的控制:每个中间件可以决定是否继续执行下游统一的错误处理:通过 try-catch 可以捕获所有中间件的错误中间件复用:可以在不同位置复用中间件逻辑请求/响应处理:方便在请求进入和响应返回时执行不同逻辑实际应用场景:日志记录:在前后置逻辑中记录请求和响应信息错误处理:在外层中间件统一捕获和处理错误认证授权:在前置逻辑中验证用户身份响应时间统计:计算请求处理总耗时响应格式化:在后置逻辑中统一处理响应格式
阅读 0·2月21日 15:53

Koa 错误处理机制和最佳实践详解

Koa 的错误处理机制非常优雅,通过 try-catch 和事件系统提供了多种错误处理方式。正确处理错误是构建健壮应用的关键。1. 使用 ctx.throw() 抛出错误:app.use(async (ctx) => { if (!ctx.query.token) { ctx.throw(401, 'Token is required'); } ctx.body = 'Success';});2. 使用 try-catch 捕获错误:app.use(async (ctx, next) => { try { await next(); } catch (err) { ctx.status = err.status || 500; ctx.body = { error: err.message, code: err.code || 'INTERNAL_ERROR' }; ctx.app.emit('error', err, ctx); }});3. 错误处理中间件:async function errorHandler(ctx, next) { try { await next(); } catch (err) { ctx.status = err.status || 500; // 开发环境返回详细错误信息 if (app.env === 'development') { ctx.body = { error: err.message, stack: err.stack, code: err.code }; } else { // 生产环境返回简化的错误信息 ctx.body = { error: 'Internal Server Error', code: 'INTERNAL_ERROR' }; } // 触发应用错误事件 ctx.app.emit('error', err, ctx); }}app.use(errorHandler);4. 自定义错误类:class AppError extends Error { constructor(status, message, code) { super(message); this.status = status; this.code = code; this.name = 'AppError'; }}class NotFoundError extends AppError { constructor(message = 'Resource not found') { super(404, message, 'NOT_FOUND'); this.name = 'NotFoundError'; }}class ValidationError extends AppError { constructor(message = 'Validation failed') { super(400, message, 'VALIDATION_ERROR'); this.name = 'ValidationError'; }}// 使用自定义错误app.use(async (ctx) => { const user = await findUser(ctx.params.id); if (!user) { throw new NotFoundError('User not found'); } ctx.body = user;});5. 全局错误监听:app.on('error', (err, ctx) => { // 记录错误日志 console.error('Server error:', err); // 发送错误通知(如邮件、Slack等) sendErrorNotification(err, ctx); // 上报错误到监控系统 reportToMonitoring(err, ctx);});6. 404 处理:// 在所有路由之后添加 404 处理app.use(async (ctx) => { ctx.status = 404; ctx.body = { error: 'Not Found', code: 'NOT_FOUND', path: ctx.url };});7. 异步错误处理:// Koa 会自动捕获 async 函数中的错误app.use(async (ctx) => { const data = await fetchData(); // 如果这里抛出错误,会被捕获 ctx.body = data;});// 对于 Promise 链,确保正确处理app.use(async (ctx) => { try { const result = await someAsyncOperation() .then(data => processData(data)) .catch(err => { throw new AppError(400, 'Processing failed', 'PROCESS_ERROR'); }); ctx.body = result; } catch (err) { ctx.throw(err.status || 500, err.message); }});8. 错误处理最佳实践:// 完整的错误处理示例const Koa = require('koa');const app = new Koa();// 自定义错误类class AppError extends Error { constructor(status, message, code) { super(message); this.status = status; this.code = code; }}// 错误处理中间件app.use(async (ctx, next) => { try { await next(); } catch (err) { // 设置状态码 ctx.status = err.status || 500; // 构建错误响应 const errorResponse = { error: err.message, code: err.code || 'INTERNAL_ERROR', timestamp: new Date().toISOString() }; // 开发环境包含堆栈信息 if (app.env === 'development') { errorResponse.stack = err.stack; } ctx.body = errorResponse; // 触发错误事件 ctx.app.emit('error', err, ctx); }});// 全局错误监听app.on('error', (err, ctx) => { console.error(`[${new Date().toISOString()}] Error:`, err.message); console.error('Path:', ctx.url); console.error('Stack:', err.stack);});// 业务路由app.use(async (ctx) => { if (ctx.path === '/error') { throw new AppError(500, 'Something went wrong', 'SERVER_ERROR'); } if (ctx.path === '/not-found') { ctx.throw(404, 'Resource not found'); } ctx.body = 'Hello Koa';});app.listen(3000);9. 常见错误处理场景:// 数据库错误处理app.use(async (ctx, next) => { try { await next(); } catch (err) { if (err.code === '23505') { // PostgreSQL 唯一约束冲突 ctx.throw(409, 'Resource already exists'); } else if (err.code === '23503') { // 外键约束冲突 ctx.throw(400, 'Invalid reference'); } else { throw err; } }});// 验证错误处理app.use(async (ctx, next) => { try { await next(); } catch (err) { if (err.name === 'ValidationError') { ctx.throw(400, err.message); } throw err; }});错误处理要点:使用 try-catch 包裹可能出错的代码创建自定义错误类,提供更清晰的错误信息区分开发环境和生产环境的错误响应实现全局错误监听,统一记录和上报为不同类型的错误提供适当的 HTTP 状态码确保错误不会泄露敏感信息在洋葱模型的最外层添加错误处理中间件
阅读 0·2月21日 15:53

Koa 中 Cookie 和 Session 管理的实现方法

Koa 的 Cookie 和 Session 管理是构建 Web 应用的基础功能,Koa 核心提供了 Cookie 操作,而 Session 需要通过中间件实现。1. Cookie 操作:Koa 核心内置了 Cookie 功能,通过 ctx.cookies 对象进行操作。设置 Cookie:app.use(async (ctx) => { // 基本设置 ctx.cookies.set('name', 'value'); // 带选项的设置 ctx.cookies.set('username', 'john', { maxAge: 3600000, // 有效期(毫秒) expires: new Date('2025-12-31'), // 过期时间 path: '/', // 路径 domain: '.example.com', // 域名 secure: true, // 仅 HTTPS httpOnly: true, // 仅 HTTP,防止 XSS sameSite: 'strict', // CSRF 保护 signed: true // 签名 Cookie }); ctx.body = 'Cookie set';});获取 Cookie:app.use(async (ctx) => { const username = ctx.cookies.get('username'); ctx.body = `Hello ${username}`;});删除 Cookie:app.use(async (ctx) => { ctx.cookies.set('username', null, { maxAge: 0, path: '/' }); ctx.body = 'Cookie deleted';});2. Session 管理:使用 koa-session 中间件实现 Session 功能。安装:npm install koa-session基本配置:const session = require('koa-session');const sessionConfig = { key: 'koa.sess', // Cookie 名称 maxAge: 86400000, // 有效期(毫秒) autoCommit: true, // 自动提交 overwrite: true, // 覆盖 httpOnly: true, // 仅 HTTP signed: true, // 签名 rolling: false, // 每次请求更新过期时间 renew: false, // 快过期时自动续期 secure: false, // 仅 HTTPS sameSite: null, // SameSite 策略};app.keys = ['your-secret-key']; // 必须设置用于签名app.use(session(sessionConfig, app));Session 使用:// 设置 Sessionapp.use(async (ctx) => { if (ctx.path === '/login') { ctx.session.user = { id: 1, name: 'John', role: 'admin' }; ctx.body = 'Logged in'; }});// 获取 Sessionapp.use(async (ctx) => { if (ctx.path === '/profile') { const user = ctx.session.user; if (user) { ctx.body = `Welcome ${user.name}`; } else { ctx.throw(401, 'Not logged in'); } }});// 删除 Sessionapp.use(async (ctx) => { if (ctx.path === '/logout') { ctx.session = null; ctx.body = 'Logged out'; }});3. Redis Session 存储:对于生产环境,建议使用 Redis 存储 Session。安装:npm install koa-session koa-redis配置 Redis Session:const session = require('koa-session');const RedisStore = require('koa-redis');const redisStore = RedisStore({ host: 'localhost', port: 6379, password: 'your-password', db: 0});const sessionConfig = { store: redisStore, key: 'koa.sess', maxAge: 86400000, httpOnly: true, signed: true};app.keys = ['your-secret-key'];app.use(session(sessionConfig, app));4. 认证中间件示例:// 认证中间件async function authMiddleware(ctx, next) { if (!ctx.session.user) { ctx.throw(401, 'Unauthorized'); } await next();}// 使用认证中间件router.get('/protected', authMiddleware, async (ctx) => { ctx.body = `Welcome ${ctx.session.user.name}`;});5. JWT Token 认证:使用 jsonwebtoken 和 koa-jwt 实现 JWT 认证。安装:npm install jsonwebtoken koa-jwt生成 Token:const jwt = require('jsonwebtoken');app.use(async (ctx) => { if (ctx.path === '/login') { const { username, password } = ctx.request.body; // 验证用户 const user = await authenticateUser(username, password); // 生成 Token const token = jwt.sign( { id: user.id, name: user.name }, 'your-secret-key', { expiresIn: '24h' } ); ctx.body = { token }; }});验证 Token:const jwt = require('koa-jwt');app.use(jwt({ secret: 'your-secret-key'}).unless({ path: [/^\/public/, '/login', '/register']}));// 访问用户信息app.use(async (ctx) => { ctx.body = ctx.state.user;});6. 完整的认证流程示例:const Koa = require('koa');const Router = require('@koa/router');const session = require('koa-session');const jwt = require('jsonwebtoken');const koaJwt = require('koa-jwt');const app = new Koa();const router = new Router();// Session 配置app.keys = ['secret-key'];app.use(session({ key: 'koa.sess', maxAge: 86400000}, app));// JWT 中间件app.use(koaJwt({ secret: 'jwt-secret'}).unless({ path: [/^\/api\/auth/]}));// 登录路由router.post('/api/auth/login', async (ctx) => { const { username, password } = ctx.request.body; // 验证用户 const user = await User.findOne({ username }); if (!user || !await user.comparePassword(password)) { ctx.throw(401, 'Invalid credentials'); } // 设置 Session ctx.session.user = { id: user.id, name: user.name }; // 生成 JWT Token const token = jwt.sign( { id: user.id, name: user.name }, 'jwt-secret', { expiresIn: '24h' } ); ctx.body = { token, user: { id: user.id, name: user.name } };});// 受保护的路由router.get('/api/user/profile', async (ctx) => { ctx.body = ctx.state.user;});// 登出路由router.post('/api/auth/logout', async (ctx) => { ctx.session = null; ctx.body = { message: 'Logged out' };});app.use(router.routes());7. 安全最佳实践:Cookie 安全:始终设置 httpOnly: true 防止 XSS生产环境使用 secure: true 仅 HTTPS设置 sameSite: 'strict' 防止 CSRF使用签名 Cookie 防止篡改Session 安全:使用强随机密钥设置合理的过期时间生产环境使用 Redis 存储登出时清除 SessionJWT 安全:使用强密钥设置合理的过期时间使用 HTTPS 传输实现 Token 刷新机制其他安全措施:限制登录尝试次数实现密码强度验证记录认证日志定期更新密钥
阅读 0·2月21日 15:53

Koa Context 对象 ctx 的核心属性和使用方法详解

Koa 的 Context 对象(ctx)是将 Node.js 的 request 和 response 对象封装到一个对象中的核心概念,通过代理模式提供了更简洁的 API。ctx 对象的核心属性和方法:请求相关属性:ctx.request:Koa 的 Request 对象ctx.req:Node.js 原生的 request 对象ctx.request.url:请求 URLctx.request.method:请求方法ctx.request.header:请求头对象ctx.request.query:解析后的查询字符串ctx.request.querystring:原始查询字符串ctx.request.path:请求路径ctx.request.body:请求体(需要 body-parser 中间件)响应相关属性:ctx.response:Koa 的 Response 对象ctx.res:Node.js 原生的 response 对象ctx.response.body:响应体ctx.response.status:响应状态码ctx.response.header:响应头对象ctx.response.type:响应 Content-Typectx.response.length:响应 Content-Length便捷访问(直接通过 ctx):ctx.url:等同于 ctx.request.urlctx.method:等同于 ctx.request.methodctx.header:等同于 ctx.request.headerctx.query:等同于 ctx.request.queryctx.body:等同于 ctx.response.bodyctx.status:等同于 ctx.response.statusctx.type:等同于 ctx.response.type其他重要属性:ctx.state:推荐的命名空间,用于传递中间件之间的信息ctx.app:应用实例引用ctx.cookies:Cookie 操作工具ctx.throw():抛出 HTTP 错误ctx.assert():断言检查代码示例:app.use(async (ctx) => { // 获取请求信息 const url = ctx.url; const method = ctx.method; const query = ctx.query; // 设置响应 ctx.status = 200; ctx.type = 'application/json'; ctx.body = { message: 'Hello Koa' }; // 使用 state 传递数据 ctx.state.user = { id: 1, name: 'John' }; // 抛出错误 if (!ctx.query.token) { ctx.throw(401, 'Token required'); }});ctx 对象的设计优势:简洁性:减少了代码冗余,提高开发效率一致性:统一的 API 接口,降低学习成本灵活性:既支持便捷访问,也支持直接操作原生对象扩展性:可以通过中间件扩展 ctx 对象的功能
阅读 0·2月21日 15:53

Kubernetes 污点(Taints)和容忍度(Tolerations)是什么?如何使用它们控制 Pod 调度?

Kubernetes 污点(Taints)和容忍度(Tolerations)是用于控制 Pod 调度的机制,它们允许节点拒绝(或接受)具有特定容忍度的 Pod。污点(Taints)污点是应用于节点上的键值对,用于阻止 Pod 调度到该节点,除非 Pod 具有匹配的容忍度。污点的组成每个污点包含三个部分:Key:污点的键(必需)Value:污点的值(可选)Effect:污点的影响(必需)污点的影响类型NoSchedule:不会调度新的 Pod 到该节点已存在的 Pod 不受影响适用于专用节点(如 GPU 节点)PreferNoSchedule:尽量不调度新的 Pod 到该节点但如果没有其他可用节点,仍可能调度适用于软性限制NoExecute:不会调度新的 Pod 到该节点已存在的 Pod 如果没有匹配的容忍度,将被驱逐适用于节点维护或故障场景添加污点# 添加 NoSchedule 污点kubectl taint nodes node1 key=value:NoSchedule# 添加 NoExecute 污点kubectl taint nodes node1 key=value:NoExecute# 添加没有值的污点kubectl taint nodes node1 key:NoSchedule查看污点# 查看节点的污点kubectl describe node node1 | grep Taint# 查看所有节点的污点kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints删除污点# 删除指定污点kubectl taint nodes node1 key:NoSchedule-# 删除所有指定键的污点kubectl taint nodes node1 key-容忍度(Tolerations)容忍度是应用于 Pod 上的配置,允许 Pod 调度到具有匹配污点的节点。容忍度的组成容忍度包含以下字段:Key:要容忍的污点键Operator:操作符(Equal 或 Exists)Value:要容忍的污点值(当 Operator 为 Equal 时需要)Effect:要容忍的污点影响TolerationSeconds:容忍时间(仅适用于 NoExecute)容忍度操作符Equal:键和值都必须匹配需要指定 ValueExists:只需要键匹配不需要指定 Value添加容忍度apiVersion: v1kind: Podmetadata: name: my-podspec: tolerations: - key: "key" operator: "Equal" value: "value" effect: "NoSchedule" containers: - name: my-container image: nginx容忍度示例容忍特定污点:tolerations:- key: "dedicated" operator: "Equal" value: "gpu" effect: "NoSchedule"容忍所有具有特定键的污点:tolerations:- key: "dedicated" operator: "Exists"容忍所有污点:tolerations:- operator: "Exists"容忍 NoExecute 污点并设置容忍时间:tolerations:- key: "node.kubernetes.io/not-ready" operator: "Exists" effect: "NoExecute" tolerationSeconds: 300污点和容忍度的匹配规则键匹配:如果容忍度的键为空,则匹配所有污点如果污点的键为空,则不能被任何容忍度匹配操作符匹配:Equal:键和值都必须匹配Exists:只需要键匹配影响匹配:如果容忍度的影响为空,则匹配所有影响否则,影响必须匹配常见使用场景1. 专用节点为特定用途的节点添加污点,确保只有特定的 Pod 可以调度到这些节点。# 为 GPU 节点添加污点kubectl taint nodes gpu-node dedicated=gpu:NoSchedule# 只有 GPU Pod 可以调度到 GPU 节点apiVersion: v1kind: Podmetadata: name: gpu-podspec: tolerations: - key: "dedicated" operator: "Equal" value: "gpu" effect: "NoSchedule" containers: - name: gpu-container image: nvidia/cuda:11.0.3-base-ubuntu20.042. 节点维护使用 NoExecute 污点驱逐 Pod,进行节点维护。# 标记节点为维护状态kubectl taint nodes node1 maintenance:NoExecute3. 特殊硬件节点为具有特殊硬件的节点添加污点,确保只有需要这些硬件的 Pod 可以调度。# 为 SSD 节点添加污点kubectl taint nodes ssd-node disktype=ssd:NoSchedule4. 故障节点Kubernetes 自动为故障节点添加污点,驱逐 Pod。# Pod 容忍节点故障tolerations:- key: "node.kubernetes.io/not-ready" operator: "Exists" effect: "NoExecute" tolerationSeconds: 300- key: "node.kubernetes.io/unreachable" operator: "Exists" effect: "NoExecute" tolerationSeconds: 300污点和容忍度 vs 亲和性| 特性 | 污点和容忍度 | 亲和性 ||------|-------------|--------|| 作用对象 | 节点和 Pod | 节点和 Pod || 作用方向 | 节点拒绝 Pod | Pod 选择节点 || 灵活性 | 较低 | 较高 || 使用场景 | 专用节点、节点维护 | 性能优化、高可用性 |最佳实践合理使用污点:避免过度使用污点,可能导致调度失败为专用节点添加污点:确保只有特定的 Pod 可以调度到专用节点设置合理的容忍时间:为 NoExecute 污点设置合理的容忍时间,避免频繁驱逐结合亲和性使用:结合使用污点/容忍度和亲和性,实现更精细的调度控制监控节点状态:监控节点的污点状态,及时处理故障节点文档化污点策略:记录污点和容忍度的使用策略,便于团队协作测试容忍度配置:在非生产环境测试容忍度配置,确保正确性故障排查查看节点污点:kubectl describe node <node-name>查看 Pod 容忍度:kubectl describe pod <pod-name>检查调度失败原因:kubectl describe pod <pod-name> | grep -A 10 Events查看调度器日志:kubectl logs -n kube-system <scheduler-pod-name>示例:多节点类型集群# Master 节点apiVersion: v1kind: Nodemetadata: name: master-nodespec: taints: - key: "node-role.kubernetes.io/master" effect: "NoSchedule"# GPU 节点apiVersion: v1kind: Nodemetadata: name: gpu-nodespec: taints: - key: "dedicated" value: "gpu" effect: "NoSchedule"# 普通 Pod(可以调度到普通节点)apiVersion: v1kind: Podmetadata: name: normal-podspec: containers: - name: nginx image: nginx# GPU Pod(只能调度到 GPU 节点)apiVersion: v1kind: Podmetadata: name: gpu-podspec: tolerations: - key: "dedicated" operator: "Equal" value: "gpu" effect: "NoSchedule" containers: - name: gpu-app image: nvidia/cuda:11.0.3-base-ubuntu20.04
阅读 0·2月21日 15:53

什么是 Kubernetes?它的核心概念和工作原理是什么?

Kubernetes 是一个开源的容器编排平台,用于自动化容器应用的部署、扩展和管理。它最初由 Google 设计并开发,于 2014 年开源,现在由 Cloud Native Computing Foundation(CNCF)管理。核心概念Kubernetes 的核心概念包括:Pod:Kubernetes 中最小的可部署单元,一个 Pod 可以包含一个或多个容器,这些容器共享网络和存储资源。Node:集群中的工作机器,可以是物理机或虚拟机。每个 Node 都运行着容器运行时(如 Docker)、kubelet 和 kube-proxy。Cluster:由多个 Node 组成的集合,是 Kubernetes 管理的计算资源池。Service:定义一组 Pod 的访问策略,提供稳定的网络端点,即使 Pod 的 IP 地址发生变化。Deployment:管理 Pod 和 ReplicaSet 的声明式更新,支持滚动更新和回滚。ConfigMap:用于存储非敏感的配置数据,可以挂载到 Pod 中或作为环境变量使用。Secret:用于存储敏感信息,如密码、OAuth 令牌和 SSH 密钥。Namespace:用于在同一个集群中创建多个虚拟集群,实现资源隔离。工作原理Kubernetes 采用主从架构:Control Plane(控制平面):负责集群的决策和响应,包括 API Server、Scheduler、Controller Manager 和 etcd。Worker Node(工作节点):运行容器化应用,包括 kubelet、kube-proxy 和容器运行时。主要特性自动化部署和回滚:支持声明式配置,自动实现应用的部署和更新。服务发现和负载均衡:自动为容器分配 IP 和 DNS 名称,实现负载均衡。自动扩缩容:根据 CPU 使用率或其他指标自动调整 Pod 数量。自愈能力:自动重启失败的容器、替换节点、重新调度 Pod。存储编排:自动挂载存储系统,如本地存储、云存储等。应用场景Kubernetes 广泛应用于微服务架构、CI/CD 流水线、大数据处理、机器学习等场景,是云原生应用的标准平台。
阅读 0·2月21日 15:53