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

服务端面试题手册

Cookie 的 Expires 和 Max-Age 属性有什么区别?如何设置 Cookie 的过期时间?

Expires 和 Max-Age 都是用于控制 Cookie 过期时间的属性,但它们的实现方式和行为有所不同。Expires 属性特点使用绝对时间(GMT 格式)指定 Cookie 的具体过期日期和时间兼容性更好,支持旧版浏览器语法// 设置 Expiresconst expires = new Date();expires.setDate(expires.getDate() + 7); // 7天后过期document.cookie = "token=abc; Expires=" + expires.toUTCString();// 完整示例document.cookie = "token=abc; Expires=Wed, 09 Jun 2026 10:18:14 GMT; Path=/";注意事项时间格式必须是 UTC (GMT)如果设置的时间早于当前时间,Cookie 立即删除客户端和服务器端时间不同步可能导致问题Max-Age 属性特点使用相对时间(秒数)指定 Cookie 从创建开始的有效期更现代的属性,优先级高于 Expires语法// 设置 Max-Age(单位:秒)document.cookie = "token=abc; Max-Age=3600"; // 1小时后过期// 完整示例document.cookie = "token=abc; Max-Age=86400; Path=/"; // 1天后过期特殊值Max-Age=0:立即删除 CookieMax-Age 为负数:立即删除 Cookie不设置 Max-Age:会话 Cookie,浏览器关闭时删除两者对比| 特性 | Expires | Max-Age ||------|---------|---------|| 时间类型 | 绝对时间 | 相对时间 || 单位 | 日期时间字符串 | 秒数 || 优先级 | 低 | 高 || 兼容性 | 所有浏览器 | 现代浏览器 || 时区问题 | 有 | 无 |优先级规则// 同时设置时,Max-Age 优先document.cookie = "token=abc; Expires=Wed, 09 Jun 2026 10:18:14 GMT; Max-Age=3600";// Cookie 会在 1 小时后过期,而不是指定日期使用场景会话 Cookie// 不设置过期时间document.cookie = "sessionId=abc";// 浏览器关闭时删除短期 Cookie// 使用 Max-Age 更清晰document.cookie = "tempToken=abc; Max-Age=1800"; // 30分钟长期 Cookie// 使用 Expires 更直观const expires = new Date();expires.setFullYear(expires.getFullYear() + 1);document.cookie = "rememberMe=true; Expires=" + expires.toUTCString();删除 Cookie// 方法 1:设置过去的 Expiresdocument.cookie = "token=abc; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/";// 方法 2:设置 Max-Age=0document.cookie = "token=abc; Max-Age=0; Path=/";最佳实践优先使用 Max-Age// 推荐document.cookie = "token=abc; Max-Age=3600";// 不推荐(除非需要兼容旧浏览器)document.cookie = "token=abc; Expires=" + new Date(Date.now() + 3600000).toUTCString();兼容性处理function setCookieWithExpiry(name, value, seconds) { let cookieString = `${name}=${value}`; // 优先使用 Max-Age if (typeof seconds === 'number') { cookieString += `; Max-Age=${seconds}`; } else { // 降级使用 Expires const expires = new Date(); expires.setSeconds(expires.getSeconds() + seconds); cookieString += `; Expires=${expires.toUTCString()}`; } document.cookie = cookieString;}安全考虑敏感信息使用短期过期时间"记住我"功能使用长期过期时间定期轮换 Token 并更新过期时间实际应用示例// 登录成功后设置 Cookiefunction setLoginCookie(token, rememberMe) { const options = { httpOnly: true, secure: true, sameSite: 'strict', path: '/' }; if (rememberMe) { // 记住我:30天 options.maxAge = 30 * 24 * 60 * 60; } else { // 不记住:1小时 options.maxAge = 60 * 60; } // 服务器端设置(Node.js Express 示例) res.cookie('authToken', token, options);}
阅读 0·3月6日 23:40

pnpm workspace 如何配置和使用?

