面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

前端阅读 05月30日 21:21

RxJS 中 switchMap、mergeMap、concatMap 该怎么选?

switchMap、mergeMap、concatMap 都是把外层值映射成内部 Observable,再把结果摊平,但它们处理“新任务来了,旧任务怎么办”的策略不同。switchMap 会取消旧任务,只要最新结果;mergeMap 会并发执行,所有结果都要;concatMap 会排队,一个完成后再做下一个。追问为什么搜索框通常用 switchMap?用户连续输入时,旧关键词请求已经没有展示价值。switchMap 会退订上一次内部流,避免慢请求晚返回覆盖新结果。mergeMap 有什么风险?mergeMap 默认不限制并发,外层值很多时会同时打出大量请求。实际项目常用第二个参数限制并发数。concatMap 为什么慢但常用?它故意排队,前一个内部 Observable 不 complete,后一个不会开始。适合顺序保存、支付步骤、分片上传。和 exhaustMap 怎么区分?exhaustMap 忙的时候忽略新任务,适合防重复提交。switchMap 抛弃旧的保留新的,exhaustMap 保留旧的忽略新的。写段代码searchText$.pipe( debounceTime(300), distinctUntilChanged(), switchMap(q => apiSearch(q).pipe(catchError(() => of([]))))).subscribe(renderList);
前端阅读 05月30日 21:21

RxJS 6 升级到 RxJS 7 时要注意哪些变化?

