面试题手册

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

服务端阅读 05月27日 22:10

Kafka 事务消息的核心机制是什么?

Kafka 事务消息的核心机制是什么?Kafka 事务消息从 0.11 版本引入,解决的是跨分区、跨会话的原子写入问题。它不是 RocketMQ 那种"半消息+本地事务回查"的模式,而是围绕 Transaction Coordinator + 两阶段提交 实现的 Exactly-Once 语义。核心三要素:幂等 Producer(PID + Sequence Number 去重)、Transaction Coordinator(事务协调器)、__transaction_state 内部 Topic(持久化事务日志)。事务消息的完整流程是怎样的?1. 查找 CoordinatorProducer 发送 FindCoordinatorRequest,根据 transactional.id 哈希定位到某个 Broker 作为该事务的 Coordinator。2. 初始化 PIDProducer 向 Coordinator 发送 InitPidRequest。Coordinator 分配 PID 和 epoch,并将 transactional.id → PID 映射写入 __transaction_state。这一步保证新 Producer 启动后,旧的同 ID Producer 被 fence 掉(Zombie Fencing)。3. 开始事务beginTransaction() 是 Producer 本地操作,仅更新内部状态,不与 Broker 交互。4. 注册分区Producer 第一次向某个 TopicPartition 发消息前,先发 AddPartitionsToTxnRequest 给 Coordinator,注册该分区到当前事务。Coordinator 将分区信息写入事务日志。5. 发送消息Producer 正常发送 ProduceRequest,消息携带 PID、epoch、sequence number。Broker 写入日志但消息对 read_committed 消费者不可见。6. 提交消费偏移(Consume-Transform-Produce 模式)Producer 发 AddOffsetsToTxnRequest 将消费偏移纳入事务,再发 TxnOffsetCommitRequest 写入 __consumer_offsets。保证消费位移和生产消息的原子性。7. 提交或中止事务Commit:Producer 发 EndTxnRequest(commit),Coordinator 先写入 PREPARECOMMIT 到事务日志,再向各分区 Leader 发 WriteTxnMarker 写入 Control Batch(commit marker),最后写入 COMPLETECOMMIT。Abort:流程对称,写 abort marker,消费者丢弃对应消息。Consumer 如何处理事务消息?isolation.level=read_committed:Broker 端过滤,只返回已 commit 事务的消息。Consumer 还会收到 abort 标记,丢弃回滚消息。isolation.level=read_uncommitted:所有消息立即可见,包括未提交的。性能更好但可能读到脏数据。关键配置有哪些?Producer 必须配置:enable.idempotence=truetransactional.id=my-txn-idacks=allConsumer 读事务消息需配置:isolation.level=read_committedBroker 端关注 transaction.state.log.replication.factor 和 transactional.id.expiration.ms(默认 7 天过期)。事务消息的典型代码怎么写?Properties props = new Properties();props.put("bootstrap.servers", "localhost:9092");props.put("transactional.id", "order-txn-1");props.put("enable.idempotence", "true");KafkaProducer<String, String> producer = new KafkaProducer<>(props);producer.initTransactions();try { producer.beginTransaction(); producer.send(new ProducerRecord<>("orders", "key1", "value1")); producer.send(new ProducerRecord<>("inventory", "key2", "value2")); producer.commitTransaction();} catch (ProducerFencedException e) { producer.close(); // 被 fence,不可恢复} catch (KafkaException e) { producer.abortTransaction();}面试追问:事务消息有什么局限?Consumer 侧保证弱:read_committed 只能过滤未提交消息,无法阻止 Consumer 重复处理已提交消息,消费端幂等需自行保证。事务超时问题:transaction.timeout.ms 默认 60s,长事务易超时被 Coordinator 主动 abort。跨系统不原子:Kafka 事务只覆盖 Kafka 内部的生产和消费位移提交,与外部数据库的联动需自行实现补偿机制,无法做到真正的跨系统两阶段提交。
服务端阅读 05月27日 22:10

Kafka 消息丢失的原因有哪些?怎么解决?

答案概览Kafka 消息丢失发生在三个环节:Producer 发送端、Broker 存储端、Consumer 消费端。核心对策:Producer 配 acks=all + 重试,Broker 配多副本 + 禁脏选举,Consumer 关自动提交改手动确认。Producer 端:消息发出去就丢了?丢失原因:acks=0 或 acks=1,Leader 写入成功就返回,Follower 还没同步 Leader 就挂了异步发送不带回调,发送失败无感知retries 未配置,网络抖动直接丢消息解决方案:acks=allretries=3enable.idempotence=truemax.in.flight.requests.per.connection=5acks=all 要求所有 ISR 副本确认写入才算成功。enable.idempotence=true 开启幂等生产者,防止重试导致消息重复。注意:开启幂等性时 max.in.flight.requests.per.connection 需小于等于 5,否则幂等性失效。用 producer.send(record, callback) 代替 producer.send(record),在回调里处理失败逻辑。Broker 端:写进去了但读不到了?丢失原因:副本数只有 1,Broker 宕机直接丢数据Leader 崩溃后,未同步完的 Follower 被选为新 Leader(脏选举),未同步消息丢失异步刷盘,数据还在 PageCache 没落盘就宕机解决方案:default.replication.factor=3min.insync.replicas=2unclean.leader.election.enable=falsereplication.factor=3 保证三副本冗余。min.insync.replicas=2 要求至少 2 个副本在 ISR 中,否则 Producer 写入报错——宁可不可用也不丢数据。unclean.leader.election.enable=false 禁止落后副本参与选举,这是防丢的关键开关。Consumer 端:消费了但白消费了?丢失原因:enable.auto.commit=true,消息拉取后自动提交 offset,但业务还没处理完就挂了,重启后这条消息不会再投递多线程消费时,处理慢的线程还没完成,offset 已被其他线程推进解决方案:enable.auto.commit=false关闭自动提交,业务处理完成后手动调用 consumer.commitSync()。多线程场景下,每个线程维护自己的 offset,处理完再提交。消费者还需实现幂等性:同一条消息可能被重复投递(rebalance 后),用唯一标识去重。追问:acks=all 就一定不丢消息吗?不一定。如果 ISR 中只剩 Leader 自己,acks=all 退化为 acks=1。所以 min.insync.replicas=2 必须配合使用——当 ISR 不足时拒绝写入,用可用性换可靠性。配置速查| 环节 | 关键配置 | 推荐值 ||------|---------|--------|| Producer | acks | all || Producer | retries | ≥3 || Producer | enable.idempotence | true || Broker | replication.factor | 3 || Broker | min.insync.replicas | 2 || Broker | unclean.leader.election.enable | false || Consumer | enable.auto.commit | false |
服务端阅读 05月27日 22:10

Kafka 支持哪些压缩算法?生产环境怎么选?

Kafka 支持哪些压缩算法Kafka 支持 Gzip、Snappy、LZ4、Zstd 四种压缩算法,以及不压缩(none)。压缩在生产者端以 batch 为单位执行,Broker 原样存储和转发,Consumer 端解压。理解各算法的取舍是选型的关键。四种算法核心差异Gzip:压缩率最高(文本数据可达 70-80%),但 CPU 开销大、速度慢。适合带宽极度受限、对延迟不敏感的场景。Snappy:Google 出品,速度与压缩率较均衡,是 Kafka 早期版本的默认推荐。适合追求稳定、不想过度调优的常规业务。LZ4:压缩和解压速度最快,CPU 消耗极低,但压缩率一般。适合高吞吐、低延迟的实时流处理场景。Zstd:Facebook 开源,压缩率接近 Gzip,速度接近 Snappy,Kafka 2.1.0 起支持。是当前综合表现最优的选择。快速对比:| 算法 | 压缩率 | 压缩速度 | CPU 消耗 | Kafka 最低版本 ||------|--------|----------|----------|---------------|| Gzip | 最高 | 慢 | 高 | 0.8.0 || Snappy | 中等 | 快 | 低 | 0.8.0 || LZ4 | 较低 | 最快 | 极低 | 0.8.2 || Zstd | 高 | 较快 | 中等 | 2.1.0 |生产环境怎么选首选 Zstd:如果你的 Kafka 版本 >= 2.1.0,Zstd 几乎是最优解——压缩率比 Snappy 高 20-30%,速度远快于 Gzip,CPU 开销可控。高吞吐场景选 LZ4:实时计算、日志采集等对延迟敏感的场景,LZ4 的极低 CPU 开销和最快解压速度更有优势。老旧集群选 Snappy:无法升级 Kafka 版本时,Snappy 仍然是可靠的兜底方案。Gzip 适合归档:只有"带宽贵过 CPU、数据量极大、延迟无所谓"的场景才考虑 Gzip,比如离线数据同步到冷存储。配置要点# Producer 端配置compression.type=zstdbatch.size=32768linger.ms=10batch.size 和 linger.ms 直接影响压缩效果——batch 越大,同一批消息的重复模式越多,压缩率越高。但 batch 过大也会增加延迟,需要权衡。注意 compression.type=producer 是 Broker 端的默认值,表示"由 Producer 决定压缩方式",Broker 不会主动解压或重新压缩。常见追问压缩对消息顺序有影响吗? 没有。压缩以 batch 为单位,batch 内消息顺序不变,batch 之间也保持顺序。Broker 端会解压吗? 一般不会。Broker 收到压缩 batch 后直接落盘和转发。只有当 Broker 端配置了不同的 compression.type 时,才会解压再重压缩——这会浪费 CPU,应避免。Consumer 端需要配置压缩算法吗? 不需要。Kafka 在消息头中记录了压缩算法,Consumer 自动识别并解压。选压缩算法本质上是在 CPU、带宽、存储三个资源之间做权衡。先明确瓶颈在哪,再对号入座,而不是盲目追求压缩率。
服务端阅读 05月27日 22:10

Nginx 如何配置 HTTPS 和 SSL 证书?

答案Nginx 启用 HTTPS 的核心是在 server 块中监听 443 端口并指定证书与私钥路径:server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m;}同时配置 HTTP 自动跳转 HTTPS:server { listen 80; server_name example.com; return 301 https://$host$request_uri;}追问一:SSL 证书有哪些类型?怎么选?自签名证书:测试用,浏览器不信任Let's Encrypt:免费 DV 证书,90 天有效期,Certbot 自动申请与续期商业证书(OV/EV):CA 机构签发,验证组织身份,适合生产环境Let's Encrypt 申请示例:sudo certbot --nginx -d example.com -d www.example.com追问二:如何提升 HTTPS 安全性?四个关键措施:仅启用 TLS 1.2+,禁用 SSLv3 和 TLS 1.0HSTS 头,防止降级攻击:add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;OCSP Stapling,减少证书验证延迟:ssl_stapling on; ssl_stapling_verify on;强密钥,至少 2048 位 RSA 或 256 位 ECC追问三:证书链不完整怎么办?浏览器验证证书需要完整的信任链。若缺少中间证书,需将服务器证书与中间证书合并:cat example.com.crt intermediate.crt > bundle.crtNginx 中指向合并后的文件:ssl_certificate /etc/nginx/ssl/bundle.crt;追问四:多域名如何共用证书?通配符证书(*.example.com)覆盖子域SAN 证书支持多个域名,Certbot 申请时加多个 -d 参数SNI(Server Name Indication)让同一 IP 托管多张证书,Nginx 原生支持配置验证用 nginx -t,无停机重载用 nginx -s reload。
前端阅读 05月27日 22:07

Prettier 支持哪些配置文件格式?

答案Prettier 支持以下配置文件格式,按查找优先级从高到低排列:| 优先级 | 文件名 | 格式 ||--------|--------|------|| 1 | package.json 中 prettier 字段 | JSON || 2 | .prettierrc | JSON/YAML || 3 | .prettierrc.json | JSON || 4 | .prettierrc.json5 | JSON5 || 5 | .prettierrc.yml / .prettierrc.yaml | YAML || 6 | .prettierrc.toml | TOML || 7 | .prettierrc.js / .prettierrc.mjs | JS(ESM) || 8 | .prettierrc.cjs | JS(CJS) || 9 | prettier.config.js / prettier.config.mjs | JS(ESM) || 10 | prettier.config.cjs | JS(CJS) |Prettier 3.5+ 还支持 .prettierrc.ts / prettier.config.ts(TypeScript 格式),适合 TS 项目直接复用类型。选哪个? 小项目用 .prettierrc.json,简洁无歧义,编辑器能做 JSON Schema 校验;需要注释或动态逻辑时用 .prettierrc.js;TS 项目可考虑 .prettierrc.ts。// .prettierrc.json — 最常用{ "printWidth": 80, "tabWidth": 2, "semi": true, "singleQuote": true, "trailingComma": "es5", "endOfLine": "lf"}// .prettierrc.js — 需要注释时module.exports = { printWidth: 80, // 每行最大字符数 tabWidth: 2, // 缩进空格数 semi: true, // 行尾分号 singleQuote: true, // 单引号 trailingComma: "es5", endOfLine: "lf",};配置解析机制Prettier 从被格式化文件所在目录开始向上搜索配置文件,直到项目根目录,不支持全局配置,确保不同机器行为一致。如果同时存在 .editorconfig,Prettier 会将其作为基础配置,但 .prettierrc 中的选项优先。overrides 按文件类型定制{ "semi": true, "overrides": [ { "files": ["*.md", "*.json"], "options": { "tabWidth": 4 } } ]}.prettierignore类似 .gitignore,排除不需要格式化的文件:node_modulesdist*.min.js追问Q: .prettierrc 和 .prettierrc.json 有什么区别?没有本质区别。.prettierrc 优先级更高,Prettier 会先尝试按 JSON 解析,失败则尝试 YAML。.prettierrc.json 只按 JSON 解析,语义更明确,编辑器也能做 schema 校验。Q: Prettier 配置和 ESLint 冲突怎么办?安装 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的规则即可。不要两边重复定义同一规则。Q: 为什么不推荐在 package.json 中配置?package.json 的 prettier 字段容易被忽略,且该文件本身体积大、职责多,配置混在一起不利于维护和代码审查。
前端阅读 05月27日 22:04

Prettier 插件有哪些?如何开发自定义插件?

直接回答Prettier 支持两类插件:语言解析器插件(为新语言提供格式化能力)和格式化增强插件(扩展现有语言的格式化行为)。开发自定义插件需实现三个核心部分:languages(语言定义)、parsers(解析器,文本→AST)、printers(打印器,AST→格式化文本)。Prettier 插件分类语言解析器插件 — 让 Prettier 支持新语言:@prettier/plugin-php、@prettier/plugin-ruby、@prettier/plugin-pug格式化增强插件 — 对已有语言做额外处理:prettier-plugin-organize-imports:自动排序 importprettier-plugin-tailwindcss:Tailwind 类名排序prettier-plugin-sort-json:JSON 键排序安装后在 .prettierrc 的 plugins 数组中声明即可。Prettier 也会自动加载 node_modules 下匹配 prettier-plugin-* 或 @scope/prettier-plugin-* 的包。如何开发自定义插件一个完整的插件至少导出 languages、parsers、printers:// my-prettier-plugin/index.jsmodule.exports = { languages: [ { name: "MyLang", parsers: ["mylang-parse"], extensions: [".mylang"], }, ], parsers: { "mylang-parse": { parse: (text) => { // 将源码文本解析为 AST // 可借助第三方解析器(如 babel, tree-sitter 等) return customParse(text); }, astFormat: "mylang-ast", locStart: (node) => node.start, locEnd: (node) => node.end, }, }, printers: { "mylang-ast": { print: (path, options, print) => { // 将 AST 节点转换为 Prettier Doc 对象 // Doc 是 Prettier 的中间表示,支持换行、缩进等 const node = path.getValue(); // 用 concat/line/hardline 等构建输出 }, }, },};开发要点:parse 函数的输入是源码字符串,返回 AST 对象locStart/locEnd 告诉 Prettier 每个 AST 节点的位置,用于错误定位print 函数返回 Doc 对象,用 concat、hardline、indent 等 API 拼接格式化输出如果只想对已有语言做预处理,只需在 parser 中实现 preprocess 函数,无需写完整 parser/printer调试方式: 运行 prettier --plugin ./my-prettier-plugin --debug-print-doc file.mylang 可查看生成的 Doc 结构。面试追问方向如果只想在格式化前对代码做预处理(如删除注释),怎么实现? — 在对应 parser 中实现 preprocess 函数即可,不需要自定义整个 parser/printer 链路。Prettier 的 Doc 是什么? — Prettier 内部的中间表示,类似虚拟 DOM。print 返回 Doc 而非字符串,Prettier 再根据行宽等配置将 Doc "渲染"为最终输出,这样能自动处理换行和缩进。插件和 Prettier 内置语言支持的区别? — 内置语言是 Prettier 核心代码的一部分,性能和兼容性更优;插件是独立模块,更新节奏自由但需自行维护与 Prettier 版本的兼容。
前端阅读 05月27日 22:04

使用 Prettier 时常见的问题有哪些?如何解决?

Prettier 配置不生效怎么办?修改 .prettierrc 后格式化无变化,是最常见的坑。排查顺序:先用 prettier --find-config-path <file> 确认实际加载的配置文件,可能被上层目录的配置覆盖;再检查 JSON 语法是否合法(多余逗号是最常见的错误);最后重启编辑器,VS Code 会缓存配置。多配置文件共存时,Prettier 按 .prettierrc > .prettierrc.json > .prettierrc.yml > prettier.config.js 的顺序查找,找到一个就停止。用 --config 显式指定可以彻底消除歧义。 追问: .prettierignore 和 .gitignore 的区别是什么? — .prettierignore 只控制 Prettier 忽略哪些文件,不影响 Git;.gitignore 不被 Prettier 自动读取,除非用 --ignore-path .gitignore 显式指定。如何忽略特定文件的格式化?三种方式,适用场景不同:项目级忽略: .prettierignore 文件,语法与 .gitignore 一致,适合忽略 dist/、vendor/ 等目录行内忽略: // prettier-ignore 注释,只忽略紧跟的下一行// prettier-ignoreconst matrix = [ [1, 2, 3], [4, 5, 6],];部分规则覆盖: overrides 字段,为特定文件类型设置不同规则{ "overrides": [ { "files": "*.md", "options": { "proseWrap": "always" } } ]} 追问: // prettier-ignore 能忽略整个文件吗? — 不能,它只作用于紧跟的下一个语法节点。要忽略整个文件,用 .prettierignore。Prettier 和 ESLint 冲突如何解决?这是面试高频题。冲突根源:ESLint 有格式规则(如缩进、引号),Prettier 也有自己的规则,两者同时作用就会打架。标准解法分两步:安装 eslint-config-prettier,它会关闭所有与 Prettier 冲突的 ESLint 规则(可选) 安装 eslint-plugin-prettier,将 Prettier 格式问题作为 ESLint 错误报告// .eslintrc.jsmodule.exports = { extends: [ 'eslint:recommended', 'plugin:prettier/recommended' // 必须放最后,否则会被前面的配置覆盖 ]};plugin:prettier/recommended 已经包含了 eslint-config-prettier,不需要重复引入。extends 的顺序很关键——后面的配置会覆盖前面的,所以 Prettier 相关配置必须放末尾。 追问: eslint-plugin-prettier 和 prettier-eslint 有什么区别? — eslint-plugin-prettier 让 ESLint 调用 Prettier;prettier-eslint 让 Prettier 的输出再过一遍 ESLint fix。前者是主流方案。大项目格式化太慢怎么办?Prettier 3.x 原生支持 --cache,只格式化变更的文件:prettier --write --cache .配合 lint-staged 只处理暂存文件更高效:{ "lint-staged": { "*.{js,ts}": ["prettier --write"] }}升级版本也有明显收益。Prettier 3 基于 ECMAScript 2024 重写了解析器,格式化速度比 2.x 快 2-3 倍。 追问: --cache 的缓存存在哪里? — 默认存在 node_modules/.cache/prettier,可以通过 --cache-location 自定义路径。VS Code 中 Prettier 保存不格式化?检查清单:安装了 Prettier 扩展settings.json 中设置 "editor.formatOnSave": true"editor.defaultFormatter" 设为 "esbenp.prettier-vscode"如果项目有多个 formatter,需要在 "[javascript]" 等语言设置中指定{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }}多编辑器协作时,加 .editorconfig 统一基础缩进和换行符,避免 Tab/Space 之争。团队版本不一致怎么处理?锁定版本是最直接的办法:// package.json{ "devDependencies": { "prettier": "3.3.3" // 不用 ^ 前缀 }}团队用 npm ci 安装依赖,它会严格按 package-lock.json 安装,忽略 node_modules 中已有的版本。CI 环境同理,npm ci 比 npm install 更可靠。升级 Prettier 大版本时,格式化结果可能变化。正确做法:单独开分支升级,确认无异常再合入主干,不要在功能分支中顺手升级。
前端阅读 05月27日 22:03

Prettier 的 .prettierignore 怎么配置?有哪些常用规则和踩坑点?

.prettierignore 文件怎么写?.prettierignore 放在项目根目录,语法和 .gitignore 完全一致。写好它才能放心跑 prettier --write .,否则会格式化不该动的文件。node_modulesdistbuildcoverage*.min.js*.min.csspackage-lock.jsonpnpm-lock.yamlPrettier 默认会忽略哪些文件?不需要手动写,Prettier 自动忽略:版本控制目录:.git、.svn、.hgnode_modules(除非显式加 --with-node-modules)另外,Prettier 自动读取同目录下的 .gitignore,所以 .gitignore 里已经排除的文件不需要在 .prettierignore 里重复写。四种忽略模式怎么用?1. 目录忽略node_modules**/dist2. 扩展名忽略*.min.js*.min.css*.d.ts3. 路径通配符src/**/*.generated.ts!src/vendor/*.js! 是否定模式,表示"前面忽略的里面,这条除外"。4. 行内忽略(prettier-ignore 注释)不想格式化某一段代码,在上方加注释:// prettier-ignoreconst matrix = [ [1, 000, 000], [0, 1, 000], [0, 000, 1]];HTML 里用 <!-- prettier-ignore -->,Markdown 里用 <!-- prettier-ignore -->,CSS 里用 /* prettier-ignore */。每种文件类型都有对应的注释语法。CLI 相关的忽略参数有哪些?--ignore-path .prettierignore.custom:指定自定义 ignore 文件路径--with-node-modules:取消默认对 node_modules 的忽略--list-different(别名 -l):只列出不符合格式的文件名,CI 场景常用# 用自定义 ignore 文件格式化prettier --write --ignore-path .prettierignore.custom "src/**/*.js"# CI 中检查是否有未格式化的文件prettier --check "src/**/*.js"常见踩坑ignore 规则写了不生效? 检查文件是否放在项目根目录,语法是否和 .gitignore 一致。想格式化被忽略的文件? 直接传绝对路径给 prettier,ignore 规则不拦截绝对路径。.prettierignore 和 .gitignore 规则冲突怎么办? .prettierignore 优先级更高。如果 .gitignore 排除了某个文件,但 .prettierignore 没有排除,Prettier 仍会格式化它。
前端阅读 05月27日 22:03

Prettier 如何在团队协作中发挥作用?有哪些最佳实践?

核心答案Prettier 的团队协作价值是用机器执行替代人工争论,分三个层次落地:配置即合约:.prettierrc 提交到版本控制,团队共享格式规则提交即格式化:Husky + lint-staged 在 pre-commit 自动处理,开发者无感知合并即拦截:CI 中 prettier --check 失败则阻断 PR核心原则:Prettier 管格式(缩进、换行、引号),ESLint 管质量,各司其职。配置与编辑器.prettierrc 是团队格式规范的单点真相:{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 80, "endOfLine": "lf"}endOfLine: "lf" 容易被忽略——Windows 默认 CRLF,混用会产生 Git diff 噪音。编辑器侧用 .vscode/settings.json 配合保存即格式化:{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true}工作流集成Pre-commit 自动格式化(只处理暂存文件):npm install --save-dev husky lint-stagednpx husky add .husky/pre-commit "npx lint-staged"{ "lint-staged": { "*.{js,ts,tsx,json,css,md}": ["prettier --write"] }}CI 兜底检查(防止绕过 hooks 提交):- name: Check format run: npx prettier --check "src/**/*.{js,ts,tsx}"与 ESLint 协作两者冲突是落地最常见的障碍。用 eslint-config-prettier 关闭 ESLint 中与 Prettier 重叠的格式规则:npm install --save-dev eslint-config-prettier// .eslintrc.jsmodule.exports = { extends: ["eslint:recommended", "prettier"] // prettier 必须放最后}执行顺序:Prettier 先格式化,ESLint 再检查质量,反过来会互相覆盖。踩坑要点版本锁定:Prettier 2 和 3 格式化结果不同,必须锁版本历史代码:单独 PR 执行 prettier --write,混入业务变更会导致 git blame 失效Monorepo:根目录放配置,子包通过 "prettier": "../.prettierrc" 引用,防止配置漂移性能:.prettierignore 排除 dist/node_modules,Prettier 3 支持 --cache 增量处理追问Prettier "不妥协的格式化"在什么场景下反而降低效率?如何应对?Prettier 3 的破坏性变更如何平滑升级?团队成员坚持用自己偏好的格式,配置如何平衡灵活性和一致性?
前端阅读 05月27日 22:02

如何在 VS Code 和 WebStorm 中配置 Prettier?

核心答案:项目级 .prettierrc + 编辑器保存时格式化不管用什么编辑器,Prettier 配置的核心原则只有一条:把规则写在项目根目录的 .prettierrc 里,编辑器只负责触发格式化。这样团队成员用不同编辑器,跑出来的结果完全一致。{ "semi": true, "singleQuote": true, "trailingComma": "es5", "printWidth": 80, "tabWidth": 2}VS Code 怎么配?安装 esbenp.prettier-vscode 扩展,在 .vscode/settings.json 中设为默认格式化器并开启保存格式化:{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true}关键设置 prettier.requireConfig: true——强制要求项目有 .prettierrc 才格式化,防止编辑器全局配置污染项目。WebStorm 怎么配?WebStorm 内置 Prettier 支持,路径:Settings → Languages & Frameworks → JavaScript → Prettier。勾选"On save"即可。如果项目 package.json 里有 prettier 依赖,WebStorm 会自动检测并启用。Vim/Neovim 怎么配?Neovim 推荐 null-ls(现已迁移至 none-ls):local null_ls = require("null-ls")null_ls.setup({ sources = { null_ls.builtins.formatting.prettier },})vim.cmd([[autocmd BufWritePre * lua vim.lsp.buf.format({ async = false })]])coc.nvim 用户::CocInstall coc-prettier,配合 BufWritePre 自动格式化。配置冲突怎么排查?Prettier 格式化不生效,按这个顺序排查:编辑器是否将 Prettier 设为默认格式化器(VS Code 常见问题:ESLint 扩展抢占了默认格式化器)项目是否有多个 .prettierrc(检查根目录和子目录),Prettier 按最近原则读取.prettierignore 是否误排除了目标文件VS Code 中 prettier.requireConfig 与项目配置是否匹配团队统一的关键动作.prettierrc 和 .prettierignore 提交到 Git.vscode/settings.json 也提交(VS Code 用户自动生效)README 中注明 WebStorm 用户需手动开启"On save"CI 中加 prettier --check . 防止未格式化代码合入
前端阅读 05月27日 22:01

Prettier overrides 配置怎么用?常见场景有哪些?

核心答案Prettier 的 overrides 允许为不同文件或目录设置差异化的格式化规则,核心语法是在配置文件中添加 overrides 数组,每项包含 files(匹配模式)和 options(覆盖选项):{ "semi": true, "singleQuote": true, "overrides": [ { "files": ["*.css", "*.scss"], "options": { "singleQuote": false } }, { "files": "*.json", "options": { "tabWidth": 4 } } ]}文件匹配模式单扩展名: "*.json" — 匹配所有 JSON 文件多扩展名: ["*.css", "*.scss", "*.less"] — 数组形式匹配多种文件目录匹配: "src/styles/**/*" — 按路径前缀限定范围排除文件: ["**/*.js", "!**/*.min.js"] — 用 ! 前缀排除,等价于 excludeFiles 字段常见场景不同语言差异化规则 — CSS 用双引号,JS 用单引号,Markdown 保留换行:{ "overrides": [ { "files": "*.css", "options": { "singleQuote": false } }, { "files": "*.md", "options": { "proseWrap": "preserve" } } ]}新老代码共存 — legacy 目录保留旧格式,新代码用新规则:{ "overrides": [ { "files": "legacy/**/*", "options": { "tabWidth": 4, "useTabs": true } }, { "files": "src/**/*", "options": { "tabWidth": 2 } } ]}指定解析器 — parser 只能写在 overrides 内,不要放顶层:{ "overrides": [ { "files": "*.vue", "options": { "parser": "vue" } } ]}优先级规则overrides 中匹配到的配置优先级高于全局配置多条 override 都匹配时,后面的覆盖前面的相同选项.editorconfig 的值会被 .prettierrc 和 overrides 依次覆盖面试追问overrides 和 .editorconfig 谁优先? — overrides 优先。Prettier 先读 .editorconfig 作为基础,再被 .prettierrc 和 overrides 层层覆盖。parser 能放顶层吗? — 不要。顶层 parser 会覆盖 Prettier 基于扩展名的自动推断,只在 overrides 中为特殊文件指定。overrides 匹配不到文件会报错吗? — 不会,未匹配的 override 静默忽略,不影响其他配置。
前端阅读 05月27日 22:01

Prettier 性能优化有哪些手段?

核心答案Prettier 性能优化有四个关键手段:缩小格式化范围、启用缓存、使用高性能解析器、升级 CLI。按收益从大到小排列:1. 只格式化变更文件——收益最大。用 lint-staged 配合 git hooks,仅处理暂存区文件,避免全量格式化:{ "lint-staged": { "*.{js,ts,tsx,json,css,md}": ["prettier --write"] }}2. 启用缓存——Prettier 3.x 支持 --cache,重复格式化时跳过未变更文件,配合 --cache-strategy=content 更精准(基于文件内容而非修改时间):prettier --write --cache --cache-strategy=content "src/**/*.{js,ts}"3. 换 Rust 解析器——安装 @prettier/plugin-oxc,用 OXC 替代默认解析器,单文件解析速度提升数倍:{ "plugins": ["@prettier/plugin-oxc"] }4. 开启实验性快速 CLI——Prettier 3.6+ 提供 --experimental-cli,实测 375 个 TSX 文件从 11 秒降到 1 秒:PRETTIER_EXPERIMENTAL_CLI=1 prettier --write .OXC 插件 + 快速 CLI 组合使用效果最佳。同时别忘配置 .prettierignore 排除 node_modules、dist、*.min.js 等无需格式化的文件。追问:Prettier 和 ESLint 怎么配合?用 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的规则,再用 eslint-plugin-prettier 把格式问题作为 lint 错误报告。简洁配置:module.exports = { extends: ["prettier"], // 放最后,覆盖前面的格式规则};追问:Prettier 慢到不可接受怎么办?考虑 Oxfmt——Oxc 团队推出的独立格式化工具,基于 Rust 构建,比 Prettier 快 30 倍以上,且已通过 100% Prettier 兼容性测试。适合大型 monorepo 中对速度极度敏感的场景。代价是脱离 Prettier 生态,部分插件不兼容。
前端阅读 05月27日 22:00

Prettier 的缓存机制是怎么工作的?

核心回答Prettier 从 2.7 版本开始支持 --cache,原理是对比缓存键决定是否跳过格式化。缓存键包括:Prettier 版本、格式化选项、Node.js 版本、文件内容或元数据。只要这些值没变,直接跳过该文件,大型项目可提速 10-20 倍。# 启用缓存prettier . --write --cache# 指定缓存位置(默认在 node_modules/.cache/prettier/)prettier . --write --cache --cache-location .prettier-cache两种缓存策略Prettier 提供两种策略,关键区别在于判断"文件是否变化"的方式:content(默认):对文件内容做哈希,准确但稍慢。CI 环境必须用这个,因为 git clone 会重置文件时间戳metadata:比较文件的修改时间和大小,更快但在时间戳被改写的场景下可能误判# CI 环境 — 用 content 更稳prettier . --write --cache --cache-strategy content# 本地开发 — metadata 足够快prettier . --write --cache --cache-strategy metadata缓存什么时候会失效以下任一变化都会导致缓存失效:Prettier 版本升级.prettierrc 或 .editorconfig 配置变更CLI 传入的格式化选项改变文件内容/路径变化注意:插件版本不在缓存键中,更新插件后需手动清缓存。实际配置建议{ "scripts": { "format": "prettier . --write --cache", "check": "prettier . --check --cache" }}CI 中缓存 .prettier-cache 目录可进一步加速,Babel monorepo 实测从 29 秒降到 1.3 秒。# 清除缓存rm -rf node_modules/.cache/prettier追问:缓存为什么没有生效?常见原因有三个:一是用了 metadata 策略但 git 操作重置了时间戳,改用 content;二是更新了 Prettier 插件但没清缓存;三是运行时没带 --cache 参数,Prettier 会自动删除已有缓存文件。
前端阅读 05月27日 21:59

如何管理 Prettier 版本?升级策略有哪些?

核心答案Prettier 版本管理的核心原则就一条:项目内锁定精确版本,全员统一。升级时按 semver 级别分策略处理,主版本升级必须走迁移流程。锁定版本的正确姿势:{ "devDependencies": { "prettier": "3.8.0" }}用 --save-exact 安装,避免 ^ 导致团队成员版本不一致。Prettier 每个 patch 都可能改变格式化输出,这不是理论风险,是实际踩过的坑。版本升级策略Patch/Minor 升级——低风险,可直接升级:npm install --save-dev --save-exact prettier@3.8.2npx prettier --check "src/**/*.{js,ts,tsx}"跑一遍 --check,无差异就提交。建议用 Dependabot 或 Renovate 自动处理。Major 升级(如 2.x → 3.x)——高风险,必须走迁移流程:创建升级分支阅读 Changelog 中 Breaking Changes检查插件兼容性(插件 API 在 3.0 有 Breaking Change)全量 --check 后逐文件修复CI 绿了再合Prettier 3.x 迁移要点从 2.x 升级到 3.x 是最常见的主版本升级场景,注意这几个坑:插件 API 变更:embed 方法签名不兼容,自定义插件必须重写CSS 解析器拆分:--parser css 不再兜底解析 SCSS/LESS,需明确指定 scss 或 lessESLint 集成:搭配 eslint-config-prettier 关闭冲突规则:// .eslintrc.jsmodule.exports = { extends: ["prettier"] // 必须放最后}异步 API:3.x 的 prettier.format() 变成异步,脚本中需 await回滚与团队协同升级出问题,回滚要快:npm install --save-dev --save-exact prettier@2.8.8git checkout HEAD -- package-lock.jsonnpm ci团队升级的核心不是技术,是同步:锁定版本 + npm ci 保证环境一致,Husky + lint-staged 保证提交前格式化,CI 中 --check 兜底。三者缺一,迟早出格式冲突。追问Prettier 和 ESLint 冲突怎么解决?——eslint-config-prettier 关闭 ESLint 格式规则,Prettier 只管风格,ESLint 只管逻辑。为什么不用 npx prettier?——npx 每次拉最新版,团队成员格式化结果不一致,是线上事故的常见原因。如何在 monorepo 中统一版本?——根 package.json 锁版本,子包用 workspace 协议引用,CI 在根目录统一 npm ci。
前端阅读 05月27日 21:56

whistle 代理工具有哪些应用场景?

答案Whistle 是基于 Node.js 的跨平台 Web 调试代理工具,通过规则文本拦截和修改 HTTP/HTTPS 请求与响应。相比直接改代码或配 hosts,核心优势是不改代码、不切环境、规则可复用且仅对本地生效。前端开发中最常用的五个场景:本地联调将线上域名指向本地开发服务,省去切环境或改代码:api.example.com host 127.0.0.1:3000联调只影响你自己,其他同事访问线上不受干扰。多个后端服务可同时指向不同本地端口:api.example.com host 127.0.0.1:3000pay.example.com host 127.0.0.1:4000需要同时联调 WebSocket 时也没问题,whistle 默认支持 ws/wss 协议代理。接口 Mock后端接口未就绪时,用本地 JSON 文件返回模拟数据:api.example.com/user resBody://{mock-user.json}在 Whistle 的 Values 面板中创建 mock-user.json,写入模拟响应体即可。也可以直接指定状态码:api.example.com/error resStatus://500用 resDelay://3000 模拟接口超时,验证前端的 loading 和兜底逻辑。需要按条件 Mock 时,用 resScript 编写动态逻辑:// timeout.js — 支付接口模拟 5 秒超时if (rules.requestHeaders.referer && rules.requestHeaders.referer.indexOf('/pay') !== -1) { rules.responseDelay = 5000; rules.responseStatus = 504;}跨域处理开发阶段给响应注入 CORS 头,绕过浏览器同源限制:api.example.com resHeaders://{cors.json}cors.json 内容示例:{ "access-control-allow-origin": "*", "access-control-allow-methods": "GET,POST,PUT,DELETE,OPTIONS", "access-allow-headers": "Content-Type,Authorization"}比后端加跨域配置更安全——只有你自己生效,不会影响线上环境。移动端抓包与调试手机配代理指向电脑 IP:8899,安装根证书后可捕获 HTTPS 请求:手机 Wi-Fi 设置中配置 HTTP 代理,服务器填电脑 IP,端口填 8899手机浏览器访问 ip:8899,下载并安装根证书iOS 需在"设置 → 通用 → 关于本机 → 证书信任设置"中手动启用信任Android 7+ 默认不信任用户证书,需用 whistle.startProxy(8899, true) 开启全局代理或 root 后安装到系统证书目录配合内置 weinre 远程调试移动端页面 DOM 和控制台:m.example.com weinre://debug也可以安装 whistle.inspect 插件注入 vConsole/Eruda,在 App 内直接查看调试面板:npm i -g whistle.inspectm.example.com whistle.inspect://vconsole异常场景模拟用规则快速构造边界条件,测试前端兜底逻辑:# 模拟接口延迟 3 秒api.example.com/api resDelay://3000# 模拟 500 错误api.example.com/api resStatus://500# 模拟弱网(限速 30kb/s)api.example.com/api reqSpeed://30复杂条件用 resScript 处理,简单场景一条规则就够。规则管理建议用 # 注释 标记每条规则的用途,方便团队协作JSON 数据统一放在 Values 面板管理,别直接内联在规则里调试完及时关闭代理并移除证书,防止隐私泄露和证书风险善用 ignore://* 跳过不需要代理的请求,减少干扰追问whistle 和 Charles/Fiddler 的区别? —— whistle 免费开源、基于规则文本配置(可版本管理)、支持 Node 插件扩展;Charles/Fiddler 偏 GUI 操作,Charles 收费且仅 macOS/Windows,Fiddler 仅 Windows。whistle 天然跨平台且规则可团队共享。如何让 whistle 代理 HTTPS 请求? —— 需要安装并信任 whistle 的根证书:在 Whistle 界面点击 HTTPS → 安装根证书,桌面端安装到系统钥匙串/iOS 信任设置中并启用信任,Android 7+ 用户证书默认不被信任需特殊处理。浏览器和系统层面都需要配置信任。SwitchyOmega 和 whistle 是什么关系? —— SwitchyOmega 是浏览器代理切换插件,负责把浏览器流量导向 whistle 所在的地址和端口;whistle 负责具体的规则匹配和请求处理。两者配合:SwitchyOmega 控制哪些流量走代理,whistle 决定代理后怎么处理。whistle 规则的优先级是什么? —— 默认从上到下匹配,先匹配到的规则优先。可用 enable://proxyFirst 改为代理优先模式。ignore:// 规则会跳过所有其他规则,优先级最高。同名规则后写的会覆盖前面的。whistle 插件有什么用? —— 通过 Node 模块扩展功能,如 whistle.inspect 注入 vConsole/Eruda、whistle.vase 做接口录制回放、whistle.autocoder 自动生成规则。安装后 w2 restart 即可使用。
服务端阅读 05月27日 21:53

如何优化 Vercel 应用的性能?

核心答案:Vercel 应用的性能优化可以从四个维度入手——渲染策略选择、构建产物瘦身、缓存配置、以及 Edge 能力利用。渲染策略:选对模式比什么都重要Vercel 上最影响性能的决策是渲染方式。SSG(静态生成)最快,ISR 兼顾更新和速度,SSR 只在需要实时数据时使用。大多数页面应该优先 SSG + ISR:export async function getStaticProps() { const data = await fetchData(); return { props: { data }, revalidate: 60 }; // ISR: 60秒后台重新生成}Next.js App Router 默认就是 Server Component,不要滥用 'use client',每个客户端组件都会增加 JS 体积。构建优化:砍掉不必要的 JS动态导入:非首屏组件用 dynamic() 按需加载。const Chart = dynamic(() => import('./Chart'), { ssr: false });Tree Shaking:用 import { debounce } from 'lodash' 而非 import _ from 'lodash'。用 @next/bundle-analyzer 定位大包。图片和字体:next/image 自动输出 WebP/AVIF 并按设备尺寸裁剪;next/font 消除字体布局偏移(CLS)。缓存:Vercel 上最容易被忽视的杠杆在 vercel.json 中配置 Cache-Control 头:{ "headers": [{ "source": "/static/:path*", "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }] }]}客户端用 SWR 做请求去重和后台刷新;服务端用 @vercel/kv 缓存计算结果。Vercel 默认对 HTML 响应设置 max-age=0, must-revalidate,静态资源则自动长期缓存。Edge Runtime:把计算推到离用户最近的地方Edge Function 冷启动在毫秒级,适合认证、重定向、A/B 测试等轻量逻辑:export const runtime = 'edge';export default function handler(req) { return new Response('Hello from Edge');}Edge Middleware 可以在请求到达源站之前拦截,做地理路由或权限校验,减少回源延迟。但注意 Edge 环境不支持 Node.js API,Prisma 等库无法直接使用。数据库连接:别让连接池成为瓶颈Serverless 环境下每次请求可能创建新的数据库连接。用全局单例复用 Prisma Client:const globalForPrisma = globalThis;export const prisma = globalForPrisma.prisma ?? new PrismaClient();if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;高并发场景考虑连接池服务(如 Vercel Postgres 自带的 pgBouncer)。追问方向:ISR 的 revalidate 时间怎么定?Edge Runtime 和 Serverless Function 的选型边界在哪?Vercel Analytics 的 Web Vitals 指标(LCP/FID/CLS)分别对应哪些优化手段?Fluid Compute 如何缓解冷启动?
服务端阅读 05月27日 21:53

Prettier 是如何工作的?

Prettier 是什么?Prettier 是一个"有主见"(opinionated)的代码格式化工具,它通过解析代码生成 AST,再用统一的规则重新输出,从而消除团队中的代码风格争议。工作原理Prettier 的格式化流程分三步:1. 解析(Parse):将源代码解析为 AST(抽象语法树)。根据语言不同,选用对应解析器(JavaScript 用 babel,TypeScript 用 typescript 解析器,CSS 用 postcss)。2. 打印(Print):遍历 AST 生成中间表示 Doc。Doc 的关键设计是"可测量"——Prettier 会先尝试将内容放在一行,超出行宽(默认 80 字符)则自动换行缩进。这比直接输出字符串灵活得多。// Prettier 内部 Doc 示意(简化)const doc = group([ "function", " ", "hello", "(", line, "world", ")", " ", "{", indent([line, "console.log(arg);"]), line, "}"]);// 一行放得下 → 单行输出;放不下 → 自动折行3. 输出:将 Doc 转换为最终字符串写回文件。相同输入永远产生相同输出(确定性)。注释处理注释不属于 AST 节点,是格式化器的经典难题。Prettier 通过独立算法将注释附着到 AST 节点上,再在打印阶段输出到正确位置。核心设计取舍有限的配置项:有意不支持大量选项(如"函数括号前是否加空格"已被移除),避免团队为风格配置争论多语言支持:JavaScript、TypeScript、CSS、HTML、JSON、Markdown 等均可用同一工具格式化插件机制:通过 parsers 和 printers 扩展新语言或自定义格式化规则Prettier vs ESLint| 维度 | Prettier | ESLint ||------|----------|--------|| 职责 | 代码格式(缩进、换行、空格) | 代码质量(未使用变量、潜在 bug) || 可配置性 | 少量选项,有态度 | 规则丰富,高度可配 || 输出 | 直接修改代码 | 报错或自动修复 |两者组合使用时,需安装 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的格式规则。Git 提交前自动格式化结合 husky + lint-staged 可在 commit 前自动格式化:// package.json{ "lint-staged": { "*.{js,ts,css,md}": "prettier --write" }}npx husky initecho "npx lint-staged" > .husky/pre-commit追问Prettier 如何处理超长单行代码的折行?Doc 的 group + line 机制是如何工作的?为什么 Prettier 要引入 Doc 中间表示而不是直接从 AST 输出字符串?Prettier 的确定性输出有什么前提条件?什么情况下可能出现不一致?
前端阅读 05月27日 21:52

Chrome 浏览器渲染流程是什么?

渲染流程六步走Chrome 将 HTML、CSS、JavaScript 转化为屏幕像素,依次经历六个阶段:构建 DOM 树:HTML 解析器将标签转为节点。遇到非阻塞资源(如图片)继续解析,遇到 <script> 则暂停 DOM 构建,等脚本执行完才恢复。构建 CSSOM 树:CSS 解析器将样式规则转为 CSS 对象模型。CSS 是阻塞渲染的资源——CSSOM 未就绪前不会进行下一步。构建渲染树:DOM 与 CSSOM 合并,只保留可见节点。<head> 及子元素、display: none 的节点不进入渲染树(visibility: hidden 仍保留,因占空间)。布局(Layout/Reflow):计算每个节点的位置和大小,输出盒模型。这是重排发生的阶段。绘制(Paint):将可见部分绘制到屏幕。样式变化不影响布局时只触发重绘,开销远小于重排。合成(Composite):多个图层合并为最终画面。使用 3D transform、will-change、video/canvas 的元素被提升为独立合成层,由 GPU 处理,跳过主线程的重排和重绘。重排、重绘与直接合成重排:几何属性变化(宽高、位置、边距、字体大小),或读取 offsetWidth/scrollTop 等触发布局计算的属性重绘:外观属性变化(颜色、背景、阴影),几何未变直接合成:仅改 transform 和 opacity,跳过布局和绘制JS 与 CSS 的阻塞关系JS 阻塞 DOM 构建:遇到 <script> 暂停解析,async/defer 可改变行为CSS 阻塞 JS 执行:脚本依赖样式时须等 CSSOM 构建完毕GUI 渲染线程与 JS 引擎线程互斥:JS 执行期间不渲染追问方向从输入 URL 到页面渲染完成,完整链路是什么?requestAnimationFrame 在渲染流程哪一步执行?与 setTimeout 有何区别?如何用 DevTools 定位重排重绘?(Performance 面板查看 Layout/Paint 事件)合成层隐式提升机制是什么?过多合成层会导致什么问题?
服务端阅读 05月27日 21:52

Nginx 的事件驱动模型是什么?如何实现高并发?

答案Nginx 采用事件驱动 + 非阻塞 I/O 模型,核心是 Master-Worker 进程架构 + epoll 事件循环。每个 Worker 进程单线程运行一个事件循环,通过 epoll 同时监听数千个连接的读写事件,事件就绪时回调处理,I/O 等待期间不阻塞进程,从而用少量进程支撑数万并发连接。事件驱动核心机制epoll 的工作方式:内核维护一个就绪队列,只有活跃连接才会触发事件通知,时间复杂度 O(1)。与传统 select/poll 的 O(n) 轮询不同,epoll 不受 FD 数量影响——这正是 Nginx 解决 C10K 问题的根基。事件循环流程:Worker 进程启动 → 注册监听 FD 到 epoll → epoll_wait 阻塞等待事件 → 事件就绪返回 → 回调处理(accept/read/write) → 继续 epoll_wait连接状态机:每个连接在 Worker 内部以状态机方式管理,经历 等待读 → 处理请求 → 等待写 → 发送响应 → 等待新请求(keepalive) 的状态转换,I/O 等待时让出 CPU 给其他连接处理。Master-Worker 进程模型worker_processes auto; # 通常等于 CPU 核心数worker_rlimit_nofile 65535; # 文件描述符上限events { worker_connections 10240; # 单 Worker 最大连接数 use epoll; # Linux 选择 epoll multi_accept on; # 一次 accept 多个连接 accept_mutex off; # 高并发下关闭,减少锁争用}Master:管理 Worker 生命周期,加载配置,不处理业务请求Worker:各自独立运行事件循环,互不干扰,进程隔离保证稳定性理论并发:worker_processes × worker_connections,4 Worker × 10240 = 40960 并发与 Apache 的本质区别| 对比维度 | Nginx | Apache (prefork) ||---------|-------|------------------|| 模型 | 事件驱动,非阻塞 | 每连接一个进程,阻塞 || 内存 | 10 连接 vs 10 连点约 2MB | 10 连接约 200MB || C10K | 原生支持 | 受限于进程数 || 上下文切换 | 极少 | 频繁 |高并发调优关键参数worker_processes:设为 auto 或 CPU 核心数worker_connections:根据内存调整,通常 10240-65535accept_mutex:高并发下关闭(off),低并发开启防惊群系统级:fs.file-max、net.core.somaxconn、tcp_tw_reuse追问epoll 的 LT 和 ET 模式有什么区别?Nginx 默认用哪个? — LT 水平触发会重复通知,ET 边沿触发只通知一次,Nginx 默认 LT,配合非阻塞 I/O 确保数据读完为什么 Worker 单线程还能处理上万连接? — 因为 99% 时间连接在等 I/O,事件驱动只在 I/O 就绪时才占用 CPUaccept_mutex 是什么?什么时候该关? — 惊群控制锁,防止所有 Worker 同时争抢新连接。连接数远大于 Worker 数时关闭可提升吞吐
服务端阅读 05月27日 21:50

Kafka 的核心概念和主要特性是什么?

答案Kafka 是一个分布式流处理平台,核心概念包括:Producer/Consumer:消息的生产者和消费者,采用 Pull 模式消费Broker:Kafka 服务节点,负责存储和转发消息Topic/Partition:Topic 是消息分类单位,Partition 是 Topic 的物理分区,分布在不同 Broker 上实现并行处理Consumer Group:消费者组,同组内各消费者分摊 Partition 消费,实现负载均衡Replica:副本,分为 Leader 和 Follower,保证数据可靠性主要特性:高吞吐(百万级 TPS)、低延迟(毫秒级)、可扩展(水平扩容 Broker)、持久化(磁盘顺序写 + 页缓存)、容错(副本机制 + ISR 同步)。追问一:Kafka 如何保证消息不丢失?三层保障:Producer 端:通过 acks 参数控制——acks=0 不等确认,acks=1 仅 Leader 确认,acks=-1(all) 等 ISR 全部确认才返回成功Broker 端:副本机制 + ISR(In-Sync Replicas),只有 ISR 中的副本全部写入后才认为消息提交成功Consumer 端:手动提交 offset,处理完业务逻辑后再 commit,避免消费失败导致消息丢失追问二:什么是 ISR?和 AR、LEO、HW 的关系?AR(Assigned Replicas):Topic 创建时分配的所有副本集合ISR(In-Sync Replicas):与 Leader 保持同步的副本子集,由 replica.lag.time.max.ms 控制剔除LEO(Log End Offset):每个副本的日志末端位移(下一条消息的 offset)HW(High Watermark):所有 ISR 副本 LEO 的最小值,Consumer 只能消费到 HW 之前的消息Leader 处理写入:先更新自身 LEO,等 ISR 全部同步后推进 HW,Follower 持续从 Leader 拉取数据追赶 LEO。追问三:Kafka 如何保证顺序消费?Partition 级别保证有序——消息在同一 Partition 内按写入顺序追加,Consumer 按 offset 顺序消费。跨 Partition 不保证全局有序。需要全局有序时,只使用一个 Partition(牺牲吞吐)。追问四:零拷贝原理?Kafka 利用 Linux 的 sendfile 系统调用实现零拷贝:数据从磁盘读取到页缓存后,直接通过 DMA 传输到网卡缓冲区,跳过用户态拷贝,大幅降低 CPU 开销和延迟。追问五:消费者 Rebalance 什么时候触发?Consumer Group 中新增或移除消费者订阅的 Topic 分区数变化(如扩容 Partition)Consumer 心跳超时(session.timeout.ms)被判定离线Consumer 主动取消订阅Rebalance 期间所有消费者暂停消费(Stop The World),频繁 Rebalance 是常见性能问题。