pnpm workspace 是 pnpm 内置的 monorepo 解决方案,用于管理多个包在一个仓库中。基本配置:# pnpm-workspace.yamlpackages: - 'packages/*' # 所有 packages 目录下的包 - 'apps/*' # 所有 apps 目录下的应用 - 'shared/*' # 共享模块项目结构示例:my-monorepo/├── pnpm-workspace.yaml├── package.json├── pnpm-lock.yaml├── packages/│ ├── ui/│ │ ├── package.json│ │ └── src/│ └── utils/│ ├── package.json│ └── src/└── apps/ ├── web/ │ ├── package.json │ └── src/ └── server/ ├── package.json └── src/包间依赖引用:// apps/web/package.json{ "name": "@my-org/web", "dependencies": { "@my-org/ui": "workspace:*", // 使用 workspace 协议 "@my-org/utils": "workspace:^1.0.0" }}workspace 协议类型:{ "dependencies": { "package": "workspace:*", // 使用最新版本 "package": "workspace:^", // 兼容版本 "package": "workspace:~", // 近似版本 "package": "workspace:^1.0.0" // 指定版本范围 }}常用命令:# 安装所有依赖pnpm install# 在特定包中运行命令pnpm --filter @my-org/ui buildpnpm -F @my-org/ui build # 简写# 在所有包中运行命令pnpm -r build # 递归执行# 只在有变化的包中运行pnpm -r --filter "...[origin/main]" build# 添加包间依赖pnpm --filter @my-org/web add @my-org/ui优势对比:| 特性 | pnpm workspace | Lerna | Yarn Workspaces ||------|---------------|-------|-----------------|| 内置支持 | ✅ | ❌ 需安装 | ✅ || 依赖共享 | ✅ 硬链接 | ❌ | ✅ 符号链接 || 磁盘效率 | 最高 | 一般 | 中等 || 配置复杂度 | 简单 | 复杂 | 简单 |最佳实践:# 使用 changesets 管理版本pnpm add -Dw @changesets/clipnpm changeset init# 发布流程pnpm changeset # 记录变更pnpm changeset version # 更新版本pnpm -r publish # 发布所有包
阅读 0·3月6日 23:39

pnpm 如何处理 peer dependencies?与 npm 有什么不同?

pnpm 对 peer dependencies 的处理更加严格和正确,这是其重要特性之一。什么是 Peer Dependencies:// react-dom/package.json{ "peerDependencies": { "react": "^18.0.0" // 需要宿主项目提供 react }}npm/Yarn 的问题:# 项目依赖{ "dependencies": { "react": "^17.0.0", "react-dom": "^18.0.0" # 需要 react 18 }}# npm/Yarn 可能静默安装,导致版本不匹配# 运行时才发现问题pnpm 的严格处理:# 安装时报错pnpm install# ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies# react-dom@18.0.0 requires react@^18.0.0 but you have react@17.0.0解决方案:正确安装匹配版本pnpm add react@18 react-dom@18使用 pnpm.overrides// package.json{ "pnpm": { "overrides": { "react": "^18.0.0" } }}忽略 peer dependency(不推荐){ "pnpm": { "peerDependenciesMeta": { "react": { "optional": true } } }}peerDependencies 的正确使用:// 插件包的 package.json{ "name": "my-react-plugin", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" }, "peerDependenciesMeta": { "react-dom": { "optional": true // 可选的 peer dependency } }}monorepo 中的处理:# pnpm-workspace.yamlpackages: - 'packages/*'# packages/plugin/package.json{ "peerDependencies": { "react": "^18.0.0" }}# packages/app/package.json{ "dependencies": { "react": "^18.0.0", "@my-org/plugin": "workspace:*" }}# pnpm 会自动将 app 中的 react 提供给 plugin自动安装 peer dependencies:# .npmrcauto-install-peers=true # 自动安装 peer dependenciesstrict-peer-dependencies=false # 不严格检查对比总结:| 特性 | npm/Yarn | pnpm ||------|----------|------|| 检查时机 | 可能延迟到运行时 | 安装时立即检查 || 错误提示 | 可能不明显 | 明确的错误信息 || 版本冲突 | 可能静默忽略 | 严格报错 || monorepo 支持 | 一般 | 优秀 |
阅读 0·3月6日 23:39

pnpm 常用命令有哪些?与 npm 命令有什么区别?