RxJS 7 不是把 RxJS 6 推倒重来,而是在保持 pipe 操作符模型的基础上,重点修了类型、弃用 API、Promise 转换和多播写法。项目从 6 升到 7,通常不用大面积重写业务流,但要重点检查 throwError、toPromise、combineLatest/concat/merge 的静态调用、shareReplay 配置,以及 TypeScript 版本和严格类型报错。追问最大的破坏性变化是什么?toPromise 被废弃,应该改成 firstValueFrom 或 lastValueFrom。前者拿第一个值后退订,后者等流 complete 后取最后一个值。throwError 为什么改成工厂函数?RxJS 7 推荐 throwError(() => error),错误对象会在订阅时创建,堆栈更准确。导入路径需要怎么改?多数项目仍从 rxjs 和 rxjs/operators 导入。更重要的是清理 rxjs/internal/* 深层导入。shareReplay 有什么坑?缓存 HTTP 时常写 shareReplay(1),但要考虑 refCount 和重置策略,否则长期服务可能一直持有缓存。写段代码const source$ = throwError(() => new Error('request failed'));const value = await firstValueFrom(apiResult$);
服务端阅读 05月30日 21:21

RxJS 在 Angular 项目中通常怎么用?

在 Angular 里,RxJS 主要用来处理会随时间变化的数据:HTTP 请求、路由参数、响应式表单、组件事件和应用状态。HttpClient 返回 Observable,路由的 paramMap、表单的 valueChanges 也是 Observable,所以 Angular 项目不是额外引入 RxJS,而是日常开发天然会碰到它。追问HTTP 为什么返回 Observable 而不是 Promise?Observable 可以取消、组合,也能和表单、路由这些流保持同一套写法。单次 HTTP 看起来和 Promise 差不多,但需要重试、超时、取消旧请求时更顺手。AsyncPipe 解决了什么问题?它自动订阅 Observable,并在组件销毁时退订,减少内存泄漏。复杂副作用仍应放在组件或服务中组织。表单搜索一般怎么写?监听 valueChanges,先过滤空值,再防抖、去重,最后用 switchMap 请求接口。关键是旧请求要能被取消。BehaviorSubject 适合做全局状态吗?小型状态可以,比如当前用户、筛选条件、侧边栏开关。状态变多或需要调试时,NgRx、Signal Store 更稳。写段代码results$ = this.form.controls.keyword.valueChanges.pipe( filter(v => !!v && v.length >= 2), debounceTime(300), distinctUntilChanged(), switchMap(q => this.api.search(q)));
前端阅读 05月30日 21:21

RxJS 性能优化应该从哪些地方下手?

RxJS 性能优化先看三件事:有没有重复订阅,有没有处理过多无效事件,有没有订阅生命周期失控。多数慢不是操作符本身慢,而是同一个 HTTP Observable 被订阅多次、输入框每个字符都打接口、组件销毁后流还在跑。优化时不要一上来堆操作符,先用浏览器 Network、Performance 和简单日志确认瓶颈在哪里。追问share 和 shareReplay 应该怎么选?share 只共享当前订阅期,晚来的订阅者拿不到历史值。shareReplay(1) 会回放最近一次结果,适合 HTTP 缓存,但要注意 refCount 和生命周期。为什么搜索框通常用 switchMap?用户连续输入时,旧关键词的请求结果已经没价值了。switchMap 会取消上一次内部订阅,避免慢请求晚返回覆盖新结果。mergeMap 限制并发有什么意义?批量请求如果不限制并发,浏览器连接数、服务端限流和内存都会被打满。可以用第二个参数控制同时执行数量。怎么避免内存泄漏?模板里优先用 async pipe;必须手动订阅时,用 takeUntilDestroyed() 或明确的 takeUntil(destroy$)。写段代码from(ids).pipe( mergeMap(id => api.load(id), 3)).subscribe();
服务端阅读 05月30日 21:21

SSH 协议是如何建立安全连接的?

SSH 不是“把密码加密后发给服务器”这么简单。它先通过 TCP 连接到服务端 22 端口,双方确认协议版本和可用算法,再用密钥交换算法协商出临时会话密钥;后续真正传输命令、文件和端口转发数据时,主要靠对称加密保证速度,靠 MAC 或 AEAD 保证完整性。服务器会拿出自己的主机公钥证明“我就是你上次连过的那台机器”,客户端再用密码、公钥或键盘交互完成用户认证。追问为什么 SSH 既用非对称加密又用对称加密?非对称加密适合身份验证和安全协商,但计算成本高,不适合持续传输大量数据。SSH 用它安全商量密钥,之后切到 AES、ChaCha20 这类对称算法。known_hosts 文件有什么用?known_hosts 记录服务器主机公钥指纹,用来发现你是否连到了同一台服务器。生产环境指纹突然变化,要先确认是否重装、迁移或被中间人劫持。公钥登录比密码登录安全吗?通常更安全,因为私钥不需要发到网络上,服务端只验证签名结果。边界是私钥文件必须有口令和合理权限。SSH 能防住所有中间人攻击吗?前提是客户端正确校验服务器主机指纹。第一次连接就接受了伪造指纹,SSH 也无法凭空知道对方是假的。写段配置PasswordAuthentication noPermitRootLogin noAllowUsers deployPubkeyAuthentication yes
服务端阅读 05月30日 20:53

SSH 公钥认证为什么更安全?如何正确配置?

SSH 公钥认证的核心不是“把公钥当密码”,而是服务器用公钥验证客户端是否持有对应私钥。登录时私钥不会离开本机,客户端只用私钥对服务器给出的挑战做签名;服务器拿 authorized_keys 里的公钥验签,通过才放行。相比密码认证,它更抗撞库,也更适合自动化。追问Ed25519、RSA、ECDSA 选哪个?新环境优先 Ed25519,密钥短、速度快、安全边界清晰。需要兼容老系统时再考虑 RSA 4096。私钥还需要密码短语吗?需要,尤其是笔记本或开发机上的私钥。密码短语能在文件被复制走时多挡一层,配合 ssh-agent 使用也不会频繁输入。authorized_keys 有公钥仍失败怎么办?先查权限,目录或文件可写范围太大时 sshd 会拒绝使用。再用 ssh -vv 看客户端是否提供了对应私钥。禁用密码认证前注意什么?先确认至少有一个普通用户能用密钥登录,并保留当前会话。云主机还要确认控制台救援方式可用。写段命令ssh-keygen -t ed25519 -C "deploy@company" -f ~/.ssh/id_ed25519_prodssh-copy-id -i ~/.ssh/id_ed25519_prod.pub user@hostchmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
服务端阅读 05月30日 20:53

SSH 端口转发怎么用?-L、-R、-D 分别适合什么场景?

SSH 端口转发就是用 SSH 连接临时搭一条加密通道,把某个端口流量转到另一个网络位置。常见参数是 -L、-R、-D:-L 是本地端口转发,-R 是远程端口转发,-D 是动态 SOCKS 代理。先判断流量入口在哪一端,就不容易选错。追问-L、-R、-D 一句话怎么区分?-L 是本机开入口访问远端资源;-R 是远端开入口访问本地资源;-D 是本机开 SOCKS 代理,目标动态决定。为什么建议绑定 127.0.0.1?绑定本地只允许本机访问,安全边界更小。绑定 0.0.0.0 可能让局域网甚至公网用户连上这个端口。端口被占用怎么办?先用 lsof -i :3307 或 ss -tlnp 找占用进程。临时调试可以换端口,不要随便杀不认识的进程。和反向代理有什么区别?SSH 转发偏临时、按连接建立;Nginx/Caddy 适合长期对外服务,有 TLS、日志、限流和路由能力。写段命令ssh -N -L 127.0.0.1:3307:db.internal:3306 user@jumpssh -N -R 8080:localhost:3000 user@publicssh -N -D 127.0.0.1:1080 user@jump
服务端阅读 05月30日 20:53

SSH 配置文件哪些选项最影响安全和连接稳定性?

SSH 配置文件要分清两类:~/.ssh/config 管客户端怎么连,/etc/ssh/sshd_config 管服务器允许谁连。客户端重点是减少手输参数、隔离不同环境密钥;服务端重点是关掉高风险入口,如 root 登录、密码登录、无用转发。不要整段复制加固清单,应该按访问路径逐项验证。追问sshconfig 和 sshdconfig 最大区别是什么?前者是客户端读的,影响你发起连接时带哪些参数;后者是服务端读的,决定连接是否被允许以及认证策略。为什么不建议关闭 StrictHostKeyChecking?它会让客户端不再认真校验主机指纹,中间人攻击时更难发现。自动化脚本应预先分发 known_hosts。改端口能提升安全吗?只能减少低级扫描噪声,不是真正安全。核心仍是禁用密码、限制用户、控制来源 IP 和保留审计日志。修改服务端配置怎样避免锁外面?保留当前 SSH 会话,另开新终端测试登录。修改前跑 sudo sshd -t,通过后再 reload。写段配置Host prod HostName prod.example.com User deploy IdentityFile ~/.ssh/id_ed25519_prod IdentitiesOnly yes ServerAliveInterval 60PermitRootLogin noPasswordAuthentication noPubkeyAuthentication yes
服务端阅读 05月30日 20:53

SSH 密钥交换算法是如何生成会话密钥的?

SSH 密钥交换负责让客户端和服务器在不可信网络上协商出同一个会话密钥,重点不是传输密钥,而是双方各自算出相同结果。现代 SSH 通常优先 curve25519-sha256,它速度快、实现成熟,并具备前向保密。传统 diffie-hellman-group1-sha1、group14-sha1 不建议继续启用。追问密钥交换和公钥认证有什么区别?密钥交换是建立加密信道,发生在登录认证之前。公钥认证是证明“你是谁”,不负责生成会话密钥。Curve25519 为什么常被推荐?它计算快、密钥短,实现上不容易踩参数选择坑。新版 OpenSSH 兼容性也足够好。禁用 SHA-1 会影响老客户端吗?会。老系统可能只支持 group14-sha1,若必须兼容,建议给遗留入口单独开策略。前向保密解决什么问题?它防的是长期私钥后来泄露,也不能直接解过去抓到的流量;但不解决当前服务器被入侵的问题。写段配置KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512ssh -Q kexssh -vv user@host
服务端阅读 05月30日 20:53

SSH 加密算法如何选择才兼顾安全和性能?

SSH 加密算法主要分三类:会话数据用对称加密保护,身份和签名用非对称算法完成,完整性由 MAC 或 AEAD 兜底。实际配置时优先保留现代 OpenSSH 支持的组合:aes256-gcm@openssh.com、chacha20-poly1305@openssh.com、aes256-ctr。CBC、3DES 只适合遗留系统,不应出现在新服务器默认列表里。追问AES-GCM 和 ChaCha20-Poly1305 怎么选?有 AES-NI 的服务器优先 AES-GCM,吞吐通常更好。ARM 或老 CPU 上,ChaCha20-Poly1305 往往更稳定。为什么不建议 CBC 和 3DES?CBC 历史上暴露过填充相关攻击面,3DES 块大小和性能都不适合现代长连接。只配置 Ciphers 就够了吗?不够,SSH 安全还依赖 KEX、HostKey、MAC。使用 CTR 时,MAC 建议选 *-etm@openssh.com。改配置最容易踩什么坑?可能把旧客户端挡在门外。生产应保留备用会话,sshd -t 通过后再 reload。写段配置Ciphers aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctrMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
服务端阅读 05月30日 20:53

SSH Agent 是什么?如何安全管理私钥和转发认证?

SSH Agent 是替你保管“已解锁私钥”的本地进程。它不会把私钥直接交给 ssh、git 或远程主机,而是在需要认证时完成签名,所以你不用每次连接都输入密钥密码。它解决效率问题,但 Agent 转发用错会放大安全风险。追问SSH Agent 和私钥文件是什么关系?私钥文件仍在磁盘上,Agent 只是把解锁后的密钥能力暂存在内存里。认证时 Agent 用私钥签名,不返回私钥内容。为什么不建议全局开启 ForwardAgent?远程机器可能使用转发过来的 agent socket。它偷不走私钥,但能在会话有效时冒用你的身份连接其他服务。Agent 转发和 ProxyJump 怎么选?只是通过跳板机连接内网服务器,用 ProxyJump 更干净。只有登录跳板机后还要 git pull 或 ssh internal,才考虑 Agent 转发。ssh-add 没有 identities 怎么办?说明 agent 正常但没加密钥。执行 ssh-add ~/.ssh/id_ed25519,再用 ssh-add -l 确认。写段命令eval "$(ssh-agent -s)"ssh-add -t 3600 ~/.ssh/id_ed25519ssh-add -lssh-add -D
服务端阅读 05月30日 20:53

SSH 隧道有哪几种?本地、远程和动态转发怎么选?

SSH 隧道本质是把一段 TCP 流量塞进 SSH 加密连接里传输。常见类型有本地隧道 -L、远程隧道 -R、动态隧道 -D。选型先看入口在哪边:你在本地访问远端内网服务,用 -L;远端要访问你本地服务,用 -R;目标很多且不固定,用 -D 做 SOCKS 代理。追问本地隧道和远程隧道最大区别是什么?区别在入口端口开在哪里。-L 入口在本地,适合“我访问远端”;-R 入口在远端,适合“远端访问我”。远程隧道为什么别人访问不到?远端端口默认可能只监听 127.0.0.1。要检查 GatewayPorts、AllowTcpForwarding、安全组和防火墙。动态隧道和 VPN 怎么取舍?ssh -D 轻量临时,只代理支持 SOCKS 的应用;VPN 覆盖整机路由,更适合长期接入内网。长连接怎么稳定?加 ServerAliveInterval 和 ServerAliveCountMax,长期运行用 autossh 或 systemd 管理。写段命令ssh -L 3307:db.internal:3306 user@jumpssh -R 8080:localhost:3000 user@publicssh -D 127.0.0.1:1080 user@jump
服务端阅读 05月30日 20:53

SSH 连接失败怎么排查?常见故障如何解决?

SSH 故障排查不要一上来就改配置,先判断卡在哪一层:网络能不能到、端口有没有开、sshd 是否监听、认证是否通过、客户端是否选错密钥。timeout 多半是包没到服务端,refused 是端口没人接,Permission denied 则说明已经连到服务但认证失败。追问timeout 和 refused 有什么区别?timeout 优先看安全组、防火墙、路由和端口开放;refused 通常说明 sshd 没监听对应端口或服务没起来。先用 nc -zv host 22 验证。authorized_keys 有公钥还失败怎么办?最常见是权限太开放、用户名不对、客户端没用对应私钥。用 ssh -vvv 看实际尝试的 key,再查服务端 auth.log 或 journalctl。可以关闭 StrictHostKeyChecking 吗?不建议。它会绕过主机身份校验,遇到 DNS 污染或中间人攻击风险更高。自动化脚本应预先分发 known_hosts。改 sshd_config 最怕什么?最怕语法错误或禁掉当前登录方式后断开连接。修改前跑 sshd -t,保留当前会话,再开新窗口测试。写段命令nc -zv hostname 22ssh -vvv user@hostnamesudo systemctl status sshdsudo sshd -t
服务端阅读 05月30日 20:53

Vercel 是什么?核心功能适合哪些项目?

Vercel 是面向现代前端和全栈应用的部署平台。典型流程是代码推到 GitHub、GitLab 或 Bitbucket 后,Vercel 自动构建、生成预览地址,并把生产版本发布到全球边缘网络。它不是让你登录机器、装 Nginx、配证书的传统服务器,而是把构建、部署、CDN、SSL、预览环境和 Serverless 能力打包成一套工作流。追问Vercel 解决的核心问题是什么?它解决分支预览、证书域名、环境变量和自动部署这些前端团队常见痛点。提交 PR 就有预览地址,产品和测试可以直接验收真实页面。Vercel 只适合 Next.js 吗?不是,React、Vue、SvelteKit、Nuxt、Astro 都能部署。只是 Next.js 在 Vercel 上支持最完整,SSR、ISR、Route Handler、Middleware 的坑更少。Serverless Functions 能替代后端吗?能替代表单、Webhook、鉴权代理、简单聚合这类轻量接口。不适合长任务、持续连接、复杂队列消费和高状态依赖服务。什么时候不适合用 Vercel?如果系统依赖长时间运行后台服务、固定出口 IP、复杂内网或大量文件处理,就要谨慎。Vercel 更适合作为前端层和轻量 API 层。团队协作最大价值是什么?Preview Deployment 最明显。每个 PR 都能被真实访问,设计和产品不必只看截图或等测试环境排期。
服务端阅读 05月30日 20:53

Vercel 环境变量怎么配置才安全?

Vercel 环境变量管理的核心,是把 Production、Preview、Development 三类环境隔离开。生产变量只给线上部署用,预览变量给分支和 PR,用来接测试库和沙箱服务,本地变量通过 vercel env pull 拉到 .env.local。真正容易出事故的不是不会添加变量,而是变量放错环境、前端误暴露密钥、改完变量后忘记重新部署。追问Production、Preview、Development 怎么取舍?Production 放真实数据库、正式支付密钥和线上 API。Preview 应该接测试库、沙箱支付和临时回调地址,避免 PR 预览页误操作生产数据。为什么变量改了页面还是旧配置?构建期读取的变量已经写进产物,改 Dashboard 不会改变旧部署。关键变量改完后要重新部署,或者确认代码是在运行时读取变量。前端变量为什么容易泄露?Next.js 中带 NEXT_PUBLIC_ 前缀的变量会打进浏览器端代码。数据库密码、私有 Token、Webhook Secret 绝不能加这个前缀。vercel.json 适合管什么?适合放构建命令、输出目录、重定向、headers、函数资源限制,不适合直接写敏感值。配置越多,迁移和排错成本越高。本地和线上变量不一致怎么排查?先执行 vercel env pull .env.local,再确认变量名大小写、环境选择、部署时间和本地服务是否重启。写段命令vercel env add MY_API_KEY productionvercel env add MY_API_KEY previewvercel env pull .env.local
服务端阅读 05月30日 20:38

为什么 Next.js 部署到 Vercel 通常更省心?

Next.js 部署到 Vercel 省心,核心原因是框架能力和平台能力基本对齐。Vercel 能自动识别 Next.js 项目,处理构建命令、路由、静态资源、图片优化、ISR 缓存、API Routes 和中间件。开发者把代码推到 Git 后,就能得到 Production 和 Preview 两套部署链路。追问Vercel 是 Next.js 的唯一最佳选择吗?不是唯一,但通常是体验最顺的选择,尤其是使用 App Router、ISR、图片优化和预览部署时。其他平台也能部署,只是可能要手动适配。ISR 在 Vercel 上有什么优势?Vercel 能把 ISR 的缓存、后台再生成和 CDN 分发串起来。需要注意 revalidate 时间不能乱设,太短会增加回源压力。API Routes 都应该放在 Vercel 吗?轻量接口、鉴权回调和页面数据聚合适合;长任务、复杂事务、文件处理和高频数据库写入不一定适合。Preview Deployment 的价值是什么?每个 PR 都能生成独立预览地址,改 UI、验 SEO、看接口环境更快。坑在于 Preview 环境变量和数据库要隔离。
服务端阅读 05月30日 20:38

Vercel 自定义域名和 SSL 怎么配置才不踩坑?

在 Vercel 配自定义域名,流程是进入项目 Settings 的 Domains,添加根域名或子域名,再按提示到 DNS 服务商添加记录。根域名通常指向 Vercel 的 A 记录,www、blog、app 这类子域名一般用 CNAME 指向 cname.vercel-dns.com。DNS 生效后,Vercel 会自动签发和续期 SSL 证书。追问根域名和 www 怎么选?两者都可以,但要选一个主域名,另一个做 301 跳转,避免搜索引擎看到重复页面。SSL 一直 Pending 怎么排查?先检查 DNS 记录是否和 Vercel 提示一致,再看是否有 CAA 记录阻止证书机构签发。Cloudflare 还要确认代理和 SSL 模式。Cloudflare 能和 Vercel 一起用吗?可以,但不要两边都做过多缓存和重写。常见做法是 Cloudflare 管 DNS 和安全策略,应用部署交给 Vercel。上线前最容易漏什么?常漏 301 跳转、旧 URL、Cookie Domain、OAuth 回调地址和搜索控制台验证。域名切换前逐项检查。
服务端阅读 05月30日 20:38

Vercel Serverless Functions 适合什么场景,有哪些限制?

Vercel Serverless Functions 适合把轻量后端逻辑放在前端项目旁边,例如表单提交、Webhook、登录回调、BFF 聚合接口、简单数据库读写。它不用维护服务器,随部署一起发布,流量低时几乎没有空转成本,流量上来时平台自动扩缩容。追问Serverless 和 Edge Functions 有什么区别?Serverless 更接近普通 Node.js 后端,生态兼容更好;Edge 更靠近用户、延迟低,但运行时 API 更受限。冷启动会不会影响线上接口?低频接口可能遇到冷启动。可以通过减少依赖体积、拆分函数、选择区域和缓存结果降低影响。数据库连接为什么容易出问题?Serverless 会并发创建函数实例,如果每个实例都新建连接,数据库连接数很快被打满。常见做法是连接池代理或 serverless 友好数据库。哪些任务不该放进去?视频处理、批量爬取、长轮询、定时大批量同步和复杂报表生成不合适,应放到队列或 worker。
服务端阅读 05月30日 20:38

Vercel 定价怎么选,Hobby 和 Pro 什么时候够用?

Vercel 定价的关键不是先看月费,而是看项目是否商业化、团队人数和资源用量。Hobby 适合个人项目、学习 Demo、开源展示站;Pro 适合客户项目、SaaS 官网、内容站和需要预览协作的团队;Enterprise 解决合规、SLA、组织权限、专属支持和复杂安全要求。追问Hobby 计划可以放商业项目吗?不建议。只要涉及客户交付、公司官网、付费产品或团队协作,就应该按 Pro 或 Enterprise 评估。Pro 最容易超的是哪几项?常见是带宽、构建分钟、函数执行时长和图片优化次数。营销活动页或媒体站尤其要关注带宽。什么时候需要 Enterprise?需要 SAML/SSO、审计、SLA、合规采购、专属支持或更细组织权限时,Enterprise 才有明显价值。如何控制成本?静态资源尽量走 CDN 缓存,接口避免每次都走 Serverless;检查构建缓存、图片尺寸、ISR revalidate 和无效预览部署。
服务端阅读 05月30日 20:38

Vercel 日志和错误排查应该怎么看?

Vercel 的错误排查可以按三类看:构建错误看 Build Logs,接口和 Serverless Function 问题看 Runtime Logs,线上体验和性能问题看 Analytics、Speed Insights 或第三方监控。高效做法不是等报错后翻日志,而是在部署、运行时、告警和错误追踪之间建立完整链路。追问Build Logs 和 Function Logs 有什么区别?Build Logs 记录部署生成过程,适合查依赖、构建命令、类型错误和环境变量缺失。Function Logs 记录线上函数执行,适合查接口报错、超时和数据库连接。为什么本地正常,Vercel 上失败?最常见是 Node.js 版本不同、环境变量没配、大小写路径在本地不敏感但线上敏感。也可能是 Vercel 运行环境访问不到内网数据库。日志里应该打印哪些信息?打印请求 ID、接口名、耗时、状态、关键业务 ID 和错误 message。不要打印完整请求体、密钥、cookie、手机号等敏感数据。函数超时怎么排查?先看外部 API 慢、数据库查询慢,还是代码做了重计算。Vercel Functions 不适合长任务,必要时拆到队列或 worker。写段代码try { return Response.json(await loadData()); }catch (err) { console.error('loadData failed', { message: err.message }); return Response.json({ error: 'Internal error' }, { status: 500 }); }