首页
直播
壁纸
友链
搜索
1
微信小程序 Skyline 渲染引擎实战:worklet 动画从原理到落地
327 阅读
2
微信小程序支付全链路实战:JSAPI 下单、调起支付、回调验签与退款
308 阅读
3
微信小程序云开发实战:云函数、云数据库与云存储的正确使用姿势
280 阅读
4
小程序接口安全攻防:签名、防重放、防刷与数据加密实战
276 阅读
5
微信小程序分包进阶:独立分包、预下载与分包异步化实战
272 阅读
服务器运维
后端技术
前端技术
梯子
数据库
小程序
登录
搜索
标签搜索
fastadmin
Redis
微信小程序
前端开发
RabbitMQ
Go
Golang
服务器
codex
buildadmin
小程序
mysql
Nginx
Docker
Vue3
Node.js
MySQL优化
Linux
TypeScript
JWT
沿途的风景
累计撰写
82
篇文章
累计收到
0
条评论
首页
栏目
服务器运维
后端技术
前端技术
梯子
数据库
小程序
页面
直播
壁纸
友链
搜索到
82
篇与
» admin
的结果
2026-03-08
Nginx 反向代理配置详解与性能优化实践
前言Nginx 作为高性能 HTTP 服务器和反向代理,在生产环境中应用极为广泛。本文将系统讲解 Nginx 反向代理的核心配置,并分享实际项目中的性能优化经验。一、反向代理基础配置1.1 基本反向代理server { listen 80; server_name example.com; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }1.2 负载均衡配置upstream backend { server 192.168.1.10:3000 weight=3; server 192.168.1.11:3000 weight=2; server 192.168.1.12:3000 weight=1; keepalive 32; } server { listen 80; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } }二、性能优化要点2.1 Worker 进程配置worker_processes auto; worker_cpu_affinity auto; events { worker_connections 10240; use epoll; multi_accept on; }2.2 Gzip 压缩gzip on; gzip_min_length 1k; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml; gzip_vary on; gzip_proxied any;2.3 静态文件缓存location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ { expires 30d; add_header Cache-Control "public, immutable"; access_log off; }三、SSL/TLS 配置server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; add_header Strict-Transport-Security "max-age=63072000" always; }四、常见问题排查4.1 502 Bad Gateway通常原因:后端服务未启动、端口不通、网络问题。# 检查后端服务 curl -I http://127.0.0.1:3000 # 查看错误日志 tail -f /var/log/nginx/error.log # 检查端口 ss -tlnp | grep 30004.2 504 Gateway Timeout后端响应超时,适当调大 proxy_read_timeout。五、总结Nginx 反向代理配置看似简单,但要在生产环境中跑出最佳性能,需要在 worker 模型、连接复用、压缩缓存、SSL 优化等多个维度精细调优。建议每次修改配置后用 nginx -t 检查语法,逐步调整观察效果。
2026年03月08日
25 阅读
0 评论
0 点赞
2026-03-07
Docker Compose 多容器编排实战指南
前言Docker Compose 是定义和运行多容器 Docker 应用的工具。本文通过一个完整的 Web 应用示例,讲解 Compose 的核心用法和最佳实践。一、项目结构myapp/ ├── docker-compose.yml ├── nginx/ │ └── default.conf ├── php/ │ └── Dockerfile ├── src/ │ └── index.php └── mysql/ └── init.sql二、完整的 LNMP 编排version: "3.8" services: nginx: image: nginx:1.25-alpine ports: - "80:80" - "443:443" volumes: - ./src:/var/www/html - ./nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - php restart: unless-stopped networks: - app-network php: build: context: ./php dockerfile: Dockerfile volumes: - ./src:/var/www/html environment: - DB_HOST=mysql - DB_NAME=myapp - DB_USER=root - DB_PASS=secret networks: - app-network mysql: image: mysql:8.0 ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: myapp networks: - app-network redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis-data:/data networks: - app-network volumes: mysql-data: redis-data: networks: app-network: driver: bridge三、常用命令# 启动所有服务 docker-compose up -d # 查看运行状态 docker-compose ps # 查看日志 docker-compose logs -f nginx # 重新构建 docker-compose build --no-cache php # 停止并删除容器 docker-compose down # 停止并删除容器和数据卷 docker-compose down -v四、进阶技巧4.1 健康检查mysql: image: mysql:8.0 healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 3 start_period: 30s4.2 资源限制php: build: ./php deploy: resources: limits: cpus: "2" memory: 512M reservations: memory: 256M4.3 按需启动依赖php: depends_on: mysql: condition: service_healthy redis: condition: service_started五、总结Docker Compose 让多容器应用的定义、运行和管理变得简单。通过合理的目录结构、网络隔离、数据卷管理和健康检查,可以构建出生产级的容器编排方案。
2026年03月07日
22 阅读
0 评论
0 点赞
2026-03-05
Vue3 Composition API 深度解析:从 ref 到自定义 Hook
前言Vue3 的 Composition API 是对 Options API 的范式升级。它解决了大型组件中逻辑复用和组织的问题,让代码更灵活、更可维护。一、响应式基础1.1 ref vs reactiveimport { ref, reactive, toRefs } from "vue"; // ref:适用于基本类型 const count = ref(0); count.value++; // reactive:适用于对象 const state = reactive({ name: "张三", age: 25, }); // reactive 的陷阱:解构会失去响应性 const { name } = state; // name 不再是响应式 // 解决方案:toRefs const { name: reactiveName } = toRefs(state);1.2 computedimport { ref, computed } from "vue"; const firstName = ref("张"); const lastName = ref("三"); // 只读计算属性 const fullName = computed(() => `${firstName.value}${lastName.value}`); // 可写计算属性 const fullNameWritable = computed({ get() { return `${firstName.value}${lastName.value}`; }, set(newValue: string) { firstName.value = newValue[0]; lastName.value = newValue.slice(1); }, });二、watch 和 watchEffect2.1 watchimport { ref, watch } from "vue"; const keyword = ref(""); // 监听单个 ref watch(keyword, (newVal, oldVal) => { console.log(`关键词从 ${oldVal} 变为 ${newVal}`); }); // 监听多个源 watch([keyword, category], ([newKeyword, newCategory]) => { search(newKeyword, newCategory); }); // 深度监听对象 watch( () => state, (newState) => { saveToStorage(newState); }, { deep: true } );2.2 watchEffectimport { ref, watchEffect } from "vue"; const userId = ref(1); // 自动追踪依赖,立即执行 watchEffect(() => { fetchUser(userId.value); // userId 变化时自动重新执行 });三、自定义 Hook(组合式函数)3.1 useMousePosition// composables/useMousePosition.ts import { ref, onMounted, onUnmounted } from "vue"; export function useMousePosition() { const x = ref(0); const y = ref(0); function update(e: MouseEvent) { x.value = e.pageX; y.value = e.pageY; } onMounted(() => { window.addEventListener("mousemove", update); }); onUnmounted(() => { window.removeEventListener("mousemove", update); }); return { x, y }; }使用:<script setup lang="ts"> import { useMousePosition } from "@/composables/useMousePosition"; const { x, y } = useMousePosition(); </script> <template> <p>鼠标位置: {{ x }}, {{ y }}</p> </template>3.2 useFetch// composables/useFetch.ts import { ref, watchEffect } from "vue"; export function useFetch<T>(url: () => string) { const data = ref<T | null>(null); const error = ref<string | null>(null); const loading = ref(false); watchEffect(async () => { loading.value = true; error.value = null; try { const res = await fetch(url()); if (!res.ok) throw new Error(`HTTP ${res.status}`); data.value = await res.json() as T; } catch (e) { error.value = (e as Error).message; } finally { loading.value = false; } }); return { data, error, loading }; }四、生命周期钩子import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from "vue"; onBeforeMount(() => { /* 组件挂载前 */ }); onMounted(() => { /* DOM 已挂载 */ }); onBeforeUpdate(() => { /* 响应式数据变更,DOM 更新前 */ }); onUpdated(() => { /* DOM 更新后 */ }); onBeforeUnmount(() => { /* 组件卸载前 */ }); onUnmounted(() => { /* 组件已卸载 */ });五、总结Composition API 的核心优势在于逻辑复用和代码组织。通过自定义 Hook,可以将组件逻辑提取为可复用的函数,告别 Options API 时代 mixin 的命名冲突和数据来源不透明问题。建议在项目中优先使用 <script setup> 语法,配合 TypeScript 获得最佳开发体验。
2026年03月05日
29 阅读
0 评论
0 点赞
2026-03-02
Node.js JWT 认证完整实现:注册登录到权限控制
前言JWT(JSON Web Token)是现代 Web 应用中最流行的认证方案之一。本文将使用 Node.js + Express 实现一个完整的 JWT 认证系统,包含注册、登录、Token 刷新和权限控制。一、项目初始化mkdir jwt-auth && cd jwt-auth npm init -y npm install express jsonwebtoken bcryptjs cors dotenv npm install -D typescript @types/express @types/node ts-node-dev二、项目结构src/ ├── config/ │ └── index.ts ├── middleware/ │ └── auth.ts ├── routes/ │ └── auth.ts ├── utils/ │ └── jwt.ts ├── types/ │ └── index.ts └── app.ts三、核心实现3.1 JWT 工具类// src/utils/jwt.ts import jwt from "jsonwebtoken"; import { config } from "../config"; export interface JwtPayload { userId: string; role: string; } export function signAccessToken(payload: JwtPayload): string { return jwt.sign(payload, config.JWT_SECRET, { expiresIn: "15m", }); } export function signRefreshToken(payload: JwtPayload): string { return jwt.sign(payload, config.JWT_REFRESH_SECRET, { expiresIn: "7d", }); } export function verifyAccessToken(token: string): JwtPayload { return jwt.verify(token, config.JWT_SECRET) as JwtPayload; } export function verifyRefreshToken(token: string): JwtPayload { return jwt.verify(token, config.JWT_REFRESH_SECRET) as JwtPayload; }3.2 认证中间件// src/middleware/auth.ts import { Request, Response, NextFunction } from "express"; import { verifyAccessToken } from "../utils/jwt"; // 扩展 Request 类型 declare global { namespace Express { interface Request { user?: { userId: string; role: string }; } } } // 基本认证 export function authRequired(req: Request, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; if (!authHeader?.startsWith("Bearer ")) { return res.status(401).json({ code: 401, msg: "未提供认证令牌" }); } const token = authHeader.substring(7); try { const payload = verifyAccessToken(token); req.user = { userId: payload.userId, role: payload.role }; next(); } catch { return res.status(401).json({ code: 401, msg: "令牌无效或已过期" }); } } // 角色权限控制 export function requireRole(...roles: string[]) { return (req: Request, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ code: 401, msg: "未认证" }); } if (!roles.includes(req.user.role)) { return res.status(403).json({ code: 403, msg: "权限不足" }); } next(); }; }3.3 路由实现// src/routes/auth.ts import { Router, Response } from "express"; import bcrypt from "bcryptjs"; import { authRequired, requireRole } from "../middleware/auth"; import { signAccessToken, signRefreshToken, verifyRefreshToken } from "../utils/jwt"; const router = Router(); // 模拟用户存储(实际应使用数据库) const users: Map<string, { id: string; username: string; password: string; role: string }> = new Map(); const refreshTokens: Set<string> = new Set(); // 注册 router.post("/register", async (req, res: Response) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ code: 400, msg: "用户名和密码不能为空" }); } if (users.has(username)) { return res.status(409).json({ code: 409, msg: "用户已存在" }); } const hashedPassword = await bcrypt.hash(password, 10); const id = Date.now().toString(); users.set(username, { id, username, password: hashedPassword, role: "user" }); res.json({ code: 0, msg: "注册成功" }); }); // 登录 router.post("/login", async (req, res: Response) => { const { username, password } = req.body; const user = users.get(username); if (!user) { return res.status(404).json({ code: 404, msg: "用户不存在" }); } const valid = await bcrypt.compare(password, user.password); if (!valid) { return res.status(401).json({ code: 401, msg: "密码错误" }); } const payload = { userId: user.id, role: user.role }; const accessToken = signAccessToken(payload); const refreshToken = signRefreshToken(payload); refreshTokens.add(refreshToken); res.json({ code: 0, msg: "登录成功", data: { accessToken, refreshToken, user: { id: user.id, username: user.username, role: user.role } }, }); }); // 刷新 Token router.post("/refresh", (req, res: Response) => { const { refreshToken } = req.body; if (!refreshToken || !refreshTokens.has(refreshToken)) { return res.status(401).json({ code: 401, msg: "无效的刷新令牌" }); } try { const payload = verifyRefreshToken(refreshToken); refreshTokens.delete(refreshToken); const newPayload = { userId: payload.userId, role: payload.role }; const newAccessToken = signAccessToken(newPayload); const newRefreshToken = signRefreshToken(newPayload); refreshTokens.add(newRefreshToken); res.json({ code: 0, msg: "刷新成功", data: { accessToken: newAccessToken, refreshToken: newRefreshToken }, }); } catch { refreshTokens.delete(refreshToken); return res.status(401).json({ code: 401, msg: "刷新令牌已过期" }); } }); // 登出 router.post("/logout", (req, res: Response) => { const { refreshToken } = req.body; refreshTokens.delete(refreshToken); res.json({ code: 0, msg: "已登出" }); }); // 受保护的路由 router.get("/profile", authRequired, (req, res: Response) => { res.json({ code: 0, data: { userId: req.user!.userId, role: req.user!.role } }); }); // 管理员路由 router.get("/admin/users", authRequired, requireRole("admin"), (req, res: Response) => { const userList = Array.from(users.values()).map(u => ({ id: u.id, username: u.username, role: u.role, })); res.json({ code: 0, data: userList }); }); export default router;3.4 主应用// src/app.ts import express from "express"; import cors from "cors"; import dotenv from "dotenv"; import authRoutes from "./routes/auth"; dotenv.config(); const app = express(); app.use(cors()); app.use(express.json()); app.use("/api/auth", authRoutes); app.get("/", (_req, res) => { res.json({ msg: "JWT Auth API" }); }); app.listen(3000, () => { console.log("Server running on http://localhost:3000"); });四、安全最佳实践Access Token 短时效(15 分钟),Refresh Token 长时效(7 天)Refresh Token 一次性使用:每次刷新后旧 Token 失效HTTPS 传输:生产环境必须使用 HTTPS密码加密:使用 bcrypt,不要用 MD5/SHA1环境变量管理密钥:不要硬编码到代码中Token 黑名单:登出时将 Token 加入黑名单(Redis)总结JWT 认证方案无状态、易扩展,适合前后端分离架构。通过 Access Token + Refresh Token 双 Token 机制,可以在安全性和用户体验之间取得平衡。生产环境中建议将 Refresh Token 存储在 Redis 中,方便统一管理和吊销。
2026年03月02日
26 阅读
0 评论
0 点赞
2026-03-02
MySQL 慢查询优化实战:从 EXPLAIN 到索引调优
前言MySQL 慢查询是影响应用性能的常见瓶颈。本文从慢查询日志分析入手,结合 EXPLAIN 执行计划解读,系统讲解索引优化和 SQL 调优方法。一、慢查询日志1.1 开启慢查询日志-- 查看慢查询配置 SHOW VARIABLES LIKE "%slow_query%"; -- 开启慢查询日志 SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; -- 超过 1 秒的查询 SET GLOBAL slow_query_log_file = "/var/log/mysql/slow.log"; -- 记录未使用索引的查询 SET GLOBAL log_queries_not_using_indexes = ON;1.2 使用 mysqldumpslow 分析# 按平均查询时间排序,取前 10 mysqldumpslow -s t -t 10 /var/log/mysql/slow.log # 按总耗时排序 mysqldumpslow -s at -t 10 /var/log/mysql/slow.log # 按次数排序 mysqldumpslow -s c -t 10 /var/log/mysql/slow.log二、EXPLAIN 执行计划2.1 基本用法EXPLAIN SELECT * FROM orders WHERE user_id = 10086 AND status = "paid";2.2 关键字段解读字段说明重点关注type访问类型至少达到 range 级别,避免 ALLkey实际使用的索引不为 NULLrows预估扫描行数越小越好Extra附加信息避免 Using filesort 和 Using temporary2.3 type 类型(从好到差)system > const > eq_ref > ref > range > index > ALLconst:主键或唯一索引等值查询eq_ref:JOIN 时使用主键或唯一索引ref:非唯一索引等值查询range:索引范围查询(BETWEEN、>、<、IN)index:扫描整个索引树ALL:全表扫描,必须优化三、索引优化3.1 创建合适的索引-- 单列索引 CREATE INDEX idx_user_id ON orders(user_id); -- 联合索引(注意最左前缀原则) CREATE INDEX idx_user_status ON orders(user_id, status, created_at); -- 覆盖索引(查询字段都在索引中) CREATE INDEX idx_cover ON orders(user_id, status, amount);3.2 最左前缀原则-- 联合索引 (a, b, c) -- 能命中索引: SELECT * FROM t WHERE a = 1; SELECT * FROM t WHERE a = 1 AND b = 2; SELECT * FROM t WHERE a = 1 AND b = 2 AND c = 3; -- 不能命中索引: SELECT * FROM t WHERE b = 2; SELECT * FROM t WHERE c = 3; SELECT * FROM t WHERE b = 2 AND c = 3;3.3 避免索引失效-- 函数操作导致索引失效 SELECT * FROM orders WHERE YEAR(created_at) = 2026; -- 优化: SELECT * FROM orders WHERE created_at >= "2026-01-01" AND created_at < "2027-01-01"; -- 隐式类型转换 SELECT * FROM orders WHERE order_no = 10086; -- order_no 是 varchar -- 优化: SELECT * FROM orders WHERE order_no = "10086"; -- LIKE 前导通配符 SELECT * FROM users WHERE name LIKE "%张"; -- 优化:使用全文索引或调整业务逻辑 SELECT * FROM users WHERE name LIKE "张%"; -- OR 导致索引失效 SELECT * FROM orders WHERE user_id = 1 OR status = "paid"; -- 优化:使用 UNION ALL SELECT * FROM orders WHERE user_id = 1 UNION ALL SELECT * FROM orders WHERE status = "paid" AND user_id != 1;四、SQL 调优实战4.1 分页优化-- 慢:OFFSET 大时性能差 SELECT * FROM orders ORDER BY id LIMIT 1000000, 20; -- 优化方案 1:延迟关联 SELECT * FROM orders o INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id; -- 优化方案 2:游标分页(记住上一页最后一条 ID) SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;4.2 批量插入-- 慢:循环单条插入 INSERT INTO logs (msg) VALUES ("log1"); INSERT INTO logs (msg) VALUES ("log2"); -- 优化:批量插入 INSERT INTO logs (msg) VALUES ("log1"), ("log2"), ("log3");4.3 使用 Redis 缓存热点数据import redis import json r = redis.Redis(host="localhost", port=6379, db=0) def get_user(user_id): # 先查缓存 cache_key = f"user:{user_id}" cached = r.get(cache_key) if cached: return json.loads(cached) # 查数据库 user = db.query("SELECT * FROM users WHERE id = %s", user_id) if user: r.setex(cache_key, 3600, json.dumps(user)) # 缓存 1 小时 return user五、总结MySQL 慢查询优化的核心思路:先通过慢查询日志找到问题 SQL,再用 EXPLAIN 分析执行计划,最后通过添加索引、调整 SQL 写法、引入缓存等方式优化。记住:不是所有慢查询都需要加索引,有时调整业务逻辑比优化 SQL 更有效。
2026年03月02日
26 阅读
0 评论
0 点赞
1
...
12
13
14
...
17
0:00