pnpm 提供了丰富的 CLI 命令来管理依赖和项目。安装命令:# 安装所有依赖pnpm installpnpm i# 安装单个包pnpm add lodashpnpm add lodash@4.17.21# 安装到不同依赖类型pnpm add lodash --save-prod # dependencies (默认)pnpm add lodash --save-dev # devDependenciespnpm add lodash --save-optional # optionalDependenciespnpm add lodash -D # devDependencies 简写pnpm add lodash -O # optionalDependencies 简写# 全局安装pnpm add -g lodashpnpm add --global lodash更新命令:# 更新单个包pnpm update lodashpnpm up lodash# 更新所有依赖pnpm updatepnpm up# 更新到最新版本pnpm update --latestpnpm up -L# 交互式更新pnpm update --interactivepnpm up -i删除命令:# 删除包pnpm remove lodashpnpm rm lodash# 删除多个包pnpm remove lodash express# 删除全局包pnpm remove -g lodash运行脚本:# 运行 package.json 中的脚本pnpm run buildpnpm run test# 简写pnpm buildpnpm test# 传递参数pnpm build -- --watchMonorepo 相关命令:# 在特定包中运行命令pnpm --filter <package-name> buildpnpm -F <package-name> build# 在所有包中运行pnpm -r build# 并行运行pnpm -r --parallel build# 只在有变化的包中运行pnpm -r --filter "...[origin/main]" build# 递归执行命令pnpm -r exec rm -rf node_modules查询命令:# 查看包信息pnpm info lodashpnpm view lodash# 查看已安装的包pnpm listpnpm ls# 查看全局安装的包pnpm list -g# 查看依赖树pnpm list --depth=2# 查看过时的包pnpm outdatedStore 管理命令:# 查看 store 路径pnpm store path# 清理 storepnpm store prune# 验证 storepnpm store verify其他常用命令:# 创建项目pnpm create react-app my-apppnpm create vite# 执行包命令pnpm dlx create-react-app my-app# 导入其他锁文件pnpm import# 链接本地包pnpm link ../local-package# 检查依赖问题pnpm audit# 为什么安装了这个包pnpm why lodash配置命令:# 查看配置pnpm config list# 设置配置pnpm config set store-dir /path/to/store# 删除配置pnpm config delete store-dir命令对比:| npm | yarn | pnpm ||-----|------|------|| npm install | yarn | pnpm install || npm add lodash | yarn add lodash | pnpm add lodash || npm run build | yarn build | pnpm build || npm update | yarn upgrade | pnpm update || npm remove lodash | yarn remove lodash | pnpm remove lodash |
阅读 0·3月6日 23:39

pnpm 的 overrides 和 resolutions 有什么区别?如何使用?

pnpm 提供了强大的依赖覆盖机制,主要通过 overrides 和 resolutions 来实现。pnpm.overrides:用于强制覆盖依赖版本,无论原始依赖声明什么版本。// package.json{ "pnpm": { "overrides": { "lodash": "^4.17.21", "react": "^18.0.0" } }}使用场景:修复安全漏洞{ "pnpm": { "overrides": { "minimist@<1.2.6": "^1.2.6" } }}强制统一版本{ "pnpm": { "overrides": { "typescript": "^5.0.0" } }}替换包{ "pnpm": { "overrides": { "node-sass": "sass" } }}路径覆盖:{ "pnpm": { "overrides": { "react": "$react", // 使用项目中的版本 "webpack>lodash": "^4.17.21" // 只覆盖 webpack 的 lodash } }}resolutions(Yarn 兼容):// package.json{ "resolutions": { "lodash": "^4.17.21" }}区别对比:| 特性 | pnpm.overrides | resolutions ||------|----------------|-------------|| 适用范围 | 所有依赖 | 所有依赖 || 优先级 | 高 | 中 || Yarn 兼容 | ❌ | ✅ || 路径指定 | ✅ | ❌ || 版本引用 | ✅ | ❌ |实际应用示例:// 复杂覆盖场景{ "dependencies": { "react": "^18.0.0", "react-dom": "^18.0.0", "antd": "^5.0.0" }, "pnpm": { "overrides": { // 确保所有包使用相同 React 版本 "react": "$react", "react-dom": "$react-dom", // 修复 antd 的某个依赖漏洞 "antd>rc-util": "^5.30.0", // 替换废弃的包 "request": "axios" } }}验证覆盖效果:# 查看实际安装的版本pnpm list react# 查看依赖树pnpm list --depth=10# 查看为什么安装了这个版本pnpm why lodash注意事项:谨慎使用全局覆盖// ❌ 不推荐:全局覆盖可能导致不兼容{ "pnpm": { "overrides": { "react": "^18.0.0" } }}// ✅ 推荐:路径指定更精确{ "pnpm": { "overrides": { "some-package>react": "^18.0.0" } }}更新锁文件# 修改 overrides 后需要重新安装pnpm install# 或强制更新pnpm install --force
阅读 0·3月6日 23:39

Shell 脚本中如何进行进程管理?如何启动、监控和终止进程?

Shell 脚本中的进程管理包括启动、监控、终止进程以及进程间通信等操作。进程启动后台运行# 在后台运行命令command &# 后台运行并忽略挂起信号nohup command &# 使用 disown 将进程从当前 shell 分离command &disown %1子进程# 使用括号创建子进程(command1; command2) &# 子进程中的变量不影响父进程( local_var="child" echo "Child: $local_var")echo "Parent: $local_var" # 输出: Parent: (空)进程监控查看进程# 查看所有进程ps aux# 查看特定用户的进程ps -u username# 查看特定进程ps -p PID# 实时监控进程tophtop# 查看进程树pstree查找进程# 使用 grep 查找进程ps aux | grep "nginx"# 使用 pgrep 查找进程 IDpgrep nginx# 使用 pidof 查找进程 IDpidof nginx# 查找进程详细信息ps -p $(pgrep nginx) -o pid,ppid,cmd监控进程状态# 检查进程是否运行if ps -p PID > /dev/null; then echo "Process is running"else echo "Process is not running"fi# 使用 pgrep 检查if pgrep -x "nginx" > /dev/null; then echo "nginx is running"fi进程终止终止进程# 发送 SIGTERM 信号(正常终止)kill PID# 发送 SIGKILL 信号(强制终止)kill -9 PID# 按名称终止进程pkill process_name# 终止所有匹配的进程killall process_name信号类型# 常用信号kill -l # 列出所有信号# SIGHUP (1) - 重新加载配置kill -1 PID# SIGINT (2) - 中断(Ctrl+C)kill -2 PID# SIGTERM (15) - 正常终止kill -15 PID# SIGKILL (9) - 强制终止kill -9 PID等待进程# 启动后台进程command &PID=$!# 等待进程完成wait $PID# 等待所有后台进程wait# 检查进程退出状态wait $PIDexit_status=$?echo "Exit status: $exit_status"进程间通信信号通信# 捕获信号的脚本#!/bin/bashtrap 'echo "Received SIGINT"; exit' INTtrap 'echo "Received SIGTERM"; exit' TERMwhile true; do echo "Running..." sleep 1done管道通信# 匿名管道command1 | command2# 命名管道(FIFO)mkfifo /tmp/my_pipecat > /tmp/my_pipe &cat < /tmp/my_pipe文件通信# 使用文件进行通信lock_file="/tmp/script.lock"# 创建锁文件if [ -f "$lock_file" ]; then echo "Script is already running" exit 1fitouch "$lock_file"# 执行任务# ...# 删除锁文件rm -f "$lock_file"实际应用示例进程守护脚本#!/bin/bash# 守护进程脚本daemon_process() { local command="$1" local lock_file="/tmp/${command}.lock" # 检查锁文件 if [ -f "$lock_file" ]; then local pid=$(cat "$lock_file") if ps -p "$pid" > /dev/null 2>&1; then echo "$command is already running (PID: $pid)" return 1 else rm -f "$lock_file" fi fi # 启动进程 $command & local pid=$! echo $pid > "$lock_file" echo "Started $command (PID: $pid)" return 0}# 使用守护进程daemon_process "nginx"进程监控脚本#!/bin/bash# 监控进程脚本monitor_process() { local process_name="$1" local max_memory="$2" # MB local max_cpu="$3" # % while true; do # 获取进程信息 local pid=$(pgrep -x "$process_name") if [ -z "$pid" ]; then echo "$process_name is not running" sleep 5 continue fi # 获取内存使用 local memory=$(ps -p "$pid" -o rss= | awk '{print $1/1024}') # 获取 CPU 使用 local cpu=$(ps -p "$pid" -o %cpu=) echo "$process_name (PID: $pid) - Memory: ${memory}MB, CPU: ${cpu}%" # 检查是否超过阈值 if (( $(echo "$memory > $max_memory" | bc -l) )); then echo "Warning: Memory usage exceeds ${max_memory}MB" fi if (( $(echo "$cpu > $max_cpu" | bc -l) )); then echo "Warning: CPU usage exceeds ${max_cpu}%" fi sleep 5 done}# 使用监控脚本monitor_process "nginx" 512 80批量进程管理#!/bin/bash# 批量终止进程kill_processes() { local pattern="$1" local signal="${2:-15}" local pids=$(pgrep -f "$pattern") if [ -z "$pids" ]; then echo "No processes found matching: $pattern" return 1 fi echo "Killing processes: $pids" kill -$signal $pids sleep 2 # 检查进程是否仍在运行 for pid in $pids; do if ps -p "$pid" > /dev/null 2>&1; then echo "Process $pid still running, forcing kill" kill -9 "$pid" fi done echo "All processes killed"}# 使用批量终止kill_processes "python.*script.py"进程重启脚本#!/bin/bash# 进程重启脚本restart_process() { local process_name="$1" local command="$2" echo "Restarting $process_name..." # 终止进程 local pid=$(pgrep -x "$process_name") if [ -n "$pid" ]; then echo "Stopping $process_name (PID: $pid)" kill "$pid" sleep 2 # 检查是否仍在运行 if ps -p "$pid" > /dev/null 2>&1; then echo "Force killing $process_name" kill -9 "$pid" fi fi # 启动进程 echo "Starting $process_name..." $command & local new_pid=$! sleep 2 # 检查是否启动成功 if ps -p "$new_pid" > /dev/null 2>&1; then echo "$process_name restarted successfully (PID: $new_pid)" return 0 else echo "Failed to start $process_name" return 1 fi}# 使用重启脚本restart_process "nginx" "nginx -g 'daemon off;'"进程管理最佳实践使用 nohup 和 disown: 确保进程在 shell 退出后继续运行使用锁文件: 防止重复启动进程优雅地终止进程: 优先使用 SIGTERM 而不是 SIGKILL监控进程状态: 定期检查进程是否正常运行记录进程信息: 便于问题排查和调试使用信号处理: 实现优雅的进程关闭设置资源限制: 防止进程占用过多资源使用进程组: 便于管理相关进程
阅读 0·3月6日 23:38

Shell 脚本中如何进行错误处理和调试?有哪些常用的技巧?

Shell 脚本中的错误处理和调试技巧对于编写健壮的脚本非常重要。错误处理退出状态码# 检查命令执行状态commandif [ $? -eq 0 ]; then echo "Command succeeded"else echo "Command failed with exit code $?"fi# 使用 && 和 || 进行条件执行command1 && command2 # command2 仅在 command1 成功时执行command1 || command2 # command2 仅在 command1 失败时执行set 命令选项# set -e: 任何命令失败时立即退出set -ecommand1command2 # 如果 command1 失败,不会执行# set -u: 使用未定义变量时报错set -uecho $undefined_var # 报错并退出# set -o pipefail: 管道中任何命令失败时返回失败状态set -o pipefailcommand1 | command2 # 如果 command1 失败,整个管道失败# 组合使用set -euo pipefail错误处理函数# 错误处理函数error_exit() { echo "Error: $1" >&2 exit 1}# 检查文件是否存在check_file() { if [ ! -f "$1" ]; then error_exit "File $1 not found" fi}# 使用函数check_file "config.txt"trap 命令# 捕获退出信号cleanup() { echo "Cleaning up..." rm -f /tmp/tempfile exit}# 捕获 EXIT 信号trap cleanup EXIT# 捕获 INT 信号(Ctrl+C)trap cleanup INT# 捕获多个信号trap cleanup EXIT INT TERM调试技巧调试模式# 启用调试模式set -x # 打印每个命令set -v # 打印输入行# 禁用调试模式set +xset +v# 组合使用set -xvcommand1command2set +xv调试输出# 使用 echo 输出调试信息echo "Debug: variable = $variable"# 使用 printf 格式化输出printf "Debug: %s = %s\n" "variable" "$variable"# 使用 >&2 输出到标准错误echo "Debug info" >&2调试函数# 调试函数debug() { if [ "$DEBUG" = "true" ]; then echo "[DEBUG] $*" >&2 fi}# 使用调试函数DEBUG=truedebug "Processing file: $filename"变量追踪# 显示变量值echo "var1 = $var1"echo "var2 = $var2"# 使用 declare 显示变量信息declare -p var1declare -p var2# 显示所有变量declare -p# 显示所有函数declare -f实际应用示例健壮的脚本模板#!/bin/bash# 设置错误处理set -euo pipefail# 定义错误处理函数error_exit() { echo "Error: $1" >&2 exit 1}# 定义清理函数cleanup() { echo "Cleaning up..." [ -n "$tempfile" ] && rm -f "$tempfile"}# 设置陷阱trap cleanup EXIT INT TERM# 定义调试函数debug() { if [ "${DEBUG:-false}" = "true" ]; then echo "[DEBUG] $*" >&2 fi}# 主函数main() { debug "Starting script" # 检查参数 if [ $# -lt 1 ]; then error_exit "Usage: $0 <filename>" fi local filename="$1" debug "Processing file: $filename" # 检查文件 if [ ! -f "$filename" ]; then error_exit "File not found: $filename" fi # 创建临时文件 tempfile=$(mktemp) || error_exit "Failed to create temp file" debug "Created temp file: $tempfile" # 处理文件 cp "$filename" "$tempfile" # 处理逻辑... debug "Script completed successfully"}# 执行主函数main "$@"带错误检查的文件操作#!/bin/bash# 安全的文件复制safe_copy() { local src="$1" local dst="$2" # 检查源文件 if [ ! -f "$src" ]; then echo "Error: Source file not found: $src" >&2 return 1 fi # 检查目标目录 local dst_dir=$(dirname "$dst") if [ ! -d "$dst_dir" ]; then echo "Error: Destination directory not found: $dst_dir" >&2 return 1 fi # 检查写权限 if [ ! -w "$dst_dir" ]; then echo "Error: No write permission for: $dst_dir" >&2 return 1 fi # 执行复制 if ! cp "$src" "$dst"; then echo "Error: Failed to copy $src to $dst" >&2 return 1 fi echo "Successfully copied $src to $dst" return 0}# 使用函数safe_copy "source.txt" "destination.txt" || exit 1带重试的操作#!/bin/bash# 带重试的函数retry_command() { local max_attempts="$1" shift local command=("$@") local attempt=1 while [ $attempt -le $max_attempts ]; do echo "Attempt $attempt of $max_attempts" if "${command[@]}"; then echo "Command succeeded" return 0 fi attempt=$((attempt + 1)) sleep 2 done echo "Command failed after $max_attempts attempts" >&2 return 1}# 使用函数retry_command 3 curl -s http://example.com || exit 1日志记录#!/bin/bash# 日志函数log() { local level="$1" shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a script.log}log_info() { log "INFO" "$@"}log_error() { log "ERROR" "$@"}log_debug() { if [ "${DEBUG:-false}" = "true" ]; then log "DEBUG" "$@" fi}# 使用日志函数log_info "Script started"log_debug "Debug information"log_error "An error occurred"调试最佳实践使用 set -euo pipefail: 提高脚本的健壮性使用 trap 进行清理: 确保资源被正确释放使用调试函数: 便于控制调试输出记录日志: 便于问题追踪和调试检查命令状态: 使用 $? 或 if 语句使用有意义的错误消息: 帮助快速定位问题测试边界情况: 确保脚本在各种情况下都能正常工作使用变量默认值: ${VAR:-default} 防止未定义变量错误
阅读 0·3月6日 23:38