<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>WY 的技术博客</title><link>https://zhouwy.top/</link><description>Recent content on WY 的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Fri, 22 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://zhouwy.top/index.xml" rel="self" type="application/rss+xml"/><item><title>ReActAgent 源码分析</title><link>https://zhouwy.top/posts/reactagent-source/reactagent-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/reactagent-source/reactagent-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/</guid><description>&lt;h2 id="概述"&gt;概述&lt;/h2&gt;
&lt;p&gt;ReActAgent 是 AgentScope Java 的核心推理引擎，位于 &lt;code&gt;agentscope-core&lt;/code&gt; 模块。它实现了 &lt;strong&gt;ReAct (Reasoning and Acting)&lt;/strong&gt; 设计模式——在推理（想下一步做什么）和执行（调工具）之间交替循环。&lt;/p&gt;
&lt;p&gt;源码位置：&lt;code&gt;agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java&lt;/code&gt;（1925 行）&lt;/p&gt;
&lt;h2 id="核心依赖"&gt;核心依赖&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ReActAgent&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; StructuredOutputCapableAgent {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Memory memory; &lt;span style="color:#75715e"&gt;// 对话历史（InMemoryMemory）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; String sysPrompt; &lt;span style="color:#75715e"&gt;// 系统提示词&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Model model; &lt;span style="color:#75715e"&gt;// LLM 模型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; maxIters; &lt;span style="color:#75715e"&gt;// 最大迭代次数（默认 10）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Toolkit toolkit; &lt;span style="color:#75715e"&gt;// 可用工具集&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; PlanNotebook planNotebook; &lt;span style="color:#75715e"&gt;// 计划本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; StatePersistence statePersistence; &lt;span style="color:#75715e"&gt;// 状态持久化策略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="核心循环reason--act--reason--act--"&gt;核心循环：reason → act → reason → act → &amp;hellip;&lt;/h2&gt;
&lt;p&gt;整个 ReAct 循环的入口：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; Mono&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Msg&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;executeIteration&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; iter) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; reasoning(iter, &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 先推理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="reasoning-阶段第-560-650-行"&gt;reasoning 阶段（第 560-650 行）&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;① 检查 maxIters → 超限则走 summarizing()
② notifyPreReasoningEvent() ← Hook 注入点：CompactionHook、SubagentsHook、WorkspaceContextHook
③ model.stream(messages + tool schemas) ← 调 LLM
④ 逐 chunk 处理 + notifyReasoningChunk() ← 流式输出
⑤ notifyPostReasoning() ← Hook 注入点
⑥ 判断：
- stopRequested → 返回（HITL 打断）
- gotoReasoning → 继续推理（结构化输出重试）
- isFinished(msg) → 没有 tool call → 返回结果
- 有 tool call → going to acting(iter)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;关键判断 &lt;code&gt;isFinished&lt;/code&gt;（第 969 行）：&lt;/p&gt;</description></item><item><title>把你的Java后端经验重新编译一次：当Netty/Epoll/JVM映射到AI Infra</title><link>https://zhouwy.top/posts/java%E5%90%8E%E7%AB%AF%E5%88%B0ai-infra-hpc-%E5%AE%8C%E6%95%B4%E8%BD%AC%E5%9E%8B%E6%8C%87%E5%8D%97/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/java%E5%90%8E%E7%AB%AF%E5%88%B0ai-infra-hpc-%E5%AE%8C%E6%95%B4%E8%BD%AC%E5%9E%8B%E6%8C%87%E5%8D%97/</guid><description>用10年Java后端工程师听得懂的语言，把AI Infra/HPC的底层原理讲透——这不是路线图，这是一次系统级的代码级映射。</description></item><item><title>系列01：内存管理——当你在管理 ByteBuf 时，你已经在管理 KV Cache 了</title><link>https://zhouwy.top/posts/java-to-ai-infra/01-%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86-bytebuf-kvcache-%E6%98%BE%E5%AD%98%E6%B1%A0/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/java-to-ai-infra/01-%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86-bytebuf-kvcache-%E6%98%BE%E5%AD%98%E6%B1%A0/</guid><description>从 Netty ByteBuf 池化、JVM 堆管理到 vLLM PagedAttention 与 GPU 显存管理，同一个问题在三个世界的不同解法。附带 4 个可运行的代码实验。</description></item><item><title>AgentScope Java Harness 架构与实战指南</title><link>https://zhouwy.top/posts/agentscope-java-harness-%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97/</link><pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/agentscope-java-harness-%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97/</guid><description>&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;AgentScope Java 的 Harness 模块是一个面向生产环境的多智能体运行时框架。本文基于官方文档，从其设计理念到具体代码实践，完整梳理 Harness 的核心架构与使用方式。&lt;/p&gt;
&lt;h2 id="一harness-是什么"&gt;一、Harness 是什么&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;agentscope-harness&lt;/code&gt; 在 &lt;code&gt;agentscope-core&lt;/code&gt; 的 &lt;code&gt;ReActAgent&lt;/code&gt; 之上，通过 &lt;strong&gt;Hook&lt;/strong&gt; 和 &lt;strong&gt;Toolkit&lt;/strong&gt; 两个扩展点，装配出一套面向长期稳定运行的工程化基础设施。它的入口只有一个类：&lt;code&gt;HarnessAgent&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;裸的 &lt;code&gt;ReActAgent&lt;/code&gt; 只有&amp;quot;请求 — 推理 — 工具 — 回复&amp;quot;一轮循环。而 Harness 要回答的是一组更贴近生产的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下一轮怎么办？&lt;/li&gt;
&lt;li&gt;下一天怎么办？&lt;/li&gt;
&lt;li&gt;上下文爆了怎么办？&lt;/li&gt;
&lt;li&gt;状态丢了怎么办？&lt;/li&gt;
&lt;li&gt;任务太重怎么办？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它不替换推理循环，而是在循环的关键时机插入 hook、为模型补上一组基础工具，把这些问题的默认工程答案打包好。&lt;/p&gt;
&lt;h3 id="快速开始"&gt;快速开始&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.agentscope&lt;span style="color:#f92672"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;agentscope-harness&lt;span style="color:#f92672"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${agentscope.version}&lt;span style="color:#f92672"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;QuickstartExample&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;(String&lt;span style="color:#f92672"&gt;[]&lt;/span&gt; args) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 1. 准备工作区&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Path workspace &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Paths.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;.agentscope/workspace&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; initWorkspaceIfAbsent(workspace);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 2. 构建模型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Model model &lt;span style="color:#f92672"&gt;=&lt;/span&gt; DashScopeChatModel.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;apiKey&lt;/span&gt;(System.&lt;span style="color:#a6e22e"&gt;getenv&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;DASHSCOPE_API_KEY&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;modelName&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;qwen-max&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;stream&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 3. 构建 HarnessAgent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; HarnessAgent agent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; HarnessAgent.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;quickstart-agent&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;sysPrompt&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;你是一个帮助用户做笔记的助手。&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;model&lt;/span&gt;(model)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;workspace&lt;/span&gt;(workspace)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;compaction&lt;/span&gt;(CompactionConfig.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;triggerMessages&lt;/span&gt;(30)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;keepMessages&lt;/span&gt;(10)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;flushBeforeCompact&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 4. 两轮对话，同一 sessionId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; RuntimeContext ctx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; RuntimeContext.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;sessionId&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;demo-session&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;userId&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Msg turn1 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; agent.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Msg.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;role&lt;/span&gt;(MsgRole.&lt;span style="color:#a6e22e"&gt;USER&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;textContent&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;我叫天宇，今天准备做一个关于 ReAct 的技术分享。&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ctx).&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Msg turn2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; agent.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Msg.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;role&lt;/span&gt;(MsgRole.&lt;span style="color:#a6e22e"&gt;USER&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;textContent&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;我叫什么？我今天要干什么？&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ctx).&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;initWorkspaceIfAbsent&lt;/span&gt;(Path workspace) &lt;span style="color:#66d9ef"&gt;throws&lt;/span&gt; Exception {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Files.&lt;span style="color:#a6e22e"&gt;createDirectories&lt;/span&gt;(workspace);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Path agentsMd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; workspace.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;AGENTS.md&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (Files.&lt;span style="color:#a6e22e"&gt;exists&lt;/span&gt;(agentsMd)) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Files.&lt;span style="color:#a6e22e"&gt;writeString&lt;/span&gt;(agentsMd, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; # 笔记助手
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; 你是一个帮助用户整理笔记和知识的助手。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ## 行为约定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - 主动记录用户提到的关键事实（姓名、计划、偏好等）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - 回答用简洁中文，必要时给出要点列表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - 对不确定的内容要主动说明，不要臆造
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述代码展示了 Harness 的三个核心价值：&lt;strong&gt;工作区驱动的人格&lt;/strong&gt;、&lt;strong&gt;会话持久化&lt;/strong&gt;（同一 sessionId 的第二轮对话记得第一轮的内容）、&lt;strong&gt;显式启用对话压缩&lt;/strong&gt;。&lt;/p&gt;</description></item><item><title>CompletableFuture 到底该怎么用：从方法、技巧、踩坑到源码</title><link>https://zhouwy.top/posts/completablefuture-%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E6%8A%80%E5%B7%A7%E8%B8%A9%E5%9D%91%E5%8E%9F%E7%90%86%E4%B8%8E%E6%BA%90%E7%A0%81/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/completablefuture-%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E6%8A%80%E5%B7%A7%E8%B8%A9%E5%9D%91%E5%8E%9F%E7%90%86%E4%B8%8E%E6%BA%90%E7%A0%81/</guid><description>&lt;h2 id="completablefuture-到底该怎么用从方法技巧踩坑到源码"&gt;CompletableFuture 到底该怎么用：从方法、技巧、踩坑到源码&lt;/h2&gt;
&lt;p&gt;很多人第一次接触 &lt;code&gt;CompletableFuture&lt;/code&gt;，感觉它很优雅：链式调用、异步编排、异常处理、多个任务聚合，看起来比手写线程池高级得多。&lt;/p&gt;
&lt;p&gt;但真到项目里，问题往往不是“不会用”，而是“以为自己会用”。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为什么有的回调在主线程执行，有的又跑到了线程池里？&lt;/li&gt;
&lt;li&gt;为什么 &lt;code&gt;thenApply()&lt;/code&gt; 和 &lt;code&gt;thenApplyAsync()&lt;/code&gt; 只差一个 &lt;code&gt;Async&lt;/code&gt;，行为却可能完全不同？&lt;/li&gt;
&lt;li&gt;为什么我明明用了异步，结果接口还是卡住了？&lt;/li&gt;
&lt;li&gt;为什么 &lt;code&gt;allOf()&lt;/code&gt; 执行完了，结果还得自己一个个 &lt;code&gt;join()&lt;/code&gt; 去拿？&lt;/li&gt;
&lt;li&gt;为什么异常被包成了 &lt;code&gt;CompletionException&lt;/code&gt;，日志看起来特别恶心？&lt;/li&gt;
&lt;li&gt;为什么线上机器 CPU 很高，最后发现是把阻塞 IO 全扔进了 &lt;code&gt;ForkJoinPool.commonPool()&lt;/code&gt;？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章不打算只列 API，而是把 &lt;code&gt;CompletableFuture&lt;/code&gt; 真正在工程里怎么用、怎么避坑、JDK 为什么这么设计，以及核心源码怎么运作，一次讲透。&lt;/p&gt;
&lt;h2 id="一先建立一个正确心智模型"&gt;一、先建立一个正确心智模型&lt;/h2&gt;
&lt;p&gt;很多人把 &lt;code&gt;CompletableFuture&lt;/code&gt; 理解成“异步线程工具”，这个说法不算错，但太浅了。&lt;/p&gt;
&lt;p&gt;更准确一点，它其实同时做了两件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它是一个 &lt;code&gt;Future&lt;/code&gt;，代表“未来某个时刻会完成的结果”。&lt;/li&gt;
&lt;li&gt;它是一个 &lt;code&gt;CompletionStage&lt;/code&gt;，代表“这个结果完成之后，还能继续挂后续动作的可编排节点”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;也就是说，&lt;code&gt;CompletableFuture&lt;/code&gt; 不只是“拿结果”，更重要的是“描述依赖关系”。&lt;/p&gt;
&lt;p&gt;你可以把它想成一个节点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;节点里最终会有一个结果，或者一个异常。&lt;/li&gt;
&lt;li&gt;节点完成后，会触发依赖它的后续节点。&lt;/li&gt;
&lt;li&gt;节点和节点之间可以串行、并行、汇聚、竞争。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这才是它和传统 &lt;code&gt;Future&lt;/code&gt; 最大的区别。&lt;/p&gt;
&lt;p&gt;传统 &lt;code&gt;Future&lt;/code&gt; 的典型写法是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Future&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Order&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; future &lt;span style="color:#f92672"&gt;=&lt;/span&gt; executor.&lt;span style="color:#a6e22e"&gt;submit&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;::queryOrder);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Order order &lt;span style="color:#f92672"&gt;=&lt;/span&gt; future.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;问题在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get()&lt;/code&gt; 是阻塞的。&lt;/li&gt;
&lt;li&gt;不能优雅地声明“拿到订单后再查用户”。&lt;/li&gt;
&lt;li&gt;多个 &lt;code&gt;Future&lt;/code&gt; 的组合很难写。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而 &lt;code&gt;CompletableFuture&lt;/code&gt; 的核心价值，就是把“等待结果”和“结果完成后的动作”拆开。&lt;/p&gt;
&lt;h2 id="二最常用的几类-api到底该怎么分"&gt;二、最常用的几类 API，到底该怎么分&lt;/h2&gt;
&lt;h3 id="1-创建任务"&gt;1. 创建任务&lt;/h3&gt;
&lt;p&gt;最常见的是这两个：&lt;/p&gt;</description></item><item><title>彻底搞懂 Monad：从概念到 Java 实战</title><link>https://zhouwy.top/posts/java-monad-%E4%B8%8E-stream-%E7%9A%84-flatmapmapmulti-%E6%8E%A2%E7%A7%98%E4%BB%8E%E6%A6%82%E5%BF%B5%E5%88%B0%E6%BA%90%E7%A0%81/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/java-monad-%E4%B8%8E-stream-%E7%9A%84-flatmapmapmulti-%E6%8E%A2%E7%A7%98%E4%BB%8E%E6%A6%82%E5%BF%B5%E5%88%B0%E6%BA%90%E7%A0%81/</guid><description>&lt;h1 id="彻底搞懂-monad从概念到-java-实战"&gt;彻底搞懂 Monad：从概念到 Java 实战&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;你可能每天都在用 &lt;code&gt;Optional.flatMap&lt;/code&gt;、&lt;code&gt;Stream.flatMap&lt;/code&gt;、&lt;code&gt;CompletableFuture.thenCompose&lt;/code&gt;，却不知道它们背后的统一模型——&lt;strong&gt;Monad&lt;/strong&gt;。本文带你一次性搞懂 Monad 是什么、为什么需要它、在 Java 中如何应用，以及如何自己设计 Monad。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="1-从问题出发为什么需要-monad"&gt;1. 从问题出发：为什么需要 Monad？&lt;/h2&gt;
&lt;p&gt;先看一段典型的代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;User user &lt;span style="color:#f92672"&gt;=&lt;/span&gt; findUserById(id);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (user &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Address addr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; user.&lt;span style="color:#a6e22e"&gt;getAddress&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (addr &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; String city &lt;span style="color:#f92672"&gt;=&lt;/span&gt; addr.&lt;span style="color:#a6e22e"&gt;getCity&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; city;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Unknown&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段代码里有 &lt;strong&gt;两个判空&lt;/strong&gt;，如果还有更多步骤，&lt;code&gt;if&lt;/code&gt; 会层层嵌套，难以阅读和维护。&lt;br&gt;
这种 &lt;strong&gt;“带有空值可能性的计算”&lt;/strong&gt; 非常常见，每次我们都要手动检查，写一堆样板代码。&lt;/p&gt;
&lt;p&gt;类似的场景还有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;异步计算&lt;/strong&gt;：拿到 &lt;code&gt;Future&lt;/code&gt; 后必须等它完成才能继续，还要处理异常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多值计算&lt;/strong&gt;：处理集合时，每个元素返回一个集合，然后要手动 &lt;code&gt;addAll&lt;/code&gt; 合并。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可失败计算&lt;/strong&gt;：方法可能抛出异常，每一步都要 &lt;code&gt;try-catch&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Monad 的使命就是 &lt;strong&gt;把这些重复的控制逻辑抽离出来&lt;/strong&gt;，让你只关心“下一步做什么”，而不是“上一步是否成功/有值/已完成”。&lt;/p&gt;
&lt;h2 id="2-monad-是什么"&gt;2. Monad 是什么？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Monad 是一种设计模式&lt;/strong&gt;，它定义了一个容器类型（比如 &lt;code&gt;Optional&amp;lt;T&amp;gt;&lt;/code&gt;），并提供两个操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;unit&lt;/code&gt;（也叫 &lt;code&gt;return&lt;/code&gt;、&lt;code&gt;of&lt;/code&gt;、&lt;code&gt;pure&lt;/code&gt;）&lt;/strong&gt;：把一个普通值包装进 Monad。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;flatMap&lt;/code&gt;（也叫 &lt;code&gt;bind&lt;/code&gt;、&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;、&lt;code&gt;thenCompose&lt;/code&gt;）&lt;/strong&gt;：把容器里的值取出来，应用一个函数（这个函数返回同类型容器），然后自动 &lt;strong&gt;展平&lt;/strong&gt; 结果，避免嵌套。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用公式表达（Haskell 风格）：&lt;/p&gt;</description></item><item><title>构建生产级 Agent Memory 系统架构</title><link>https://zhouwy.top/posts/%E6%9E%84%E5%BB%BA%E7%94%9F%E4%BA%A7%E7%BA%A7-agent-memory-%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/%E6%9E%84%E5%BB%BA%E7%94%9F%E4%BA%A7%E7%BA%A7-agent-memory-%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/</guid><description>&lt;h1 id="构建生产级-agent-memory-系统架构"&gt;构建生产级 Agent Memory 系统架构&lt;/h1&gt;
&lt;p&gt;很多人在做 Agent 时，第一版 Memory 的实现大概长这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append({&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;你好&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append({&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;你好！有什么可以帮你的？&amp;#34;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; java.util.*;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;List&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Map&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;String, String&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; memory &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(Map.&lt;span style="color:#a6e22e"&gt;of&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;你好&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(Map.&lt;span style="color:#a6e22e"&gt;of&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;assistant&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;你好！有什么可以帮你的？&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;跑几天后发现问题了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对话一长，Token 消耗爆炸&lt;/li&gt;
&lt;li&gt;用户上次说过的偏好，下次完全不记得&lt;/li&gt;
&lt;li&gt;多个 Session 之间的信息没法共享&lt;/li&gt;
&lt;li&gt;想查某个历史事实，得翻完整条对话&lt;/li&gt;
&lt;li&gt;模型在长上下文里注意力稀释，反而答得更差&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是开始往里面塞向量数据库、塞总结、塞 Graph，结果又发现：&lt;strong&gt;Memory 系统不是越复杂越好，而是越适合你 Agent 的行为模式越好。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这篇文章不讲泛泛的概念，而是从工程角度拆解：一个生产级的 Agent Memory 系统到底该考虑哪些维度，每种方案解决什么问题、带来什么新问题，以及如何组合它们。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E4%B8%80agent-memory-%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88"&gt;一、Agent Memory 到底是什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%BA%8C%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84%E4%B8%89%E5%B1%82%E5%88%86%E7%A6%BB"&gt;二、整体架构：三层分离&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%89episodic-memory%E6%83%85%E8%8A%82%E8%AE%B0%E5%BF%86%E6%9C%80%E5%9F%BA%E7%A1%80%E4%B9%9F%E6%9C%80%E5%AE%B9%E6%98%93%E6%90%9E%E7%A0%B8%E7%9A%84%E9%83%A8%E5%88%86"&gt;三、Episodic Memory（情节记忆）：最基础也最容易搞砸的部分&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E9%97%AE%E9%A2%98%E6%9C%AC%E8%B4%A8"&gt;问题本质&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%B8%B8%E8%A7%81%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F%E5%AF%B9%E6%AF%94"&gt;常见实现方式对比&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8E%A8%E8%8D%90%E6%96%B9%E6%A1%88%E5%88%86%E5%B1%82%E7%AE%A1%E7%90%86"&gt;推荐方案：分层管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%B7%A5%E7%A8%8B%E5%AE%9E%E7%8E%B0%E8%A6%81%E7%82%B9"&gt;工程实现要点&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%9B%9Bsemantic-memory%E8%AF%AD%E4%B9%89%E8%AE%B0%E5%BF%86%E4%BB%8E%E7%BB%8F%E5%8E%86%E4%B8%AD%E6%8F%90%E7%82%BC%E4%BA%8B%E5%AE%9E"&gt;四、Semantic Memory（语义记忆）：从经历中提炼事实&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E6%A0%B8%E5%BF%83%E6%80%9D%E8%B7%AF"&gt;核心思路&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8F%90%E5%8F%96%E4%BB%80%E4%B9%88"&gt;提取什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AD%98%E5%82%A8%E9%80%89%E5%9E%8B"&gt;存储选型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8F%90%E5%8F%96%E7%AD%96%E7%95%A5%E8%A2%AB%E5%8A%A8-vs-%E4%B8%BB%E5%8A%A8"&gt;提取策略：被动 vs 主动&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%B3%E9%94%AE%E9%9A%BE%E7%82%B9%E4%BA%8B%E5%AE%9E%E7%9A%84%E6%9B%B4%E6%96%B0%E4%B8%8E%E5%86%B2%E7%AA%81"&gt;关键难点：事实的更新与冲突&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%BA%94procedural-memory%E7%A8%8B%E5%BA%8F%E8%AE%B0%E5%BF%86agent-%E6%80%8E%E4%B9%88%E5%AD%A6%E4%BC%9A%E5%81%9A%E4%BA%8B%E6%83%85%E7%9A%84%E6%96%B9%E5%BC%8F"&gt;五、Procedural Memory（程序记忆）：Agent 怎么学会&amp;quot;做事情的方式&amp;quot;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F"&gt;实现方式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%BD%95%E6%97%B6%E5%86%99%E5%85%A5"&gt;何时写入&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%ADcontext-builder%E5%86%B3%E5%AE%9A%E5%BE%80-prompt-%E9%87%8C%E5%A1%9E%E4%BB%80%E4%B9%88"&gt;六、Context Builder：决定往 Prompt 里塞什么&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E7%BB%84%E8%A3%85%E7%AD%96%E7%95%A5"&gt;组装策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#token-%E9%A2%84%E7%AE%97%E5%88%86%E9%85%8D"&gt;Token 预算分配&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%A3%80%E7%B4%A2%E8%A7%A6%E5%8F%91%E7%AD%96%E7%95%A5"&gt;检索触发策略&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%83%E6%95%B0%E6%8D%AE%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E4%B8%8E%E7%BB%B4%E6%8A%A4"&gt;七、数据生命周期与维护&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#ttl-%E4%B8%8E%E8%BF%87%E6%9C%9F"&gt;TTL 与过期&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#consolidation%E5%90%88%E5%B9%B6"&gt;Consolidation（合并）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#importance-scoring%E9%87%8D%E8%A6%81%E6%80%A7%E8%AF%84%E5%88%86"&gt;Importance Scoring（重要性评分）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%AB%E4%BB%8E-claude-code-%E4%B8%AD%E7%9C%8B%E5%88%B0%E7%9A%84%E4%BC%98%E7%A7%80%E8%AE%BE%E8%AE%A1"&gt;八、从 Claude Code 中看到的优秀设计&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E6%96%87%E4%BB%B6%E5%8D%B3%E6%95%B0%E6%8D%AE%E5%BA%93"&gt;文件即数据库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E7%B1%BB%E5%9E%8B%E5%8C%96%E8%AE%B0%E5%BF%86"&gt;类型化记忆&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%A3%80%E7%B4%A2%E6%97%B6%E7%9A%84%E9%AA%8C%E8%AF%81%E6%9C%BA%E5%88%B6"&gt;检索时的验证机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%8D%E6%B7%B7%E6%B7%86%E8%AE%B0%E5%BF%86%E5%92%8C%E4%BB%BB%E5%8A%A1%E7%8A%B6%E6%80%81"&gt;不混淆&amp;quot;记忆&amp;quot;和&amp;quot;任务状态&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B9%9D%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E7%9A%84-memory-%E5%AE%9E%E7%8E%B0%E6%A1%88%E4%BE%8B"&gt;九、主流框架的 Memory 实现案例&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#1-langchain%E6%A8%A1%E5%9D%97%E5%8C%96-memory-%E4%BD%93%E7%B3%BB"&gt;LangChain：模块化 Memory 体系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#2-crewai%E5%9F%BA%E4%BA%8E%E8%A7%92%E8%89%B2%E7%9A%84%E8%AE%B0%E5%BF%86%E5%88%86%E5%B7%A5"&gt;CrewAI：基于角色的记忆分工&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#3-mem0%E4%B8%93%E6%B3%A8%E8%AE%B0%E5%BF%86%E5%B1%82%E7%9A%84%E7%8B%AC%E7%AB%8B%E6%A1%86%E6%9E%B6"&gt;Mem0：专注&amp;quot;记忆层&amp;quot;的独立框架&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#4-spring-aijava-%E7%94%9F%E6%80%81%E7%9A%84-memory-%E5%AE%9E%E7%8E%B0"&gt;Spring AI：Java 生态的 Memory 实现&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#5-agentscope4jobservecall-%E5%88%86%E7%A6%BB%E4%B8%8E-msghub-%E8%AE%B0%E5%BF%86%E5%85%B1%E4%BA%AB"&gt;AgentScope4J：observe/call 分离与 MsgHub 记忆共享&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#6-hermes-agentnous-research%E5%9B%9B%E5%B1%82%E8%AE%B0%E5%BF%86%E4%B8%8E%E5%8F%AF%E6%8F%92%E6%8B%94-provider"&gt;Hermes Agent：四层记忆与可插拔 Provider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#7-openclaw%E6%96%87%E4%BB%B6%E5%8D%B3%E8%AE%B0%E5%BF%86%E7%9A%84%E6%A2%A6%E5%A2%83%E7%B3%BB%E7%BB%9F"&gt;OpenClaw：文件即记忆的&amp;quot;梦境&amp;quot;系统&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#8-autogenmicrosoft%E5%AF%B9%E8%AF%9D%E9%A9%B1%E5%8A%A8%E7%9A%84%E8%AE%B0%E5%BF%86%E5%85%B1%E4%BA%AB"&gt;AutoGen：对话驱动的记忆共享&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%A1%86%E6%9E%B6%E5%AF%B9%E6%AF%94%E5%B0%8F%E7%BB%93hermes-vs-openclaw"&gt;框架对比小结：Hermes vs OpenClaw&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%8D%81%E7%94%9F%E4%BA%A7%E7%BA%A7-checklist"&gt;十、生产级 Checklist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%8D%81%E4%B8%80%E6%80%BB%E7%BB%93"&gt;十一、总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="一agent-memory-到底是什么"&gt;一、Agent Memory 到底是什么&lt;/h2&gt;
&lt;p&gt;很多人把 Memory 等同于&amp;quot;聊天记录&amp;quot;，这是第一个坑。&lt;/p&gt;</description></item><item><title>AgentScope4J 实战：MCP、Function Calling、Hermes、Skills 全链路落地</title><link>https://zhouwy.top/posts/agentscope4j-%E5%AE%9E%E6%88%98mcpfunction-callinghermesskills-%E5%85%A8%E9%93%BE%E8%B7%AF%E8%90%BD%E5%9C%B0/</link><pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/agentscope4j-%E5%AE%9E%E6%88%98mcpfunction-callinghermesskills-%E5%85%A8%E9%93%BE%E8%B7%AF%E8%90%BD%E5%9C%B0/</guid><description>&lt;h1 id="agentscope4j-实战mcpfunction-callinghermesskills-全链路落地"&gt;AgentScope4J 实战：MCP、Function Calling、Hermes、Skills 全链路落地&lt;/h1&gt;
&lt;p&gt;很多团队在做 AI Agent 时，会把 &lt;code&gt;MCP&lt;/code&gt;、&lt;code&gt;Function Calling&lt;/code&gt;、&lt;code&gt;Workflow&lt;/code&gt;、&lt;code&gt;Skills&lt;/code&gt; 混在一起，最后导致系统边界不清晰：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模型在“决策层”做了太多“执行层”的事&lt;/li&gt;
&lt;li&gt;工具能力散落在不同模块，难治理、难审计&lt;/li&gt;
&lt;li&gt;Demo 阶段能跑，线上阶段一旦扩展就失控&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章给出一个工程化拆解：基于 &lt;code&gt;agentscope4j&lt;/code&gt; 搭建一套清晰的分层，把这四个关键能力拼成一条可扩展、可观测、可治理的链路。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一先把概念边界讲清楚"&gt;一、先把概念边界讲清楚&lt;/h2&gt;
&lt;p&gt;很多讨论无效，根源是“词对了，语义不对”。下面先做边界划分。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;核心职责&lt;/th&gt;
&lt;th&gt;解决的问题&lt;/th&gt;
&lt;th&gt;不负责的事情&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Function Calling&lt;/td&gt;
&lt;td&gt;让模型按 JSON Schema 选择并调用本地函数&lt;/td&gt;
&lt;td&gt;结构化参数、减少自由文本歧义&lt;/td&gt;
&lt;td&gt;跨进程发现工具、权限治理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP&lt;/td&gt;
&lt;td&gt;标准化“模型如何发现并调用远端工具”&lt;/td&gt;
&lt;td&gt;工具注册、工具描述、跨系统接入&lt;/td&gt;
&lt;td&gt;业务编排策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes（编排层）&lt;/td&gt;
&lt;td&gt;统一调度策略与执行控制&lt;/td&gt;
&lt;td&gt;路由、重试、超时、回退、审批&lt;/td&gt;
&lt;td&gt;工具协议本身&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills&lt;/td&gt;
&lt;td&gt;可复用能力单元（提示词+工具+策略）&lt;/td&gt;
&lt;td&gt;把“能力”沉淀为可组合模块&lt;/td&gt;
&lt;td&gt;具体传输协议&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一句话总结：&lt;br&gt;
&lt;code&gt;Function Calling&lt;/code&gt; 是“调用形态”，&lt;code&gt;MCP&lt;/code&gt; 是“工具接入协议”，&lt;code&gt;Hermes&lt;/code&gt; 是“执行与治理中枢”，&lt;code&gt;Skills&lt;/code&gt; 是“业务能力封装”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二目标架构四层分离"&gt;二、目标架构：四层分离&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-mermaid" data-lang="mermaid"&gt;flowchart TD
U[&amp;#34;User Request&amp;#34;] --&amp;gt; A[&amp;#34;AgentScope4J Agent (Planner)&amp;#34;]
A --&amp;gt; H[&amp;#34;Hermes Orchestrator&amp;#34;]
H --&amp;gt; S1[&amp;#34;Skill: Research&amp;#34;]
H --&amp;gt; S2[&amp;#34;Skill: Report&amp;#34;]
S1 --&amp;gt; FC[&amp;#34;Function Calling Adapter&amp;#34;]
S1 --&amp;gt; MCP[&amp;#34;MCP Client Adapter&amp;#34;]
FC --&amp;gt; T1[&amp;#34;Local Tool: SqlTool/FileTool&amp;#34;]
MCP --&amp;gt; T2[&amp;#34;Remote MCP Tools&amp;#34;]
S2 --&amp;gt; T3[&amp;#34;Formatter/Exporter&amp;#34;]
T1 --&amp;gt; O[&amp;#34;Observability: Trace/Log/Metrics&amp;#34;]
T2 --&amp;gt; O
T3 --&amp;gt; O
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;设计原则：&lt;/p&gt;</description></item><item><title>JDK线程、虚拟线程、Go协程与Netty Event Loop：调度模型和阻塞队列全景解析</title><link>https://zhouwy.top/posts/jdk%E7%BA%BF%E7%A8%8B%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8Bgo%E5%8D%8F%E7%A8%8B%E4%B8%8Enetty-event-loop%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B%E5%92%8C%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E5%85%A8%E6%99%AF%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/jdk%E7%BA%BF%E7%A8%8B%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8Bgo%E5%8D%8F%E7%A8%8B%E4%B8%8Enetty-event-loop%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B%E5%92%8C%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E5%85%A8%E6%99%AF%E8%A7%A3%E6%9E%90/</guid><description>&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E7%9B%AE%E5%BD%95"&gt;目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%95%E5%AD%90%E8%BF%99%E5%87%A0%E4%B8%AA%E4%B8%9C%E8%A5%BF%E4%B8%BA%E4%BB%80%E4%B9%88%E6%80%BB%E8%A2%AB%E6%8B%BF%E6%9D%A5%E4%B8%80%E8%B5%B7%E6%AF%94%E8%BE%83"&gt;引子：这几个东西为什么总被拿来一起比较&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%88%E7%BB%9F%E4%B8%80%E4%B8%80%E4%B8%AA%E8%A7%86%E8%A7%92%E5%AE%83%E4%BB%AC%E6%9C%AC%E8%B4%A8%E4%B8%8A%E9%83%BD%E5%9C%A8%E8%A7%A3%E5%86%B3%E8%B0%83%E5%BA%A6%E9%97%AE%E9%A2%98"&gt;先统一一个视角：它们本质上都在解决调度问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jdk-%E5%B9%B3%E5%8F%B0%E7%BA%BF%E7%A8%8B11-%E6%98%A0%E5%B0%84%E5%86%85%E6%A0%B8%E7%BA%BF%E7%A8%8B"&gt;JDK 平台线程：1:1 映射内核线程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jdk-%E8%99%9A%E6%8B%9F%E7%BA%BF%E7%A8%8Bmn-%E8%B0%83%E5%BA%A6%E4%BD%86%E8%AF%AD%E4%B9%89%E4%BB%8D%E7%84%B6%E6%98%AF-thread"&gt;JDK 虚拟线程：M:N 调度，但语义仍然是 Thread&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#go-%E5%8D%8F%E7%A8%8B%E8%AF%AD%E8%A8%80%E8%BF%90%E8%A1%8C%E6%97%B6%E4%B8%BB%E5%AF%BC%E7%9A%84-gmp-%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B"&gt;Go 协程：语言运行时主导的 GMP 调度模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#netty-event-loop%E5%B0%91%E9%87%8F%E7%BA%BF%E7%A8%8B%E9%A9%B1%E5%8A%A8%E5%A4%A7%E9%87%8F%E8%BF%9E%E6%8E%A5"&gt;Netty Event Loop：少量线程驱动大量连接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8A%8A%E5%9B%9B%E8%80%85%E6%94%BE%E5%9C%A8%E4%B8%80%E5%BC%A0%E5%9B%BE%E9%87%8C%E7%9C%8B"&gt;把四者放在一张图里看&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%83%E4%BB%AC%E4%B9%8B%E9%97%B4%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88%E5%85%B3%E7%B3%BB"&gt;它们之间到底是什么关系&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%80%E4%B8%AA%E5%85%B3%E9%94%AE%E9%97%AE%E9%A2%98%E9%98%BB%E5%A1%9E%E5%88%B0%E5%BA%95%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88"&gt;一个关键问题：阻塞到底意味着什么&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#jdk-%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E5%85%A8%E6%99%AF%E6%A2%B3%E7%90%86"&gt;JDK 阻塞队列全景梳理&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#arrayblockingqueue"&gt;ArrayBlockingQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#linkedblockingqueue"&gt;LinkedBlockingQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#synchronousqueue"&gt;SynchronousQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#priorityblockingqueue"&gt;PriorityBlockingQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#delayqueue"&gt;DelayQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#linkedtransferqueue"&gt;LinkedTransferQueue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#linkedblockingdeque"&gt;LinkedBlockingDeque&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97%E4%B9%8B%E9%97%B4%E7%9A%84%E6%A0%B8%E5%BF%83%E5%8C%BA%E5%88%AB"&gt;阻塞队列之间的核心区别&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%BF%99%E4%BA%9B%E9%98%9F%E5%88%97%E5%92%8C%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B%E6%80%8E%E4%B9%88%E9%85%8D%E5%90%88"&gt;这些队列和线程模型怎么配合&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%80%8E%E4%B9%88%E9%80%89%E6%8C%89%E5%9C%BA%E6%99%AF%E7%BB%99%E7%BB%93%E8%AE%BA"&gt;怎么选：按场景给结论&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%9C%80%E5%90%8E%E6%80%BB%E7%BB%93"&gt;最后总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="引子这几个东西为什么总被拿来一起比较"&gt;引子：这几个东西为什么总被拿来一起比较&lt;/h2&gt;
&lt;p&gt;很多人第一次接触这几个概念时，会感觉它们像是同一层面的东西：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK 线程&lt;/li&gt;
&lt;li&gt;JDK 虚拟线程&lt;/li&gt;
&lt;li&gt;Go 协程&lt;/li&gt;
&lt;li&gt;Netty Event Loop&lt;/li&gt;
&lt;li&gt;JDK 阻塞队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但它们其实分布在不同层次：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;线程 / 协程 / 虚拟线程&lt;/code&gt; 解决的是&lt;strong&gt;执行单元&lt;/strong&gt;怎么表示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;调度器&lt;/code&gt; 解决的是&lt;strong&gt;谁来跑、什么时候跑&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Event Loop&lt;/code&gt; 解决的是&lt;strong&gt;I/O 事件如何复用少量线程&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;阻塞队列&lt;/code&gt; 解决的是&lt;strong&gt;任务或数据如何在并发单元之间传递&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把这些层次混在一起，就很容易得出错误结论，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“虚拟线程就是 Java 版协程”&lt;/li&gt;
&lt;li&gt;“Netty loop 就是协程调度器”&lt;/li&gt;
&lt;li&gt;“用了虚拟线程，就不需要线程池和队列了”&lt;/li&gt;
&lt;li&gt;“所有生产者消费者问题都用 LinkedBlockingQueue 就行”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章的目标，就是把这些东西放到一张统一地图里。&lt;/p&gt;</description></item><item><title>AgentScope MsgHub 多智能体通信机制详解</title><link>https://zhouwy.top/posts/agentscope-msghub-%E5%A4%9A%E6%99%BA%E8%83%BD%E4%BD%93%E9%80%9A%E4%BF%A1%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/agentscope-msghub-%E5%A4%9A%E6%99%BA%E8%83%BD%E4%BD%93%E9%80%9A%E4%BF%A1%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</guid><description>&lt;p&gt;多智能体系统里，Agent 之间的通信是个容易被低估的难题。本文以 AgentScope-Java 的 &lt;code&gt;MsgHub&lt;/code&gt; 为例，聊聊我们在实践中踩过的坑，以及订阅-广播机制的设计思路。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;一、为什么要专门做个通信层&lt;/p&gt;
&lt;p&gt;1.1 常见但别扭的做法&lt;/p&gt;
&lt;p&gt;三个狼人 Agent 讨论今晚刀谁，你会怎么写？&lt;/p&gt;
&lt;p&gt;手动透传消息&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 没有 MsgHub 的情况&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Msg aResponse &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wolfA.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wolfB.&lt;span style="color:#a6e22e"&gt;observe&lt;/span&gt;(aResponse).&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wolfC.&lt;span style="color:#a6e22e"&gt;observe&lt;/span&gt;(aResponse).&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Msg bResponse &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wolfB.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wolfA.&lt;span style="color:#a6e22e"&gt;observe&lt;/span&gt;(bResponse).&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wolfC.&lt;span style="color:#a6e22e"&gt;observe&lt;/span&gt;(bResponse).&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 代码重复，维护噩梦&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;并发调用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 同时调用所有 Agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; responses &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Flux.&lt;span style="color:#a6e22e"&gt;just&lt;/span&gt;(wolfA, wolfB, wolfC)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;flatMap&lt;/span&gt;(agent &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; agent.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;collectList&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 问题：每个 Agent 基于相同的初始上下文，无法互相影响&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同步阻塞（死锁风险）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 如果每个 Agent 都在等其他人先发言&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Msg aResponse &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wolfA.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// A 等 B&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Msg bResponse &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wolfB.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// B 等 C&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Msg cResponse &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wolfC.&lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;block&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// C 等 A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 死锁&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些方案要么啰嗦，要么有隐患。我们需要一种让 Agent 既能&amp;quot;听到&amp;quot;彼此，又不会互相卡住的机制。&lt;/p&gt;</description></item><item><title>Claude Code源代码</title><link>https://zhouwy.top/posts/claude-code%E6%BA%90%E4%BB%A3%E7%A0%81/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/claude-code%E6%BA%90%E4%BB%A3%E7%A0%81/</guid><description>&lt;h1 id="claude-code源代码"&gt;Claude Code源代码&lt;/h1&gt;
&lt;p&gt;参考URL:&lt;/p&gt;
&lt;p&gt;如何评价Claude Code源代码泄漏？ - 卜寒兮的回答 - 知乎
&lt;a href="https://www.zhihu.com/question/2022392127145911515/answer/2022520709964931142"&gt;https://www.zhihu.com/question/2022392127145911515/answer/2022520709964931142&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="总结值得借鉴的设计模式"&gt;总结：值得借鉴的设计模式&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;在 Claude Code 里的体现&lt;/th&gt;
&lt;th&gt;可借鉴的场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AsyncGenerator 作为流式原语&lt;/td&gt;
&lt;td&gt;query() 的整体结构&lt;/td&gt;
&lt;td&gt;任何需要流式输出 + 可中断的 agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分层错误恢复&lt;/td&gt;
&lt;td&gt;五层上下文压缩策略&lt;/td&gt;
&lt;td&gt;资源受限下的 long-running task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;流式并发工具执行&lt;/td&gt;
&lt;td&gt;StreamingToolExecutor&lt;/td&gt;
&lt;td&gt;多工具并行调用的 agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;投机性预计算&lt;/td&gt;
&lt;td&gt;startSpeculativeClassifierCheck&lt;/td&gt;
&lt;td&gt;任何有必然后续操作的流程&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;工具自声明并发安全性&lt;/td&gt;
&lt;td&gt;isConcurrencySafe() 接口&lt;/td&gt;
&lt;td&gt;混合读写操作的任务编排&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forked Agent 共享缓存&lt;/td&gt;
&lt;td&gt;CacheSafeParams + compact agent&lt;/td&gt;
&lt;td&gt;需要 fork 子任务但不想丢失 prompt cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hook 与主循环解耦&lt;/td&gt;
&lt;td&gt;Post-sampling / stop hooks&lt;/td&gt;
&lt;td&gt;任何需要插入评估逻辑的 agent 系统&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;回到上面那句话：&lt;strong&gt;harness 编码了对”模型不能独立完成什么”的假设（&lt;/strong&gt;&lt;em&gt;“the harness itself encodes assumptions about what models cannot do independently”&lt;/em&gt;。）&lt;/p&gt;</description></item><item><title>Java引用类型详解：从源码到实战</title><link>https://zhouwy.top/posts/java%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E8%AF%A6%E8%A7%A3%E4%BB%8E%E6%BA%90%E7%A0%81%E5%88%B0%E5%AE%9E%E6%88%98/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/java%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E8%AF%A6%E8%A7%A3%E4%BB%8E%E6%BA%90%E7%A0%81%E5%88%B0%E5%AE%9E%E6%88%98/</guid><description>&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E7%9B%AE%E5%BD%95"&gt;目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%95%E8%A8%80"&gt;引言&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%88%E6%90%9E%E6%B8%85%E6%A5%9A%E4%B8%80%E4%B8%AA%E5%89%8D%E6%8F%90%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90"&gt;先搞清楚一个前提：可达性分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%BA%E5%BC%95%E7%94%A8strong-reference"&gt;强引用（Strong Reference）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%BD%AF%E5%BC%95%E7%94%A8soft-reference"&gt;软引用（Soft Reference）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95"&gt;基本用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90mybatis%E7%9A%84softcache"&gt;源码级分析：MyBatis的SoftCache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E5%81%9A%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98"&gt;实际开发：用软引用做本地缓存&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%BC%B1%E5%BC%95%E7%94%A8weak-reference"&gt;弱引用（Weak Reference）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-1"&gt;基本用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90threadlocal%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8"&gt;源码级分析：ThreadLocal为什么用弱引用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90weakhashmap%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86"&gt;源码级分析：WeakHashMap的工作原理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8weakhashmap%E9%81%BF%E5%85%8D%E7%9B%91%E5%90%AC%E5%99%A8%E6%B3%84%E6%BC%8F"&gt;实际开发：用WeakHashMap避免监听器泄漏&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91tomcat%E7%9A%84concurrentcache"&gt;实际开发：Tomcat的ConcurrentCache&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%99%9A%E5%BC%95%E7%94%A8phantom-reference"&gt;虚引用（Phantom Reference）&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95-2"&gt;基本用法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%99%9A%E5%BC%95%E7%94%A8-vs-finalize%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E7%94%A8%E8%99%9A%E5%BC%95%E7%94%A8"&gt;虚引用 vs finalize()：为什么要用虚引用&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90jdk%E7%9A%84cleaner%E6%9C%BA%E5%88%B6"&gt;源码级分析：JDK的Cleaner机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90directbytebuffer%E5%A6%82%E4%BD%95%E9%87%8A%E6%94%BE%E5%A0%86%E5%A4%96%E5%86%85%E5%AD%98"&gt;源码级分析：DirectByteBuffer如何释放堆外内存&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8%E8%99%9A%E5%BC%95%E7%94%A8%E7%9B%91%E6%8E%A7%E8%B5%84%E6%BA%90%E6%B3%84%E6%BC%8F"&gt;实际开发：用虚引用监控资源泄漏&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#referencequeue%E5%BC%95%E7%94%A8%E5%9B%9E%E6%94%B6%E7%9A%84%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6"&gt;ReferenceQueue：引用回收的通知机制&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E6%A0%B8%E5%BF%83%E6%9C%BA%E5%88%B6"&gt;核心机制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%BA%90%E7%A0%81%E7%BA%A7%E5%88%86%E6%9E%90referencehandler%E7%BA%BF%E7%A8%8B"&gt;源码级分析：ReferenceHandler线程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%AE%9E%E9%99%85%E5%BC%80%E5%8F%91%E7%94%A8referencequeue%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%8A%A8%E6%B8%85%E7%90%86%E7%9A%84%E7%BC%93%E5%AD%98"&gt;实际开发：用ReferenceQueue实现自动清理的缓存&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%80%E5%BC%A0%E5%9B%BE%E6%80%BB%E7%BB%93"&gt;一张图总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A7%A3%E7%AD%94"&gt;常见问题与解答&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#q1threadlocal%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%98%E4%BC%9A%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F"&gt;Q1：ThreadLocal用弱引用为什么还会内存泄漏？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q2%E8%BD%AF%E5%BC%95%E7%94%A8%E5%92%8C%E5%BC%B1%E5%BC%95%E7%94%A8%E5%81%9A%E7%BC%93%E5%AD%98%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB"&gt;Q2：软引用和弱引用做缓存有什么区别？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q3%E8%99%9A%E5%BC%95%E7%94%A8%E7%9A%84get%E4%B8%BA%E4%BB%80%E4%B9%88%E6%B0%B8%E8%BF%9C%E8%BF%94%E5%9B%9Enull"&gt;Q3：虚引用的get()为什么永远返回null？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q4referencequeue%E7%9A%84poll%E5%92%8Cremove%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB"&gt;Q4：ReferenceQueue的poll和remove有什么区别？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#q5%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E8%AF%A5%E7%94%A8weakhashmap"&gt;Q5：什么时候该用WeakHashMap？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="引言"&gt;引言&lt;/h2&gt;
&lt;p&gt;提到Java的引用类型，很多人脑子里只有一句&amp;quot;强软弱虚&amp;quot;，但真到面试或者写代码的时候，往往一问就懵。这篇文章我打算换个思路——不搞概念罗列，直接从JDK源码和实际开发场景出发，把每种引用类型到底解决了什么问题、怎么用的，掰开了讲清楚。&lt;/p&gt;
&lt;h2 id="先搞清楚一个前提可达性分析"&gt;先搞清楚一个前提：可达性分析&lt;/h2&gt;
&lt;p&gt;在讲引用类型之前，得先明白JVM是怎么判断一个对象该不该回收的。JVM使用的是&lt;strong&gt;可达性分析算法&lt;/strong&gt;：从GC Roots出发，沿着引用链往下找，如果某个对象到GC Roots没有任何引用链可达，那这个对象就是可回收的。&lt;/p&gt;
&lt;p&gt;四种引用类型的区别，本质上就是：&lt;strong&gt;GC Roots到对象的这条引用链，有多&amp;quot;硬&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强引用：铁链子，GC死也不断&lt;/li&gt;
&lt;li&gt;软引用：橡皮筋，内存不够就断&lt;/li&gt;
&lt;li&gt;弱引用：纸糊的，GC一来就断&lt;/li&gt;
&lt;li&gt;虚引用：根本不算链子，只是个事后通知&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="强引用strong-reference"&gt;强引用（Strong Reference）&lt;/h2&gt;
&lt;p&gt;强引用没什么好说的，就是平时写的 &lt;code&gt;Object obj = new Object()&lt;/code&gt;。只要引用还在，GC宁可抛OOM也不会回收这个对象。&lt;/p&gt;
&lt;p&gt;但强引用有个容易踩的坑——&lt;strong&gt;长生命周期对象持有短生命周期对象的引用&lt;/strong&gt;。比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserController&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 这是一个长生命周期的缓存，value是强引用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Map&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Long, User&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; userCache &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; HashMap&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; User &lt;span style="color:#a6e22e"&gt;getUser&lt;/span&gt;(Long userId) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; userCache.&lt;span style="color:#a6e22e"&gt;computeIfAbsent&lt;/span&gt;(userId, id &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; loadFromDb(id));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 &lt;code&gt;userCache&lt;/code&gt; 是 static 的，生命周期跟类一样长。放进去的 User 对象永远不会被回收，哪怕已经没有任何业务代码在使用它了。这就是典型的&lt;strong&gt;内存泄漏&lt;/strong&gt;——对象已经没用了，但因为强引用还在，GC回收不了。&lt;/p&gt;</description></item><item><title>Ta4j 框架初学者完整教程</title><link>https://zhouwy.top/posts/ta4j-%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E6%A1%86%E6%9E%B6%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E6%88%98/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/ta4j-%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E6%A1%86%E6%9E%B6%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E6%88%98/</guid><description>&lt;h1 id="ta4j-框架初学者完整教程"&gt;Ta4j 框架初学者完整教程&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%BF%B0%E4%B8%8E%E6%A0%B8%E5%BF%83%E6%9E%B6%E6%9E%84"&gt;项目概述与核心架构&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E4%BB%80%E4%B9%88%E6%98%AF-ta4j"&gt;什么是 Ta4j？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8A%80%E6%9C%AF%E6%A0%88%E4%B8%8E%E7%89%88%E6%9C%AC"&gt;技术栈与版本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E9%A1%B9%E7%9B%AE%E7%BB%84%E7%BB%87%E7%BB%93%E6%9E%84"&gt;项目组织结构&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5%E8%AF%A6%E8%A7%A3"&gt;核心概念详解&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#barseries---%E6%95%B0%E6%8D%AE%E5%AE%B9%E5%99%A8"&gt;BarSeries - 数据容器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#indicator---%E6%8A%80%E6%9C%AF%E6%8C%87%E6%A0%87"&gt;Indicator - 技术指标&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#rule---%E4%BA%A4%E6%98%93%E8%A7%84%E5%88%99"&gt;Rule - 交易规则&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#strategy---%E4%BA%A4%E6%98%93%E7%AD%96%E7%95%A5"&gt;Strategy - 交易策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tradingrecord---%E4%BA%A4%E6%98%93%E8%AE%B0%E5%BD%95"&gt;TradingRecord - 交易记录&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E5%AE%9E%E6%88%98%E7%A4%BA%E4%BE%8B%E8%A7%A3%E6%9E%90"&gt;实战示例解析&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#rsi2-%E7%AD%96%E7%95%A5%E8%AF%A6%E8%A7%A3"&gt;RSI2 策略详解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E7%A7%BB%E5%8A%A8%E5%8A%A8%E9%87%8F%E7%AD%96%E7%95%A5"&gt;移动动量策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%97%B6%E9%97%B4%E7%AD%96%E7%95%A5%E7%A4%BA%E4%BE%8B"&gt;时间策略示例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#cci-%E4%BF%AE%E6%AD%A3%E7%AD%96%E7%95%A5"&gt;CCI 修正策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%85%A8%E5%B1%80%E6%9E%81%E5%80%BC%E7%AD%96%E7%95%A5"&gt;全局极值策略&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E6%95%B0%E6%8D%AE%E6%BA%90%E7%B3%BB%E7%BB%9F%E8%AF%A6%E8%A7%A3"&gt;数据源系统详解&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#csv-%E6%95%B0%E6%8D%AE%E6%BA%90"&gt;CSV 数据源&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#json-%E6%95%B0%E6%8D%AE%E6%BA%90"&gt;JSON 数据源&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#http-%E6%95%B0%E6%8D%AE%E6%BA%90"&gt;HTTP 数据源&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#bitstamp-%E6%95%B0%E6%8D%AE%E6%BA%90"&gt;Bitstamp 数据源&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E5%9B%9E%E6%B5%8B%E7%B3%BB%E7%BB%9F%E8%AF%A6%E8%A7%A3"&gt;回测系统详解&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E7%AE%80%E5%8D%95%E5%9B%9E%E6%B5%8B%E7%A4%BA%E4%BE%8B"&gt;简单回测示例&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%AD%A5%E8%BF%9B%E5%BC%8F%E5%9B%9E%E6%B5%8B"&gt;步进式回测&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E9%9B%85%E8%99%8E%E8%B4%A2%E7%BB%8F%E5%9B%9E%E6%B5%8B"&gt;雅虎财经回测&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%9B%9E%E6%B5%8B%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98"&gt;回测性能调优&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E5%9B%BE%E8%A1%A8%E7%B3%BB%E7%BB%9F%E8%AF%A6%E8%A7%A3"&gt;图表系统详解&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%9B%BE%E8%A1%A8%E5%B7%A5%E4%BD%9C%E6%B5%81"&gt;图表工作流&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%8C%87%E6%A0%87%E5%8F%AF%E8%A7%86%E5%8C%96"&gt;指标可视化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#k%E7%BA%BF%E5%9B%BE%E7%BB%98%E5%88%B6"&gt;K线图绘制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%BA%A4%E6%98%93%E4%BF%A1%E5%8F%B7%E5%8F%AF%E8%A7%86%E5%8C%96"&gt;交易信号可视化&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E9%AB%98%E7%BA%A7%E4%B8%BB%E9%A2%98"&gt;高级主题&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E8%89%BE%E7%95%A5%E7%89%B9%E6%B3%A2%E6%B5%AA%E5%88%86%E6%9E%90"&gt;艾略特波浪分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%A8%81%E7%A7%91%E5%A4%AB%E5%88%86%E6%9E%90"&gt;威科夫分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%95%B0%E5%80%BC%E7%B2%BE%E5%BA%A6%E4%B8%8E%E6%80%A7%E8%83%BD"&gt;数值精度与性能&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E7%AD%96%E7%95%A5%E6%89%A7%E8%A1%8C%E6%97%A5%E5%BF%97"&gt;策略执行日志&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5"&gt;最佳实践&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E4%BB%A3%E7%A0%81%E7%BB%84%E7%BB%87%E8%A7%84%E8%8C%83"&gt;代码组织规范&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%BB%BA%E8%AE%AE"&gt;性能优化建议&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%B0%83%E8%AF%95%E6%8A%80%E5%B7%A7"&gt;调试技巧&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94"&gt;常见问题解答&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E5%AD%A6%E4%B9%A0%E8%B7%AF%E5%BE%84%E5%BB%BA%E8%AE%AE"&gt;学习路径建议&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#%E5%88%9D%E5%AD%A6%E8%80%85%E8%B7%AF%E7%BA%BF"&gt;初学者路线&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E4%B8%AD%E7%BA%A7%E5%BC%80%E5%8F%91%E8%80%85%E8%B7%AF%E7%BA%BF"&gt;中级开发者路线&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E9%AB%98%E7%BA%A7%E5%BA%94%E7%94%A8%E8%B7%AF%E7%BA%BF"&gt;高级应用路线&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="#%E9%99%84%E5%BD%95"&gt;附录&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#api-%E5%BF%AB%E9%80%9F%E5%8F%82%E8%80%83"&gt;API 快速参考&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E8%B5%84%E6%BA%90%E9%93%BE%E6%8E%A5"&gt;资源链接&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#%E6%9C%AF%E8%AF%AD%E8%A1%A8"&gt;术语表&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="项目概述与核心架构"&gt;项目概述与核心架构&lt;/h2&gt;
&lt;h3 id="什么是-ta4j"&gt;什么是 Ta4j？&lt;/h3&gt;
&lt;p&gt;Ta4j (Technical Analysis for Java) 是一个开源的 Java 技术分析库，专门用于金融市场的技术分析和算法交易策略回测。它为开发者提供了一套完整的框架来：&lt;/p&gt;</description></item><item><title>Ta4j 量化交易框架完整指南：趋势指标解析</title><link>https://zhouwy.top/posts/ta4j-%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E6%A1%86%E6%9E%B6%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%E8%B6%8B%E5%8A%BF%E6%8C%87%E6%A0%87%E8%A7%A3%E6%9E%90/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/ta4j-%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93%E6%A1%86%E6%9E%B6%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%E8%B6%8B%E5%8A%BF%E6%8C%87%E6%A0%87%E8%A7%A3%E6%9E%90/</guid><description>&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;指标&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;典型参数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SMAIndicator&lt;/td&gt;
&lt;td&gt;简单移动平均&lt;/td&gt;
&lt;td&gt;周期 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EMAIndicator&lt;/td&gt;
&lt;td&gt;指数移动平均&lt;/td&gt;
&lt;td&gt;周期 9, 26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WMAIndicator&lt;/td&gt;
&lt;td&gt;加权移动平均&lt;/td&gt;
&lt;td&gt;周期 14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HMAIndicator&lt;/td&gt;
&lt;td&gt;赫尔移动平均（滞后极小）&lt;/td&gt;
&lt;td&gt;周期 16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KAMAIndicator&lt;/td&gt;
&lt;td&gt;考夫曼自适应移动平均&lt;/td&gt;
&lt;td&gt;周期 10, 快慢 EMA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JMAIndicator&lt;/td&gt;
&lt;td&gt;Jurik 移动平均（零滞后）&lt;/td&gt;
&lt;td&gt;周期 14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TMAIndicator&lt;/td&gt;
&lt;td&gt;三角移动平均（双重平滑）&lt;/td&gt;
&lt;td&gt;周期 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VIDYAIndicator&lt;/td&gt;
&lt;td&gt;可变指数动态平均&lt;/td&gt;
&lt;td&gt;周期 14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZLEMAIndicator&lt;/td&gt;
&lt;td&gt;零滞后指数移动平均&lt;/td&gt;
&lt;td&gt;周期 20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DoubleEMAIndicator&lt;/td&gt;
&lt;td&gt;双重指数移动平均&lt;/td&gt;
&lt;td&gt;周期 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TripleEMAIndicator&lt;/td&gt;
&lt;td&gt;三重指数移动平均&lt;/td&gt;
&lt;td&gt;周期 10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这11个趋势指标大致可以分为三个核心流派：&lt;strong&gt;追求响应速度的“反应型”&lt;/strong&gt;、&lt;strong&gt;追求极致平滑的“稳健型”&lt;/strong&gt;，以及&lt;strong&gt;追求两者平衡的“自适应型”&lt;/strong&gt;。它们的计算逻辑和哲学思考如下。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一-经典平滑派速度与稳健的权衡"&gt;一、 经典平滑派：速度与稳健的权衡&lt;/h2&gt;
&lt;p&gt;这类指标是技术分析的基石，它们的差异体现了在“反应速度”和“信号稳健”之间的哲学权衡。&lt;/p&gt;
&lt;h3 id="1-简单移动平均-sma-simple-moving-average"&gt;1. 简单移动平均 (SMA, Simple Moving Average)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;数学原理：&lt;/strong&gt;
这是最基础的均线，计算特定周期内的价格算数平均值。每天的价格权重完全一样。
&lt;code&gt;SMA = (P1 + P2 + ... + Pn) / n&lt;/code&gt;&lt;/p&gt;</description></item><item><title>从布隆过滤器到 AC 自动机：一个敏感词过滤工具的重写笔记</title><link>https://zhouwy.top/posts/%E4%BB%8E%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E5%88%B0-ac-%E8%87%AA%E5%8A%A8%E6%9C%BA%E4%B8%80%E4%B8%AA%E6%95%8F%E6%84%9F%E8%AF%8D%E8%BF%87%E6%BB%A4%E5%B7%A5%E5%85%B7%E7%9A%84%E9%87%8D%E5%86%99%E7%AC%94%E8%AE%B0/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/%E4%BB%8E%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E5%88%B0-ac-%E8%87%AA%E5%8A%A8%E6%9C%BA%E4%B8%80%E4%B8%AA%E6%95%8F%E6%84%9F%E8%AF%8D%E8%BF%87%E6%BB%A4%E5%B7%A5%E5%85%B7%E7%9A%84%E9%87%8D%E5%86%99%E7%AC%94%E8%AE%B0/</guid><description>&lt;h1 id="从布隆过滤器到-ac-自动机一个敏感词过滤工具的重写笔记"&gt;从布隆过滤器到 AC 自动机：一个敏感词过滤工具的重写笔记&lt;/h1&gt;
&lt;p&gt;前段时间在做 AI 流式输出的内容审核，需要一套敏感词检测方案。本来想找个现成的库直接用，但后来还是决定自己写一遍——主要是想把里边的原理摸透。这篇文章算是把整个思考过程、踩过的坑和最终的实现串起来，做个记录。&lt;/p&gt;
&lt;h2 id="一开始的想法布隆过滤器--词库匹配"&gt;一开始的想法：布隆过滤器 + 词库匹配&lt;/h2&gt;
&lt;p&gt;第一反应是搞个布隆过滤器。这东西内存占用小，判断“肯定不存在”极快，拿来当第一道筛子很合适。&lt;/p&gt;
&lt;p&gt;我很快写了个简单的布隆过滤器，用 MD5 做哈希，加上几个不同的盐值，凑出 k 个哈希函数。然后把敏感词库全塞进去。对于一段文本，如果布隆说过滤器认为“肯定不包含敏感词”，那就直接放行；如果说“可能存在”，再交给后端的精确匹配（比如一个 HashMap 词库）去验证。&lt;/p&gt;
&lt;p&gt;测试下来，安全文本的过滤效率确实很高。但我很快就发现了一个致命问题——流式输出。&lt;/p&gt;
&lt;h3 id="流式场景下的尴尬"&gt;流式场景下的尴尬&lt;/h3&gt;
&lt;p&gt;AI 生成内容是逐字往外吐的。假设敏感词是 &lt;code&gt;ab&lt;/code&gt;，第一个片段只来了 &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt;，第二个片段来了 &lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt;。如果对每个片段单独过布隆过滤器，&lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt; 不在词库里，放行；&lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt; 也不在，放行。结果 &lt;code&gt;&amp;quot;ab&amp;quot;&lt;/code&gt; 就这么漏过去了。&lt;/p&gt;
&lt;p&gt;这个问题不是布隆过滤器独有的。任何无状态的、逐片段独立检测的方案都绕不开。要解决它，必须让检测器记住“刚才已经看到了一个 &lt;code&gt;a&lt;/code&gt;，再等等看后面有没有 &lt;code&gt;b&lt;/code&gt;”。&lt;/p&gt;
&lt;p&gt;也就是说，我需要一个有状态的匹配器。&lt;/p&gt;
&lt;h2 id="ac-自动机的救场"&gt;AC 自动机的救场&lt;/h2&gt;
&lt;p&gt;后来想起来大学时学过的一个算法——AC 自动机。这东西本来就是为了解决“一次扫描，匹配多个模式串”的问题。而且它天生可以维护状态：匹配完一个片段后，记住当前走到了 Trie 树的哪个节点，下一个片段来了接着往下走。&lt;/p&gt;
&lt;p&gt;AC 自动机的结构其实不复杂：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先把所有敏感词建一棵 Trie 树。&lt;/li&gt;
&lt;li&gt;然后用 BFS 给每个节点补一条失败指针（fail）。失败指针的作用是，当在当前节点匹配不上下一个字符时，应该跳到哪个节点继续匹配——有点像 KMP 的 next 数组，但搬到了树上。&lt;/li&gt;
&lt;li&gt;匹配时，拿着文本的每个字符沿着树往下走，走不通就顺着 fail 跳，走到有输出的节点就记下来。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个过程是严格 O(n) 的，跟敏感词数量无关。流式场景下，只需要在每次处理完一个片段后把当前节点存下来，下一个片段来了接着用。&lt;/p&gt;
&lt;p&gt;我写了一个普通的 AC 自动机实现，用 &lt;code&gt;HashMap&lt;/code&gt; 存子节点，结构清晰，调试也方便。对于十万级别的词库，内存占用几十 MB，完全能接受。&lt;/p&gt;
&lt;h3 id="分片测试"&gt;分片测试&lt;/h3&gt;
&lt;p&gt;用之前的例子：敏感词 &lt;code&gt;[&amp;quot;ab&amp;quot;, &amp;quot;bc&amp;quot;]&lt;/code&gt;，分三次喂入 &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt;、&lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt;、&lt;code&gt;&amp;quot;c&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一次 &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt;：状态从根走到节点 &lt;code&gt;a&lt;/code&gt;，没有输出。&lt;/li&gt;
&lt;li&gt;第二次 &lt;code&gt;&amp;quot;b&amp;quot;&lt;/code&gt;：从节点 &lt;code&gt;a&lt;/code&gt; 走到 &lt;code&gt;ab&lt;/code&gt;，输出 &lt;code&gt;&amp;quot;ab&amp;quot;&lt;/code&gt;。同时因为 &lt;code&gt;ab&lt;/code&gt; 的 fail 指向第一层的 &lt;code&gt;b&lt;/code&gt; 节点，而 &lt;code&gt;b&lt;/code&gt; 节点本身没有输出，所以只输出一个词。&lt;/li&gt;
&lt;li&gt;第三次 &lt;code&gt;&amp;quot;c&amp;quot;&lt;/code&gt;：当前节点是 &lt;code&gt;ab&lt;/code&gt;，没有 &lt;code&gt;c&lt;/code&gt; 子节点，于是顺着 fail 跳到 &lt;code&gt;b&lt;/code&gt;，发现 &lt;code&gt;b&lt;/code&gt; 有子节点 &lt;code&gt;c&lt;/code&gt;，走过去到达 &lt;code&gt;bc&lt;/code&gt;，输出 &lt;code&gt;&amp;quot;bc&amp;quot;&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完美。跨片段的 &lt;code&gt;&amp;quot;ab&amp;quot;&lt;/code&gt; 和 &lt;code&gt;&amp;quot;bc&amp;quot;&lt;/code&gt; 都抓到了。&lt;/p&gt;</description></item><item><title>线程池里用 InheritableThreadLocal 传值，怎么就丢了？</title><link>https://zhouwy.top/posts/inheritablethreadlocal-%E4%BC%A0%E5%80%BC%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/inheritablethreadlocal-%E4%BC%A0%E5%80%BC%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/</guid><description>&lt;h2 id="线程池里用-inheritablethreadlocal-传值怎么就丢了"&gt;线程池里用 InheritableThreadLocal 传值，怎么就丢了？&lt;/h2&gt;
&lt;p&gt;你肯定遇到过这种怪事：代码里明明用了 &lt;code&gt;InheritableThreadLocal&lt;/code&gt;，信心满满地觉得子线程能自动继承主线程的变量，结果一跑，值没了，或者串了。&lt;/p&gt;
&lt;p&gt;我第一次碰到这问题时， debug 了一下午，最后发现是线程池的锅。今天我就把当时挖出来的东西从头捋一遍，包括 Java 源码里到底是怎么拷贝的，还有阿里那个 &lt;code&gt;TransmittableThreadLocal&lt;/code&gt; 到底是怎么救场的。&lt;/p&gt;
&lt;h3 id="先看-inheritablethreadlocal-是怎么继承的"&gt;先看 InheritableThreadLocal 是怎么“继承”的&lt;/h3&gt;
&lt;p&gt;每个 Java 线程，也就是 &lt;code&gt;Thread&lt;/code&gt; 类的实例，肚子里都揣着两个 &lt;code&gt;ThreadLocalMap&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个叫 &lt;code&gt;threadLocals&lt;/code&gt;，专门给普通的 &lt;code&gt;ThreadLocal&lt;/code&gt; 存变量。&lt;/li&gt;
&lt;li&gt;另一个叫 &lt;code&gt;inheritableThreadLocals&lt;/code&gt;，给 &lt;code&gt;InheritableThreadLocal&lt;/code&gt; 用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两个 map 平时都是懒加载的，你第一次 &lt;code&gt;set&lt;/code&gt; 的时候才会创建。&lt;/p&gt;
&lt;p&gt;当我们 &lt;code&gt;new&lt;/code&gt; 一个子线程的时候，事情就发生在 &lt;code&gt;Thread&lt;/code&gt; 的 &lt;code&gt;init()&lt;/code&gt; 方法里。这个方法会在子线程的构造函数中被调用，关键的一段逻辑是这样的（我摘了简化版的源码）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (parent.&lt;span style="color:#a6e22e"&gt;inheritableThreadLocals&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;inhertableThreadLocals&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ThreadLocal.&lt;span style="color:#a6e22e"&gt;createInheritedMap&lt;/span&gt;(parent.&lt;span style="color:#a6e22e"&gt;inheritableThreadLocals&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，如果父线程（也就是创建子线程的那个线程）的 &lt;code&gt;inheritableThreadLocals&lt;/code&gt; 里面有东西，就把整个 map 拷贝一份，作为子线程的 &lt;code&gt;inheritableThreadLocals&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那 &lt;code&gt;createInheritedMap&lt;/code&gt; 是怎么拷贝的呢？它实际上是调用了 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 的一个特殊构造方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ThreadLocalMap&lt;/span&gt;(ThreadLocalMap parentMap) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... 一些初始化代码 ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 0; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; len; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Entry e &lt;span style="color:#f92672"&gt;=&lt;/span&gt; parentMap.&lt;span style="color:#a6e22e"&gt;table&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;i&lt;span style="color:#f92672"&gt;]&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (e &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ThreadLocal&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (ThreadLocal&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;) e.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (key &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 注意这一行：调用了 childValue 方法&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Object value &lt;span style="color:#f92672"&gt;=&lt;/span&gt; key.&lt;span style="color:#a6e22e"&gt;childValue&lt;/span&gt;(e.&lt;span style="color:#a6e22e"&gt;value&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Entry c &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Entry(key, value);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... 把新 entry 放到子线程的 map 里 ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重点就是这个 &lt;code&gt;childValue&lt;/code&gt; 方法。它是 &lt;code&gt;InheritableThreadLocal&lt;/code&gt; 里定义的一个 protected 方法，默认实现就是直接返回传进来的值：&lt;/p&gt;</description></item><item><title>写作规范</title><link>https://zhouwy.top/posts/resume-deep-dive/%E5%86%99%E4%BD%9C%E8%A7%84%E8%8C%83/</link><pubDate>Fri, 04 Jul 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/%E5%86%99%E4%BD%9C%E8%A7%84%E8%8C%83/</guid><description>&lt;h1 id="写作规范"&gt;写作规范&lt;/h1&gt;
&lt;h2 id="文章定位"&gt;文章定位&lt;/h2&gt;
&lt;p&gt;每篇文章都服务于三个目标：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;帮助自己真正理解简历中的技术点。&lt;/li&gt;
&lt;li&gt;帮助面试时讲清项目背景、技术方案和取舍。&lt;/li&gt;
&lt;li&gt;帮助后续继续扩展为博客、知识库或面试手册。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="统一结构"&gt;统一结构&lt;/h2&gt;
&lt;p&gt;每篇文章默认使用下面的结构，可以根据主题微调：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# 标题
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 1. 对应简历中的哪一段
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 2. 业务背景是什么
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 3. 问题为什么会出现
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 4. 核心技术原理
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 5. 项目里怎么落地
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 6. 关键代码或伪代码
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 7. 常见坑与排查方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 8. 面试官可能怎么追问
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 9. 推荐回答
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## 10. 延伸学习路线
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="写作要求"&gt;写作要求&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;不写纯概念解释，每个概念都尽量落到保险销售、电商交易、商机流转、信创迁移、报表优化或 AI 客服场景。&lt;/li&gt;
&lt;li&gt;不把简历写成无法自圆其说的“神话”，遇到不确定的数据用稳妥表达。&lt;/li&gt;
&lt;li&gt;技术深度要能支撑资深 Java 面试，尤其要写清楚取舍、风险、边界和排查路径。&lt;/li&gt;
&lt;li&gt;代码以伪代码或简化 Java 示例为主，重点说明设计思路。&lt;/li&gt;
&lt;li&gt;每篇文章末尾必须有“面试追问”和“推荐回答”。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>面试深挖问题清单</title><link>https://zhouwy.top/posts/resume-deep-dive/%E9%9D%A2%E8%AF%95%E6%B7%B1%E6%8C%96%E9%97%AE%E9%A2%98%E6%B8%85%E5%8D%95/</link><pubDate>Thu, 03 Jul 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/%E9%9D%A2%E8%AF%95%E6%B7%B1%E6%8C%96%E9%97%AE%E9%A2%98%E6%B8%85%E5%8D%95/</guid><description>&lt;h1 id="面试深挖问题清单"&gt;面试深挖问题清单&lt;/h1&gt;
&lt;p&gt;这个文件用于持续记录简历中每个技术点可能被追问的问题。后续每写完一篇文章，都把关键问题补充进来。&lt;/p&gt;
&lt;h2 id="java-并发与高并发治理"&gt;Java 并发与高并发治理&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;线程池核心参数如何设置？为什么不能直接使用 &lt;code&gt;Executors.newFixedThreadPool&lt;/code&gt;？&lt;/li&gt;
&lt;li&gt;销售活动高峰时，如何判断瓶颈在线程池、数据库、MQ 还是下游接口？&lt;/li&gt;
&lt;li&gt;异步化以后如何保证链路可观测、异常可追踪、结果可补偿？&lt;/li&gt;
&lt;li&gt;限流、熔断、降级、隔离分别解决什么问题？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CompletableFuture&lt;/code&gt; 适合哪些并发编排场景？异常如何统一处理？&lt;/li&gt;
&lt;li&gt;本地锁、数据库锁、Redis 分布式锁分别适合什么边界？&lt;/li&gt;
&lt;li&gt;MQ 削峰后，如何避免消息堆积把问题延后爆发？&lt;/li&gt;
&lt;li&gt;月底结算高峰时，哪些链路应该同步返回，哪些链路适合异步化？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jvm-调优与线上诊断"&gt;JVM 调优与线上诊断&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Full GC 频繁时，第一步看什么？&lt;/li&gt;
&lt;li&gt;如何通过 GC 日志判断是内存泄漏、分配速率过高还是老年代碎片？&lt;/li&gt;
&lt;li&gt;MAT 里如何定位内存泄漏？&lt;/li&gt;
&lt;li&gt;Arthas 的 &lt;code&gt;trace&lt;/code&gt;、&lt;code&gt;watch&lt;/code&gt;、&lt;code&gt;thread&lt;/code&gt; 分别适合什么场景？&lt;/li&gt;
&lt;li&gt;大 Excel 导出为什么容易 OOM？分页查询和流式写入分别解决什么问题？&lt;/li&gt;
&lt;li&gt;线上诊断时如何避免 Arthas 命令对生产系统造成额外压力？&lt;/li&gt;
&lt;li&gt;稳定性治理里，接口耗时、线程池、GC、MQ 堆积应该如何联动观察？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="spring--mybatis--架构升级"&gt;Spring / MyBatis / 架构升级&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Spring3 到 Spring Boot 迁移最容易踩哪些坑？&lt;/li&gt;
&lt;li&gt;MyBatis 插件机制的拦截点有哪些？&lt;/li&gt;
&lt;li&gt;AOP + SpEL 做数据权限时如何避免性能问题？&lt;/li&gt;
&lt;li&gt;Quartz 迁移到 XXL-Job 时如何保证任务不重复执行？&lt;/li&gt;
&lt;li&gt;WebLogic 到 Undertow 的容器差异主要体现在哪些地方？&lt;/li&gt;
&lt;li&gt;MyBatis 分页拦截器如何识别和改写不同数据库方言？&lt;/li&gt;
&lt;li&gt;Spring 事务失效有哪些典型原因？&lt;/li&gt;
&lt;li&gt;老系统升级时如何设计灰度、回滚和验收策略？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="数据库与信创迁移"&gt;数据库与信创迁移&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Oracle 的 &lt;code&gt;ROWNUM&lt;/code&gt; 和 MySQL/OceanBase 的 &lt;code&gt;LIMIT&lt;/code&gt; 语义有什么差异？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONNECT BY&lt;/code&gt; 迁移到递归 CTE 时如何避免死循环？&lt;/li&gt;
&lt;li&gt;OceanBase 慢 SQL 如何看执行计划？&lt;/li&gt;
&lt;li&gt;新旧 SQL 结果对比工具应该如何设计？&lt;/li&gt;
&lt;li&gt;Oracle 空字符串和 &lt;code&gt;NULL&lt;/code&gt; 的差异会影响哪些业务判断？&lt;/li&gt;
&lt;li&gt;存储过程迁移到 Java 服务时，事务边界和批处理性能如何保证？&lt;/li&gt;
&lt;li&gt;物化视图适合哪些报表场景？数据新鲜度如何权衡？&lt;/li&gt;
&lt;li&gt;关系型数据库和图数据库在人脉关系查询上的边界是什么？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="分布式中间件与稳定性"&gt;分布式中间件与稳定性&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;RocketMQ 如何保证消息不丢？&lt;/li&gt;
&lt;li&gt;事务消息适合什么场景，不适合什么场景？&lt;/li&gt;
&lt;li&gt;Kafka 为什么适合行为轨迹链路？&lt;/li&gt;
&lt;li&gt;Sentinel 的限流和熔断规则如何设计？&lt;/li&gt;
&lt;li&gt;Seata AT 模式的原理和局限是什么？&lt;/li&gt;
&lt;li&gt;消息消费如何保证幂等？重复消费和乱序消费怎么处理？&lt;/li&gt;
&lt;li&gt;Nacos 配置变更如何避免误发布影响生产？&lt;/li&gt;
&lt;li&gt;MDC + ELK 如何串起一次跨系统调用？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ai-工程化"&gt;AI 工程化&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;RAG 如何处理知识过期、权限隔离和召回不准？&lt;/li&gt;
&lt;li&gt;Function Calling 调业务 API 时如何做权限控制和审计？&lt;/li&gt;
&lt;li&gt;SSE 和 WebSocket 在 AI 对话场景下如何取舍？&lt;/li&gt;
&lt;li&gt;MCP 的价值是什么？和普通 API 封装有什么区别？&lt;/li&gt;
&lt;li&gt;大模型调用业务接口时，如何防止越权、误操作和敏感数据泄露？&lt;/li&gt;
&lt;li&gt;AI 客服如何区分“可直接回答”“需要检索”“需要工具调用”“需要人工介入”？&lt;/li&gt;
&lt;li&gt;OpenClaw 做业务流程自动化时，如何处理失败重试和人工确认？&lt;/li&gt;
&lt;li&gt;ReAct 在研发效能工具里如何避免无限循环和错误推理扩散？&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>简历深挖知识库：资深 Java 金融系统面试手册</title><link>https://zhouwy.top/posts/resume-deep-dive/00-%E6%80%BB%E7%9B%AE%E5%BD%95/</link><pubDate>Wed, 02 Jul 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/00-%E6%80%BB%E7%9B%AE%E5%BD%95/</guid><description>&lt;h1 id="简历深挖知识库资深-java-金融系统面试手册"&gt;简历深挖知识库：资深 Java 金融系统面试手册&lt;/h1&gt;
&lt;p&gt;这个知识库围绕周维扬简历中的真实项目经历展开，目标不是堆概念，而是把每个简历亮点拆成“能理解、能复盘、能讲清、能被追问”的技术文章。&lt;/p&gt;
&lt;h2 id="写作目标"&gt;写作目标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;把简历中的关键技术点系统化，形成长期可复习的个人知识库。&lt;/li&gt;
&lt;li&gt;每篇文章都要能支撑一次 10 到 20 分钟的面试深挖。&lt;/li&gt;
&lt;li&gt;每个专题都要结合保险销售、电商交易、商机流转、信创迁移、报表优化、AI 客服等真实业务背景。&lt;/li&gt;
&lt;li&gt;避免空泛八股，强调项目场景、技术取舍、落地步骤、排查路径和面试回答。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="专题规划"&gt;专题规划&lt;/h2&gt;
&lt;h3 id="01-java-并发与高并发治理"&gt;01. Java 并发与高并发治理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;线程池在保险销售高峰场景中的设计与调优&lt;/li&gt;
&lt;li&gt;JUC 核心工具详解：CountDownLatch、Semaphore、CompletableFuture、BlockingQueue&lt;/li&gt;
&lt;li&gt;锁机制详解：synchronized、ReentrantLock、读写锁与分布式锁的边界&lt;/li&gt;
&lt;li&gt;MQ 削峰填谷在商机流转系统中的落地&lt;/li&gt;
&lt;li&gt;异步化改造：从同步调用到事件驱动&lt;/li&gt;
&lt;li&gt;高并发场景下的限流、降级、熔断与隔离&lt;/li&gt;
&lt;li&gt;月底结算与营销活动高峰的稳定性设计复盘&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="02-jvm-调优与线上诊断"&gt;02. JVM 调优与线上诊断&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JVM 内存模型与保险业务系统常见内存问题&lt;/li&gt;
&lt;li&gt;GC 日志如何看：从 Full GC 频繁到问题定位&lt;/li&gt;
&lt;li&gt;堆 Dump 分析实战：如何定位内存泄漏&lt;/li&gt;
&lt;li&gt;Arthas 在线诊断实战：trace、watch、jad、thread、dashboard&lt;/li&gt;
&lt;li&gt;大 Excel 导出 OOM 的原因、排查与优化&lt;/li&gt;
&lt;li&gt;生产环境稳定性治理：指标、告警、日志与应急流程&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="03-spring--mybatis--遗留系统升级"&gt;03. Spring / MyBatis / 遗留系统升级&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Spring3 到 Spring Boot 迁移的关键问题与改造路径&lt;/li&gt;
&lt;li&gt;WebLogic 到 Undertow：容器迁移中的兼容性问题&lt;/li&gt;
&lt;li&gt;MyBatis 执行流程与插件机制详解&lt;/li&gt;
&lt;li&gt;MyBatis 拦截器实现分页 SQL 自动转换&lt;/li&gt;
&lt;li&gt;AOP + SpEL 实现动态数据权限&lt;/li&gt;
&lt;li&gt;Spring 事务机制与保险业务一致性问题&lt;/li&gt;
&lt;li&gt;Quartz 扩展与 XXL-Job 迁移实践&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="04-数据库与信创迁移"&gt;04. 数据库与信创迁移&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Oracle 到 OceanBase 信创迁移整体方法论&lt;/li&gt;
&lt;li&gt;ROWNUM 到 LIMIT：分页语义差异与改造方式&lt;/li&gt;
&lt;li&gt;CONNECT BY 到递归 CTE：树查询迁移详解&lt;/li&gt;
&lt;li&gt;Oracle 空字符串、函数、日期、序列兼容问题&lt;/li&gt;
&lt;li&gt;OceanBase 执行计划与慢 SQL 优化&lt;/li&gt;
&lt;li&gt;物化视图、索引设计与报表性能优化&lt;/li&gt;
&lt;li&gt;新旧 SQL 与存储过程结果对比工具设计&lt;/li&gt;
&lt;li&gt;从关系查询到图数据库：Neo4j 调研与适用边界&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="05-分布式中间件与稳定性"&gt;05. 分布式中间件与稳定性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;RocketMQ 在商机流转系统中的解耦设计&lt;/li&gt;
&lt;li&gt;RocketMQ 事务消息与最终一致性&lt;/li&gt;
&lt;li&gt;Kafka 在客户行为轨迹链路中的使用&lt;/li&gt;
&lt;li&gt;Sentinel 限流熔断在营销活动中的落地&lt;/li&gt;
&lt;li&gt;Seata AT 模式在资金划扣场景中的使用&lt;/li&gt;
&lt;li&gt;Nacos 配置中心与服务注册实践&lt;/li&gt;
&lt;li&gt;MDC + ELK 全链路日志系统建设&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="06-ai-工程化与传统业务系统接入"&gt;06. AI 工程化与传统业务系统接入&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;公司内部 AI 客服系统整体架构设计&lt;/li&gt;
&lt;li&gt;RAG 在保险产品问答中的落地方式&lt;/li&gt;
&lt;li&gt;Function Calling 如何安全调用业务 API&lt;/li&gt;
&lt;li&gt;MCP 思想与业务工具标准化封装&lt;/li&gt;
&lt;li&gt;SSE 流式响应在 AI 对话系统中的使用&lt;/li&gt;
&lt;li&gt;OpenClaw 在保全操作自动化中的应用&lt;/li&gt;
&lt;li&gt;Spring AI / AgentScope / ReAct 在研发效能工具中的实践&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="第一批文章"&gt;第一批文章&lt;/h2&gt;
&lt;p&gt;第一批先完成每个专题的核心文章，用来确定深度、结构和风格：&lt;/p&gt;</description></item><item><title>如何使用这套资料</title><link>https://zhouwy.top/posts/resume-deep-dive/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E8%BF%99%E5%A5%97%E8%B5%84%E6%96%99/</link><pubDate>Tue, 01 Jul 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E8%BF%99%E5%A5%97%E8%B5%84%E6%96%99/</guid><description>&lt;h1 id="如何使用这套资料"&gt;如何使用这套资料&lt;/h1&gt;
&lt;p&gt;这套资料不是普通博客合集，而是围绕简历构建的个人技术深挖手册。建议按下面的方式使用。&lt;/p&gt;
&lt;h2 id="1-先从简历句子开始"&gt;1. 先从简历句子开始&lt;/h2&gt;
&lt;p&gt;每篇文章都对应简历中的一句或几句描述。复习时不要先背概念，而是先问自己：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这句话在简历里对应哪个项目？&lt;/li&gt;
&lt;li&gt;当时业务为什么需要这个能力？&lt;/li&gt;
&lt;li&gt;如果没有这个方案，会出现什么问题？&lt;/li&gt;
&lt;li&gt;我在里面负责什么？&lt;/li&gt;
&lt;li&gt;这个技术点能不能讲清楚取舍和边界？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-再补技术原理"&gt;2. 再补技术原理&lt;/h2&gt;
&lt;p&gt;项目经历是入口，技术原理是支撑。面试官通常会从项目追到原理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你为什么这么设计？&lt;/li&gt;
&lt;li&gt;有没有其他方案？&lt;/li&gt;
&lt;li&gt;这个方案的缺点是什么？&lt;/li&gt;
&lt;li&gt;出问题怎么排查？&lt;/li&gt;
&lt;li&gt;数据量或并发量再扩大十倍怎么办？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以每篇文章都要同时看“业务背景”和“核心原理”。&lt;/p&gt;
&lt;h2 id="3-最后练推荐回答"&gt;3. 最后练推荐回答&lt;/h2&gt;
&lt;p&gt;每篇文章的“面试追问”和“推荐回答”用于口述训练。建议不要逐字背，而是练成三段式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先讲业务背景。&lt;/li&gt;
&lt;li&gt;再讲技术方案。&lt;/li&gt;
&lt;li&gt;最后讲结果、风险和后续优化。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如回答“你们线程池怎么设计”时，不要直接背核心参数，而是从销售活动高峰、下游隔离、队列堆积、拒绝策略、监控告警讲起。&lt;/p&gt;
&lt;h2 id="4-复习顺序建议"&gt;4. 复习顺序建议&lt;/h2&gt;
&lt;p&gt;第一轮按项目价值复习：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Oracle 到 OceanBase 信创迁移&lt;/li&gt;
&lt;li&gt;保险销售系统遗留架构升级&lt;/li&gt;
&lt;li&gt;B2B 商机管理与 RocketMQ 解耦&lt;/li&gt;
&lt;li&gt;海量报表优化与 JVM/OOM 排查&lt;/li&gt;
&lt;li&gt;AI 客服系统整体架构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第二轮按技术专题复习：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Java 并发与线程池&lt;/li&gt;
&lt;li&gt;JVM 与 Arthas&lt;/li&gt;
&lt;li&gt;Spring / MyBatis / 迁移改造&lt;/li&gt;
&lt;li&gt;数据库与 SQL 优化&lt;/li&gt;
&lt;li&gt;中间件与稳定性&lt;/li&gt;
&lt;li&gt;AI 工程化&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第三轮按面试追问复习：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每天挑 5 个问题，用 3 分钟口述回答。&lt;/li&gt;
&lt;li&gt;回答时必须包含背景、方案、结果、风险。&lt;/li&gt;
&lt;li&gt;说不清楚的问题，回到对应文章补知识点。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>OpenClaw 在保全操作自动化中的应用</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/openclaw%E5%9C%A8%E4%BF%9D%E5%85%A8%E6%93%8D%E4%BD%9C%E8%87%AA%E5%8A%A8%E5%8C%96%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/</link><pubDate>Sat, 07 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/openclaw%E5%9C%A8%E4%BF%9D%E5%85%A8%E6%93%8D%E4%BD%9C%E8%87%AA%E5%8A%A8%E5%8C%96%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/</guid><description>&lt;h1 id="openclaw-在保全操作自动化中的应用"&gt;OpenClaw 在保全操作自动化中的应用&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“在传统保险业务系统难以改造的情况下，结合 OpenClaw 自动化流程对部分高频保全后台操作进行低侵入接入，支持 AI 客服生成操作计划、触发受控自动化、回传执行状态，并保留人工确认、权限校验和审计记录”的经历。&lt;/p&gt;
&lt;p&gt;面试时这段不要讲成“用脚本点页面”。资深面试官会更关注：为什么保全场景需要自动化，哪些操作适合自动化，如何避免 AI 直接操作生产系统，如何处理页面变化、失败回滚、人工确认、审计追责，以及如何和传统 Java 系统集成。&lt;/p&gt;
&lt;p&gt;可以这样表述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;面向保全业务中高频、规则明确但传统系统接口缺失的操作，设计 OpenClaw 自动化接入方案。AI 侧只负责生成操作意图和材料清单，后端根据权限和风险决策触发预定义自动化流程，由 OpenClaw 完成页面级操作或半自动填单，并将执行状态、截图证据和审计日志回传客服工作台。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历体现的是“在不能大改存量系统时，如何工程化接入 AI 自动化能力”。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险保全是指保单生效后对合同相关信息或权益进行变更、维护和服务的业务。常见保全操作包括联系方式变更、地址变更、受益人变更、缴费账号变更、保单贷款、减额交清、退保申请、补发保单、续期缴费方式调整等。&lt;/p&gt;
&lt;p&gt;保全系统通常历史较长，流程复杂，权限严格。一些操作有完整 API，但也有不少操作只存在于内部后台页面，或者接口不适合外部系统直接调用。原因可能是系统年代久、接口文档缺失、改造成本高、交易风险大、厂商系统不开放。&lt;/p&gt;
&lt;p&gt;客服或运营人员每天会处理大量重复保全请求。比如客户来电要求修改联系地址，坐席需要核验身份、查询保单、确认可办理项目、进入保全后台、选择保全项目、填写字段、提交初审或生成工单。整个过程规则明确，但跨系统点击多，容易出错。&lt;/p&gt;
&lt;p&gt;AI 接入后，有两个诉求：一是让 AI 帮坐席判断办理路径和材料；二是对低风险、高频、规则固定的后台操作做自动化辅助。但这里绝不能让大模型直接“控制浏览器随便点”。金融生产系统里的保全操作可能改变合同信息，必须受控。&lt;/p&gt;
&lt;p&gt;所以 OpenClaw 的定位不是替代业务系统，而是在接口缺失或改造困难时，作为低侵入自动化适配层，执行预定义、可审计、可回放的流程。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;OpenClaw 可以理解为自动化执行框架，用于把页面操作、脚本任务或业务流程封装成可调用能力。放在 AI 工程化架构里，它不是直接暴露给模型，而是作为工具层背后的一个执行适配器。&lt;/p&gt;
&lt;p&gt;核心原则有三个。&lt;/p&gt;
&lt;p&gt;第一，流程预定义。自动化流程由研发或业务专家提前定义，包括入口页面、字段定位、输入规则、校验点、提交条件、异常处理和截图节点。模型不能临时生成任意页面操作脚本。&lt;/p&gt;
&lt;p&gt;第二，AI 只产出意图，不直接执行。用户说“帮客户修改手机号”，模型可以识别为“联系方式变更”意图，生成所需字段和材料清单，但真正能否执行，要由后端权限、风控和业务规则决定。&lt;/p&gt;
&lt;p&gt;第三，执行可观测。自动化每一步都要有状态、日志、截图或页面证据。失败时要能知道卡在哪一步，是登录失效、元素找不到、业务校验失败，还是页面提示不能办理。&lt;/p&gt;
&lt;p&gt;在系统架构上，AI 编排服务调用 Tool Runtime，Tool Runtime 根据工具定义判断该工具底层由 OpenClaw 执行，然后创建 automation job。OpenClaw worker 拉取任务，打开目标系统页面或调用自动化脚本，按步骤执行，并把状态回写。&lt;/p&gt;
&lt;p&gt;这种模式把风险控制在两个边界内：模型不能自由操作页面，自动化也只能执行预定义流程。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;落地时先选择适合自动化的保全场景。不是所有保全都适合自动化。优先选择规则明确、字段少、失败可恢复、风险较低、人工重复度高的流程，比如联系方式变更草稿创建、保全材料清单生成、补发保单申请工单创建、保全进度查询。退保、受益人变更、银行卡变更这类高风险动作应先做半自动化或草稿生成。&lt;/p&gt;
&lt;p&gt;然后对每个流程建模。一个保全自动化流程至少包括：流程编码、业务名称、风险等级、输入参数、前置条件、权限点、是否需要用户确认、执行步骤、校验点、回滚或中止策略、审计要求。&lt;/p&gt;
&lt;p&gt;例如“联系方式变更草稿创建”可以设计成：输入 policyNo、customerId、newMobile、newAddress；前置条件是用户已完成身份核验，保单状态有效，当前坐席有保全受理权限；流程只创建草稿或待办，不直接终审生效；执行后返回草稿号和截图证据。&lt;/p&gt;
&lt;p&gt;Java 系统中可以有一个 AutomationService：&lt;/p&gt;</description></item><item><title>Spring AI、AgentScope 与 ReAct 在研发效能工具中的实践</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/spring-aiagentscope%E4%B8%8Ereact%E5%9C%A8%E7%A0%94%E5%8F%91%E6%95%88%E8%83%BD%E5%B7%A5%E5%85%B7%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</link><pubDate>Fri, 06 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/spring-aiagentscope%E4%B8%8Ereact%E5%9C%A8%E7%A0%94%E5%8F%91%E6%95%88%E8%83%BD%E5%B7%A5%E5%85%B7%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h1 id="spring-aiagentscope-与-react-在研发效能工具中的实践"&gt;Spring AI、AgentScope 与 ReAct 在研发效能工具中的实践&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“在研发效能场景中探索 Spring AI、AgentScope 和 ReAct 思想，构建面向 Java 团队的智能研发助手，支持需求理解、代码检索、接口说明、日志分析、测试建议、发布检查和工单辅助处理”的经历。&lt;/p&gt;
&lt;p&gt;面试时这段不要讲成“写了一个 ChatGPT 套壳”。资深面试官会关注：为什么研发效能工具需要 Agent，Spring AI 在 Java 体系里解决什么问题，AgentScope 适合做什么编排，ReAct 如何让模型边思考边调用工具，以及如何控制工具权限、上下文长度、误操作和结果可信度。&lt;/p&gt;
&lt;p&gt;可以这样表述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;面向 Java 研发团队搭建内部研发效能助手，使用 Spring AI 统一接入模型、Prompt、Embedding 和向量检索能力，借鉴 AgentScope 的多 Agent 编排思想拆分需求分析、代码检索、日志诊断和测试建议角色，并基于 ReAct 模式让模型在推理过程中按需调用代码搜索、知识库、CI、日志平台和工单系统工具，提高排障和需求理解效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历重点体现 AI 工程化框架选择、Agent 设计和研发流程结合能力。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;大型 Java 团队的研发效率瓶颈不只在写代码本身。很多时间消耗在理解需求、查历史逻辑、找接口文档、定位日志、分析 CI 失败、补测试、写发布说明和处理工单上。&lt;/p&gt;
&lt;p&gt;比如一个新需求来了，研发需要看产品文档、查相关服务、理解数据库表、找历史工单、确认接口影响面。一个线上问题来了，需要查日志、看链路追踪、定位最近发布、比对配置、判断是否回滚。很多信息散落在 Git 仓库、Wiki、接口平台、日志平台、CI、工单系统和聊天记录里。&lt;/p&gt;
&lt;p&gt;AI 研发助手的价值是把这些分散信息通过自然语言入口串起来。但如果只是把问题发给模型，模型并不知道公司代码、内部接口、近期发布和日志。它必须能检索内部知识、调用工具、逐步分析。&lt;/p&gt;
&lt;p&gt;这就引出三个技术点。&lt;/p&gt;
&lt;p&gt;Spring AI 适合 Java 团队，因为它把模型调用、Prompt 模板、Embedding、VectorStore、ChatClient 等能力封装成 Spring 风格，便于接入现有 Spring Boot 系统。&lt;/p&gt;
&lt;p&gt;AgentScope 更强调 Agent 编排思想，可以把复杂任务拆给不同角色或步骤，例如需求分析 Agent、代码检索 Agent、测试建议 Agent、发布检查 Agent。&lt;/p&gt;</description></item><item><title>SSE 流式响应在 AI 对话系统中的使用</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/sse%E6%B5%81%E5%BC%8F%E5%93%8D%E5%BA%94%E5%9C%A8ai%E5%AF%B9%E8%AF%9D%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/</link><pubDate>Thu, 05 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/sse%E6%B5%81%E5%BC%8F%E5%93%8D%E5%BA%94%E5%9C%A8ai%E5%AF%B9%E8%AF%9D%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/</guid><description>&lt;h1 id="sse-流式响应在-ai-对话系统中的使用"&gt;SSE 流式响应在 AI 对话系统中的使用&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“在内部 AI 客服系统中基于 SSE 实现大模型对话流式响应，支持模型 token 增量输出、工具调用状态推送、RAG 引用展示、异常中断处理和前端会话体验优化”的经历。&lt;/p&gt;
&lt;p&gt;面试时这部分不要只说“用了 SseEmitter”。资深 Java / AI 工程化面试会继续问：为什么选择 SSE 而不是 WebSocket，流式输出如何和工具调用结合，断线如何处理，后端线程怎么释放，网关和 Nginx 会不会缓冲，怎么做超时、取消、审计和敏感词拦截。&lt;/p&gt;
&lt;p&gt;可以把项目描述成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 AI 客服对话链路中设计 SSE 流式响应协议，将模型 token、检索状态、工具调用进度、引用来源和最终审计结果以事件流形式返回前端，提升坐席等待体验，并在 Java 后端实现连接生命周期管理、异常兜底、用户取消、超时控制和代理缓冲配置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的价值在于把“模型慢”变成“用户可感知的渐进式反馈”。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;AI 客服系统和传统业务系统最大的体验差异之一是响应时间。传统接口通常几百毫秒到一两秒返回，而大模型回答可能需要数秒甚至十几秒。如果中间还要做 RAG 检索、rerank、工具调用、结果汇总，用户等待时间会更长。&lt;/p&gt;
&lt;p&gt;客服坐席的工作节奏很快。如果系统在十秒内没有任何反馈，坐席会以为卡住了，可能重复点击、刷新页面或转去手工查询。AI 系统即使最终答案正确，体验也会很差。&lt;/p&gt;
&lt;p&gt;SSE 的作用是让后端可以持续向浏览器推送事件。模型生成一个 token 或一小段文本，就推给前端展示；工具调用开始、完成、失败，也可以推送状态；最后再推送完整回答、引用来源和审计 ID。&lt;/p&gt;
&lt;p&gt;为什么很多 AI 对话系统选择 SSE？因为 AI 文本生成通常是服务端到客户端的单向流，前端不需要频繁向后端推送消息。SSE 基于 HTTP，浏览器原生支持 EventSource，穿透代理比 WebSocket 简单，和现有 Spring MVC / WebFlux 系统集成成本较低。&lt;/p&gt;
&lt;p&gt;当然，SSE 不是万能的。如果需要双向实时协作、多人编辑、语音实时交互，WebSocket 更合适。但内部客服文本对话场景，SSE 通常足够。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;SSE，全称 Server-Sent Events，是一种基于 HTTP 长连接的服务端推送机制。响应头通常是：&lt;/p&gt;</description></item><item><title>MCP 思想与业务工具标准化封装</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/mcp%E6%80%9D%E6%83%B3%E4%B8%8E%E4%B8%9A%E5%8A%A1%E5%B7%A5%E5%85%B7%E6%A0%87%E5%87%86%E5%8C%96%E5%B0%81%E8%A3%85/</link><pubDate>Wed, 04 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/mcp%E6%80%9D%E6%83%B3%E4%B8%8E%E4%B8%9A%E5%8A%A1%E5%B7%A5%E5%85%B7%E6%A0%87%E5%87%86%E5%8C%96%E5%B0%81%E8%A3%85/</guid><description>&lt;h1 id="mcp-思想与业务工具标准化封装"&gt;MCP 思想与业务工具标准化封装&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“借鉴 MCP 思想，将公司内部传统业务能力标准化封装为 AI 可发现、可调用、可治理的工具集合，统一接入保单、客户、产品、工单、保全、知识库等系统，降低大模型应用与存量业务系统的耦合”的经历。&lt;/p&gt;
&lt;p&gt;面试时这部分不要只讲“MCP 是一个协议”。资深面试更关心你是否理解 MCP 背后的工程思想：工具描述标准化、上下文传递标准化、权限和审计标准化、资源访问标准化，以及如何把大量不统一的内部系统封装成稳定的 AI 工具层。&lt;/p&gt;
&lt;p&gt;可以这样表述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在内部 AI 工程化平台中，参考 MCP 的资源、工具和上下文抽象，将传统业务 API 封装为统一 Tool Registry。每个工具以标准 schema 描述能力、参数、权限、风险等级、调用方式和返回结构，AI 编排层通过统一协议发现和调用工具，从而避免每个 AI 应用重复对接保单、客户、产品、工单等系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的核心是“用标准化工具层把 AI 应用和传统系统隔离开”。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;企业内部系统通常不是为大模型准备的。保险公司尤其明显：核心保单系统可能是多年前的 Java 单体，客户中心可能是 Dubbo 服务，产品中心可能有 REST 接口，影像系统可能是老 SOAP，保全后台可能只有页面操作，工单系统又有自己的一套权限和状态机。&lt;/p&gt;
&lt;p&gt;如果每个 AI 应用都直接接这些系统，会出现几个问题。&lt;/p&gt;
&lt;p&gt;第一，重复开发。AI 客服要查保单，研发助手也要查工单，运营助手也要查产品配置。每个系统各写一套适配器，维护成本很高。&lt;/p&gt;
&lt;p&gt;第二，接口不稳定。传统 API 的字段、错误码、分页、鉴权方式不统一。模型应用如果直接依赖这些细节，会非常脆弱。&lt;/p&gt;
&lt;p&gt;第三，安全难统一。不同团队各自接接口，权限校验、脱敏、审计、限流可能标准不一致，风险很大。&lt;/p&gt;
&lt;p&gt;第四，模型难理解。内部 API 往往是面向系统调用设计的，比如 queryPolicyBaseInfoV2、getUwRuleByPrdAndPlan，模型不容易从这些名字判断何时使用。&lt;/p&gt;
&lt;p&gt;第五，演进困难。以后要更换模型、增加 Agent、接入工作流平台，如果业务能力没有标准化，就会陷入大量点对点集成。&lt;/p&gt;
&lt;p&gt;MCP 思想的价值在这里体现出来：不是简单追逐新协议，而是把企业内部能力抽象成模型和 Agent 能理解、能发现、能安全调用的工具与资源。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;MCP 可以理解为大模型应用和外部能力之间的标准连接方式。它强调几个抽象：工具 Tool、资源 Resource、提示 Prompt、上下文 Context。落到企业业务系统里，可以转化为四个设计原则。&lt;/p&gt;</description></item><item><title>RAG 在保险产品问答中的落地方式</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/rag%E5%9C%A8%E4%BF%9D%E9%99%A9%E4%BA%A7%E5%93%81%E9%97%AE%E7%AD%94%E4%B8%AD%E7%9A%84%E8%90%BD%E5%9C%B0%E6%96%B9%E5%BC%8F/</link><pubDate>Tue, 03 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/rag%E5%9C%A8%E4%BF%9D%E9%99%A9%E4%BA%A7%E5%93%81%E9%97%AE%E7%AD%94%E4%B8%AD%E7%9A%84%E8%90%BD%E5%9C%B0%E6%96%B9%E5%BC%8F/</guid><description>&lt;h1 id="rag-在保险产品问答中的落地方式"&gt;RAG 在保险产品问答中的落地方式&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“在公司内部 AI 客服系统中，基于 RAG 构建保险产品知识问答能力，支持产品条款、投保规则、续保政策、理赔材料、客服 FAQ 等多源知识检索，并通过引用溯源、版本控制和人工反馈闭环降低大模型幻觉”的经历。&lt;/p&gt;
&lt;p&gt;面试时不要把这段说成“把 PDF 切片后丢进向量库”。资深 Java / AI 工程化面试更关心的是：保险产品知识为什么难，RAG 怎么和产品版本、渠道、地区、生效日期绑定，召回结果怎么评估，回答怎么受控，如何接入传统内容系统和客服工作台，以及出现错误回答后如何追责和修复。&lt;/p&gt;
&lt;p&gt;推荐的简历复述可以是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;负责内部 AI 客服产品问答模块的 RAG 架构设计与落地，将产品条款、销售手册、运营公告、FAQ 和制度文档统一治理为可检索知识库；通过结构化切分、元数据过滤、混合召回、rerank、引用溯源和人工反馈闭环，提高保险产品问答准确率，并在合规边界内支持客服坐席快速解释产品规则。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的价值在于，它不是一个玩具级知识库，而是在金融业务上下文里把非结构化知识变成可控、可审计、可持续运营的问答能力。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险产品问答有几个典型难点。&lt;/p&gt;
&lt;p&gt;第一，文档复杂。一个产品往往同时存在条款、费率说明、销售手册、核保规则、理赔指引、运营公告和客服 FAQ。条款强调法律表达，销售手册强调业务解释，FAQ 强调话术，公告强调临时调整。用户问一句“这个产品还能续保吗”，背后可能涉及产品版本、保单年度、停售公告、渠道政策和客户年龄。&lt;/p&gt;
&lt;p&gt;第二，版本复杂。同名产品可能有 2022 版、2024 版、互联网渠道版、银保渠道版，不同版本的等待期、责任免除、续保规则并不完全一致。如果 RAG 只按文本相似度召回，很容易拿旧版本回答新问题。&lt;/p&gt;
&lt;p&gt;第三，问题表达不标准。坐席或业务人员不一定使用条款里的标准术语。他们会问“甲状腺结节能不能买”“百万医疗断缴了还能接着交吗”“这个病算不算重疾”“小孩买要不要体检”。系统必须把口语问题映射到条款概念、产品配置和业务规则。&lt;/p&gt;
&lt;p&gt;第四，合规压力高。保险客服回答不能夸大保障，不能承诺一定赔付，不能把核保和理赔结论提前说死，也不能在证据不足时编造解释。AI 问答系统必须做到“有依据才回答，依据不足就拒答或转人工”。&lt;/p&gt;
&lt;p&gt;第五，知识运营成本高。产品、公告、FAQ 会持续变化，客服质检也会不断发现不准确回答。RAG 如果没有知识更新、评测和反馈机制，很快就会变成一个“上线时可用、三个月后不可信”的系统。&lt;/p&gt;
&lt;p&gt;所以保险产品 RAG 的核心目标不是炫技，而是建立一套稳定机制：让模型回答时绑定正确知识、正确版本、正确边界，并且能够被运营人员持续维护。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;RAG 的基本链路是：问题理解、知识召回、证据重排、上下文构造、模型生成、引用溯源、反馈回流。保险场景要在每一步增加业务约束。&lt;/p&gt;
&lt;p&gt;问题理解阶段，系统需要识别问题中是否包含产品名、产品代码、疾病名称、保单号、渠道、地区、时间、客户身份等要素。比如“尊享百万医疗去年买的还能不能续”至少要识别产品、续保意图和时间条件；如果当前会话绑定了保单，还要从上下文里拿到产品版本。&lt;/p&gt;
&lt;p&gt;知识召回阶段，不能只依赖向量相似度。向量召回适合处理语义相近问题，但产品代码、条款号、病种名称、公告编号更适合全文检索。工程上通常采用混合召回：向量检索负责语义，BM25 或 Elasticsearch 负责关键词，元数据过滤负责版本边界，再通过 reranker 对候选片段排序。&lt;/p&gt;
&lt;p&gt;证据构造阶段，要把召回结果转换成模型可理解的上下文。保险文档切片时必须保留标题路径、文档类型、版本号、生效日期、来源链接。否则模型即使答对了，客服也不知道依据来自哪里。&lt;/p&gt;
&lt;p&gt;生成阶段，Prompt 必须明确约束模型：只能基于给定证据回答；证据不足要说明无法确认；涉及赔付、核保、退保金额时不能做最终承诺；必须给出引用来源；面向客户和面向内部坐席的话术要区分。&lt;/p&gt;
&lt;p&gt;评测阶段，要建立黄金问题集。不能只看“回答像不像”，还要看证据是否正确、版本是否正确、有没有越权引用、有没有承诺性话术、是否给出转人工边界。保险 RAG 的评测更接近合规质检，而不是普通闲聊体验评分。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;落地时可以拆成四个模块：知识治理、索引服务、问答编排、运营评测。&lt;/p&gt;
&lt;p&gt;知识治理模块负责把 PDF、Word、HTML、FAQ 表格、公告系统内容统一转成标准文档。每份文档进入知识库前，需要记录 productCode、productName、docType、version、effectiveDate、expireDate、channel、region、ownerDept、riskLevel、sourceUrl 等元数据。对于保险条款，解析时要尽量保留章节编号和标题路径；对于表格，要转成“行列语义明确”的文本，不要简单拼接。&lt;/p&gt;</description></item><item><title>Function Calling 如何安全调用业务 API</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/function-calling%E5%A6%82%E4%BD%95%E5%AE%89%E5%85%A8%E8%B0%83%E7%94%A8%E4%B8%9A%E5%8A%A1api/</link><pubDate>Mon, 02 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/function-calling%E5%A6%82%E4%BD%95%E5%AE%89%E5%85%A8%E8%B0%83%E7%94%A8%E4%B8%9A%E5%8A%A1api/</guid><description>&lt;h1 id="function-calling-如何安全调用业务-api"&gt;Function Calling 如何安全调用业务 API&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“在内部 AI 客服和业务辅助系统中，通过 Function Calling 将用户自然语言意图转换为结构化业务工具调用，安全接入保单、客户、产品、工单、保全等传统业务 API，并实现权限校验、参数校验、审批确认、脱敏审计和风险分级”的经历。&lt;/p&gt;
&lt;p&gt;面试时这部分不要只说“模型可以调用函数”。真正有含金量的是：模型如何知道能调用哪些工具，工具参数如何校验，用户有没有权限，模型能不能越权查别人的保单，执行类接口如何二次确认，接口异常如何回到对话，以及调用记录如何审计追责。&lt;/p&gt;
&lt;p&gt;可以把简历里的项目描述成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 AI 客服系统中设计工具调用编排层，将传统 Java 业务 API 封装为受控工具，供大模型通过 Function Calling 发起查询或辅助操作。工具层统一处理 schema 注册、参数绑定、身份透传、权限校验、风控拦截、结果脱敏、调用审计和人工确认，确保大模型只能在业务授权范围内调用接口。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的重点是把 Function Calling 从“模型能力”落成“企业可控的业务集成能力”。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;内部客服系统里的很多问题都不能只靠知识库回答。比如用户问：“这个客户名下有几张有效保单？”“这张保单为什么续保失败？”“当前工单应该转哪个队列？”“能不能帮我生成保全变更材料清单？”这些问题需要访问实时业务系统。&lt;/p&gt;
&lt;p&gt;传统做法是客服坐席在多个系统里手工查询。AI 接入后，希望坐席用自然语言提问，系统自动判断要查哪个接口，聚合结果后生成回答。这就是 Function Calling 的典型价值：模型负责理解意图和组织参数，后端负责执行确定性 API。&lt;/p&gt;
&lt;p&gt;但是保险业务 API 很敏感。保单、客户、健康告知、银行卡、联系方式都属于敏感信息。一个普通坐席不能查全公司的客户，一个渠道人员不能看其他渠道保单，一个模型也不能因为用户说“帮我查张三身份证号”就绕过权限。&lt;/p&gt;
&lt;p&gt;更复杂的是，有些接口是只读查询，有些接口会改变业务状态。查询保单摘要风险较低，创建工单风险中等，提交退保、变更银行卡、发起保全则风险很高。Function Calling 如果没有风险分级和确认机制，就会把自然语言入口变成一个危险的万能后门。&lt;/p&gt;
&lt;p&gt;所以项目目标不是“让模型可以调用业务接口”，而是“让模型只能以受控、可审计、可回滚或可确认的方式调用允许的业务工具”。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Function Calling 的核心原理是把可调用能力描述给模型，让模型在对话中输出结构化调用请求。工具描述通常包含 name、description、parameters schema 和必填字段。模型根据用户问题选择工具，并生成参数 JSON。&lt;/p&gt;
&lt;p&gt;但生产系统里不能让模型输出什么就执行什么。安全调用链路至少要分成五步。&lt;/p&gt;
&lt;p&gt;第一，工具注册。每个业务 API 被封装成一个工具定义，包含工具名称、参数 schema、风险等级、权限点、是否需要确认、超时时间、限流策略、结果脱敏规则和审计类型。工具注册信息由服务端维护，不由前端或用户动态传入。&lt;/p&gt;
&lt;p&gt;第二，模型选择。编排服务把当前用户可见的工具子集提供给模型。比如普通客服只能看到查询类工具，高权限主管才能看到部分工单处理工具。模型输出 toolName 和 arguments，但这只是“调用建议”。&lt;/p&gt;
&lt;p&gt;第三，调用前校验。后端根据工具定义做 JSON schema 校验、业务参数校验、权限校验、数据范围校验、幂等校验和风控校验。比如 policyNo 必须属于当前机构或当前工单绑定客户；身份证号不能作为任意查询入口；批量查询要限量。&lt;/p&gt;</description></item><item><title>公司内部 AI 客服系统整体架构设计</title><link>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/%E5%85%AC%E5%8F%B8%E5%86%85%E9%83%A8ai%E5%AE%A2%E6%9C%8D%E7%B3%BB%E7%BB%9F%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</link><pubDate>Sun, 01 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/06-ai-engineering/%E5%85%AC%E5%8F%B8%E5%86%85%E9%83%A8ai%E5%AE%A2%E6%9C%8D%E7%B3%BB%E7%BB%9F%E6%95%B4%E4%BD%93%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</guid><description>&lt;h1 id="公司内部-ai-客服系统整体架构设计"&gt;公司内部 AI 客服系统整体架构设计&lt;/h1&gt;
&lt;h2 id="1-对应简历中的哪一段"&gt;1. 对应简历中的哪一段&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“内部大模型、智能客服、保险产品问答、RAG、Function Calling、MCP、SSE、工具调用、权限校验、审计脱敏、OpenClaw 自动化流程、传统金融系统低侵入接入”等描述。&lt;/p&gt;
&lt;p&gt;面试时这段经历不能只讲“接了一个大模型接口”或者“做了知识库问答”。真正有含金量的点在于：在一个已有多年历史的保险业务体系里，把 AI 能力作为新的交互层和自动化编排层接进去，同时不能破坏原来的交易链路、权限体系、审计要求和合规边界。&lt;/p&gt;
&lt;p&gt;可以把简历里的项目表述理解为下面这段更完整的背景：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在公司内部客服和业务支持场景中，设计并落地基于大模型的智能问答与业务辅助系统，支持保险产品条款问答、客户权益解释、保单状态查询、续保和保全办理指引等能力。系统采用 RAG 增强保险产品知识回答，通过 Function Calling 和 MCP 思想封装传统业务 API，通过 SSE 实现流式响应；同时接入统一身份认证、数据权限、敏感信息脱敏、操作审计和工具调用审批机制，并结合 OpenClaw 自动化流程对部分高频后台操作进行低侵入集成。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段话面试官可能会继续追问：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保险产品问答为什么不能直接让大模型回答？&lt;/li&gt;
&lt;li&gt;RAG 的知识库怎么切分、召回和防幻觉？&lt;/li&gt;
&lt;li&gt;Function Calling 调业务接口如何做权限校验？&lt;/li&gt;
&lt;li&gt;MCP 和普通 API 网关有什么区别？&lt;/li&gt;
&lt;li&gt;SSE 流式输出怎么和工具调用结果组合？&lt;/li&gt;
&lt;li&gt;传统金融系统不能大改时，怎么接入 AI？&lt;/li&gt;
&lt;li&gt;涉及客户隐私、保单信息时如何脱敏、审计和追责？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以本文的重点不是介绍某个框架，而是讲清楚一套面向金融内部场景的 AI 客服整体架构。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险公司的客服和内部支持系统通常有几个特点。&lt;/p&gt;
&lt;p&gt;第一，产品规则复杂。一个保险产品不是简单的一段介绍，而是由主险、附加险、责任免除、等待期、犹豫期、缴费期、保障期间、职业类别、年龄限制、地区限制、续保规则、理赔材料等规则共同组成。客服人员和销售人员经常需要在多个 PDF 条款、产品手册、运营通知、FAQ 和后台页面之间来回查找。&lt;/p&gt;
&lt;p&gt;第二，问题高频但表达不标准。用户可能会问“这个重疾险能不能赔甲状腺癌”“小孩买这个产品要不要体检”“断缴之后还能不能续”“这个客户去年买的保单现在能不能退”，这些问题背后既有产品知识，也可能涉及客户保单数据和交易状态。&lt;/p&gt;
&lt;p&gt;第三，传统系统很多。保险系统里通常会有核心保单系统、客户中心、产品中心、订单系统、支付系统、理赔系统、保全系统、影像系统、营销系统和客服工单系统。很多系统历史较长，接口风格不统一，有的只有 RPC，有的是老 Spring MVC，有的是存储过程，有的是页面操作，没有办法为了 AI 项目大规模重构。&lt;/p&gt;
&lt;p&gt;第四，合规要求高。保险行业涉及个人身份信息、联系方式、银行卡、保单号、健康告知、理赔材料等敏感数据。AI 系统不能把内部数据随意送给模型，不能让没有权限的人通过自然语言绕过原有系统权限，也不能让大模型直接生成不负责任的承诺性话术。&lt;/p&gt;
&lt;p&gt;第五，客服效率压力明显。人工客服需要同时处理咨询、查询、解释和工单流转。内部业务人员也希望通过“问一句”的方式快速获得产品解释、客户保单摘要、下一步办理建议，减少查手册和切系统的时间。&lt;/p&gt;
&lt;p&gt;所以内部 AI 客服的目标不是替代核心交易系统，而是做三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;让复杂知识更容易被检索和解释。&lt;/li&gt;
&lt;li&gt;让多系统查询和低风险操作更容易被编排。&lt;/li&gt;
&lt;li&gt;在权限、审计、脱敏和人工兜底之内，提高客服和业务人员效率。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="3-总体架构"&gt;3. 总体架构&lt;/h2&gt;
&lt;p&gt;整体架构可以分成七层：入口层、会话层、编排层、模型层、知识层、工具层、安全审计层。&lt;/p&gt;</description></item><item><title>我的技术学习路线与规划</title><link>https://zhouwy.top/posts/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF%E4%B8%8E%E8%A7%84%E5%88%92/</link><pubDate>Sun, 01 Jun 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/%E6%88%91%E7%9A%84%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF%E4%B8%8E%E8%A7%84%E5%88%92/</guid><description>&lt;h1 id="我的技术学习路线与规划"&gt;我的技术学习路线与规划&lt;/h1&gt;
&lt;p&gt;技术学习没有终点，但有了清晰的路线图，每一步都能走得更扎实。这篇文章记录了我在 AI、数据库、Java 框架、新技术和量化交易等方向的阶段性规划，既是 To-Do List，也是学习路标的公开存档。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;持续更新中，欢迎交流。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="ai--机器学习"&gt;AI / 机器学习&lt;/h2&gt;
&lt;p&gt;打好 AI 基础，重点在经典算法、深度学习框架和工程落地。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;scikit-learn&lt;/strong&gt;：经典 ML 算法库，掌握分类、回归、聚类、降维的工程实现&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;NumPy / Pandas&lt;/strong&gt;：数据处理基本功，向量化操作、DataFrame 变换、缺失值处理&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;PyTorch&lt;/strong&gt;：深度学习主流框架，从张量操作到模型训练、推理部署全链路&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;Spring AI / MCP&lt;/strong&gt;：AI 工程化方向，将大模型能力整合到 Java 后端服务中&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="数据库"&gt;数据库&lt;/h2&gt;
&lt;p&gt;深入数据库内核原理，从理论到底层实现。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;极客时间 MySQL 课程&lt;/strong&gt;：系统学习 InnoDB 存储引擎、锁机制、性能优化&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;OceanBase OBCP 认证&lt;/strong&gt;：分布式数据库原理、OceanBase 集群运维与调优&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;慕课网手写数据库&lt;/strong&gt;：从零实现一个简单的关系型数据库，理解解析器、执行器、存储引擎的完整链路&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="java-框架"&gt;Java 框架&lt;/h2&gt;
&lt;p&gt;源码阅读 + 手写实现，吃透核心框架的设计思想。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;Spring / Spring Boot 源码 + 手写&lt;/strong&gt;：IoC 容器、AOP 原理、自动配置、事务管理&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;MyBatis 源码 + 手写&lt;/strong&gt;：ORM 映射原理、动态 SQL、插件机制&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;Tomcat 源码 + 手写&lt;/strong&gt;：Servlet 容器、HTTP 处理、类加载架构&lt;/li&gt;
&lt;li&gt;&lt;input disabled="" type="checkbox"&gt; &lt;strong&gt;JDK 源码&lt;/strong&gt;：重点攻集合框架、并发包（JUC）、NIO、Stream 与 Lambda&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="新技术"&gt;新技术&lt;/h2&gt;
&lt;p&gt;保持对 Java 生态前沿的关注。&lt;/p&gt;</description></item><item><title>OceanBase 集群架构详解：从图书馆比喻到原理剖析</title><link>https://zhouwy.top/posts/oceanbase-%E9%9B%86%E7%BE%A4%E6%9E%B6%E6%9E%84%E8%AF%A6%E8%A7%A3%E4%BB%8E%E5%9B%BE%E4%B9%A6%E9%A6%86%E6%AF%94%E5%96%BB%E5%88%B0%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90/</link><pubDate>Fri, 30 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/oceanbase-%E9%9B%86%E7%BE%A4%E6%9E%B6%E6%9E%84%E8%AF%A6%E8%A7%A3%E4%BB%8E%E5%9B%BE%E4%B9%A6%E9%A6%86%E6%AF%94%E5%96%BB%E5%88%B0%E5%8E%9F%E7%90%86%E5%89%96%E6%9E%90/</guid><description>&lt;h1 id="oceanbase-集群架构详解从图书馆比喻到原理剖析"&gt;OceanBase 集群架构详解：从图书馆比喻到原理剖析&lt;/h1&gt;
&lt;p&gt;OceanBase 是蚂蚁集团自主研发的分布式关系型数据库，其集群架构包含集群、Zone、Observer、租户、分区、Tablet、日志流、副本等多个概念。这些概念相互关联，初次接触容易混淆。本文将用&amp;quot;图书馆&amp;quot;作为贯穿比喻，逐一拆解每个组件的职责，最后串联成完整的架构图景。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;阅读本文前，建议先了解前文《数据库核心机制详解》中的 WAL、MVCC、Redo Log 等基础知识，这些是理解 OceanBase 底层机制的前提。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="一核心组件的形象比喻"&gt;一、核心组件的形象比喻&lt;/h2&gt;
&lt;p&gt;在深入细节之前，先用一个&amp;quot;大型图书馆系统&amp;quot;的比喻建立直观印象：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;图书馆类比&lt;/th&gt;
&lt;th&gt;核心功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;集群&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;整个图书馆系统（含所有分馆、书籍、管理员）&lt;/td&gt;
&lt;td&gt;管理所有资源，对外提供统一服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zone&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;不同城市的分馆（如北京馆、上海馆、深圳馆）&lt;/td&gt;
&lt;td&gt;物理隔离的独立区域，用于容灾和资源分配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;分馆内的书架（每个书架含多排书籍）&lt;/td&gt;
&lt;td&gt;物理服务器，存储和处理数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;租户&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;不同类型的借阅者（如学生、教师、员工）&lt;/td&gt;
&lt;td&gt;逻辑隔离的用户组，拥有独立资源配额和权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;分区&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;书籍按类别划分的区域（如小说区、科技区、历史区）&lt;/td&gt;
&lt;td&gt;将大表数据分散到多个 Observer，提升扩展性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tablet&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;每个类别中的具体书架排（如小说区第 1-10 排）&lt;/td&gt;
&lt;td&gt;数据的最小存储单元&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;日志流&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;书籍借阅/归还的记录台账&lt;/td&gt;
&lt;td&gt;记录 Tablet 的所有变更，保证一致性&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;副本&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;同一本书的多个复本&lt;/td&gt;
&lt;td&gt;数据冗余备份，保证高可用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="二组件关系的深度解析"&gt;二、组件关系的深度解析&lt;/h2&gt;
&lt;h3 id="1-集群与-zone全球图书馆网络"&gt;1. 集群与 Zone：全球图书馆网络&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;集群（Cluster）&lt;/strong&gt; 是整个 OceanBase 实例的最高层级，包含所有 Zone、Observer、租户和数据。一个集群对外提供统一的数据库服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zone&lt;/strong&gt; 是物理隔离的独立区域，通常对应不同的数据中心或机房。每个 Zone 有独立的供电、网络和冷却系统，可以应对城市级灾难。&lt;/p&gt;
&lt;p&gt;典型的部署模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;三地五中心&lt;/strong&gt;：三个城市五个 Zone，适合金融级高可用场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同城三中心&lt;/strong&gt;：同城三个 Zone，延迟低，容灾能力适中&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;两地三中心&lt;/strong&gt;：两个城市三个 Zone，平衡成本和可用性&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;图书馆比喻&lt;/strong&gt;：集群是整个图书馆系统，Zone 是分布在不同城市的分馆。当北京馆因火灾关闭时，深圳馆的副本数据仍可提供服务，实现城市级容灾。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="2-observer-与租户书架与借阅者"&gt;2. Observer 与租户：书架与借阅者&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Observer&lt;/strong&gt; 是 OceanBase 的运行节点，运行在物理服务器或容器上，负责数据的存储和计算。每个 Zone 包含多个 Observer。&lt;/p&gt;</description></item><item><title>MDC 加 ELK 全链路日志系统建设</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/mdc%E5%8A%A0elk%E5%85%A8%E9%93%BE%E8%B7%AF%E6%97%A5%E5%BF%97%E7%B3%BB%E7%BB%9F%E5%BB%BA%E8%AE%BE/</link><pubDate>Wed, 07 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/mdc%E5%8A%A0elk%E5%85%A8%E9%93%BE%E8%B7%AF%E6%97%A5%E5%BF%97%E7%B3%BB%E7%BB%9F%E5%BB%BA%E8%AE%BE/</guid><description>&lt;h1 id="mdc-加-elk-全链路日志系统建设"&gt;MDC 加 ELK 全链路日志系统建设&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在微服务稳定性治理中，负责建设基于 MDC、Logback、Filebeat、Logstash、Elasticsearch、Kibana 的全链路日志系统。通过 traceId、spanId、userId、bizId、eventId 等上下文字段贯穿 HTTP 请求、Feign 调用、MQ 生产消费和异步线程，统一 JSON 日志格式、索引模板、脱敏规则和查询看板，显著提升商机流转、活动报名、出单回写等跨服务问题的定位效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历强调的是可观测性。资深 Java 面试中，日志系统不是“把日志打到 ES”，而是如何让一次业务请求在多个线程、多个服务、多个中间件之间被串起来，并且能在故障时快速缩小范围。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;商机和营销系统往往是典型微服务链路：入口网关接收请求，活动服务处理报名，客户中心建档，商机中心生成商机，销售工作台生成待办，RocketMQ 发送事件，报表消费者异步落库。任何一个环节失败，业务方看到的可能只是“报名后没有待办”或“出单后商机没更新”。&lt;/p&gt;
&lt;p&gt;如果每个服务只打普通文本日志，排查就会非常痛苦。你需要按时间猜测请求，登录多台机器 grep，查完 HTTP 日志再查 MQ 日志，再查消费者日志。遇到高峰期日志量大，几乎无法定位。&lt;/p&gt;
&lt;p&gt;全链路日志系统的目标是：拿到一个 &lt;code&gt;traceId&lt;/code&gt;、&lt;code&gt;bizId&lt;/code&gt; 或 &lt;code&gt;eventId&lt;/code&gt;，就能查到这次请求从入口到下游、从同步到异步的完整路径。它不替代链路追踪系统，但能作为最基础、最可控、最贴近业务的排查工具。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;MDC 是 SLF4J 提供的线程上下文机制，本质上是基于 ThreadLocal 存储一组 key-value。日志框架输出日志时，可以把 MDC 中的字段写入日志。常见字段包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;traceId&lt;/code&gt;：一次入口请求或业务链路的全局追踪 ID。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spanId&lt;/code&gt;：链路中的局部调用片段。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;userId&lt;/code&gt;：当前登录用户。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bizId&lt;/code&gt;：业务主键，如商机 ID、订单 ID、保单 ID。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eventId&lt;/code&gt;：MQ 事件 ID。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;source&lt;/code&gt;：来源系统或渠道。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ELK 负责采集、解析、存储和查询。应用输出 JSON 日志到文件，Filebeat 采集并发送到 Logstash 或 Elasticsearch，Logstash 做解析、过滤、脱敏和路由，Elasticsearch 建索引，Kibana 用于检索和看板。&lt;/p&gt;</description></item><item><title>Nacos 配置中心与服务注册实践</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/nacos%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E4%B8%8E%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E5%AE%9E%E8%B7%B5/</link><pubDate>Tue, 06 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/nacos%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E4%B8%8E%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h1 id="nacos-配置中心与服务注册实践"&gt;Nacos 配置中心与服务注册实践&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在微服务治理改造中，负责 Nacos 配置中心与服务注册发现落地，统一管理客户、商机、营销、出单、报表等服务的环境配置、动态开关、限流规则和服务实例。通过 namespace、group、dataId 分层，配置灰度、动态刷新、敏感配置隔离、服务健康检查、权重路由和注册发现治理，降低多环境配置混乱和服务调用不可控风险。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的关键是把 Nacos 放在“微服务治理基础设施”的位置上讲，而不是只说“用 Nacos 存配置”。面试官更关心配置怎么分层、动态刷新怎么保证安全、注册发现怎么处理实例上下线和故障隔离。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;随着系统从单体拆成客户中心、商机中心、营销活动、销售工作台、出单系统、报表系统，配置数量会快速膨胀。数据库地址、Redis 地址、MQ Topic、线程池参数、限流规则、活动开关、灰度开关、第三方接口地址都散落在各个服务和环境中。&lt;/p&gt;
&lt;p&gt;如果仍然靠本地 &lt;code&gt;application.yml&lt;/code&gt; 管理，会出现几个问题：测试、预发、生产配置不一致；紧急开关需要重新发版；限流阈值不能活动期间动态调整；实例扩缩容后调用方感知慢；下线实例仍被请求打到。&lt;/p&gt;
&lt;p&gt;Nacos 在项目里承担两件事：配置中心负责统一配置和动态刷新，注册中心负责服务实例注册、发现和健康检查。它让微服务从“写死地址和配置”变成“按环境、服务和规则动态治理”。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;作为配置中心，Nacos 通过 namespace、group、dataId 定位一份配置。客户端启动时拉取配置，并与服务端保持长轮询，配置变更后客户端收到通知并刷新本地配置。&lt;/p&gt;
&lt;p&gt;作为注册中心，服务启动时把自己的 IP、端口、服务名、集群、元数据注册到 Nacos。调用方通过服务名获取实例列表，结合负载均衡选择实例。临时实例通过心跳维持，心跳异常会被标记不健康或剔除。&lt;/p&gt;
&lt;p&gt;常见分层方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;namespace 区分环境，如 dev、test、pre、prod。&lt;/li&gt;
&lt;li&gt;group 区分业务域或应用组，如 B2B、MARKETING、POLICY。&lt;/li&gt;
&lt;li&gt;dataId 区分服务和配置类型，如 &lt;code&gt;opportunity-service.yml&lt;/code&gt;、&lt;code&gt;sentinel-flow-rules.json&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;配置中心要关注一致性和安全性，注册中心要关注可用性和健康状态。两者都不能随意修改生产配置，因为一次错误配置可能比一次代码 bug 影响更大。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;配置中心落地时，可以把配置分为四类：&lt;/p&gt;
&lt;p&gt;第一类是基础连接配置，如数据库、Redis、MQ、ES。这类配置通常不频繁变更，生产环境要严格权限控制，敏感值最好接入密钥系统或加密。&lt;/p&gt;
&lt;p&gt;第二类是业务开关，如活动是否开启、某渠道是否准入、出单回写是否走新链路。这类配置需要动态刷新，但必须有默认值和回滚方案。&lt;/p&gt;
&lt;p&gt;第三类是治理规则，如 Sentinel 限流规则、线程池参数、降级开关。这类配置活动期间可能频繁调整，需要审计和监控。&lt;/p&gt;
&lt;p&gt;第四类是灰度配置，如指定渠道、销售团队、客户等级使用新策略。这类配置要支持按条件判断，避免全量误放。&lt;/p&gt;
&lt;p&gt;服务注册落地时，需要关注实例元数据。比如同一个服务有 &lt;code&gt;zone&lt;/code&gt;、&lt;code&gt;version&lt;/code&gt;、&lt;code&gt;gray&lt;/code&gt;、&lt;code&gt;weight&lt;/code&gt; 等信息，调用方可以按元数据做灰度路由或同机房优先。服务下线时要优雅停机，先从注册中心摘除或标记不健康，再等待流量排空，最后关闭进程。&lt;/p&gt;
&lt;h2 id="5-关键配置或伪代码"&gt;5. 关键配置或伪代码&lt;/h2&gt;
&lt;p&gt;Spring Cloud Alibaba 配置示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;application&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;opportunity-service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cloud&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;nacos&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server-addr&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;nacos-prod:8848&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;discovery&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;group&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;B2B&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster-name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;shanghai-a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;gray&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;false&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;config&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;group&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;B2B&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;file-extension&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;shared-configs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;data-id&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;common-db.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;group&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;COMMON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;refresh&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;data-id&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sentinel-flow-rules.json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;group&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;GOVERNANCE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;refresh&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;动态配置类：&lt;/p&gt;</description></item><item><title>Sentinel 限流熔断在营销活动中的落地</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/sentinel%E9%99%90%E6%B5%81%E7%86%94%E6%96%AD%E5%9C%A8%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8%E4%B8%AD%E7%9A%84%E8%90%BD%E5%9C%B0/</link><pubDate>Mon, 05 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/sentinel%E9%99%90%E6%B5%81%E7%86%94%E6%96%AD%E5%9C%A8%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8%E4%B8%AD%E7%9A%84%E8%90%BD%E5%9C%B0/</guid><description>&lt;h1 id="sentinel-限流熔断在营销活动中的落地"&gt;Sentinel 限流熔断在营销活动中的落地&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在营销活动和商机转化系统中，负责基于 Sentinel 建设接口级限流、热点参数限流、熔断降级和系统自适应保护能力。针对活动报名、线索导入、报价试算、销售分配、短信触达等高峰场景，配置 QPS、并发线程数、慢调用比例、异常比例和热点客户参数规则，结合降级兜底、排队削峰、监控告警和压测基线，降低活动峰值对核心客户、商机和出单系统的冲击。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段简历要讲清楚两个关键词：营销活动的流量不可预测，以及稳定性治理要保护核心链路。Sentinel 的价值不只是“拦请求”，而是让系统在高峰和下游异常时有秩序地退化。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;营销活动系统经常呈现典型峰值特征。运营会在固定时间群发短信、企微推送或投放广告，客户在短时间内集中访问活动页、提交报名、领取权益、试算报价。销售团队也可能在活动后集中导入线索、批量分配商机、触发短信回访。&lt;/p&gt;
&lt;p&gt;如果没有流量治理，入口流量会一路穿透到客户中心、商机中心、报价系统、库存权益系统、短信平台和数据库。一个慢接口可能占满 Tomcat 线程，一个外部短信平台超时可能拖垮活动报名，一个热门活动 ID 可能把某张库存表锁住。&lt;/p&gt;
&lt;p&gt;项目目标不是让所有请求都成功，而是在资源有限时保证最重要的请求成功。比如活动页静态内容可以降级，重复点击可以限流，报表统计可以延迟，权益领取要严格保护库存和幂等，出单链路要优先保障。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Sentinel 以资源为维度做流量控制。资源可以是 HTTP 接口、Dubbo 方法、Spring Cloud OpenFeign 调用，也可以是代码中手动定义的一段逻辑。每个资源都可以配置规则。&lt;/p&gt;
&lt;p&gt;常用规则包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;流控规则：按 QPS 或并发线程数限制访问量。&lt;/li&gt;
&lt;li&gt;热点参数规则：针对某些参数值单独限流，比如热门活动 ID、热门产品 ID。&lt;/li&gt;
&lt;li&gt;熔断规则：根据慢调用比例、异常比例或异常数触发降级。&lt;/li&gt;
&lt;li&gt;系统规则：根据系统 Load、CPU、入口 QPS、线程数等做整体保护。&lt;/li&gt;
&lt;li&gt;授权规则：按来源控制黑白名单。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;限流解决的是“流量太大”，熔断解决的是“下游或自身已经不健康”，降级解决的是“失败时给出可接受的兜底”。这三者要一起设计。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;以活动报名链路为例，入口请求会经过活动服务、客户中心、商机服务、权益库存、短信通知和报表埋点。我们可以把链路拆分为三类资源：&lt;/p&gt;
&lt;p&gt;第一类是入口资源，例如 &lt;code&gt;/api/activity/register&lt;/code&gt;。它直接面对用户，需要设置入口 QPS 上限和排队等待策略，避免瞬时流量打满线程池。&lt;/p&gt;
&lt;p&gt;第二类是核心依赖资源，例如 &lt;code&gt;customerService.getOrCreateCustomer&lt;/code&gt;、&lt;code&gt;benefitService.lockQuota&lt;/code&gt;、&lt;code&gt;opportunityService.createLead&lt;/code&gt;。这些调用需要配置慢调用比例和异常比例熔断，下游异常时快速失败，避免请求长期阻塞。&lt;/p&gt;
&lt;p&gt;第三类是非核心资源，例如短信通知、运营埋点、报表刷新。它们可以异步化或降级，不能影响报名主链路。&lt;/p&gt;
&lt;p&gt;热点参数限流在营销活动里非常重要。活动列表可能有多个活动，但只有一个爆款活动瞬间被大量访问。如果只对整个报名接口限流，会影响其他活动；按 &lt;code&gt;activityId&lt;/code&gt; 做热点限流，可以只保护爆款活动对应的库存、规则和数据库行。&lt;/p&gt;
&lt;p&gt;项目里还应建立规则分级：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;规则&lt;/th&gt;
&lt;th&gt;兜底&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;活动页查询&lt;/td&gt;
&lt;td&gt;QPS 限流、缓存兜底&lt;/td&gt;
&lt;td&gt;返回缓存或稍后重试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;活动报名&lt;/td&gt;
&lt;td&gt;QPS + 热点参数限流&lt;/td&gt;
&lt;td&gt;提示排队或报名繁忙&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;权益库存&lt;/td&gt;
&lt;td&gt;并发线程数限制&lt;/td&gt;
&lt;td&gt;返回权益领取中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;报价试算&lt;/td&gt;
&lt;td&gt;慢调用比例熔断&lt;/td&gt;
&lt;td&gt;返回简化报价或稍后查看&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;短信通知&lt;/td&gt;
&lt;td&gt;异常比例熔断&lt;/td&gt;
&lt;td&gt;写入补发任务&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="5-关键配置或伪代码"&gt;5. 关键配置或伪代码&lt;/h2&gt;
&lt;p&gt;代码中定义资源：&lt;/p&gt;</description></item><item><title>Seata AT 模式在资金划扣场景中的使用</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/seata-at%E6%A8%A1%E5%BC%8F%E5%9C%A8%E8%B5%84%E9%87%91%E5%88%92%E6%89%A3%E5%9C%BA%E6%99%AF%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/</link><pubDate>Sun, 04 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/seata-at%E6%A8%A1%E5%BC%8F%E5%9C%A8%E8%B5%84%E9%87%91%E5%88%92%E6%89%A3%E5%9C%BA%E6%99%AF%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/</guid><description>&lt;h1 id="seata-at-模式在资金划扣场景中的使用"&gt;Seata AT 模式在资金划扣场景中的使用&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在资金划扣与订单结算链路中，参与基于 Seata AT 模式的分布式事务治理，覆盖订单状态更新、账户余额扣减、资金流水落库、营销权益核销等跨服务操作。通过全局事务、分支事务、undo_log、全局锁、幂等流水和异常补偿设计，解决多库多服务下局部成功导致资金状态不一致的问题，并结合超时控制、事务分组、监控告警和人工对账降低资金链路风险。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的难点在于资金场景天然高风险。面试时不能只说“加了 &lt;code&gt;@GlobalTransactional&lt;/code&gt; 就行”，而要讲清楚 AT 模式适用边界、隔离级别、回滚机制、脏写风险、悬挂空回滚等问题。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;资金划扣场景通常涉及多个系统：订单系统负责订单状态，账户系统负责余额和冻结金额，资金系统负责流水，营销系统负责优惠券或权益核销，风控系统负责校验。一个支付或划扣动作看似简单，实际可能要更新多张表、多个库、多个服务。&lt;/p&gt;
&lt;p&gt;如果不用分布式事务，常见问题是订单已经改成支付中，账户扣款失败；账户扣了钱，资金流水没写成功；优惠券核销成功，订单最终失败。资金链路不能只靠“之后再修”，因为用户和财务都会直接感知。&lt;/p&gt;
&lt;p&gt;但资金链路也不是所有动作都必须放在一个大事务里。强一致部分通常包括订单状态、账户余额、资金流水；通知、报表、积分、短信可以异步。Seata AT 模式适合改造成本较低、基于关系型数据库、本地事务清晰的场景。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Seata AT 模式包括 TC、TM、RM 三个角色。TM 开启全局事务，TC 负责协调全局事务状态，RM 负责管理分支事务和资源。&lt;/p&gt;
&lt;p&gt;AT 模式的一阶段会代理业务 SQL，记录修改前后的镜像，生成 &lt;code&gt;undo_log&lt;/code&gt;，同时提交本地事务。二阶段如果全局提交，异步删除 undo_log；如果全局回滚，根据 undo_log 反向补偿，把数据恢复到修改前状态。&lt;/p&gt;
&lt;p&gt;它的优势是业务侵入相对低，不需要像 TCC 那样为每个接口写 Try、Confirm、Cancel。但代价是依赖数据库代理和 undo_log，对 SQL 类型、隔离级别、全局锁和长事务比较敏感。&lt;/p&gt;
&lt;p&gt;资金场景里必须理解全局锁。比如账户余额扣减时，Seata 会在一阶段提交前申请全局锁，防止其他全局事务同时修改同一行。否则两个分布式事务交叉提交和回滚，可能导致余额不一致。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;以“订单资金划扣”为例，链路可以拆为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;订单服务校验订单可支付，状态从 &lt;code&gt;WAIT_PAY&lt;/code&gt; 改为 &lt;code&gt;PAYING&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;账户服务扣减可用余额，增加已扣金额。&lt;/li&gt;
&lt;li&gt;资金服务写资金流水，记录划扣请求号、账户号、金额、状态。&lt;/li&gt;
&lt;li&gt;营销服务核销优惠券或权益。&lt;/li&gt;
&lt;li&gt;全局事务成功后订单改为 &lt;code&gt;PAID&lt;/code&gt;，并发送出单或履约消息。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里要谨慎处理事务边界。可以把强一致的订单预更新、账户扣款、资金流水放入 Seata 全局事务；营销权益如果和资金强绑定，也进入事务；短信、报表、通知不要放进全局事务。&lt;/p&gt;
&lt;p&gt;关键表设计上，账户扣款必须有幂等流水号。即使用了 Seata，也要防止接口重试导致重复扣款。账户表可以有 &lt;code&gt;available_amount&lt;/code&gt;、&lt;code&gt;frozen_amount&lt;/code&gt;、&lt;code&gt;version&lt;/code&gt;，资金流水表用 &lt;code&gt;request_no&lt;/code&gt; 唯一索引。&lt;/p&gt;
&lt;p&gt;undo_log 表要在每个参与 AT 模式的数据库里创建，并保证字段类型、字符集、索引符合 Seata 版本要求。生产环境还要关注 undo_log 堆积，因为二阶段清理异常会导致表膨胀。&lt;/p&gt;</description></item><item><title>Kafka 在客户行为轨迹链路中的使用</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/kafka%E5%9C%A8%E5%AE%A2%E6%88%B7%E8%A1%8C%E4%B8%BA%E8%BD%A8%E8%BF%B9%E9%93%BE%E8%B7%AF%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/</link><pubDate>Sat, 03 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/kafka%E5%9C%A8%E5%AE%A2%E6%88%B7%E8%A1%8C%E4%B8%BA%E8%BD%A8%E8%BF%B9%E9%93%BE%E8%B7%AF%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8/</guid><description>&lt;h1 id="kafka-在客户行为轨迹链路中的使用"&gt;Kafka 在客户行为轨迹链路中的使用&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在客户经营与营销转化分析项目中，负责基于 Kafka 建设客户行为轨迹采集链路，接入官网访问、活动报名、短信点击、销售跟进、报价试算、出单回写等行为事件。通过 Topic 分层、分区键设计、批量发送、消费者组隔离、失败重放、日志链路追踪和离线明细落库，支撑客户画像、活动归因、销售漏斗和运营看板分析，提升客户行为数据的完整性与可追溯性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历体现的不是“我会写 Kafka Producer 和 Consumer”，而是你理解行为数据链路的特点：数据量大于业务交易链路，实时性要求高但通常允许最终一致，消费方多且诉求不同，最重要的是可回放、可扩展和可追踪。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;客户行为轨迹链路本质上是在回答几个问题：客户从哪里来，看过什么活动，点击过哪些触达内容，销售什么时候跟进，客户是否试算报价，最终有没有出单，哪一个渠道或活动贡献最大。&lt;/p&gt;
&lt;p&gt;在 B2B 或保险营销场景中，行为事件来源非常散：官网表单、H5 活动页、短信平台、企微触达、销售工作台、报价系统、出单系统、客服系统都会产生事件。如果每个系统都直接写报表库或画像库，会导致依赖混乱、字段口径不一致、写入峰值不可控，后续新增一个实时推荐或风控订阅方也要改造多个生产系统。&lt;/p&gt;
&lt;p&gt;Kafka 更适合承接这类高吞吐事件流。生产系统只负责把标准化行为事件写入 Kafka，不直接关心下游谁消费。画像、报表、实时规则、离线数仓、搜索明细可以作为不同消费者组独立消费。以后新增算法训练或营销归因，也可以从 Kafka 或沉淀后的明细数据中重放。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Kafka 的核心是分布式提交日志。Topic 按分区存储，生产者把消息追加到分区日志，消费者组按 offset 拉取消息。一个消费者组内，同一个分区同一时刻只会分配给一个消费者；不同消费者组之间互不影响，可以各自保存 offset。&lt;/p&gt;
&lt;p&gt;这决定了 Kafka 很适合客户行为轨迹：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;高吞吐：行为事件可以批量发送和顺序追加，写入性能高。&lt;/li&gt;
&lt;li&gt;可扩展：Topic 增加分区后，消费者组可以横向扩容。&lt;/li&gt;
&lt;li&gt;可回放：保留期内可以重置 offset，重新构建画像或修复报表。&lt;/li&gt;
&lt;li&gt;多订阅：同一份行为流可以被画像、报表、风控、推荐分别消费。&lt;/li&gt;
&lt;li&gt;顺序性可控：同一客户的事件用 &lt;code&gt;customerId&lt;/code&gt; 作为 key，可以尽量进入同一分区，保证客户维度的相对顺序。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但 Kafka 不会自动解决所有数据质量问题。消息重复、乱序、字段演进、生产端丢日志、消费者处理失败、offset 提交时机，都需要业务方案配合。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;项目里可以把行为事件抽象成统一模型：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;eventId&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;BEH202605010001&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;eventType&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;QUOTE_CALCULATED&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;customerId&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;CUST10086&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;anonymousId&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;device-9f2b&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;source&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;quote-service&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;channel&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;sms-campaign&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;occurredAt&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2026-05-01T10:30:00+08:00&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;traceId&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;9f2b1c&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;productCode&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;P001&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;premium&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5800&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;activityId&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;ACT202605&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Topic 可以按数据域拆分，而不是每个事件一个 Topic：&lt;/p&gt;</description></item><item><title>RocketMQ 事务消息与最终一致性</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/rocketmq%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E4%B8%8E%E6%9C%80%E7%BB%88%E4%B8%80%E8%87%B4%E6%80%A7/</link><pubDate>Fri, 02 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/rocketmq%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E4%B8%8E%E6%9C%80%E7%BB%88%E4%B8%80%E8%87%B4%E6%80%A7/</guid><description>&lt;h1 id="rocketmq-事务消息与最终一致性"&gt;RocketMQ 事务消息与最终一致性&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 B2B 商机与出单流转链路中，负责基于 RocketMQ 事务消息建设最终一致性方案。针对商机创建、销售待办生成、活动入池、出单回写等跨系统状态同步场景，设计本地事务表、事务回查、消费幂等、死信补偿和对账任务，解决数据库提交成功但消息丢失、消息重复消费、消费者异常导致状态不一致等问题，保障核心业务链路在高峰期可恢复、可追踪、可补偿。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段经历的重点不是“会用事务消息 API”，而是能解释为什么需要它、它解决哪一段一致性问题、它不能解决什么问题，以及项目如何靠监控和补偿把最终一致性真正落地。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;在商机系统里，一个业务动作经常会产生多个后续影响。例如销售创建一条商机后，商机中心要落库，客户中心要更新客户标签，销售工作台要生成待办，活动系统要判断是否纳入活动名单，报表系统要统计渠道转化。同步调用看起来直接，但任何一个下游失败都会拖垮主流程。&lt;/p&gt;
&lt;p&gt;如果完全异步，又会遇到经典问题：商机已经写入数据库，但 MQ 发送失败，下游永远不知道这条商机；或者消息已经发送出去，但本地事务回滚，消费者拿到一条不存在的商机。这个问题不是普通重试能彻底解决的，因为进程可能在数据库提交和消息发送之间宕机，也可能在发送成功后还没记录状态就崩掉。&lt;/p&gt;
&lt;p&gt;所以项目里会把“核心状态落库”和“业务事件发布”绑定起来。事务消息关注的是源头事件不丢、不误发，消费者侧再通过幂等、重试、死信和对账达成最终一致。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;RocketMQ 事务消息采用两阶段思路。生产者先发送半消息，Broker 保存但暂不投递；生产者执行本地事务；本地事务成功后提交消息，Broker 才投递给消费者；本地事务失败则回滚消息。若生产者在提交或回滚前异常，Broker 会发起事务回查，由生产者根据本地事务状态决定提交、回滚或继续未知。&lt;/p&gt;
&lt;p&gt;这个机制解决的是“本地事务和消息发送的一致性”。它不负责消费者处理成功，也不负责跨系统强一致事务。消费者仍然可能失败、超时、重复执行或乱序执行，因此最终一致性方案至少由四部分组成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;源头可靠发布：事务消息或本地消息表保证业务事件不丢。&lt;/li&gt;
&lt;li&gt;消费幂等：同一消息重复投递不会重复产生业务副作用。&lt;/li&gt;
&lt;li&gt;失败可见：重试、死信、异常表和告警让失败不被吞掉。&lt;/li&gt;
&lt;li&gt;对账补偿：定时扫描业务状态和消息状态，修复长期不一致。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;面试时要强调边界。事务消息不是分布式事务的银弹，它不会让多个系统的数据库同时提交或回滚。它更适合“本系统状态已经确定，通知其他系统最终跟上”的场景。&lt;/p&gt;
&lt;h2 id="4-项目落地"&gt;4. 项目落地&lt;/h2&gt;
&lt;p&gt;以“出单完成后回写商机阶段”为例。出单系统收到核心出单结果后，需要更新本地保单状态，并通知商机中心把商机阶段改成已出单，同时报表系统统计成交金额。&lt;/p&gt;
&lt;p&gt;生产者侧流程可以这样设计：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;生成全局 &lt;code&gt;eventId&lt;/code&gt;，消息体包含 &lt;code&gt;policyId&lt;/code&gt;、&lt;code&gt;opportunityId&lt;/code&gt;、&lt;code&gt;customerId&lt;/code&gt;、&lt;code&gt;amount&lt;/code&gt;、&lt;code&gt;version&lt;/code&gt;、&lt;code&gt;traceId&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;发送 RocketMQ 半消息。&lt;/li&gt;
&lt;li&gt;在本地事务中更新保单状态为 &lt;code&gt;ISSUED&lt;/code&gt;，插入事件表，事件状态记为 &lt;code&gt;COMMIT&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;本地事务成功后提交半消息。&lt;/li&gt;
&lt;li&gt;如果提交阶段异常，Broker 后续通过回查确认事件表状态。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;事件表不是摆设，它是回查、审计和补偿的依据。字段通常包括 &lt;code&gt;event_id&lt;/code&gt;、&lt;code&gt;biz_id&lt;/code&gt;、&lt;code&gt;event_type&lt;/code&gt;、&lt;code&gt;payload&lt;/code&gt;、&lt;code&gt;tx_status&lt;/code&gt;、&lt;code&gt;send_status&lt;/code&gt;、&lt;code&gt;retry_count&lt;/code&gt;、&lt;code&gt;created_at&lt;/code&gt;、&lt;code&gt;updated_at&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;消费者侧分为商机消费者和报表消费者。商机消费者收到 &lt;code&gt;POLICY_ISSUED&lt;/code&gt; 后，先检查 &lt;code&gt;eventId + consumer_group&lt;/code&gt; 是否已成功处理，再根据商机当前阶段和版本号判断是否允许更新。报表消费者则使用 &lt;code&gt;policyId + metric_type&lt;/code&gt; 做唯一键，避免重复统计。&lt;/p&gt;
&lt;p&gt;一致性目标也要写清楚：出单完成后 30 秒内商机阶段应更新，超过 5 分钟告警；报表允许分钟级延迟；每天凌晨跑一次出单与商机状态对账，发现出单已完成但商机未更新，则重新投递事件或生成补偿工单。&lt;/p&gt;
&lt;h2 id="5-关键配置或伪代码"&gt;5. 关键配置或伪代码&lt;/h2&gt;
&lt;p&gt;简化后的事务消息发送伪代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TransactionSendResult result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; rocketMQTemplate.&lt;span style="color:#a6e22e"&gt;sendMessageInTransaction&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;policy_tx_group&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;b2b_policy_event:POLICY_ISSUED&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; MessageBuilder.&lt;span style="color:#a6e22e"&gt;withPayload&lt;/span&gt;(event)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;setHeader&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;KEYS&amp;#34;&lt;/span&gt;, event.&lt;span style="color:#a6e22e"&gt;getEventId&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;setHeader&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;TRACE_ID&amp;#34;&lt;/span&gt;, traceId)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;事务监听器示例：&lt;/p&gt;</description></item><item><title>RocketMQ 在商机流转系统中的解耦设计</title><link>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/rocketmq%E5%9C%A8%E5%95%86%E6%9C%BA%E6%B5%81%E8%BD%AC%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E8%A7%A3%E8%80%A6%E8%AE%BE%E8%AE%A1/</link><pubDate>Thu, 01 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/05-middleware-stability/rocketmq%E5%9C%A8%E5%95%86%E6%9C%BA%E6%B5%81%E8%BD%AC%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E8%A7%A3%E8%80%A6%E8%AE%BE%E8%AE%A1/</guid><description>&lt;h1 id="rocketmq-在商机流转系统中的解耦设计"&gt;RocketMQ 在商机流转系统中的解耦设计&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;对应简历中可以这样描述的一段项目经历：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 B2B 商机管理系统中，负责客户、销售、活动、出单等核心链路的系统解耦与稳定性治理。基于 RocketMQ 建设异步事件流转机制，承接日均 3 万+ 线索、商机、活动、出单状态变更消息，通过 Topic/Tag 分层、事务消息、消费重试、幂等控制、死信补偿和监控告警，实现商机流转链路削峰填谷和最终一致性，降低系统间同步调用耦合，提升营销活动高峰期系统稳定性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段简历不是在强调“我用过 RocketMQ”，而是在强调三件事：&lt;/p&gt;
&lt;p&gt;第一，业务链路足够复杂。B2B 商机从线索进入，到客户建档、销售分配、跟进记录、活动邀约、报价方案、出单状态回写，中间会穿过多个系统。如果每一步都同步调用，任何一个下游慢或者不可用，都会反向影响主流程。&lt;/p&gt;
&lt;p&gt;第二，数据量不是互联网秒杀级，但对企业系统已经有明显峰谷。日均 3 万+ 线索商机活动出单流转，平均下来不高，但活动日、渠道批量导入、销售集中跟进、出单回写时会形成短时间尖峰。稳定性设计重点不是追求极限 TPS，而是让峰值不要把核心系统打穿。&lt;/p&gt;
&lt;p&gt;第三，系统目标是“业务可持续流转”。在保险、企业客户或 B2B 销售场景中，商机状态、客户标签、销售待办、活动触达、出单结果都不允许长期不一致，但也不一定要求所有系统在同一个数据库事务里立即完成。因此 MQ 的价值在于把强同步链路拆成事件驱动链路，在可控时间内达成最终一致。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;商机流转系统通常处在多个系统的交汇处。上游可能有官网表单、广告投放线索、合作渠道导入、客服录入、营销活动报名、老客户转介绍；中间有客户中心、商机中心、销售工作台、活动运营系统；下游还有报价、核保、出单、佣金、报表、消息触达等系统。&lt;/p&gt;
&lt;p&gt;一个典型流程可能是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;渠道系统推送一条企业客户线索。&lt;/li&gt;
&lt;li&gt;商机系统做去重、清洗、黑名单校验、客户归并。&lt;/li&gt;
&lt;li&gt;客户中心创建或更新企业客户档案。&lt;/li&gt;
&lt;li&gt;商机中心生成商机，并根据区域、行业、客户等级分配销售。&lt;/li&gt;
&lt;li&gt;销售工作台生成待办任务。&lt;/li&gt;
&lt;li&gt;活动系统根据客户标签加入营销活动名单。&lt;/li&gt;
&lt;li&gt;销售跟进后提交报价或投保意向。&lt;/li&gt;
&lt;li&gt;出单系统完成出单后回写商机阶段。&lt;/li&gt;
&lt;li&gt;报表系统统计渠道转化率、销售跟进率、活动出单率。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果把这些动作全部放在一个同步请求里，接口会越来越长，故障半径会越来越大。客户中心慢一点，线索入口就超时；活动系统不可用，商机创建也失败；报表写入慢，销售工作台也被拖住。这种设计在日常低峰时看起来还能跑，一到活动集中投放或者月底出单冲刺，就会暴露大量问题：接口超时、线程池耗尽、数据库连接占满、重复提交、状态错乱、运营看不到实时数据。&lt;/p&gt;
&lt;p&gt;所以这个项目的核心不是“引入一个中间件”，而是重新划分业务边界：哪些动作必须在主流程立即完成，哪些动作可以异步完成，哪些状态需要事务保证，哪些状态接受最终一致，哪些失败必须人工介入。&lt;/p&gt;
&lt;h2 id="3-为什么需要-mq"&gt;3. 为什么需要 MQ&lt;/h2&gt;
&lt;p&gt;在商机流转系统里，MQ 主要解决四类问题。&lt;/p&gt;
&lt;p&gt;第一是解耦。客户、销售、活动、出单系统都有自己的业务模型和发布节奏。商机系统不应该硬编码调用所有下游接口，也不应该因为新增一个报表订阅方就修改主流程。通过 RocketMQ，商机系统只发布“商机已创建”“商机已分配”“活动已报名”“出单已完成”等领域事件，下游系统按需订阅。新增消费者不会改变生产者主逻辑。&lt;/p&gt;
&lt;p&gt;第二是削峰填谷。日均 3 万+ 流转量，按 24 小时平均只有每秒不到 1 条，但真实情况不是均匀分布。比如上午营销活动开始后 30 分钟内导入 8000 条线索，销售团队下午集中处理，晚上出单系统批量回写。如果同步调用，下游接口和数据库要承受瞬时峰值。MQ 可以把峰值先沉淀在 Broker，消费者按照自身能力平稳消费。&lt;/p&gt;
&lt;p&gt;第三是失败隔离。同步链路里，下游失败会直接让上游失败；异步链路里，下游失败会转化为消息重试、延迟消费、死信补偿、告警处理。主流程可以先完成关键数据落库，避免用户或销售在入口处被阻塞。&lt;/p&gt;
&lt;p&gt;第四是最终一致性。商机创建后，客户标签、销售待办、活动名单、报表明细可以在几秒到几十秒内陆续完成。只要有可追踪的消息状态、幂等消费和补偿机制，业务上是可接受的。面试时要特别说明，不是所有场景都适合 MQ。如果是支付扣款、保单核心状态强一致更新，不能简单丢给异步消息，而要结合本地事务、事务消息、对账和补偿。&lt;/p&gt;
&lt;h2 id="4-topictag-设计"&gt;4. Topic/Tag 设计&lt;/h2&gt;
&lt;p&gt;Topic/Tag 设计的关键是不要过粗，也不要过细。过粗会导致消费者过滤困难、权限边界模糊、监控指标不好看；过细会导致 Topic 数量膨胀、运维复杂度上升、业务演进困难。&lt;/p&gt;</description></item><item><title>数据库核心机制详解：原地更新、WAL、MVCC、Redo Log 与文件系统</title><link>https://zhouwy.top/posts/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%A0%B8%E5%BF%83%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3%E5%8E%9F%E5%9C%B0%E6%9B%B4%E6%96%B0walmvccredo-log-%E4%B8%8E%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/</link><pubDate>Thu, 01 May 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%A0%B8%E5%BF%83%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3%E5%8E%9F%E5%9C%B0%E6%9B%B4%E6%96%B0walmvccredo-log-%E4%B8%8E%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/</guid><description>&lt;h1 id="数据库核心机制详解原地更新walmvccredo-log-与文件系统"&gt;数据库核心机制详解：原地更新、WAL、MVCC、Redo Log 与文件系统&lt;/h1&gt;
&lt;p&gt;今年的主线任务之一，是系统学习 MySQL、Oracle、OceanBase 的原理，并尝试手写一个简单的数据库。这篇文章记录了数据库领域的一些核心术语和基础知识，包括原地更新、WAL、MVCC、Redo Log 以及底层文件系统 ext4 和 XFS 的对比。理解这些概念是深入数据库内核的第一步。&lt;/p&gt;
&lt;h2 id="原地更新in-place-update"&gt;原地更新（In-Place Update）&lt;/h2&gt;
&lt;h3 id="1-核心原理"&gt;1. 核心原理&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原地更新&lt;/strong&gt; 是数据库领域一种数据更新策略，指当修改数据库中的记录时，直接在数据原本存储的物理位置上进行修改，无需将数据移动或复制到新的存储位置。&lt;/p&gt;
&lt;p&gt;数据库中的数据通常按 &lt;strong&gt;&amp;ldquo;数据页&amp;rdquo;（Page）&lt;/strong&gt; 存储（如 InnoDB 的页大小默认为 16KB）。原地更新时，数据库引擎定位到目标记录所在的数据页，直接修改页内的记录内容，不改变其物理存储位置。更新过程中可能对数据页或记录加锁，确保事务的原子性和一致性，同时通过日志（如 redo/undo 日志）记录修改前的状态，用于故障恢复或回滚。&lt;/p&gt;
&lt;h3 id="2-典型场景与实现"&gt;2. 典型场景与实现&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据长度不变或缩小&lt;/strong&gt;：若更新后的数据长度不超过原存储空间（如将字符串 &amp;ldquo;abc&amp;rdquo; 改为 &amp;ldquo;abd&amp;rdquo;），通常可在原地直接修改。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据长度增加但页内有空间&lt;/strong&gt;：若数据变长但所在数据页仍有足够剩余空间，也可原地更新。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引影响&lt;/strong&gt;：若更新的列包含索引，需同步更新索引条目。二级索引的更新可能也是原地操作（若索引键长度不变），但聚簇索引（如 InnoDB 的主键索引）的更新可能因数据行移动而变为非原地。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-对比非原地更新如写时复制"&gt;3. 对比非原地更新（如写时复制）&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;原地更新&lt;/th&gt;
&lt;th&gt;非原地更新（如 MVCC、写时复制）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;存储方式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;直接修改原数据页&lt;/td&gt;
&lt;td&gt;生成新数据版本，保留旧版本（如 InnoDB 的回滚段）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;并发控制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;依赖锁机制（可能引发锁竞争）&lt;/td&gt;
&lt;td&gt;多版本并发控制（MVCC），读不阻塞写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;空间利用&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;可能产生碎片（数据页删除/更新后留白）&lt;/td&gt;
&lt;td&gt;旧版本数据需额外空间，需定期清理（如 VACUUM）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;典型场景&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OLTP 短事务、快速更新（如银行账户扣款）&lt;/td&gt;
&lt;td&gt;高并发读、历史版本查询（如 PostgreSQL、InnoDB 快照）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="4-优缺点分析"&gt;4. 优缺点分析&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;效率高&lt;/strong&gt;：无需复制数据，减少 I/O 和内存操作，适合快速修改单个记录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间节省&lt;/strong&gt;：无冗余版本存储（除非启用事务日志），适合存储成本敏感场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;缺点&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;锁竞争&lt;/strong&gt;：多事务同时修改同一数据页时易产生锁冲突，影响并发性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;碎片问题&lt;/strong&gt;：频繁更新可能导致数据页内碎片，需定期重组（如 MySQL 的 OPTIMIZE TABLE）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事务日志压力&lt;/strong&gt;：每次修改需记录完整日志，可能增加日志写入开销&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5-数据库中的实际应用"&gt;5. 数据库中的实际应用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL InnoDB&lt;/strong&gt;：聚簇索引上的更新通常是原地操作（若数据行大小不变或页内有空间），否则可能导致页分裂或数据行迁移。二级索引更新时，若索引值不变，仅修改数据行指针；若索引值变化，则需先删除旧索引条目，再插入新条目（非原地）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQL Server&lt;/strong&gt;：堆表的更新优先原地进行，若数据行变长且页空间不足，触发&amp;quot;行迁移&amp;quot;（迁移到新页，原页保留指针）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Oracle&lt;/strong&gt;：数据更新时生成新的行版本（通过 UNDO 段实现 MVCC），但物理存储仍可能在原地（若页内空间允许），旧版本由事务控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="walwrite-ahead-logging机制"&gt;WAL（Write-Ahead Logging）机制&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;WAL&lt;/strong&gt;，即预写式日志，是数据库系统中用于保证数据完整性和事务持久性的核心技术。&lt;/p&gt;</description></item><item><title>Hexo 博客搭建过程记录</title><link>https://zhouwy.top/posts/hexo-%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/</link><pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/hexo-%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E8%BF%87%E7%A8%8B%E8%AE%B0%E5%BD%95/</guid><description>&lt;h1 id="hexo-博客搭建过程记录"&gt;Hexo 博客搭建过程记录&lt;/h1&gt;
&lt;p&gt;Hexo 是一个基于 Node.js 的静态博客框架，生成速度快、主题丰富、部署灵活。这篇文章记录了我从零搭建 Hexo 博客的完整过程，包括环境配置、主题选择、插件安装、自动化部署以及踩坑记录。&lt;/p&gt;
&lt;h2 id="环境准备"&gt;环境准备&lt;/h2&gt;
&lt;h3 id="安装-nodejs-与-npm"&gt;安装 Node.js 与 npm&lt;/h3&gt;
&lt;p&gt;Hexo 依赖 Node.js 运行，推荐使用 LTS 版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 确认版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;node -v &lt;span style="color:#75715e"&gt;# v18+ 即可&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果网络环境受限（如国内），建议配置 npm 镜像源加速：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm config set registry https://registry.npmmirror.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="安装-hexo-cli"&gt;安装 Hexo CLI&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -g hexo-cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装完成后就可以用 &lt;code&gt;hexo&lt;/code&gt; 命令初始化博客项目了。&lt;/p&gt;
&lt;h2 id="初始化博客"&gt;初始化博客&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hexo init myblog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd myblog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;hexo init&lt;/code&gt; 会自动生成博客骨架目录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;myblog/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── _config.yml # 博客配置文件（核心）
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── package.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── scaffolds/ # 模板文件
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;├── source/ # 源文件（文章、图片等）
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;│ └── _posts/ # 文章目录
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;└── themes/ # 主题目录
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="核心配置-_configyml"&gt;核心配置 _config.yml&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;我的博客&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;subtitle&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;记录技术与思考&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;author&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Your Name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;language&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;zh-CN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;timezone&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Asia/Shanghai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 域名与 URL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;url&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://chinazhouwy.github.io&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;root&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;permalink&lt;/span&gt;: :&lt;span style="color:#ae81ff"&gt;year/:month/:day/:title/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="主题选择hacker"&gt;主题选择：Hacker&lt;/h2&gt;
&lt;p&gt;在主题选择上，我倾向于&lt;strong&gt;简洁风&lt;/strong&gt;。Hacker 主题颜色鲜明、排版专注，适合技术博客。&lt;/p&gt;
&lt;p&gt;安装方式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://github.com/iissnan/hexo-theme-hacker themes/hacker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;_config.yml&lt;/code&gt; 中启用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;theme&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hacker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;主题通常有自己的独立配置文件 &lt;code&gt;themes/hacker/_config.yml&lt;/code&gt;，可以定制导航栏、社交链接、评论系统等。&lt;/p&gt;
&lt;h3 id="主题定制要点"&gt;主题定制要点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;导航栏&lt;/strong&gt;：添加&amp;quot;首页&amp;quot;、&amp;ldquo;归档&amp;rdquo;、&amp;ldquo;关于&amp;quot;等页面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;社交链接&lt;/strong&gt;：配置 GitHub、知乎等社交账号的图标链接&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;评论系统&lt;/strong&gt;：集成 Gitalk 或 Valine 等无后端评论方案&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="写文章与本地预览"&gt;写文章与本地预览&lt;/h2&gt;
&lt;p&gt;创建新文章：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hexo new &lt;span style="color:#e6db74"&gt;&amp;#34;文章标题&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该命令会在 &lt;code&gt;source/_posts/&lt;/code&gt; 下生成一个 Markdown 文件，文件头包含 Front-matter：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;文章标题&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;date&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;2025-04-30 09:24:55&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tags&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;Hexo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本地预览：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hexo server
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 或指定端口&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hexo server -p &lt;span style="color:#ae81ff"&gt;4000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;浏览器打开 &lt;code&gt;http://localhost:4000&lt;/code&gt; 即可查看。&lt;/p&gt;
&lt;h2 id="额外插件配置"&gt;额外插件配置&lt;/h2&gt;
&lt;h3 id="自动部署插件"&gt;自动部署插件&lt;/h3&gt;
&lt;p&gt;Hexo 生成的静态文件需要部署到服务器或 Pages 服务。最方便的方式是使用 &lt;code&gt;hexo-deployer-git&lt;/code&gt; 插件，直接推送到 GitHub Pages：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install hexo-deployer-git --save
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;_config.yml&lt;/code&gt; 中添加：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;repo&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;git@github.com:chinazhouwy/chinazhouwy.github.io&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branch&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部署命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hexo clean &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; hexo generate &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; hexo deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 简写：hexo clean &amp;amp;&amp;amp; hexo g &amp;amp;&amp;amp; hexo d&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hexo clean&lt;/code&gt;：清除缓存&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hexo g&lt;/code&gt;：生成静态文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hexo d&lt;/code&gt;：部署到远端&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="自动摘要插件不推荐已关闭"&gt;自动摘要插件（不推荐，已关闭）&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install hexo-excerpt --save
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个插件会自动从文章正文截取摘要。但自动截取往往不够准确，不如手动在 Front-matter 中设置 &lt;code&gt;excerpt&lt;/code&gt; 字段或使用 `&lt;/p&gt;</description></item><item><title>从关系查询到图数据库：Neo4j 调研与适用边界</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/%E4%BB%8E%E5%85%B3%E7%B3%BB%E6%9F%A5%E8%AF%A2%E5%88%B0%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93neo4j%E8%B0%83%E7%A0%94%E4%B8%8E%E9%80%82%E7%94%A8%E8%BE%B9%E7%95%8C/</link><pubDate>Tue, 08 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/%E4%BB%8E%E5%85%B3%E7%B3%BB%E6%9F%A5%E8%AF%A2%E5%88%B0%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93neo4j%E8%B0%83%E7%A0%94%E4%B8%8E%E9%80%82%E7%94%A8%E8%BE%B9%E7%95%8C/</guid><description>&lt;h1 id="从关系查询到图数据库neo4j-调研与适用边界"&gt;从关系查询到图数据库：Neo4j 调研与适用边界&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“在数据库信创迁移和复杂关系查询优化过程中，调研 Neo4j 图数据库在客户关系、组织关系、推荐链路和风险关联分析中的适用性，并评估其与关系型数据库的边界”等经历。&lt;/p&gt;
&lt;p&gt;面试里提到 Neo4j，最怕讲成“图数据库查询关系更快”。资深面试官会追问：什么关系适合图？为什么普通 join 不够？数据怎么建模？Cypher 怎么写？事务系统能不能直接换图数据库？图数据库和 OceanBase、MySQL、ES、数仓分别怎么配合？最终为什么用或不用？&lt;/p&gt;
&lt;p&gt;这段经历推荐讲成“调研与边界判断”，而不是强行包装成大规模落地。比如：在迁移过程中，我们发现组织层级、客户推荐链、代理人团队、保单关联、家庭关系等查询在关系库中需要多层 join 或递归，因而调研 Neo4j；最后判断图数据库适合关系探索、路径查询和关联分析，但不适合作为核心交易主库，因此采用关系库承载交易一致性，图数据库作为分析和辅助查询能力。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;传统关系型数据库擅长存储结构化交易数据：客户表、保单表、机构表、人员表、活动表、产品表。通过主键、外键和索引可以完成大多数 CRUD、列表查询和报表统计。&lt;/p&gt;
&lt;p&gt;但有些业务问题天然是“关系网络”：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. 一个客户通过哪些推荐人、活动和代理人形成转化？
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. 某个代理人团队下面的客户之间是否存在家庭、企业、转介绍关系？
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. 一个手机号、证件号、银行卡、设备号关联了哪些客户和保单？
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4. 从某个机构出发，下级团队、人员、客户、保单之间的路径是什么？
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5. 是否存在异常团伙，例如多个客户共用联系方式、地址或支付账户？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在关系库中，这些问题通常需要多表 join、自连接、递归 CTE 或临时表。层级固定时还可以接受；一旦路径深度不固定、关系类型很多、需要探索“几跳以内的关联”，SQL 会变得复杂，性能也难以稳定。&lt;/p&gt;
&lt;p&gt;例如查客户两跳关系，SQL 可能还能写；查三跳、四跳、多类型关系时，SQL 会迅速膨胀。而图数据库的模型天然由节点和边组成，更适合表达和查询关系路径。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Neo4j 使用属性图模型。核心概念是节点、关系和属性。&lt;/p&gt;
&lt;p&gt;节点代表实体，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Customer {customerId, name, certNo})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Agent {agentId, name, orgId})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Policy {policyNo, premium})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Org {orgId, orgName})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Mobile {mobileNo})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关系代表实体之间的连接，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Agent)-[:SERVES]-&amp;gt;(:Customer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Customer)-[:HOLDS]-&amp;gt;(:Policy)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Customer)-[:USES_MOBILE]-&amp;gt;(:Mobile)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Customer)-[:REFERRED_BY]-&amp;gt;(:Customer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(:Org)-[:PARENT_OF]-&amp;gt;(:Org)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关系也可以有属性，例如推荐时间、关系来源、置信度、生效状态。&lt;/p&gt;
&lt;p&gt;Cypher 是 Neo4j 的查询语言，擅长模式匹配：&lt;/p&gt;</description></item><item><title>新旧 SQL 与存储过程结果对比工具设计</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/%E6%96%B0%E6%97%A7sql%E4%B8%8E%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B%E7%BB%93%E6%9E%9C%E5%AF%B9%E6%AF%94%E5%B7%A5%E5%85%B7%E8%AE%BE%E8%AE%A1/</link><pubDate>Mon, 07 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/%E6%96%B0%E6%97%A7sql%E4%B8%8E%E5%AD%98%E5%82%A8%E8%BF%87%E7%A8%8B%E7%BB%93%E6%9E%9C%E5%AF%B9%E6%AF%94%E5%B7%A5%E5%85%B7%E8%AE%BE%E8%AE%A1/</guid><description>&lt;h1 id="新旧-sql-与存储过程结果对比工具设计"&gt;新旧 SQL 与存储过程结果对比工具设计&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“负责 Oracle 到 OceanBase 迁移中新旧 SQL、存储过程和批处理结果对比工具建设，通过自动化双跑、字段归一化、差异报告和回归验证保障迁移正确性”等经历。&lt;/p&gt;
&lt;p&gt;这类经历在面试里很加分，因为它说明你不是靠人工点页面验证迁移，而是把迁移风险工具化。面试官通常会追问：如何采集 SQL？如何绑定参数？如何比较结果集？排序不稳定怎么办？金额和日期精度怎么处理？存储过程有副作用怎么双跑？差异报告如何定位原因？&lt;/p&gt;
&lt;p&gt;推荐回答的主线是：我把验证对象分为查询 SQL、报表 SQL、批处理和存储过程；对无副作用查询做自动双跑，对有副作用逻辑使用隔离环境和快照数据；对比时不只比较行数，还比较主键集合、字段值、排序、金额精度、日期格式和空值语义；最后输出可追踪差异报告，帮助开发快速定位是 SQL 改写、数据同步、函数差异还是排序问题。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;数据库迁移最怕的不是 SQL 报错，而是 SQL 不报错但结果变了。比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. 空字符串与 NULL 差异导致少查客户。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. ROWNUM 到 LIMIT 改造后分页顺序不稳定。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. CONNECT BY 到递归 CTE 后少了一层机构。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4. 日期函数改写后边界时间漏算。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5. DECODE 改 CASE WHEN 后 NULL 状态映射错误。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6. 存储过程 Java 化后批处理金额汇总差一分钱。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些问题靠人工页面测试很难覆盖。老系统有成百上千条 Mapper SQL、报表 SQL、存储过程和定时任务。迁移项目时间紧，如果每条都让测试手工构造数据、点页面、看截图，不现实，也不可靠。&lt;/p&gt;
&lt;p&gt;因此需要设计新旧结果对比工具。它的目标不是替代所有测试，而是建立迁移正确性的自动化底座：同一份参数，在 Oracle 执行旧 SQL，在 OceanBase 执行新 SQL，把结果归一化后比较，并输出差异。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;结果对比工具的核心由五部分组成：用例采集、执行引擎、归一化、差异比较、报告输出。&lt;/p&gt;
&lt;p&gt;用例采集要解决“测什么”。来源可以是 Mapper XML、线上 SQL 日志、接口录制、人工配置的高风险 SQL、存储过程清单。每个用例至少包含：&lt;/p&gt;</description></item><item><title>物化视图、索引设计与报表性能优化</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/%E7%89%A9%E5%8C%96%E8%A7%86%E5%9B%BE%E7%B4%A2%E5%BC%95%E8%AE%BE%E8%AE%A1%E4%B8%8E%E6%8A%A5%E8%A1%A8%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</link><pubDate>Sun, 06 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/%E7%89%A9%E5%8C%96%E8%A7%86%E5%9B%BE%E7%B4%A2%E5%BC%95%E8%AE%BE%E8%AE%A1%E4%B8%8E%E6%8A%A5%E8%A1%A8%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</guid><description>&lt;h1 id="物化视图索引设计与报表性能优化"&gt;物化视图、索引设计与报表性能优化&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“负责核心业务系统报表 SQL 优化，参与 Oracle 物化视图、索引和统计口径迁移改造，在 OceanBase 环境下通过预聚合、索引设计和异步任务提升报表性能”等经历。&lt;/p&gt;
&lt;p&gt;面试官问报表优化，通常不满足于“加索引后快了”。他会继续问：报表为什么慢？是明细数据太大、join 太多、聚合太重、日期条件不走索引，还是统计口径适合预计算？物化视图迁移到目标库后怎么替代？索引怎么兼顾查询和写入？日报、月报、实时看板分别怎么设计？&lt;/p&gt;
&lt;p&gt;成熟回答要体现“按时效性和成本选方案”：实时列表优先靠索引和 SQL 改写；准实时看板可以增量汇总；T+1 报表适合批处理预聚合；复杂多维分析则考虑宽表、汇总表或数仓，而不是把所有压力压在 OLTP 主库上。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;老系统中有大量经营分析和管理报表，比如机构保费日报、客户转化漏斗、活动效果统计、代理人业绩排名、产品销售分布、续保提醒明细。这些报表通常要关联客户、保单、机构、人员、活动、产品等多张表。&lt;/p&gt;
&lt;p&gt;Oracle 时代可能使用物化视图承载部分预计算：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; MATERIALIZED &lt;span style="color:#66d9ef"&gt;VIEW&lt;/span&gt; mv_org_daily_premium
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;REFRESH COMPLETE &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; DEMAND
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; org_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TRUNC(create_time) &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; biz_date,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SUM&lt;/span&gt;(premium) &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; total_premium,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;COUNT&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; policy_count
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; policy_order
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; status &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;PAID&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;GROUP&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; org_id, TRUNC(create_time);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;迁移到 OceanBase 后，物化视图能力、刷新机制和兼容性要重新评估。即使目标库支持类似能力，也不能盲目照搬，因为报表的瓶颈不一定是“没有物化视图”，可能是统计口径混乱、索引设计不合理、实时性要求过高，或者 OLTP 与报表负载混在一起。&lt;/p&gt;
&lt;p&gt;业务上，报表有不同实时性要求。销售首页可能希望接近实时；管理层日报通常 T+1 可以接受；月度结算要求准确但不一定秒级；导出明细可以异步。优化方案必须围绕这些要求设计。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;报表 SQL 慢的原因主要有四类。&lt;/p&gt;
&lt;p&gt;第一，扫描数据多。报表经常按月、季度、机构范围统计，涉及百万到千万级明细。没有有效分区或索引时，只能大范围扫描。&lt;/p&gt;
&lt;p&gt;第二，聚合成本高。&lt;code&gt;GROUP BY&lt;/code&gt;、&lt;code&gt;COUNT DISTINCT&lt;/code&gt;、多维度统计会产生排序、哈希聚合和临时结果。&lt;/p&gt;
&lt;p&gt;第三，join 链路长。客户、机构、人员、产品、活动维表都要关联，任何一个 join 条件缺索引都会放大中间结果。&lt;/p&gt;
&lt;p&gt;第四，实时性和准确性要求冲突。越实时，越倾向查明细；越快，越倾向预聚合。二者需要按场景取舍。&lt;/p&gt;
&lt;p&gt;物化视图的本质是把查询结果提前计算并存储起来。它适合读多写少、口径稳定、刷新窗口可控的报表。缺点是刷新成本、数据延迟、存储成本和口径变更成本。&lt;/p&gt;
&lt;p&gt;索引适合加速选择性较高、访问模式稳定的查询。报表索引设计常见思路是：&lt;/p&gt;</description></item><item><title>OceanBase 执行计划与慢 SQL 优化</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/oceanbase%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%B8%8E%E6%85%A2sql%E4%BC%98%E5%8C%96/</link><pubDate>Sat, 05 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/oceanbase%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%B8%8E%E6%85%A2sql%E4%BC%98%E5%8C%96/</guid><description>&lt;h1 id="oceanbase-执行计划与慢-sql-优化"&gt;OceanBase 执行计划与慢 SQL 优化&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“Oracle 到 OceanBase 迁移后负责慢 SQL 治理、执行计划分析、索引优化、分页与报表性能回归、上线后稳定性保障”等经历。&lt;/p&gt;
&lt;p&gt;面试里讲慢 SQL 优化，不能停留在“加索引”。资深面试官会问：你怎么发现慢 SQL？怎么看执行计划？如何判断是全表扫描、回表、排序、临时表、分布式执行、锁等待还是租户资源不足？OceanBase 与单机 MySQL、Oracle 的差异是什么？优化后如何验证没有改变业务结果？&lt;/p&gt;
&lt;p&gt;推荐把经历讲成一套闭环：采集慢 SQL 和业务入口，定位执行计划和资源消耗，结合 SQL 语义、索引、统计信息、租户资源和数据分布做改造，最后用压测、双跑和生产观测验证。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;信创迁移完成后，应用从 Oracle 切到 OceanBase。语法兼容只是第一关，真正上线后最容易暴露的是性能问题。老系统中有大量列表页、报表页、批处理和权限范围查询。迁移前 Oracle 上可能依赖历史优化器、索引、统计信息和硬件资源，迁移后执行计划变化，原来可接受的 SQL 可能变慢。&lt;/p&gt;
&lt;p&gt;典型问题包括：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. ROWNUM 改 LIMIT 后，深分页扫描大量数据。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. TRUNC(create_time) 改写不彻底，日期字段索引失效。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. CONNECT BY 改递归 CTE 后，组织树结果再 join 大表变慢。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4. 报表 SQL 复杂聚合，临时表和排序消耗高。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5. 迁移后统计信息不准确，优化器选择了错误索引。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6. OceanBase 租户 CPU、内存、并发资源限制导致抖动。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;业务侧感知通常是“页面打开慢”“导出超时”“晚上批处理跑不完”“数据库连接池占满”。因此慢 SQL 优化不能只在数据库里看单条 SQL，而要把业务入口、调用频率、用户等待时间和系统资源一起看。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;执行计划是数据库对 SQL 的执行路径描述。看计划时重点关注几个问题。&lt;/p&gt;</description></item><item><title>CONNECT BY 到递归 CTE：树查询迁移详解</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/connect-by%E5%88%B0%E9%80%92%E5%BD%92cte%E6%A0%91%E6%9F%A5%E8%AF%A2%E8%BF%81%E7%A7%BB%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 04 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/connect-by%E5%88%B0%E9%80%92%E5%BD%92cte%E6%A0%91%E6%9F%A5%E8%AF%A2%E8%BF%81%E7%A7%BB%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h1 id="connect-by-到递归-cte树查询迁移详解"&gt;CONNECT BY 到递归 CTE：树查询迁移详解&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“参与 Oracle 到 OceanBase 信创迁移，负责组织架构、客户归属、销售团队等树形查询从 &lt;code&gt;CONNECT BY&lt;/code&gt; 到递归 CTE 的改造，并处理层级、排序、环路和性能问题”等经历。&lt;/p&gt;
&lt;p&gt;面试里讲树查询迁移，不能只背一段 &lt;code&gt;WITH RECURSIVE&lt;/code&gt; 模板。资深面试官会继续问：&lt;code&gt;CONNECT BY PRIOR&lt;/code&gt; 的方向怎么判断？&lt;code&gt;LEVEL&lt;/code&gt;、&lt;code&gt;SYS_CONNECT_BY_PATH&lt;/code&gt;、&lt;code&gt;CONNECT_BY_ROOT&lt;/code&gt; 怎么替代？&lt;code&gt;ORDER SIBLINGS BY&lt;/code&gt; 怎么保持？如果数据有环怎么办？递归 CTE 在大组织树上会不会慢？这些问题才是项目经验的分水岭。&lt;/p&gt;
&lt;p&gt;可以把这段经历定位为：我负责把历史系统中强依赖 Oracle 层级查询的 SQL 梳理出来，按“向下查子树、向上查父链、查根节点、查路径、查叶子节点、按兄弟排序展示”分类，然后分别用递归 CTE、路径字段、闭包表或应用层缓存改造，并通过结果对比工具校验节点集合、层级、路径和排序。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险、银行、销售管理类系统里树形数据非常多。典型例子包括销售机构树、部门树、代理人团队树、客户归属树、产品分类树、地区行政区划树、菜单权限树等。老 Oracle 系统里这类查询常用 &lt;code&gt;CONNECT BY&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; org_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; parent_org_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; org_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;LEVEL&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; org_level
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; sales_org
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;START&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;WITH&lt;/span&gt; org_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; :rootOrgId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CONNECT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;PRIOR&lt;/span&gt; org_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; parent_org_id;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;业务页面可能依赖它展示“某个机构下所有子机构”，权限系统可能依赖它判断“当前用户能看哪些下级机构”，报表统计可能依赖它聚合“某分公司及所有下级机构业绩”。一旦树查询迁移出错，影响面非常大：用户看不到数据、看到越权数据、统计口径变小或变大、前端树顺序混乱。&lt;/p&gt;
&lt;p&gt;迁移到 OceanBase MySQL 模式后，Oracle 的 &lt;code&gt;CONNECT BY&lt;/code&gt; 不能直接使用，通常要改成递归 CTE：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WITH&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;RECURSIVE&lt;/span&gt; org_tree &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; org_id, parent_org_id, org_name, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;AS&lt;/span&gt; org_level
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; sales_org
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; org_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; :rootOrgId
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;UNION&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ALL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.org_id, &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.parent_org_id, &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.org_name, p.org_level &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; sales_org &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;JOIN&lt;/span&gt; org_tree p &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;c&lt;/span&gt;.parent_org_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; p.org_id
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; org_tree;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但树查询迁移的真实难点在于历史 SQL 往往使用了 Oracle 的一整套层级查询能力，不只是查子节点。比如用 &lt;code&gt;LEVEL&lt;/code&gt; 控制层级，用 &lt;code&gt;CONNECT_BY_ISLEAF&lt;/code&gt; 判断叶子，用 &lt;code&gt;SYS_CONNECT_BY_PATH&lt;/code&gt; 拼路径，用 &lt;code&gt;ORDER SIBLINGS BY&lt;/code&gt; 控制兄弟节点排序，用 &lt;code&gt;CONNECT_BY_ROOT&lt;/code&gt; 取根机构，用 &lt;code&gt;NOCYCLE&lt;/code&gt; 防止环。&lt;/p&gt;</description></item><item><title>ROWNUM 到 LIMIT：分页语义差异与改造方式</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/rownum%E5%88%B0limit%E5%88%86%E9%A1%B5%E8%AF%AD%E4%B9%89%E5%B7%AE%E5%BC%82%E4%B8%8E%E6%94%B9%E9%80%A0%E6%96%B9%E5%BC%8F/</link><pubDate>Thu, 03 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/rownum%E5%88%B0limit%E5%88%86%E9%A1%B5%E8%AF%AD%E4%B9%89%E5%B7%AE%E5%BC%82%E4%B8%8E%E6%94%B9%E9%80%A0%E6%96%B9%E5%BC%8F/</guid><description>&lt;h1 id="rownum-到-limit分页语义差异与改造方式"&gt;ROWNUM 到 LIMIT：分页语义差异与改造方式&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“负责 Oracle 到 OceanBase 信创迁移中的分页 SQL 改造、MyBatis 方言适配、列表页性能优化和新旧结果一致性验证”等经历。&lt;/p&gt;
&lt;p&gt;面试中如果只说“把 ROWNUM 改成 LIMIT”，会显得很浅。资深 Java 面试官真正关心的是：你是否理解 Oracle &lt;code&gt;ROWNUM&lt;/code&gt; 的生成时机，是否知道 &lt;code&gt;ORDER BY&lt;/code&gt; 和分页截断的先后关系，是否处理了深分页性能问题，是否改造了框架层分页插件，以及是否验证了翻页结果不会重复、遗漏、乱序。&lt;/p&gt;
&lt;p&gt;推荐把这段经历讲成一个完整闭环：我先扫描 Mapper 中所有 &lt;code&gt;ROWNUM&lt;/code&gt; 分页 SQL，按“简单列表、嵌套排序、报表聚合、动态排序、深分页”分层；低风险 SQL 由分页拦截器统一生成 OceanBase MySQL 模式的 &lt;code&gt;LIMIT&lt;/code&gt;；高风险 SQL 手工改造并补充稳定排序键；最后用新旧库双跑工具对同一查询条件下的总数、每页主键序列和边界页结果做对比。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;老系统以 Oracle 为核心数据库，Java 应用层主要使用 Spring、MyBatis 和自研分页组件。大量客户列表、保单列表、活动名单、销售人员绩效列表、报表明细页都采用 Oracle 经典分页写法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; t.&lt;span style="color:#f92672"&gt;*&lt;/span&gt;, ROWNUM rn
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; policy_order
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; status &lt;span style="color:#f92672"&gt;=&lt;/span&gt; :status
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ORDER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;BY&lt;/span&gt; create_time &lt;span style="color:#66d9ef"&gt;DESC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) t
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; ROWNUM &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; :endRow
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; rn &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; :startRow;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;迁移到 OceanBase MySQL 模式后，自然会想到改成：&lt;/p&gt;</description></item><item><title>Oracle 空字符串、函数、日期、序列兼容问题</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/oracle%E7%A9%BA%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%87%BD%E6%95%B0%E6%97%A5%E6%9C%9F%E5%BA%8F%E5%88%97%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98/</link><pubDate>Wed, 02 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/oracle%E7%A9%BA%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%87%BD%E6%95%B0%E6%97%A5%E6%9C%9F%E5%BA%8F%E5%88%97%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98/</guid><description>&lt;h1 id="oracle-空字符串函数日期序列兼容问题"&gt;Oracle 空字符串、函数、日期、序列兼容问题&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“负责 Oracle 到 OceanBase 信创迁移中的 SQL 兼容性治理，处理空字符串、常用函数、日期条件、序列主键和 Java 参数绑定差异”等经历。&lt;/p&gt;
&lt;p&gt;面试官问这类问题，通常不是想听一张函数替换表，而是想确认你是否知道“语法能跑”和“业务语义一致”之间的差距。Oracle 中空字符串等同 &lt;code&gt;NULL&lt;/code&gt;，日期函数和隐式转换很宽松，Sequence 使用方式深入业务代码和存储过程。迁移到 OceanBase MySQL 模式后，如果不系统治理，结果可能出现看似偶发的错账、漏数、查不到、重复主键或索引失效。&lt;/p&gt;
&lt;p&gt;比较成熟的表达是：我把兼容问题分为数据语义、SQL 函数、日期范围、主键生成四类。先通过扫描和样本数据识别风险字段，再建立函数映射和改写规范；日期条件尽量从“字段套函数”改成“范围查询”；序列则按业务主键策略统一到数据库兼容序列、应用号段或分布式 ID，并在双写阶段避免冲突。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;老系统长期运行在 Oracle 上，很多代码默认接受 Oracle 的行为。比如用户未填写手机号，Oracle 表中可能存的是 &lt;code&gt;NULL&lt;/code&gt;，也可能是应用传入的空字符串 &lt;code&gt;''&lt;/code&gt;，但在 Oracle 里二者通常都会按 &lt;code&gt;NULL&lt;/code&gt; 表现。SQL 中常见：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; mobile &lt;span style="color:#66d9ef"&gt;IS&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;迁移到 OceanBase MySQL 模式后，&lt;code&gt;NULL&lt;/code&gt; 和 &lt;code&gt;''&lt;/code&gt; 是两个不同值。原来能查出的“未填写手机号客户”，迁移后可能少一部分。&lt;/p&gt;
&lt;p&gt;函数差异也非常常见。Oracle SQL 里大量使用 &lt;code&gt;NVL&lt;/code&gt;、&lt;code&gt;DECODE&lt;/code&gt;、&lt;code&gt;TO_DATE&lt;/code&gt;、&lt;code&gt;TO_CHAR&lt;/code&gt;、&lt;code&gt;TRUNC&lt;/code&gt;、&lt;code&gt;SYSDATE&lt;/code&gt;、&lt;code&gt;ADD_MONTHS&lt;/code&gt;、&lt;code&gt;MONTHS_BETWEEN&lt;/code&gt;、&lt;code&gt;SUBSTR&lt;/code&gt;、&lt;code&gt;INSTR&lt;/code&gt;、&lt;code&gt;||&lt;/code&gt; 字符串拼接。这些函数有的在目标库中没有，有的名字类似但语义细节不同。&lt;/p&gt;
&lt;p&gt;日期问题尤其危险。很多历史 SQL 写成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; TRUNC(create_time) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; TO_DATE(:bizDate, &lt;span style="color:#e6db74"&gt;&amp;#39;yyyy-mm-dd&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这在 Oracle 中很常见，但迁移后如果改成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; DATE(create_time) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; :bizDate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然结果可能一致，却会让 &lt;code&gt;create_time&lt;/code&gt; 上的索引失效，导致生产慢 SQL。&lt;/p&gt;
&lt;p&gt;序列问题也很典型。Oracle 中常用：&lt;/p&gt;</description></item><item><title>Oracle 到 OceanBase 信创迁移整体方法论</title><link>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/oracle%E5%88%B0oceanbase%E4%BF%A1%E5%88%9B%E8%BF%81%E7%A7%BB%E6%95%B4%E4%BD%93%E6%96%B9%E6%B3%95%E8%AE%BA/</link><pubDate>Tue, 01 Apr 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/04-database-xinchuang/oracle%E5%88%B0oceanbase%E4%BF%A1%E5%88%9B%E8%BF%81%E7%A7%BB%E6%95%B4%E4%BD%93%E6%96%B9%E6%B3%95%E8%AE%BA/</guid><description>&lt;h1 id="oracle-到-oceanbase-信创迁移整体方法论"&gt;Oracle 到 OceanBase 信创迁移整体方法论&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“参与核心业务系统信创改造，负责 Oracle 到 OceanBase 迁移、SQL 兼容性改造、存储过程与定时任务 Java 化、分页与树查询改造、新旧 SQL 结果对比工具建设、慢 SQL 优化和上线验证”等经历。&lt;/p&gt;
&lt;p&gt;面试里这段经历通常会被追问得很细。因为它不是简单“把数据库换一下”，而是同时涉及数据库语法、执行计划、事务一致性、批处理调度、MyBatis 插件、数据校验、灰度上线和生产性能治理。真正能讲清楚的人，需要说明三个层次：&lt;/p&gt;
&lt;p&gt;第一，为什么要迁移。也就是信创背景、国产数据库替换、运维标准化、成本和风险控制。&lt;/p&gt;
&lt;p&gt;第二，怎么迁移。包括迁移评估、对象梳理、SQL 扫描、兼容性改造、数据同步、双跑验证、灰度切换和回退预案。&lt;/p&gt;
&lt;p&gt;第三，迁移后怎么稳定运行。包括慢 SQL 优化、OceanBase 执行计划分析、索引调整、分页策略改造、批任务改造、监控告警和问题复盘。&lt;/p&gt;
&lt;p&gt;如果面试官问“你在 Oracle 到 OceanBase 迁移中具体做了什么”，不能只回答“改了 SQL”。更好的表达是：我负责应用侧和数据库侧之间的兼容性治理，把 Oracle 方言、存储过程、定时任务和慢 SQL 按风险分层处理，并通过工具化手段做新旧结果对比和性能回归，最终让迁移不是一次性人工改造，而是一套可重复执行的方法论。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;业务系统可以理解为保险销售、客户经营和交易管理类的核心系统。系统历史比较长，早期以 Oracle 为主，Java 应用层使用 Spring、MyBatis，部分复杂逻辑沉淀在数据库里，比如存储过程、函数、定时作业、报表 SQL、客户层级查询和月底批处理。&lt;/p&gt;
&lt;p&gt;这种系统的典型特点是“业务稳定但技术债重”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL 数量多，很多 SQL 写法强依赖 Oracle。&lt;/li&gt;
&lt;li&gt;报表查询复杂，存在大量嵌套子查询、&lt;code&gt;ROWNUM&lt;/code&gt; 分页、&lt;code&gt;NVL&lt;/code&gt;、&lt;code&gt;DECODE&lt;/code&gt;、&lt;code&gt;TO_DATE&lt;/code&gt;、&lt;code&gt;TRUNC&lt;/code&gt; 等函数。&lt;/li&gt;
&lt;li&gt;组织架构、客户层级、代理人团队等数据经常使用 &lt;code&gt;CONNECT BY PRIOR&lt;/code&gt; 做树查询。&lt;/li&gt;
&lt;li&gt;批处理逻辑有一部分在 OGG、存储过程、数据库 Job 或调度脚本里，应用侧对这些逻辑感知不足。&lt;/li&gt;
&lt;li&gt;空字符串和 &lt;code&gt;NULL&lt;/code&gt; 在 Oracle 中语义特殊，迁移后可能影响查询条件、唯一约束、字段默认值和 Java 判空逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;信创迁移的目标不是单纯追求“数据库国产化”这个动作，而是在不影响现有业务连续性的前提下，把底层数据库从 Oracle 迁移到 OceanBase，并尽量减少对上层业务的扰动。对于保险销售系统来说，迁移窗口通常非常敏感：白天有出单、保全、客户跟进、活动转化，晚上有批处理、报表和对账。任何一个 SQL 结果不一致、批任务漏跑或慢查询放大，都可能影响业务人员第二天的使用。&lt;/p&gt;</description></item><item><title>WebLogic到Undertow：容器迁移中的兼容性问题</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/weblogic%E5%88%B0undertow%E5%AE%B9%E5%99%A8%E8%BF%81%E7%A7%BB%E4%B8%AD%E7%9A%84%E5%85%BC%E5%AE%B9%E6%80%A7%E9%97%AE%E9%A2%98/</link><pubDate>Fri, 07 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/weblogic%E5%88%B0undertow%E5%AE%B9%E5%99%A8%E8%BF%81%E7%A7%BB%E4%B8%AD%E7%9A%84%E5%85%BC%E5%AE%B9%E6%80%A7%E9%97%AE%E9%A2%98/</guid><description>&lt;h1 id="weblogic到undertow容器迁移中的兼容性问题"&gt;WebLogic到Undertow：容器迁移中的兼容性问题&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“将部署在 WebLogic 的 Spring3 + MyBatis 遗留应用迁移到 Spring Boot + Undertow 内嵌容器”的项目经历。简历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;负责核心保险业务系统从 WebLogic WAR 部署向 Spring Boot + Undertow 可执行 JAR 部署迁移，完成 JNDI 数据源替换、类加载冲突治理、Servlet 过滤器顺序适配、Session 与编码兼容、连接池和线程池参数重建，并通过灰度切流、日志回放和压测保障迁移期间交易链路稳定。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官看到这段，通常不会满足于“换了一个容器”。真正的追问会集中在：WebLogic 和 Undertow 的运行模型有什么差异？原来依赖容器提供的能力迁移后由谁承担？为什么上线后可能出现 &lt;code&gt;NoSuchMethodError&lt;/code&gt;、乱码、上传失败、事务行为变化、连接池耗尽？如果生产上出现接口偶发 500，你如何判断是容器兼容问题还是业务代码问题？&lt;/p&gt;
&lt;p&gt;回答这类问题时，要把容器迁移讲成一次“运行时契约重建”。WebLogic 是完整 Java EE 应用服务器，承担了连接池、JNDI、安全域、部署描述符、类加载隔离、线程池、Session 管理等职责。Undertow 是轻量级 Servlet 容器，优势是启动快、内嵌部署简单、适合 Spring Boot 服务化，但它不会自动继承 WebLogic 的历史配置。迁移的难点不是启动一个 Boot 应用，而是把过去藏在服务器控制台、部署描述符和运维脚本里的隐性配置全部显式化、版本化、可验证。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险公司很多核心系统早期采用 WebLogic 部署，原因很现实：当年 Java EE 体系成熟，WebLogic 提供控制台、集群、JNDI、连接池、事务、安全域、监控和热部署，适合支撑大型企业应用。一个传统系统可能以 WAR 包部署，内部包含销售出单、保费试算、客户查询、保单状态同步、回访工单、运营报表等模块。数据库连接池在 WebLogic 控制台配置，应用通过 JNDI 名称获取；上下文路径、Session 超时、错误页和资源引用写在 &lt;code&gt;web.xml&lt;/code&gt;、&lt;code&gt;weblogic.xml&lt;/code&gt; 或运维文档中；部分公共 jar 放在 domain lib 或 server lib 下。&lt;/p&gt;</description></item><item><title>Quartz扩展与XXL-Job迁移实践</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/quartz%E6%89%A9%E5%B1%95%E4%B8%8Exxl-job%E8%BF%81%E7%A7%BB%E5%AE%9E%E8%B7%B5/</link><pubDate>Thu, 06 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/quartz%E6%89%A9%E5%B1%95%E4%B8%8Exxl-job%E8%BF%81%E7%A7%BB%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h1 id="quartz扩展与xxl-job迁移实践"&gt;Quartz扩展与XXL-Job迁移实践&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“遗留 Quartz 定时任务治理与 XXL-Job 平台化迁移”的项目经历。简历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;负责保险核心系统定时任务治理，将应用内 Quartz 任务逐步迁移至 XXL-Job，完成任务资产盘点、幂等改造、分片执行、失败重试、执行日志、阻塞策略和灰度切换，解决集群环境下任务重复执行、不可观测和补偿困难问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官会追问：Quartz 和 XXL-Job 的定位差异是什么？为什么要迁移？Quartz 集群如何防止重复执行？XXL-Job 的调度中心和执行器如何交互？迁移时如何保证任务不漏跑、不重复？失败重试和幂等怎么设计？日终任务、补偿任务、报表任务、状态同步任务有什么不同策略？&lt;/p&gt;
&lt;p&gt;回答这类问题要注意，不要把 XXL-Job 说成“更高级的定时器”。Quartz 是优秀的调度框架，适合嵌入应用；XXL-Job 更偏任务调度平台，提供控制台、执行日志、手动触发、路由策略、分片、失败重试和告警。迁移的本质是把散落在应用内的任务调度能力平台化、可观测化、可运维化。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险系统里的定时任务很多，而且重要程度不低。常见任务包括：保单状态同步、支付状态补偿、核保超时查询、短信失败重发、客户标签刷新、佣金结算、日终报表、渠道数据同步、文件对账、风险名单更新、临期保单提醒、回访任务生成等。这些任务有的分钟级执行，有的日终批处理，有的依赖外部文件，有的需要分机构并发，有的不能并发执行。&lt;/p&gt;
&lt;p&gt;老系统通常把 Quartz 嵌入应用。单机时代问题不大，应用启动时 Scheduler 启动，按 cron 执行 Job。进入集群后问题开始出现：如果没有 Quartz 集群配置，多实例会重复执行同一任务；如果用了数据库锁，配置和表结构又需要维护；任务执行日志散在应用日志里，运营无法手动触发；任务失败后是否重试不清楚；任务参数修改需要发版；某个任务卡住会影响应用线程；业务方问“昨天状态同步跑没跑完”，开发只能翻日志。&lt;/p&gt;
&lt;p&gt;迁移到 XXL-Job 的目标是让任务统一调度、统一查看、统一告警、可手动补偿、可分片扩展。对保险业务来说，这能显著提升问题处理效率。比如支付状态补偿失败，运营可以看到失败原因并触发重跑；日终报表耗时过长，可以按机构分片；核保超时查询可以设置失败重试；任务切换期间可以通过调度开关避免 Quartz 和 XXL-Job 同时执行。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Quartz 的核心概念是 Scheduler、Job、JobDetail、Trigger 和 JobStore。Scheduler 负责调度，Job 是执行逻辑，Trigger 描述执行时间，JobStore 保存任务和触发器。Quartz 可以使用 RAMJobStore，也可以使用 JDBC JobStore 支持集群。集群模式下多个节点共享数据库表，通过锁和 trigger 状态协调执行，避免同一 trigger 被多个节点同时获取。&lt;/p&gt;
&lt;p&gt;Quartz 优点是成熟、灵活、可嵌入、表达能力强，适合应用内部复杂调度。但它的运维界面、执行日志、手动触发、告警、分片等能力通常需要团队自己扩展。遗留系统中常见做法是封装一个任务基类，记录开始结束日志，防止并发执行，再加数据库任务锁。&lt;/p&gt;
&lt;p&gt;XXL-Job 的核心是调度中心加执行器。调度中心负责管理任务、触发时间、路由策略、执行日志和告警；执行器嵌入业务应用，注册到调度中心，接收调度请求并执行具体 handler。它把调度控制面从业务应用中抽离出来，使任务开关、参数、日志和失败重试可以在平台上管理。&lt;/p&gt;
&lt;p&gt;XXL-Job 常用能力包括路由策略、阻塞处理、失败重试、分片广播、任务参数、执行日志和手动触发。路由策略决定选择哪个执行器实例；阻塞策略决定上一次未结束时新调度如何处理；分片广播可以让多个执行器实例分别处理不同分片；失败重试由调度中心触发，但业务侧必须保证幂等。&lt;/p&gt;</description></item><item><title>AOP加SpEL实现动态数据权限</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/aop%E5%8A%A0spel%E5%AE%9E%E7%8E%B0%E5%8A%A8%E6%80%81%E6%95%B0%E6%8D%AE%E6%9D%83%E9%99%90/</link><pubDate>Wed, 05 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/aop%E5%8A%A0spel%E5%AE%9E%E7%8E%B0%E5%8A%A8%E6%80%81%E6%95%B0%E6%8D%AE%E6%9D%83%E9%99%90/</guid><description>&lt;h1 id="aop加spel实现动态数据权限"&gt;AOP加SpEL实现动态数据权限&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“基于 Spring AOP + SpEL 实现动态数据权限控制，统一处理机构、渠道、角色和业务归属范围”的项目经历。简历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;设计动态数据权限框架，基于自定义注解、Spring AOP 和 SpEL 表达式解析用户上下文与业务参数，生成机构、渠道、岗位、客户经理等维度的数据范围，并与 MyBatis 查询条件和审计日志集成，解决保险运营后台多角色数据隔离问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官通常会追问：为什么不用硬编码 if else？AOP 拦截在哪一层？SpEL 怎么取方法参数？权限上下文如何传递到 MyBatis？如何避免越权？如何处理管理员、机构负责人、普通坐席、渠道人员的不同范围？权限表达式会不会有性能问题？如果一个接口查列表和导出都要控制，如何保证一致？&lt;/p&gt;
&lt;p&gt;回答时要强调：数据权限不是简单的菜单权限。菜单权限决定“能不能访问功能”，数据权限决定“能看到哪些数据”。在保险业务里，同一个保单列表，不同角色看到的数据范围完全不同，且范围可能由登录人机构、岗位、渠道、保单归属、客户经理、产品线共同决定。动态数据权限的价值，是把这些规则从业务 SQL 和 Controller 中抽离出来，形成可复用、可审计、可测试的横切能力。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险运营系统中，数据权限是非常典型的复杂场景。总部运营可以查看全国数据；分公司经理只能查看本机构及下级机构；渠道经理只能看自己负责渠道的保单；客服坐席只能看分配给自己的回访工单；代理人只能看自己名下客户；风控人员可能按产品线或风险等级查看；外包团队还要屏蔽敏感字段。&lt;/p&gt;
&lt;p&gt;如果每个查询都在 SQL 中手写权限条件，系统很快会失控。第一，规则重复。客户列表、保单列表、订单列表、导出接口、统计接口都要写类似机构条件。第二，容易漏控。新接口上线时开发者可能忘记追加权限条件，造成越权。第三，难维护。组织架构、角色模型、渠道关系变化后，需要改大量 SQL。第四，难审计。安全团队很难确认某个接口到底应用了哪些数据范围。第五，测试成本高。不同角色、机构、渠道组合多，靠人工点页面容易漏。&lt;/p&gt;
&lt;p&gt;因此项目中引入 AOP + SpEL。AOP 负责在业务方法执行前统一识别需要数据权限的场景；注解描述权限类型、参数来源和策略；SpEL 从方法参数、登录上下文或业务对象中动态取值；权限服务根据规则计算数据范围；最终通过 MyBatis 参数、ThreadLocal 上下文或插件注入 SQL 条件。这样，业务方法保持清晰，权限规则集中治理。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Spring AOP 基于代理实现，适合拦截 Spring Bean 的方法调用。数据权限通常放在 Service 层或 Mapper 层之前。放在 Controller 层可以拿到请求信息，但容易和业务参数转换耦合；放在 Mapper 层接近 SQL，但缺少业务语义；放在 Service 层通常较平衡，因为它既有业务参数，又能覆盖列表、详情、导出和统计等入口。&lt;/p&gt;
&lt;p&gt;自定义注解用于描述权限需求。例如 &lt;code&gt;@DataScope(resource = &amp;quot;policy&amp;quot;, key = &amp;quot;#query.orgCode&amp;quot;, mode = ORG_AND_CHANNEL)&lt;/code&gt; 表示当前方法查询保单资源，机构参数从 &lt;code&gt;query.orgCode&lt;/code&gt; 取，权限模式是机构加渠道。注解不要承载过多业务逻辑，否则会变成另一套难懂的 DSL。复杂规则应该由权限服务实现。&lt;/p&gt;</description></item><item><title>MyBatis拦截器实现分页SQL自动转换</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/mybatis%E6%8B%A6%E6%88%AA%E5%99%A8%E5%AE%9E%E7%8E%B0%E5%88%86%E9%A1%B5sql%E8%87%AA%E5%8A%A8%E8%BD%AC%E6%8D%A2/</link><pubDate>Tue, 04 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/mybatis%E6%8B%A6%E6%88%AA%E5%99%A8%E5%AE%9E%E7%8E%B0%E5%88%86%E9%A1%B5sql%E8%87%AA%E5%8A%A8%E8%BD%AC%E6%8D%A2/</guid><description>&lt;h1 id="mybatis拦截器实现分页sql自动转换"&gt;MyBatis拦截器实现分页SQL自动转换&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“基于 MyBatis 插件机制实现统一分页，减少业务 XML 重复分页 SQL，兼容 Oracle/MySQL 方言”的项目经历。简历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;设计并落地 MyBatis 分页拦截器，基于 &lt;code&gt;MappedStatement&lt;/code&gt; 和 &lt;code&gt;BoundSql&lt;/code&gt; 自动识别分页参数，按数据库方言改写分页 SQL 和 count SQL，统一处理排序白名单、参数绑定、插件顺序和慢 SQL 记录，降低遗留系统分页 XML 维护成本。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官会继续追问：为什么不用每个 XML 自己写分页？插件拦截 &lt;code&gt;Executor.query&lt;/code&gt; 还是 &lt;code&gt;StatementHandler.prepare&lt;/code&gt;？如何生成 count SQL？复杂 SQL、&lt;code&gt;order by&lt;/code&gt;、&lt;code&gt;group by&lt;/code&gt;、&lt;code&gt;union&lt;/code&gt; 怎么处理？如何避免 SQL 注入？如何保证数据权限插件和分页插件顺序正确？分页参数从哪里来？如果查询很慢，是 count 慢还是数据页慢？&lt;/p&gt;
&lt;p&gt;这类项目的重点不是“我会写拦截器”，而是你能不能把 SQL 改写这件事做成工程上可控的能力。分页插件一旦写错，影响的是所有列表查询；如果没有方言、白名单、测试和降级策略，很容易把一个便利功能变成生产风险。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险运营系统中列表查询非常多，例如客户列表、保单列表、订单列表、回访工单、渠道人员、产品配置、佣金结算、批处理记录、异常流水等。早期 MyBatis XML 通常每个查询自己写分页：Oracle 使用双层 &lt;code&gt;ROWNUM&lt;/code&gt;，MySQL 使用 &lt;code&gt;limit offset,size&lt;/code&gt;，有些还要额外写一条 count SQL。随着业务迭代，问题越来越明显。&lt;/p&gt;
&lt;p&gt;第一，重复代码多。每个列表都要写分页包装和 count，开发效率低，且容易出现排序和条件不一致。第二，数据库方言耦合。老系统如果从 Oracle 扩展到 MySQL，或者测试环境和生产环境数据库不同，XML 中硬编码方言会带来迁移成本。第三，权限条件难统一。机构、渠道、数据范围等条件如果散落在每个 XML 中，既容易漏，也难以审计。第四，分页性能参差不齐。有些查询先查全量再内存分页，有些 count 带复杂排序，有些深分页没有优化。第五，接口风格不统一。不同模块分页参数命名、页码从 0 还是 1 开始、最大页大小限制都不一致。&lt;/p&gt;</description></item><item><title>MyBatis执行流程与插件机制详解</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/mybatis%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B%E4%B8%8E%E6%8F%92%E4%BB%B6%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</link><pubDate>Mon, 03 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/mybatis%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B%E4%B8%8E%E6%8F%92%E4%BB%B6%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h1 id="mybatis执行流程与插件机制详解"&gt;MyBatis执行流程与插件机制详解&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“熟悉 MyBatis 执行流程，处理遗留 XML 兼容、自定义插件、分页改造和 SQL 性能治理”的项目经历。简历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;深入梳理核心系统 MyBatis 执行链路，完成 Mapper XML 加载校验、动态 SQL 兼容、分页插件顺序治理、自定义拦截器适配和慢 SQL 诊断，支撑 Spring Boot 迁移过程中数百个 Mapper 平稳运行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官会从这段继续追问：Mapper 接口为什么不用实现类也能执行？&lt;code&gt;SqlSession&lt;/code&gt;、&lt;code&gt;Executor&lt;/code&gt;、&lt;code&gt;StatementHandler&lt;/code&gt;、&lt;code&gt;ParameterHandler&lt;/code&gt;、&lt;code&gt;ResultSetHandler&lt;/code&gt; 分别做什么？插件到底拦截了哪个对象？为什么插件顺序会影响分页和数据权限？一级缓存和二级缓存在哪里生效？MyBatis 和 Spring 事务是怎么结合的？&lt;/p&gt;
&lt;p&gt;这类问题不能只背源码类名。更好的回答方式是把一次查询从 Mapper 方法调用开始，讲到 SQL 解析、参数绑定、执行器调用、结果集映射、缓存处理和插件拦截。再结合项目说明为什么理解执行流程能解决真实问题，例如分页 SQL 改写、动态数据权限注入、SQL 日志脱敏、慢查询定位、迁移后 XML 未加载排查。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;遗留保险系统中，MyBatis 往往承担最核心的数据访问职责。系统可能有几百个 Mapper XML，覆盖客户、保单、订单、产品、渠道、机构、佣金、回访、报表等模块。早期项目大量使用手写 SQL，这是 MyBatis 的优势：可以精细控制复杂查询、Oracle 方言、动态条件、批量操作和存储过程调用。但随着系统演进，问题也会累积。&lt;/p&gt;
&lt;p&gt;第一，XML 数量多，命名空间、SQL id、ResultMap、TypeHandler、动态 SQL 依赖复杂。迁移到 Spring Boot 后，只要扫描路径、资源加载或 &lt;code&gt;SqlSessionFactory&lt;/code&gt; 配置有差异，就可能出现运行时找不到 statement。第二，分页、数据权限、租户隔离、SQL 日志等横切能力通常靠插件实现，插件顺序一变，最终 SQL 就可能不同。第三，老系统常用 Oracle 特性，例如 &lt;code&gt;ROWNUM&lt;/code&gt;、&lt;code&gt;CONNECT BY&lt;/code&gt;、序列、存储过程、&lt;code&gt;DECODE&lt;/code&gt;，迁移过程中需要保证 SQL 语义不变。第四，保险查询场景复杂，运营后台一个列表可能关联客户、保单、机构、产品、状态流水和标签表，慢 SQL 很常见。第五，事务问题经常被误判为 MyBatis 问题，例如同一事务内查询读到缓存、批量执行未 flush、异常被吞导致不回滚。&lt;/p&gt;</description></item><item><title>Spring事务机制与保险业务一致性问题</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/spring%E4%BA%8B%E5%8A%A1%E6%9C%BA%E5%88%B6%E4%B8%8E%E4%BF%9D%E9%99%A9%E4%B8%9A%E5%8A%A1%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98/</link><pubDate>Sun, 02 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/spring%E4%BA%8B%E5%8A%A1%E6%9C%BA%E5%88%B6%E4%B8%8E%E4%BF%9D%E9%99%A9%E4%B8%9A%E5%8A%A1%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98/</guid><description>&lt;h1 id="spring事务机制与保险业务一致性问题"&gt;Spring事务机制与保险业务一致性问题&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“处理保险核心业务交易一致性，熟悉 Spring 声明式事务、传播行为、回滚规则和补偿机制”的项目经历。简历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;负责保险销售与订单流转链路的一致性治理，基于 Spring 声明式事务规范本地数据库操作，梳理事务传播、异常回滚、MyBatis 会话绑定和外部接口调用边界，并通过业务流水、幂等键、补偿任务和对账机制解决跨系统最终一致性问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官通常会追问：&lt;code&gt;@Transactional&lt;/code&gt; 为什么有时不生效？默认回滚哪些异常？传播行为怎么选？同一个类内部方法调用为什么没有事务？MyBatis 和 Spring 事务怎么绑定？保险出单过程中数据库写成功、下游核保失败怎么办？能不能把外部 HTTP 调用放在事务里？分布式事务和补偿事务怎么取舍？&lt;/p&gt;
&lt;p&gt;回答这类问题时，要把 Spring 事务机制和业务一致性分开讲。Spring 本地事务解决的是单数据源或单事务资源内的原子性；保险业务一致性经常跨数据库、第三方接口、消息、任务和人工处理，不可能只靠一个 &lt;code&gt;@Transactional&lt;/code&gt; 解决。资深回答要能说明事务边界、失败分类、回滚策略、幂等设计、补偿和对账。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;保险业务链路天然复杂。以投保为例，用户提交订单后，系统可能要保存投保单、客户信息、被保人信息、险种责任、保费试算结果、支付流水、营销活动信息；随后调用核保、风控、支付、短信、电子保单、佣金系统；还要支持撤单、退保、批改、状态回写和人工复核。每一步都可能失败，而且失败后业务状态不能混乱。&lt;/p&gt;
&lt;p&gt;在老系统中，常见问题包括：Service 方法没有事务导致部分表写入成功；异常被 catch 后没有抛出导致事务不回滚；&lt;code&gt;@Transactional&lt;/code&gt; 加在 private 方法或同类内部调用上不生效；事务范围过大，把外部接口调用也包进去导致数据库连接长时间占用；多数据源操作只回滚了一个库；定时任务补偿重复执行造成状态错乱；MyBatis 批量执行异常后定位困难。&lt;/p&gt;
&lt;p&gt;保险场景对一致性的要求不是所有步骤都必须强一致，而是不同环节要采用不同策略。保单主状态、订单金额、支付流水属于强一致要求高的本地数据，必须在本地事务中保证。短信通知、邮件、日志、报表、佣金试算可以最终一致。调用外部核保、支付、保司接口时，需要业务流水和状态机支撑，因为外部系统不可由本地事务回滚。&lt;/p&gt;
&lt;h2 id="3-核心原理"&gt;3. 核心原理&lt;/h2&gt;
&lt;p&gt;Spring 声明式事务基于 AOP 代理。方法调用进入代理后，&lt;code&gt;TransactionInterceptor&lt;/code&gt; 根据事务属性获取或创建事务，执行业务方法，成功则提交，异常则按规则回滚。底层事务由 &lt;code&gt;PlatformTransactionManager&lt;/code&gt; 实现，常见的是 &lt;code&gt;DataSourceTransactionManager&lt;/code&gt;、&lt;code&gt;JpaTransactionManager&lt;/code&gt; 和 JTA 事务管理器。&lt;/p&gt;
&lt;p&gt;事务传播行为决定当前方法如何参与已有事务。&lt;code&gt;REQUIRED&lt;/code&gt; 最常用，有事务就加入，没有就新建；&lt;code&gt;REQUIRES_NEW&lt;/code&gt; 挂起当前事务并新建事务，适合独立审计日志或失败记录；&lt;code&gt;NESTED&lt;/code&gt; 使用保存点，依赖数据库和事务管理器支持；&lt;code&gt;SUPPORTS&lt;/code&gt; 有事务就加入，没有就非事务；&lt;code&gt;MANDATORY&lt;/code&gt; 要求必须已有事务；&lt;code&gt;NOT_SUPPORTED&lt;/code&gt; 挂起事务；&lt;code&gt;NEVER&lt;/code&gt; 禁止事务。&lt;/p&gt;
&lt;p&gt;默认回滚规则是运行时异常和 Error 回滚，受检异常默认不回滚。保险业务中很多异常是业务异常，如果继承 &lt;code&gt;Exception&lt;/code&gt; 而不是 &lt;code&gt;RuntimeException&lt;/code&gt;，又没有配置 &lt;code&gt;rollbackFor&lt;/code&gt;，就可能出现“抛异常但提交了”。推荐明确异常体系：参数校验、业务拒绝、下游失败、系统异常分层，并在事务注解中明确回滚规则。&lt;/p&gt;
&lt;p&gt;事务失效常见原因有：同类内部自调用绕过代理；方法不是 public；对象不是 Spring Bean；异常被捕获没有继续抛出；数据库引擎不支持事务；使用了错误的事务管理器；多线程中事务上下文不传播；&lt;code&gt;@Transactional&lt;/code&gt; 加在接口或类上但代理方式和调用方式不匹配。&lt;/p&gt;
&lt;p&gt;MyBatis 与 Spring 事务通过 &lt;code&gt;SqlSessionTemplate&lt;/code&gt; 和 &lt;code&gt;TransactionSynchronizationManager&lt;/code&gt; 集成。同一事务内获取到的 SqlSession 会绑定到当前线程，Executor 使用同一个 JDBC Connection。事务提交时才真正提交连接，回滚时回滚连接。因此 MyBatis 本身不单独提交，除非开发者绕开 Spring 管理手动打开 SqlSession。&lt;/p&gt;</description></item><item><title>Spring3到SpringBoot迁移的关键问题与改造路径</title><link>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/spring3%E5%88%B0springboot%E8%BF%81%E7%A7%BB%E7%9A%84%E5%85%B3%E9%94%AE%E9%97%AE%E9%A2%98%E4%B8%8E%E6%94%B9%E9%80%A0%E8%B7%AF%E5%BE%84/</link><pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/03-spring-mybatis-modernization/spring3%E5%88%B0springboot%E8%BF%81%E7%A7%BB%E7%9A%84%E5%85%B3%E9%94%AE%E9%97%AE%E9%A2%98%E4%B8%8E%E6%94%B9%E9%80%A0%E8%B7%AF%E5%BE%84/</guid><description>&lt;h1 id="spring3到springboot迁移的关键问题与改造路径"&gt;Spring3到SpringBoot迁移的关键问题与改造路径&lt;/h1&gt;
&lt;h2 id="1-对应简历段落"&gt;1. 对应简历段落&lt;/h2&gt;
&lt;p&gt;这篇文章对应简历中“遗留系统架构升级、保险业务核心系统现代化改造、从传统单体应用迁移到 Spring Boot 微服务体系”的项目经历。可以在简历中表述为：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参与公司核心业务系统现代化改造，负责将 Ant 构建的 Spring3 + MyBatis + WebLogic 遗留应用逐步迁移为 Maven + Spring Boot + Undertow 的服务化架构；改造过程中完成 MyBatis 兼容适配、配置中心接入、服务注册发现、统一网关、ELK 日志链路、定时任务平台化等能力建设，支撑保险销售、订单流转、客户服务、运营管理等业务模块平稳迁移。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个段落真正想表达的不是“我会 Spring Boot”，而是“我能把一个多年运行、技术栈陈旧、依赖复杂、业务风险高的系统，拆解为可控的迁移工程”。面试官通常不会只问 Spring Boot 自动配置原理，还会追问：为什么要迁移？迁移过程中哪些地方最危险？老系统和新系统如何并行？WebLogic 和 Undertow 行为差异怎么处理？MyBatis 老 XML 怎么兼容？配置、日志、任务和网关如何治理？上线失败如何回滚？&lt;/p&gt;
&lt;p&gt;因此，这类项目要讲成一条完整的改造链路：从构建体系开始，解决可编译、可依赖、可打包；再解决框架升级，解决 Bean、事务、MVC、配置加载差异；然后解决运行容器差异，处理 JNDI、ClassLoader、Session、编码、线程池；最后补上分布式运行能力，包括 Nacos、Gateway、ELK、XXL-Job、灰度发布和回滚机制。&lt;/p&gt;
&lt;h2 id="2-业务背景"&gt;2. 业务背景&lt;/h2&gt;
&lt;p&gt;遗留系统通常不是因为“代码丑”才迁移，而是因为它已经影响业务迭代效率、稳定性和运维成本。以保险销售和客户运营系统为例，老系统最初可能是一个部署在 WebLogic 上的传统 WAR 包，业务覆盖代理人管理、客户资料、产品配置、保单试算、订单提交、回访工单、运营报表等模块。早期系统采用 Spring3、Spring MVC、MyBatis、Oracle、Quartz、Log4j，构建方式使用 Ant 脚本，依赖 jar 包放在 &lt;code&gt;lib&lt;/code&gt; 目录，环境配置通过 properties、XML 或 WebLogic 控制台维护。&lt;/p&gt;
&lt;p&gt;这种架构在业务规模较小时能够稳定运行，但随着线上渠道增加、营销活动频繁、移动端和内部运营平台不断接入，问题会逐渐暴露。第一，交付效率低。Ant 构建脚本缺少标准依赖管理，jar 包版本靠人工维护，不同开发环境和测试环境经常出现“我本地能跑、测试环境缺类”的问题。第二，部署复杂。WebLogic 启停慢，单个 WAR 包变大后发布窗口长，一次小改动也可能需要完整重启。第三，配置分散。不同环境的数据库、缓存、外部接口、任务开关散落在服务器目录和控制台里，缺少集中审计。第四，日志不可观测。出现客户投保失败、订单状态不一致、接口超时等问题时，只能登录机器翻日志，缺少 traceId、调用链和统一检索。第五，任务不可控。Quartz 任务随应用部署，集群环境下需要处理锁和重复执行，任务失败重试、手动触发、执行历史都不友好。&lt;/p&gt;
&lt;p&gt;业务侧的诉求也很明确：核心交易不能中断，已有接口不能大面积改动，数据一致性不能破坏；同时，新业务希望更快上线，服务能够水平扩容，日志能够快速定位，任务能够统一调度，配置能够动态发布。也就是说，这不是一次“技术升级秀”，而是一次在业务连续性约束下完成的工程治理。&lt;/p&gt;</description></item><item><title>生产环境稳定性治理：指标、告警、日志与应急流程</title><link>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E7%A8%B3%E5%AE%9A%E6%80%A7%E6%B2%BB%E7%90%86%E6%8C%87%E6%A0%87%E5%91%8A%E8%AD%A6%E6%97%A5%E5%BF%97%E4%B8%8E%E5%BA%94%E6%80%A5%E6%B5%81%E7%A8%8B/</link><pubDate>Thu, 06 Feb 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E7%A8%B3%E5%AE%9A%E6%80%A7%E6%B2%BB%E7%90%86%E6%8C%87%E6%A0%87%E5%91%8A%E8%AD%A6%E6%97%A5%E5%BF%97%E4%B8%8E%E5%BA%94%E6%80%A5%E6%B5%81%E7%A8%8B/</guid><description>&lt;h1 id="生产环境稳定性治理指标告警日志与应急流程"&gt;生产环境稳定性治理：指标、告警、日志与应急流程&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;简历中关于稳定性治理可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参与生产环境稳定性治理，围绕 JVM、接口、线程池、数据库、缓存、消息队列和业务关键链路建立指标、告警、日志与应急流程，推动故障从被动救火转向可观测、可预警、可复盘的闭环管理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这类表述看起来很宏观，面试官会追问落地细节：你监控哪些指标？告警阈值怎么设？什么样的日志才算有用？Full GC 告警来了如何处置？接口超时和错误率升高怎么分级？应急时谁来决策回滚、扩容、降级？故障复盘怎么避免流于形式？&lt;/p&gt;
&lt;p&gt;这篇文章把稳定性治理拆成可讲、可做、可复盘的部分。它不是单纯 JVM 调优文章，但和 JVM 线上诊断强相关，因为真正成熟的 JVM 治理一定嵌在生产稳定性体系里。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险核心系统对稳定性要求很高。承保链路影响新单出单，理赔链路影响客户赔付，保全链路影响合同变更，缴费链路影响续期和扣款，运营后台影响人工审核和批量处理。一个 JVM Full GC、线程池耗尽或导出 OOM，表面看是技术故障，业务上可能造成出单失败、理赔延迟、财务对账中断、客服投诉。&lt;/p&gt;
&lt;p&gt;保险系统还有明显的业务峰谷。工作日上午运营人员集中查询和导出，月底月初财务对账压力大，凌晨批处理任务集中，促销或渠道活动期间承保请求上升。稳定性治理不能只看平均值，而要关注峰值、长尾、批处理窗口和关键业务链路。&lt;/p&gt;
&lt;p&gt;早期团队常见问题是：监控很多但不知道看什么，告警很多但没人响应，日志很多但故障时搜不到关键字段，应急靠个人经验，复盘只写“加强监控”。这种状态下，即使个别工程师会用 GC 日志和 Arthas，也很难形成团队级稳定性能力。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;稳定性治理的核心是四个闭环：可观测、可告警、可处置、可复盘。&lt;/p&gt;
&lt;p&gt;可观测是指系统运行状态能够被看见。它包括指标、日志、链路追踪和事件记录。指标适合看趋势和阈值，比如堆使用率、Full GC 次数、接口 P99、错误率；日志适合还原具体请求和业务上下文；链路追踪适合定位跨服务耗时；事件记录适合关联发布、配置变更、定时任务、降级开关。&lt;/p&gt;
&lt;p&gt;可告警是指异常能在影响扩大前被发现。告警不是越多越好，而是要有分级、降噪和责任人。一个服务堆使用率 80% 不一定要半夜叫醒人，但连续 Full GC、核心接口错误率升高、线程池队列打满、容器重启就必须及时处理。&lt;/p&gt;
&lt;p&gt;可处置是指故障发生后有预案。比如 Full GC 频繁时先摘除异常实例、停止大导出任务、降低批处理并发、必要时扩容或回滚；数据库慢时切换降级策略；消息堆积时扩消费者或限流入口。没有预案的告警，只会制造焦虑。&lt;/p&gt;
&lt;p&gt;可复盘是指故障结束后沉淀改进。复盘不是找人背锅，而是明确触发条件、影响范围、发现时间、响应时间、根因、止血动作、永久修复和预防措施。稳定性能力就是靠一次次复盘长出来的。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;指标体系可以分成五层。&lt;/p&gt;
&lt;p&gt;第一层是基础资源指标：CPU、Load、内存、磁盘、网络、容器重启、进程 RSS。它们回答“机器和容器是否健康”。&lt;/p&gt;
&lt;p&gt;第二层是 JVM 指标：堆使用率、老年代使用率、年轻代使用率、Full GC 次数和耗时、Young GC 次数和耗时、线程数、类加载数、直接内存、元空间。它们回答“JVM 是否稳定”。&lt;/p&gt;
&lt;p&gt;第三层是应用指标：接口 QPS、P95/P99 RT、错误率、超时率、线程池活跃数、队列长度、拒绝次数、连接池使用率、本地缓存大小、导出任务数量。它们回答“应用是否还能正常服务”。&lt;/p&gt;
&lt;p&gt;第四层是依赖指标：数据库慢 SQL、连接池等待、Redis 延迟、MQ 堆积、文件服务耗时、外部接口成功率。它们回答“问题是不是来自下游或依赖”。&lt;/p&gt;
&lt;p&gt;第五层是业务指标：出单成功率、理赔提交成功率、保全处理量、支付成功率、导出任务失败率、批处理完成时间。它们回答“技术异常对业务造成了什么影响”。&lt;/p&gt;
&lt;p&gt;告警设计要围绕症状和影响，而不是只围绕资源。比如“老年代使用率超过 85%”可以作为预警，但真正需要高优先级的是“10 分钟内 Full GC 超过 3 次且接口 P99 超过阈值”或“核心承保接口错误率超过 1%”。告警要带上服务名、实例、环境、时间窗口、当前值、阈值、最近发布、相关日志链接和应急手册链接。&lt;/p&gt;</description></item><item><title>大Excel导出OOM的原因、排查与优化</title><link>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/%E5%A4%A7excel%E5%AF%BC%E5%87%BAoom%E7%9A%84%E5%8E%9F%E5%9B%A0%E6%8E%92%E6%9F%A5%E4%B8%8E%E4%BC%98%E5%8C%96/</link><pubDate>Wed, 05 Feb 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/%E5%A4%A7excel%E5%AF%BC%E5%87%BAoom%E7%9A%84%E5%8E%9F%E5%9B%A0%E6%8E%92%E6%9F%A5%E4%B8%8E%E4%BC%98%E5%8C%96/</guid><description>&lt;h1 id="大excel导出oom的原因排查与优化"&gt;大Excel导出OOM的原因、排查与优化&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;简历中关于大 Excel 导出的经历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;针对运营后台大数据量 Excel 导出导致 OOM、Full GC 频繁和接口超时的问题，完成导出链路重构：同步导出改异步任务，数据库分页读取，流式写出文件，限制导出行数和并发数，显著降低堆内存峰值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这是非常适合 Java 面试展开的项目，因为它同时涉及 JVM、数据库、文件 IO、线程池、业务体验和稳定性治理。面试官可能追问：为什么 Excel 导出会 OOM？POI 的 &lt;code&gt;XSSFWorkbook&lt;/code&gt; 和 &lt;code&gt;SXSSFWorkbook&lt;/code&gt; 有什么区别？分页查询是不是一定安全？为什么 &lt;code&gt;response.getOutputStream()&lt;/code&gt; 直接写也可能内存高？怎么设计异步导出？怎么防止多个大导出同时把服务打垮？&lt;/p&gt;
&lt;p&gt;这篇文章围绕保险运营后台最常见的大报表导出场景，讲清楚原因、证据、改造和面试表达。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险公司运营后台有大量导出诉求。承保部门要导出保单清单，理赔部门要导出赔案明细，财务部门要导出保费和佣金对账，客服部门要导出客户回访名单，渠道部门要导出代理人业绩。数据量经常是几万、几十万甚至上百万行，字段也很宽，包含保单号、客户信息、产品信息、渠道信息、缴费计划、状态、时间、金额等。&lt;/p&gt;
&lt;p&gt;早期系统为了开发方便，常见写法是：接口接收查询条件，一次性从数据库查出所有数据；把 Entity 转成 DTO；再把 DTO 转成 Excel VO；使用 Apache POI 的 &lt;code&gt;XSSFWorkbook&lt;/code&gt; 创建 Workbook、Sheet、Row、Cell；最后写到 HTTP 响应。这个流程在几千行时没问题，一旦数据量变大，就会出现堆内存飙升、Full GC 频繁、接口超时甚至 OOM。&lt;/p&gt;
&lt;p&gt;大 Excel 导出还有一个麻烦点：它往往由业务人员手动触发，时间不可控。如果多个运营人员同时导出，或者定时任务也在跑，服务实例会瞬间承受很高的内存和数据库压力。由于导出接口通常部署在和核心后台同一个应用里，它还可能影响正常查询、审核、核保等功能。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;Excel 导出 OOM 的根因通常不是“Excel 文件本身太大”这么简单，而是导出过程中同时存在多份数据和大量中间对象。&lt;/p&gt;
&lt;p&gt;第一份是数据库查询结果。如果一次性 &lt;code&gt;selectList&lt;/code&gt; 60 万行，每行几十个字段，Java 堆里会有大量 Entity 对象、字符串、BigDecimal、Date、集合节点。&lt;/p&gt;
&lt;p&gt;第二份是转换结果。很多系统会把 Entity 转成 DTO，再转成 VO，再转成用于 Excel 的数组或 Map。每一层转换都会创建新对象，字段里的字符串可能还会拼接、格式化、字典翻译。&lt;/p&gt;</description></item><item><title>堆Dump分析实战：如何定位内存泄漏</title><link>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/%E5%A0%86dump%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98%E5%A6%82%E4%BD%95%E5%AE%9A%E4%BD%8D%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</link><pubDate>Tue, 04 Feb 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/%E5%A0%86dump%E5%88%86%E6%9E%90%E5%AE%9E%E6%88%98%E5%A6%82%E4%BD%95%E5%AE%9A%E4%BD%8D%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</guid><description>&lt;h1 id="堆dump分析实战如何定位内存泄漏"&gt;堆Dump分析实战：如何定位内存泄漏&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;简历中关于堆 Dump 和内存泄漏的表述，通常可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参与核心保险业务系统线上内存泄漏排查，通过堆 Dump、MAT、Arthas heapdump、GC 日志和业务链路分析定位静态缓存、ThreadLocal、导出任务上下文等对象无法释放的问题，完成缓存治理和对象生命周期优化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官看到这段，很容易追问：你怎么抓 Dump？线上抓 Dump 会不会影响服务？MAT 里 Dominator Tree、Histogram、Retained Size、Shallow Size 分别怎么看？怎么从 GC Roots 引用链证明是泄漏？怎么区分内存泄漏和内存峰值？如果 Dump 很大打不开怎么办？&lt;/p&gt;
&lt;p&gt;这篇文章的重点是把“会用 MAT”升级为“会用 Dump 证明问题”。资深候选人不能只说“某对象很多”，还要能解释这些对象为什么活着、由谁引用、业务上为什么不该活、怎么改造，以及改造后如何验证。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险业务系统里，内存泄漏很少是“传统 C 语言意义上的忘记释放内存”，更多是对象已经没有业务价值，但仍然被 Java 引用链持有，GC 无法回收。比如运营后台做导出任务时，把每次导出的查询条件、用户信息、结果摘要放进一个静态 &lt;code&gt;Map&lt;/code&gt;，只新增不删除；批量核保任务为了复用上下文，把保单明细放进 &lt;code&gt;ThreadLocal&lt;/code&gt;，线程池复用后没有 &lt;code&gt;remove&lt;/code&gt;；权限系统把用户机构树缓存在本地 &lt;code&gt;ConcurrentHashMap&lt;/code&gt;，离职用户、历史渠道、临时权限长期不清理。&lt;/p&gt;
&lt;p&gt;这类问题的线上表现通常不是立刻 OOM，而是慢慢变坏。第一天 Full GC 后老年代还能降到 40%，第二天只能降到 55%，第三天降到 70%，到了高峰期再来一个大导出就直接 OOM。应用重启后恢复，运行一段时间又复现。这个特征非常适合用 GC 日志发现趋势，用堆 Dump 找证据。&lt;/p&gt;
&lt;p&gt;真实项目里，泄漏排查往往发生在压力很大的场景：服务已经抖动，业务方催促恢复，Dump 文件很大，机器磁盘紧张，线上又不能随便停服务。因此排查前要有清晰流程，先止血，再留证，再分析，再改造。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;Java 的内存泄漏本质是“无用对象仍然可达”。GC 判断对象是否可回收，不关心业务是否还需要它，只关心从 GC Roots 出发能不能到达它。GC Roots 包括线程栈中的局部变量、静态字段、JNI 引用、正在运行的线程、类加载器等。&lt;/p&gt;
&lt;p&gt;堆 Dump 是某一时刻堆内对象的快照。它能告诉我们：有哪些对象、各有多少、占用多少内存、对象之间如何引用、哪些对象因为某条引用链而无法回收。但 Dump 不是时间序列，它只是一张照片，所以最好结合 GC 日志、监控和多个时间点 Dump 一起判断。&lt;/p&gt;</description></item><item><title>Arthas在线诊断实战：trace、watch、jad、thread、dashboard</title><link>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/arthas%E5%9C%A8%E7%BA%BF%E8%AF%8A%E6%96%AD%E5%AE%9E%E6%88%98tracewatchjadthreaddashboard/</link><pubDate>Mon, 03 Feb 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/arthas%E5%9C%A8%E7%BA%BF%E8%AF%8A%E6%96%AD%E5%AE%9E%E6%88%98tracewatchjadthreaddashboard/</guid><description>&lt;h1 id="arthas在线诊断实战tracewatchjadthreaddashboard"&gt;Arthas在线诊断实战：trace、watch、jad、thread、dashboard&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;简历中和 Arthas 相关的经历可以写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;熟练使用 Arthas 对生产 Java 服务进行在线诊断，结合 dashboard、thread、trace、watch、jad、heapdump 等命令定位接口耗时、线程阻塞、参数异常、代码版本不一致和内存问题，支撑核心保险业务系统线上故障快速恢复。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;面试官看到这段会非常容易追问细节：&lt;code&gt;trace&lt;/code&gt; 和 &lt;code&gt;watch&lt;/code&gt; 有什么区别？线上执行 &lt;code&gt;watch&lt;/code&gt; 会不会有风险？&lt;code&gt;jad&lt;/code&gt; 反编译解决过什么问题？&lt;code&gt;thread -n 5&lt;/code&gt; 看到 CPU 高线程后怎么继续分析？&lt;code&gt;dashboard&lt;/code&gt; 能看出哪些 JVM 风险？Arthas 是怎么 attach 到进程的？你有没有因为表达式写得太重影响线上？&lt;/p&gt;
&lt;p&gt;这篇文章不把 Arthas 当命令手册，而是放到保险业务系统的线上诊断场景里讲。重点是：什么时候用哪个命令、看到结果后如何判断、如何控制风险、如何把观察结果转成修复方案。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险系统的线上问题经常发生在“不能重启、不能加日志、不能马上发版”的时候。比如承保接口突然变慢，但日志只打印了入口和异常，没有打印中间耗时；理赔查询返回数据为空，但测试环境无法复现；大 Excel 导出偶发 OOM，但不知道一次到底查了多少数据；某个定时任务凌晨跑满 CPU，但代码路径很深；新版本发布后怀疑某个实例没有更新到正确代码。&lt;/p&gt;
&lt;p&gt;传统方式通常是补日志、发版、等复现。但生产问题等不起，尤其是核心交易链路，一次发版也有流程成本和风险。Arthas 的价值就在于在线观察 JVM 运行状态、方法调用、参数返回、线程栈和实际加载的字节码，不重启服务也能补齐很多证据。&lt;/p&gt;
&lt;p&gt;一个典型场景是保单导出接口变慢。业务方说“导出卡住”，数据库同事说慢 SQL 不明显，应用日志只看到接口总耗时 60 秒。此时可以用 &lt;code&gt;trace&lt;/code&gt; 看耗时到底在查询、对象转换、字典补全、Excel 写出还是文件上传；用 &lt;code&gt;watch&lt;/code&gt; 看入参和返回值大小；用 &lt;code&gt;thread&lt;/code&gt; 看是否有线程阻塞；用 &lt;code&gt;dashboard&lt;/code&gt; 看 GC 和线程总体状态。如果怀疑线上代码与仓库不一致，可以用 &lt;code&gt;jad&lt;/code&gt; 反编译实际运行的类。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;Arthas 通过 Java Attach 机制连接到目标 JVM，并利用字节码增强技术在运行时观察方法调用。它不是简单读日志，而是在目标方法执行前后插入观测逻辑，所以功能强大，也必须控制使用范围。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dashboard&lt;/code&gt; 是全局体检命令，可以快速看到线程、内存、GC、运行时间、系统负载等信息。它适合故障初期判断应用是否处于 GC 压力、线程暴涨、CPU 高或内存接近上限。&lt;/p&gt;</description></item><item><title>GC日志如何看：从FullGC频繁到问题定位</title><link>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/gc%E6%97%A5%E5%BF%97%E5%A6%82%E4%BD%95%E7%9C%8B%E4%BB%8Efullgc%E9%A2%91%E7%B9%81%E5%88%B0%E9%97%AE%E9%A2%98%E5%AE%9A%E4%BD%8D/</link><pubDate>Sun, 02 Feb 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/gc%E6%97%A5%E5%BF%97%E5%A6%82%E4%BD%95%E7%9C%8B%E4%BB%8Efullgc%E9%A2%91%E7%B9%81%E5%88%B0%E9%97%AE%E9%A2%98%E5%AE%9A%E4%BD%8D/</guid><description>&lt;h1 id="gc日志如何看从fullgc频繁到问题定位"&gt;GC日志如何看：从FullGC频繁到问题定位&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;简历里关于 JVM 调优的描述，经常会写成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;负责核心保险业务系统 JVM 性能调优，结合 GC 日志、监控指标和业务链路定位 Full GC 频繁、接口抖动、批量任务高峰内存飙升等问题，优化堆参数、导出链路和批处理并发策略，降低停顿时间并提升系统稳定性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这句话在面试里一定会被追问。面试官不会满足于“我看过 GC 日志”，而会继续问：你怎么看？看哪些字段？Full GC 频繁一定是内存泄漏吗？G1 的 Young GC、Mixed GC、Full GC 分别代表什么？你怎么从日志推断是大对象、晋升失败、老年代泄漏还是批量任务峰值？参数是怎么调的，调完怎么验证？&lt;/p&gt;
&lt;p&gt;这篇文章的目标，是把 GC 日志从“看起来很吓人的一串文本”拆成可复盘、可讲述、可落地的诊断链路。面向资深 Java 面试，不只背概念，而是能把一次线上 Full GC 频繁问题讲成完整项目经验。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险业务系统的 JVM 压力往往不是来自持续高 QPS，而是来自低频但重型的业务操作。比如承保系统在月末集中处理批量核保，理赔系统批量生成赔付清单，运营后台导出几十万行保单数据，财务系统凌晨跑对账和佣金计算。这些任务会在短时间内创建大量 DTO、List、Map、字符串、Excel 单元格对象和 JSON 中间对象。&lt;/p&gt;
&lt;p&gt;线上表现通常是这样的：接口响应时间突然从几百毫秒升到数秒；网关开始出现 502、504；业务日志中大量请求超时；机器 CPU 看起来很高，但业务线程并没有明显计算热点；监控里 Full GC 次数突然变多，单次停顿从几百毫秒到十几秒不等。此时如果只看数据库慢 SQL，可能会误判为数据库问题；如果只把堆调大，可能只是推迟下一次故障。&lt;/p&gt;
&lt;p&gt;一个典型案例是运营人员在工作日上午发起“保单全量导出”。接口一次性查出 60 万条保单，每条保单又关联客户、险种、渠道、缴费计划等信息。应用堆设置为 &lt;code&gt;-Xms4g -Xmx4g&lt;/code&gt;，使用 G1。导出开始后年轻代快速被填满，大量对象还没来得及死亡就晋升到老年代，老年代空间被导出对象、缓存对象和日志字符串挤占。几分钟内出现连续 Full GC，每次 Full GC 后老年代只回落一点点，接口进入长时间停顿。&lt;/p&gt;
&lt;p&gt;GC 日志在这个场景里的作用，是把“系统卡了”转化为可验证的问题：什么时候开始 GC 频繁，什么类型的 GC，停顿多久，GC 前后堆变化如何，老年代是否回落，是否有 Humongous 对象，是否出现 To-space exhausted 或 evacuation failure。&lt;/p&gt;</description></item><item><title>JVM内存模型与保险业务系统常见内存问题</title><link>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E4%B8%8E%E4%BF%9D%E9%99%A9%E4%B8%9A%E5%8A%A1%E7%B3%BB%E7%BB%9F%E5%B8%B8%E8%A7%81%E5%86%85%E5%AD%98%E9%97%AE%E9%A2%98/</link><pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/02-jvm-diagnostics/jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E4%B8%8E%E4%BF%9D%E9%99%A9%E4%B8%9A%E5%8A%A1%E7%B3%BB%E7%BB%9F%E5%B8%B8%E8%A7%81%E5%86%85%E5%AD%98%E9%97%AE%E9%A2%98/</guid><description>&lt;h1 id="jvm内存模型与保险业务系统常见内存问题"&gt;JVM内存模型与保险业务系统常见内存问题&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在简历中，和本文对应的经历通常会写成类似下面的表述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;负责核心保险业务系统 JVM 性能调优与线上问题排查，结合 GC 日志、堆 Dump、Arthas、监控告警定位内存泄漏、Full GC 频繁、大 Excel 导出 OOM 等问题；通过参数优化、代码改造、批处理拆分、对象生命周期治理等手段，降低 Full GC 频率，提升系统稳定性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段话看起来只有几行，但面试官往往会继续追问：你调过哪些参数？怎么看 GC 日志？Dump 怎么分析？Arthas 具体用过哪些命令？大 Excel 导出为什么会 OOM？你是怎么证明不是数据库慢，而是 JVM 内存问题？Full GC 降频之后有什么指标变化？这些问题不是背几个 JVM 名词就能回答的，需要把业务场景、内存结构、定位链路和项目落地串起来。&lt;/p&gt;
&lt;p&gt;保险系统的特点是业务对象复杂、批量数据多、历史数据时间跨度长、报表导出需求重、定时任务集中、峰值访问明显。比如承保系统要处理投保单、保单、险种、责任、客户、缴费计划；理赔系统要处理报案、立案、查勘、定损、赔付、影像材料；保全系统要处理批改、退保、受益人变更；运营后台还会有各种 Excel 导出、对账文件生成、续期提醒、批量短信、批量核保任务。对象层级深、一次查询返回数据多、缓存和集合使用频繁，这些都会放大 JVM 内存问题。&lt;/p&gt;
&lt;p&gt;所以这篇文章的目标不是孤立讲 JVM 内存模型，而是回答一个更贴近项目的问题：在保险业务系统里，常见内存问题是怎么产生的，如何定位，如何改造，如何在面试中讲得可信。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险业务系统通常是典型的 Java 后端系统，技术栈可能包括 Spring Boot、Spring Cloud、MyBatis、Redis、消息队列、定时任务调度、分布式文件服务、报表服务等。线上部署一般是多实例集群，单个 JVM 设置固定堆大小，例如 &lt;code&gt;-Xms4g -Xmx4g&lt;/code&gt; 或 &lt;code&gt;-Xms8g -Xmx8g&lt;/code&gt;，根据服务类型配置 G1、CMS 或其他收集器。&lt;/p&gt;
&lt;p&gt;系统日常流量并不一定很高，但内存风险经常来自“低频重操作”。例如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;运营人员导出几十万行保单清单，接口把所有数据一次性查到内存，再用 Apache POI 的 &lt;code&gt;XSSFWorkbook&lt;/code&gt; 生成 Excel，瞬间占满堆。&lt;/li&gt;
&lt;li&gt;理赔影像或附件元数据查询时，把大字段、Base64 内容、历史轨迹一次性组装到 DTO，导致单次请求对象非常大。&lt;/li&gt;
&lt;li&gt;批量续保、批量对账、批量佣金计算任务在凌晨集中执行，多个任务同时拉取大批数据，年轻代快速晋升到老年代，引发频繁 Full GC。&lt;/li&gt;
&lt;li&gt;为了提升查询性能，开发人员使用本地 &lt;code&gt;Map&lt;/code&gt; 缓存产品、机构、渠道或用户权限数据，但没有过期策略，随着业务运行不断膨胀。&lt;/li&gt;
&lt;li&gt;线程池、异步任务、ThreadLocal、静态集合、监听器等释放不当，导致对象被长期引用，GC 无法回收。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;保险业务还有一个特殊点：数据结构天然“宽而深”。一个保单对象可能关联投保人、被保人、受益人、险种、责任、批单、缴费计划、核保结论、影像材料、销售人员、渠道信息等。如果在代码中无节制地使用全量对象转换、深拷贝、JSON 序列化、日志打印，就会让短时间内创建的对象数量非常惊人。很多问题表面是“接口慢”或“机器 CPU 高”，本质可能是内存分配过快和 GC 压力过大。&lt;/p&gt;</description></item><item><title>月底结算与营销活动高峰的稳定性设计复盘</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E6%9C%88%E5%BA%95%E7%BB%93%E7%AE%97%E4%B8%8E%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8%E9%AB%98%E5%B3%B0%E7%9A%84%E7%A8%B3%E5%AE%9A%E6%80%A7%E8%AE%BE%E8%AE%A1%E5%A4%8D%E7%9B%98/</link><pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E6%9C%88%E5%BA%95%E7%BB%93%E7%AE%97%E4%B8%8E%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8%E9%AB%98%E5%B3%B0%E7%9A%84%E7%A8%B3%E5%AE%9A%E6%80%A7%E8%AE%BE%E8%AE%A1%E5%A4%8D%E7%9B%98/</guid><description>&lt;h1 id="月底结算与营销活动高峰的稳定性设计复盘"&gt;月底结算与营销活动高峰的稳定性设计复盘&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售系统中，参与月底佣金结算、营销活动高峰、商机流转和保单状态回写等核心链路建设。针对月底批处理任务与在线活动流量叠加导致的线程池打满、数据库连接耗尽、MQ 积压、接口超时等问题，设计线程池隔离、MQ 削峰、限流降级、批量任务分片、幂等补偿和监控告警方案，提升系统整体稳定性。&lt;/p&gt;
&lt;p&gt;这段简历适合做一次完整复盘。资深面试官会希望听到的不只是“我用了哪些技术”，而是问题发生前的架构状态、故障表现、根因分析、改造方案、验证方式和最终收益。复盘型回答最能体现工程判断力。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;月底结算和营销活动高峰在保险业务里经常撞在一起。月底要计算代理人佣金、活动奖励、渠道费用、机构业绩，还要生成对账明细和报表。营销活动则会带来投保提交、客户咨询、商机分配、代理人跟进、短信通知等在线流量。一个偏批处理，一个偏在线交易，但它们可能共享同一套应用、数据库、线程池和 MQ 集群。&lt;/p&gt;
&lt;p&gt;早期系统为了快速交付，很多异步任务共用一个默认线程池，数据库连接池也没有按业务隔离。结算任务在月底一次性扫描大量订单，按批次提交任务；活动高峰时，投保提交也会触发商机流转和通知。两个高峰叠加后，低优先级任务可能占住高优先级资源。&lt;/p&gt;
&lt;p&gt;典型现象是：活动提交接口 RT 从几百毫秒涨到数秒；商机分配延迟增加；MQ 消费积压；数据库连接池等待明显；结算任务本身也变慢；运维看到 CPU 不一定满，但线程大量阻塞在数据库连接、外部 RPC 或队列等待上。这类问题的难点在于它不是单个 bug，而是容量、隔离和任务模型共同失衡。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;稳定性复盘要先区分在线链路和离线链路。在线链路关注低延迟和用户体验，典型请求包括活动页查询、投保提交、商机查询。离线链路关注吞吐和最终完成，典型任务包括佣金结算、对账报表、批量同步。两类链路对资源的使用方式不同，不能完全共用资源。&lt;/p&gt;
&lt;p&gt;月底结算的风险来自批量放大。单条结算明细计算可能很快，但几十万条订单一起处理，会放大数据库扫描、锁冲突、批量写入、缓存访问和 MQ 投递。结算任务如果没有分片、限速和状态管理，就会把压力集中打到数据库。&lt;/p&gt;
&lt;p&gt;营销活动的风险来自脉冲流量。活动开始后的几分钟内，入口请求可能远高于平时。在线链路如果同步执行后置动作，会把短信、CRM、保司等弱依赖的抖动传递给用户接口。&lt;/p&gt;
&lt;p&gt;稳定性设计的核心是四个词：分层、隔离、削峰、补偿。分层是把核心请求和后置任务拆开；隔离是不同优先级任务不用同一份线程和队列；削峰是用 MQ 和限流把瞬时压力变平；补偿是允许部分动作延迟或失败，但最终可恢复。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;第一，入口层做限流和核心链路瘦身。活动提交接口只做资格校验、幂等校验、投保意向落库和事件写入，不在请求内同步做短信、CRM、报表刷新。超过承载能力时，入口快速返回繁忙提示，避免请求堆在线程里。&lt;/p&gt;
&lt;p&gt;第二，异步层按业务拆 topic、消费组和线程池。商机流转、通知、CRM 同步、结算明细生成、运营埋点分别使用独立消费组和线程池。商机流转优先级最高，通知和埋点可以延迟，结算任务限制并发并尽量放在低峰窗口。&lt;/p&gt;
&lt;p&gt;第三，结算任务分片和限速。按月份、机构、产品、代理人或订单 ID 范围拆分结算批次。每个批次都有独立状态：待处理、处理中、成功、失败、超时。调度器不一次性提交全部任务，而是按窗口和并发阈值派发。写入结算明细时使用批量提交，但批次大小受数据库能力约束。&lt;/p&gt;
&lt;p&gt;第四，数据库访问优化。结算扫描使用覆盖索引和游标分页，避免深分页和大事务。结算结果写入采用幂等键，例如 &lt;code&gt;settleMonth + policyNo + agentId + rewardType&lt;/code&gt;。在线活动表和结算明细表避免在同一事务中互相等待。必要时读写分离或结算库分离。&lt;/p&gt;
&lt;p&gt;第五，外部依赖治理。保司、CRM、短信通道都设置独立超时、重试和熔断。在线链路中外部依赖失败时进入异步补偿，不让用户接口长时间等待。结算中外部依赖尽量提前拉取快照，减少结算过程中实时调用。&lt;/p&gt;
&lt;p&gt;第六，监控告警从技术指标扩展到业务指标。除了 CPU、内存、线程池、连接池、MQ 积压，还要监控投保提交成功率、商机分配延迟、结算批次完成率、失败批次数、状态超时数量、补偿成功率。稳定性不是机器没挂，而是核心业务按预期完成。&lt;/p&gt;
&lt;h2 id="关键伪代码或流程"&gt;关键伪代码或流程&lt;/h2&gt;
&lt;p&gt;结算批次创建：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;createSettlementTask&lt;/span&gt;(String month) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;ShardKey&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; shards &lt;span style="color:#f92672"&gt;=&lt;/span&gt; shardService.&lt;span style="color:#a6e22e"&gt;splitByOrgAndAgent&lt;/span&gt;(month);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (ShardKey shard : shards) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; settlementTaskRepository.&lt;span style="color:#a6e22e"&gt;insertIgnore&lt;/span&gt;(SettlementTask.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;taskKey&lt;/span&gt;(month &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; shard.&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;month&lt;/span&gt;(month)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;shard&lt;/span&gt;(shard)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;INIT&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;任务抢占：&lt;/p&gt;</description></item><item><title>JUC核心工具详解：CountDownLatch、Semaphore、CompletableFuture、BlockingQueue</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/juc%E6%A0%B8%E5%BF%83%E5%B7%A5%E5%85%B7%E8%AF%A6%E8%A7%A3countdownlatchsemaphorecompletablefutureblockingqueue/</link><pubDate>Mon, 06 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/juc%E6%A0%B8%E5%BF%83%E5%B7%A5%E5%85%B7%E8%AF%A6%E8%A7%A3countdownlatchsemaphorecompletablefutureblockingqueue/</guid><description>&lt;h1 id="juc核心工具详解countdownlatchsemaphorecompletablefutureblockingqueue"&gt;JUC核心工具详解：CountDownLatch、Semaphore、CompletableFuture、BlockingQueue&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售活动、商机流转、月底结算等高并发链路中，基于 JUC 工具完成批量任务编排、外部接口并发控制、异步结果聚合和任务队列治理。熟悉 &lt;code&gt;CountDownLatch&lt;/code&gt;、&lt;code&gt;Semaphore&lt;/code&gt;、&lt;code&gt;CompletableFuture&lt;/code&gt;、&lt;code&gt;BlockingQueue&lt;/code&gt; 等并发工具的底层语义和适用边界，能够结合 RocketMQ、线程池、限流熔断、幂等补偿设计稳定的异步处理方案。&lt;/p&gt;
&lt;p&gt;这段简历表面是在说“会用 JUC”，但资深面试官通常会继续追问：为什么这里用 &lt;code&gt;Semaphore&lt;/code&gt; 而不是调大线程池？&lt;code&gt;CompletableFuture.allOf&lt;/code&gt; 异常怎么处理？&lt;code&gt;BlockingQueue&lt;/code&gt; 容量怎么估？&lt;code&gt;CountDownLatch&lt;/code&gt; 卡死怎么排查？这些问题的本质不是 API 记忆，而是你是否理解并发工具背后的资源约束、失败传播和业务一致性。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险销售系统里经常出现“一个请求触发多类子任务”的场景。比如代理人进入客户详情页，系统需要并行查询客户画像、历史保单、活动资格、产品推荐、商机跟进记录和权益信息。如果全部串行调用，接口延迟会被所有下游耗时累加；如果无脑并行，又可能把数据库连接池、保司接口或画像服务打满。&lt;/p&gt;
&lt;p&gt;商机流转也类似。投保意向创建后，需要完成商机分配、代理人通知、客户标签刷新、运营埋点、待办生成、活动库存预占等动作。不同动作优先级不同，有的必须成功，有的可以延迟，有的失败后要重试，有的可以降级。并发工具的价值，是把这些动作从“代码顺序调用”升级成“有边界、有预算、有补偿的并发编排”。&lt;/p&gt;
&lt;p&gt;月底结算的压力更典型。结算任务可能按机构、产品、代理人、月份拆成大量批次。每个批次要读取订单、计算佣金、写结算明细、生成对账记录。这里既需要并发提升吞吐，又要避免同一机构或同一代理人的数据被重复计算，还要在某个批次失败时支持重跑。JUC 工具用得好，可以把复杂批处理做得清晰；用得不好，会留下卡死、线程泄漏、任务丢失和结果不一致。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;CountDownLatch&lt;/code&gt; 是一次性倒计时门闩。它的核心语义是：一个或多个线程等待其他线程完成指定数量的事件。它适合“主线程等待多个子任务完成后再汇总”的场景，例如结算任务拆成 10 个分片，全部处理结束后更新总任务状态。它不能重置，计数归零后就永久打开。如果需要反复使用，应考虑 &lt;code&gt;CyclicBarrier&lt;/code&gt; 或重新创建对象。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Semaphore&lt;/code&gt; 是信号量，本质是许可证池。线程执行某段逻辑前先获取许可证，执行完释放许可证。它经常用于限制外部依赖并发，例如保司接口最多允许本系统同时 20 个请求，或者某个导出任务最多同时跑 3 个。它和线程池不同：线程池限制的是任务执行线程数量，&lt;code&gt;Semaphore&lt;/code&gt; 限制的是某个临界资源的并发访问量。一个线程池内可能访问多个外部资源，每个资源应该有自己的并发预算。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CompletableFuture&lt;/code&gt; 是异步编排工具。它既能提交异步任务，也能表达任务之间的依赖关系，比如并行查询后聚合、前一步成功后继续、失败后兜底、任一任务完成后返回。它比裸 &lt;code&gt;Future&lt;/code&gt; 更适合业务编排，但也更容易埋坑：默认线程池是 &lt;code&gt;ForkJoinPool.commonPool&lt;/code&gt;，异常会被包在 &lt;code&gt;CompletionException&lt;/code&gt; 里，&lt;code&gt;allOf&lt;/code&gt; 只告诉你整体完成，不直接返回每个子任务结果。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;BlockingQueue&lt;/code&gt; 是阻塞队列，是生产者消费者模型的核心组件。线程池的任务队列、异步日志队列、批量写入缓冲都依赖它。常见实现包括 &lt;code&gt;ArrayBlockingQueue&lt;/code&gt;、&lt;code&gt;LinkedBlockingQueue&lt;/code&gt;、&lt;code&gt;PriorityBlockingQueue&lt;/code&gt;、&lt;code&gt;DelayQueue&lt;/code&gt;、&lt;code&gt;SynchronousQueue&lt;/code&gt;。面试时要能说清楚：有界还是无界、公平还是非公平、是否保持优先级、是否支持延迟、入队出队是否可能阻塞，以及阻塞后如何响应中断。&lt;/p&gt;
&lt;p&gt;这四类工具的共同点是：它们都不直接解决业务问题，而是提供并发控制原语。真正的工程能力体现在你能把它们和业务状态机、超时控制、监控告警、失败补偿结合起来。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;在活动资格聚合接口中，可以用 &lt;code&gt;CompletableFuture&lt;/code&gt; 并行查询多个下游。同步链路只保留用户最关心的信息：活动资格、产品可售状态、保费试算结果。客户画像、推荐标签、营销权益可以设置更短超时，失败后返回默认值，避免弱依赖拖慢主链路。&lt;/p&gt;
&lt;p&gt;在保司接口调用中，可以用 &lt;code&gt;Semaphore&lt;/code&gt; 控制并发。即使业务线程池有 64 个线程，也不代表保司接口能承载 64 个并发。对外部系统要按 SLA 设置独立许可证数，并配合 &lt;code&gt;tryAcquire(timeout)&lt;/code&gt;。拿不到许可证时快速降级或进入稍后重试，而不是无限等待。&lt;/p&gt;
&lt;p&gt;在月底结算中，可以用 &lt;code&gt;CountDownLatch&lt;/code&gt; 等待分片任务结束，但要注意不能让主调度线程永久等待。每个分片任务必须在 &lt;code&gt;finally&lt;/code&gt; 中 &lt;code&gt;countDown&lt;/code&gt;，并且总等待要带超时。更稳妥的方式是任务状态落库，调度线程只负责派发，完成状态由分片任务异步更新；&lt;code&gt;CountDownLatch&lt;/code&gt; 适合单进程内短生命周期编排，不适合跨机器、长时间批处理的最终一致性。&lt;/p&gt;
&lt;p&gt;在商机流转消费者中，可以用 &lt;code&gt;BlockingQueue&lt;/code&gt; 做本地缓冲，但必须有容量和拒绝策略。高峰期 MQ 消费速度大于本地处理速度时，如果本地队列无界，堆内存会被任务对象慢慢撑满。更合理的做法是有界队列配合 MQ 重试：队列满时返回稍后消费，让压力留在 MQ，而不是压在 JVM 堆里。&lt;/p&gt;</description></item><item><title>高并发场景下的限流、降级、熔断与隔离</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E9%AB%98%E5%B9%B6%E5%8F%91%E5%9C%BA%E6%99%AF%E4%B8%8B%E7%9A%84%E9%99%90%E6%B5%81%E9%99%8D%E7%BA%A7%E7%86%94%E6%96%AD%E4%B8%8E%E9%9A%94%E7%A6%BB/</link><pubDate>Sun, 05 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E9%AB%98%E5%B9%B6%E5%8F%91%E5%9C%BA%E6%99%AF%E4%B8%8B%E7%9A%84%E9%99%90%E6%B5%81%E9%99%8D%E7%BA%A7%E7%86%94%E6%96%AD%E4%B8%8E%E9%9A%94%E7%A6%BB/</guid><description>&lt;h1 id="高并发场景下的限流降级熔断与隔离"&gt;高并发场景下的限流、降级、熔断与隔离&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售活动和月底结算高峰场景中，负责构建稳定性治理方案，包括 Sentinel 限流、接口降级、下游熔断、线程池隔离、MQ 削峰和核心链路监控告警。针对活动秒级流量突增、保司接口抖动、CRM 同步慢、结算任务集中触发等问题，保障核心投保和商机流转链路在资源有限的情况下稳定运行。&lt;/p&gt;
&lt;p&gt;这段简历的关键在“治理”而不是“组件”。面试官会追问：限流保护谁？降级牺牲什么？熔断依据是什么？隔离隔的是什么资源？如果只能背 Sentinel 规则，很难通过资深面试。优秀回答必须把业务优先级、资源预算、失败策略和监控闭环讲清楚。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险销售活动高峰具备突发性。活动开始、产品停售、月底冲刺、代理人集中触达都会让投保提交、保费试算、商机查询、客户画像接口瞬间升高。系统资源却是有限的：Tomcat 线程有限，业务线程池有限，数据库连接有限，保司接口也有限。&lt;/p&gt;
&lt;p&gt;如果没有治理，流量会按最自然的方式冲进系统。入口线程被占满、数据库连接池等待、下游 RPC 超时、MQ 消费堆积、重试流量反复放大。最后结果往往是所有接口一起变慢，而不是核心业务优先成功。&lt;/p&gt;
&lt;p&gt;稳定性治理的目标不是让系统处理无限流量，而是在超过承载能力时有控制地失败。核心投保、商机创建、支付或结算状态推进要优先保障；短信通知、运营埋点、推荐标签、报表刷新可以延迟或降级；外部系统慢时要及时切断影响；批处理任务不能挤占在线交易资源。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;限流是入口控制，解决“流量进来太多”的问题。常见维度包括 QPS、并发数、热点参数、用户维度、活动维度、渠道维度。限流的本质是把超过系统承载能力的请求挡在便宜的位置，避免它们进入数据库、RPC 和复杂业务逻辑。&lt;/p&gt;
&lt;p&gt;降级是功能取舍，解决“资源不够时先保什么”的问题。比如活动页可以不展示个性化推荐，客户画像可以返回基础标签，短信通知可以延迟发送。降级不是异常处理的别名，而是提前定义好的业务兜底方案。&lt;/p&gt;
&lt;p&gt;熔断是故障隔离，解决“下游持续失败或慢调用拖垮上游”的问题。当保司接口、CRM、客户画像服务出现高错误率或高慢调用比例时，上游短时间内不再持续请求它，而是快速失败、返回缓存或进入异步重试。熔断保护的是调用方线程和整体系统稳定性。&lt;/p&gt;
&lt;p&gt;隔离是资源切分，解决“不同业务互相拖垮”的问题。隔离可以是线程池隔离、连接池隔离、部署隔离、队列隔离、数据库表隔离。月底结算和在线投保不能共用同一批核心线程；短信通知不能占用商机分配线程池；外部保司调用要有独立并发预算。&lt;/p&gt;
&lt;p&gt;这四者经常组合出现：入口先限流，弱依赖可降级，下游异常时熔断，不同业务用资源隔离控制影响范围。再配合 MQ 削峰和监控告警，形成完整稳定性方案。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;活动提交流程中，入口先按活动 ID 和接口维度做限流。比如投保提交比活动页查询更核心，但提交接口成本更高，所以限流阈值要基于数据库写入能力和下游消息投递能力评估。被限流的请求返回明确提示，避免用户反复点击造成更大压力。&lt;/p&gt;
&lt;p&gt;活动页查询中，推荐标签、权益推荐、客户画像属于可降级能力。正常情况下并行查询并展示个性化内容；当画像服务超时或线程池排队过长时，返回默认推荐或基础产品信息。这样页面仍可打开，用户仍能完成核心投保路径。&lt;/p&gt;
&lt;p&gt;保司接口调用中，熔断非常关键。保司慢调用会占住业务线程，还可能导致重试叠加。可以按保司渠道配置慢调用比例和异常比例阈值。熔断打开后，在线请求不再同步等待保司，而是返回“已受理，稍后更新状态”，并通过 MQ 或补偿任务异步查询。&lt;/p&gt;
&lt;p&gt;月底结算中，隔离比限流更重要。结算任务数据量大、运行时间长、数据库写入重。如果和在线活动共用线程池、连接池或部署实例，很容易在月底拖慢用户链路。推荐单独线程池、单独消费组、独立调度窗口，必要时独立部署结算 worker。&lt;/p&gt;
&lt;p&gt;商机流转中，MQ 削峰和消费限速配合使用。生产端高峰消息先进入 MQ，消费端根据数据库和代理人分配服务承载能力设置消费线程数。消费端线程池满时返回稍后重试，而不是无限堆积本地队列。&lt;/p&gt;
&lt;h2 id="关键伪代码或流程"&gt;关键伪代码或流程&lt;/h2&gt;
&lt;p&gt;入口限流示意：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; SubmitResult &lt;span style="color:#a6e22e"&gt;submit&lt;/span&gt;(SubmitCommand command) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Entry entry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; entry &lt;span style="color:#f92672"&gt;=&lt;/span&gt; SphU.&lt;span style="color:#a6e22e"&gt;entry&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;activity-submit-&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; command.&lt;span style="color:#a6e22e"&gt;activityId&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; submitService.&lt;span style="color:#a6e22e"&gt;doSubmit&lt;/span&gt;(command);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (BlockException ex) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; SubmitResult.&lt;span style="color:#a6e22e"&gt;busy&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;活动参与人数较多，请稍后重试&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (entry &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; entry.&lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;弱依赖降级：&lt;/p&gt;</description></item><item><title>MQ削峰填谷在商机流转系统中的落地</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/mq%E5%89%8A%E5%B3%B0%E5%A1%AB%E8%B0%B7%E5%9C%A8%E5%95%86%E6%9C%BA%E6%B5%81%E8%BD%AC%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E8%90%BD%E5%9C%B0/</link><pubDate>Sat, 04 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/mq%E5%89%8A%E5%B3%B0%E5%A1%AB%E8%B0%B7%E5%9C%A8%E5%95%86%E6%9C%BA%E6%B5%81%E8%BD%AC%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E8%90%BD%E5%9C%B0/</guid><description>&lt;h1 id="mq削峰填谷在商机流转系统中的落地"&gt;MQ削峰填谷在商机流转系统中的落地&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售活动系统中，针对活动高峰期投保意向集中创建、代理人批量跟进、保司状态回写和商机流转压力突增的问题，引入 RocketMQ 完成核心链路异步解耦与削峰填谷。负责消息模型设计、消费端线程池隔离、幂等处理、重试补偿、积压监控和故障恢复，保障商机在高并发下稳定流转。&lt;/p&gt;
&lt;p&gt;这段简历的重点不是“用了 MQ”，而是你能否讲清楚：为什么这里需要 MQ？削峰削的是哪个峰？填谷填到哪里？消息失败、重复、乱序、积压时怎么保证业务正确？面试官通常会沿着“可靠性、一致性、可观测性、容量规划”连续追问。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;商机流转是保险销售链路中的核心后置流程。用户在活动页提交投保意向后，系统要创建商机、分配代理人、生成跟进待办、推送企微或短信、刷新客户标签、同步 CRM、触发后续营销策略。活动高峰时，入口请求具有明显脉冲：一分钟内可能涌入大量投保意向，但代理人通知、标签刷新、CRM 同步并不需要全部在用户请求内完成。&lt;/p&gt;
&lt;p&gt;如果同步执行这些动作，用户提交接口会被下游耗时拖慢。短信通道慢、CRM 接口抖动、标签服务超时都会影响用户提交成功率。更严重的是，同步链路中每多一个外部依赖，就多一个高峰期雪崩点。入口线程被慢调用占满后，即使数据库和核心业务还健康，用户也会看到大量超时。&lt;/p&gt;
&lt;p&gt;MQ 的价值在于把瞬时高峰变成可控消费。用户请求内只完成必要校验和主单落库，然后发送商机流转消息。后续动作由消费者按自身能力慢慢处理。这样入口系统承接的是写主单和投递消息的压力，商机处理系统承接的是可调速的消费压力。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;MQ 削峰填谷的核心是生产速度和消费速度解耦。没有 MQ 时，生产者和消费者在同一个调用栈里，消费者慢会直接拖慢生产者。有 MQ 后，生产者把事件写入队列即可返回，消费者按照配置的线程数、批量大小和下游承载能力消费。&lt;/p&gt;
&lt;p&gt;削峰不是消灭流量，而是把峰值暂存在消息系统里。填谷是指在业务低峰时继续消费积压，把高峰期没处理完的任务逐步补齐。因此，设计时不能只看入口 QPS，还要看消息积压容量、可接受延迟、消费者吞吐和失败重试成本。&lt;/p&gt;
&lt;p&gt;消息系统带来的新问题是可靠性和一致性。投保意向主单落库成功但消息发送失败，商机就不会流转；消息发送成功但消费者处理失败，需要重试；消费者处理成功但提交 offset 失败，消息可能重复；同一客户多条消息并发消费，可能导致状态乱序。因此，业务必须天然接受“至少一次投递”，通过幂等和状态机保证重复消息不会产生错误结果。&lt;/p&gt;
&lt;p&gt;常见可靠性方案包括事务消息、本地消息表、消费幂等表、业务唯一索引、失败重试队列和人工补偿入口。对保险销售系统来说，消息可以延迟，但关键商机不能丢；通知可以重复风险低，但佣金、权益、分配结果不能重复。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;商机流转消息设计时，应避免把所有业务动作塞进一个大消息。更合理的是按领域事件拆分：&lt;code&gt;OpportunityCreatedEvent&lt;/code&gt; 表示投保意向创建；&lt;code&gt;OpportunityAssignedEvent&lt;/code&gt; 表示商机已分配；&lt;code&gt;FollowTaskCreatedEvent&lt;/code&gt; 表示跟进任务已生成；&lt;code&gt;InsurerStatusChangedEvent&lt;/code&gt; 表示保司状态变化。事件之间通过业务状态推进，而不是消费者互相调用形成隐式链路。&lt;/p&gt;
&lt;p&gt;生产端要保证主单和消息的一致性。可以使用 RocketMQ 事务消息：先发送半消息，再执行本地事务创建投保意向，最后提交或回滚消息。也可以使用本地消息表：在同一个数据库事务中写主单和待发送消息，由后台任务可靠投递。两种方式都可以，选择取决于团队基础设施和运维经验。&lt;/p&gt;
&lt;p&gt;消费端要按业务优先级拆分消费组和线程池。商机分配属于核心后置链路，应使用独立消费组和线程池；短信通知、运营埋点可以单独消费，允许更长延迟；CRM 同步依赖外部系统，必须设置并发上限和熔断策略；月底结算消息不能和在线商机消息共用线程池。&lt;/p&gt;
&lt;p&gt;幂等设计是落地重点。消费者处理 &lt;code&gt;OpportunityCreatedEvent&lt;/code&gt; 时，应以 &lt;code&gt;eventId&lt;/code&gt; 或 &lt;code&gt;opportunityId + actionType&lt;/code&gt; 作为幂等键。先尝试插入消费记录，插入成功才执行业务；如果唯一键冲突，说明已经处理过或正在处理，需要根据状态返回成功或稍后重试。业务表本身也要有条件更新，例如只有 &lt;code&gt;WAIT_ASSIGN&lt;/code&gt; 状态才能分配代理人。&lt;/p&gt;
&lt;p&gt;重试策略要区分临时失败和永久失败。数据库死锁、RPC 超时、线程池满属于临时失败，可以让 MQ 延迟重试；参数缺失、业务状态非法、客户不存在属于永久失败，应记录失败原因并进入人工补偿或死信处理。不能所有异常都无限重试，否则会制造重试风暴。&lt;/p&gt;
&lt;h2 id="关键伪代码或流程"&gt;关键伪代码或流程&lt;/h2&gt;
&lt;p&gt;生产端本地消息表：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Transactional&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; SubmitResult &lt;span style="color:#a6e22e"&gt;submitOpportunity&lt;/span&gt;(SubmitCommand command) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Opportunity opportunity &lt;span style="color:#f92672"&gt;=&lt;/span&gt; opportunityRepository.&lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;(command);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DomainEvent event &lt;span style="color:#f92672"&gt;=&lt;/span&gt; DomainEvent.&lt;span style="color:#a6e22e"&gt;builder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;eventId&lt;/span&gt;(IdGenerator.&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;bizKey&lt;/span&gt;(opportunity.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;eventType&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;OPPORTUNITY_CREATED&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;payload&lt;/span&gt;(toJson(opportunity))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;INIT&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; localMessageRepository.&lt;span style="color:#a6e22e"&gt;insert&lt;/span&gt;(event);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; SubmitResult.&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt;(opportunity.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可靠投递任务：&lt;/p&gt;</description></item><item><title>锁机制详解：synchronized、ReentrantLock、读写锁与分布式锁的边界</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E9%94%81%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3synchronizedreentrantlock%E8%AF%BB%E5%86%99%E9%94%81%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E7%9A%84%E8%BE%B9%E7%95%8C/</link><pubDate>Fri, 03 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E9%94%81%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3synchronizedreentrantlock%E8%AF%BB%E5%86%99%E9%94%81%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E7%9A%84%E8%BE%B9%E7%95%8C/</guid><description>&lt;h1 id="锁机制详解synchronizedreentrantlock读写锁与分布式锁的边界"&gt;锁机制详解：synchronized、ReentrantLock、读写锁与分布式锁的边界&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售活动、商机流转和月底结算链路中，负责高并发下的共享状态保护、幂等控制和重复处理治理。熟悉 Java 锁机制，包括 &lt;code&gt;synchronized&lt;/code&gt;、&lt;code&gt;ReentrantLock&lt;/code&gt;、读写锁以及 Redis/ZooKeeper 分布式锁的适用边界，能够结合数据库唯一索引、状态机、事务消息和补偿机制保证业务一致性。&lt;/p&gt;
&lt;p&gt;这段简历面试时非常容易被追问。面试官不是想听“&lt;code&gt;synchronized&lt;/code&gt; 是关键字、&lt;code&gt;ReentrantLock&lt;/code&gt; 是类”，而是想知道你是否明白：单 JVM 锁和分布式锁保护的边界不同；锁不是越大越安全；锁只能保护临界区，不能替代幂等；高并发系统的正确性往往要靠锁、事务、唯一约束和状态流转共同完成。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险销售系统里有大量“不能重复”的动作。一个客户不能在同一活动下重复领取权益；同一投保意向不能被两个代理人同时认领；同一结算批次不能重复生成佣金明细；同一保司回调不能反复推进订单状态。高峰期这些动作会被用户重复点击、MQ 重试、定时任务补偿、运营后台批量操作同时触发。&lt;/p&gt;
&lt;p&gt;如果只在代码里加一个本地锁，在单机部署时可能暂时有效；一旦服务多实例部署，同一个业务单号的请求可能打到不同机器，本地锁就失效。如果所有地方都上分布式锁，又会增加 Redis 压力、锁等待时间和故障复杂度。资深工程实践的关键，是先识别资源边界：这个共享状态是在单 JVM 内、单数据库行内、跨进程、跨服务，还是跨外部系统。&lt;/p&gt;
&lt;p&gt;比如活动库存扣减。如果库存存在数据库表中，最核心的保护应该是数据库原子更新和唯一流水，而不是单纯 Redis 锁。比如本地缓存刷新，多个线程同时构建同一份缓存，可以用 JVM 内锁避免缓存击穿。比如月底结算批次被多台机器扫描，可以用数据库任务抢占或分布式锁协调。不同场景的锁完全不同。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;synchronized&lt;/code&gt; 是 JVM 内置监视器锁，具备互斥和可见性。进入同步块前会获取对象监视器，退出同步块会释放锁。它是可重入锁，同一线程可以重复进入同一把锁保护的代码。现代 JVM 对它做了很多优化，包括偏向锁历史机制、轻量级锁、自旋、锁消除、锁粗化等。虽然不同 JDK 版本实现细节会变化，但面试表达时抓住语义即可：它简单、可靠、自动释放，适合临界区短、竞争不复杂的场景。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ReentrantLock&lt;/code&gt; 是基于 AQS 的显式锁。相比 &lt;code&gt;synchronized&lt;/code&gt;，它支持可中断等待、超时获取、公平锁选择、多个条件队列。代价是必须手动释放，通常要写在 &lt;code&gt;finally&lt;/code&gt; 中。它适合需要 &lt;code&gt;tryLock&lt;/code&gt;、等待可中断、精细条件唤醒的场景。比如批量任务抢占时，拿不到锁就跳过当前批次，而不是阻塞线程。&lt;/p&gt;
&lt;p&gt;读写锁的核心思想是读读共享、读写互斥、写写互斥。&lt;code&gt;ReentrantReadWriteLock&lt;/code&gt; 适合读多写少、读操作耗时相对明显、共享数据结构较稳定的场景。它不适合写频繁场景，也不能解决跨 JVM 一致性。使用读写锁时要关注锁降级、写锁饥饿、读锁持有期间禁止升级写锁等问题。&lt;/p&gt;
&lt;p&gt;分布式锁用于协调多个进程或多台机器访问同一逻辑资源。常见实现是 Redis &lt;code&gt;SET key value NX PX ttl&lt;/code&gt; 加 Lua 脚本释放，或者 ZooKeeper 临时顺序节点。Redis 锁性能高但要处理过期、误删、续期、主从切换风险；ZooKeeper 锁一致性更强但性能和运维成本更高。分布式锁不是银弹，它只能降低并发冲突概率，最终业务正确性仍要靠数据库约束、幂等键和状态机兜底。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;在客户重复提交投保意向场景中，最可靠的方案不是先加锁，而是设计幂等键。比如以 &lt;code&gt;activityId + customerId + productId&lt;/code&gt; 作为唯一约束，投保意向创建时先查再插不可靠，因为并发下两个线程都可能查不到。正确方式是在数据库层加唯一索引，插入冲突后查询已有单据返回。锁可以减少冲突，但不能替代唯一约束。&lt;/p&gt;</description></item><item><title>异步化改造：从同步调用到事件驱动</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E5%BC%82%E6%AD%A5%E5%8C%96%E6%94%B9%E9%80%A0%E4%BB%8E%E5%90%8C%E6%AD%A5%E8%B0%83%E7%94%A8%E5%88%B0%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8/</link><pubDate>Thu, 02 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E5%BC%82%E6%AD%A5%E5%8C%96%E6%94%B9%E9%80%A0%E4%BB%8E%E5%90%8C%E6%AD%A5%E8%B0%83%E7%94%A8%E5%88%B0%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8/</guid><description>&lt;h1 id="异步化改造从同步调用到事件驱动"&gt;异步化改造：从同步调用到事件驱动&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售活动和商机流转系统中，负责将投保提交、商机分配、客户通知、CRM 同步、保司状态回写等长链路从同步调用改造为事件驱动架构。通过核心链路瘦身、RocketMQ 事件解耦、线程池隔离、幂等补偿和监控告警，降低接口超时和下游抖动对用户体验的影响，提升高峰期系统稳定性。&lt;/p&gt;
&lt;p&gt;这段简历面试时要特别小心。异步化不是把 &lt;code&gt;service.call()&lt;/code&gt; 改成 &lt;code&gt;executor.submit()&lt;/code&gt;，也不是简单发一条 MQ。真正的异步化改造包括业务边界重划、状态机设计、一致性方案、可观测性和失败恢复。如果说不清这些，面试官会认为你只是做了技术包装。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;改造前的投保提交链路往往很长：校验活动资格、查询客户信息、计算保费、创建投保意向、分配代理人、发送短信、写运营埋点、同步 CRM、调用保司预核保、生成跟进待办。平时流量低时，这种同步链路看起来简单直接；活动高峰时，任何一个下游变慢都会导致整个接口超时。&lt;/p&gt;
&lt;p&gt;同步链路还有一个隐性问题：调用方和被调用方的可用性强绑定。短信通道抖动不应该影响投保意向创建，CRM 同步失败不应该让用户提交失败，运营埋点慢也不应该占用入口线程。但同步代码会天然把它们绑在一起，导致弱依赖拖垮强业务。&lt;/p&gt;
&lt;p&gt;异步化的目标不是“所有事情都异步”，而是把用户实时感知链路和后置处理链路拆开。用户请求内必须完成的是：请求合法、业务幂等、核心单据落库、用户能得到明确结果。后置链路可以延迟处理，但必须可靠、可追踪、可补偿。&lt;/p&gt;
&lt;h2 id="核心原理"&gt;核心原理&lt;/h2&gt;
&lt;p&gt;同步调用是命令式思维：A 调 B，B 成功后 A 再调 C。调用关系清晰，但耦合强、延迟累加、故障传播直接。事件驱动是事实式思维：A 完成自己的业务状态变更后发布事件，B、C、D 订阅这个事件并各自处理。发布者不关心订阅者数量和处理耗时，订阅者失败也不直接影响发布者。&lt;/p&gt;
&lt;p&gt;事件驱动的关键是事件语义。事件应该表示“已经发生的业务事实”，例如 &lt;code&gt;OpportunityCreated&lt;/code&gt;、&lt;code&gt;OpportunityAssigned&lt;/code&gt;、&lt;code&gt;PolicyStatusChanged&lt;/code&gt;，而不是 &lt;code&gt;DoAssignCommand&lt;/code&gt;、&lt;code&gt;SendSmsCommand&lt;/code&gt; 这种远程命令。事实事件更稳定，也更适合多个消费者扩展。&lt;/p&gt;
&lt;p&gt;异步化必须接受最终一致性。同步链路中，调用完成即可认为所有动作完成；异步链路中，主单创建成功时，商机可能还未分配，通知可能还未发送，CRM 可能还未同步。业务页面、运营后台和客服工具都要能展示中间状态，并支持重试和补偿。&lt;/p&gt;
&lt;p&gt;异步化还要处理失败传播方式的变化。同步调用失败可以直接抛异常；异步消费失败不能让用户重新提交，它需要重试、死信、失败表、人工处理和状态回滚策略。没有这些配套，异步化只会把错误藏起来。&lt;/p&gt;
&lt;h2 id="项目落地"&gt;项目落地&lt;/h2&gt;
&lt;p&gt;第一步是链路梳理。把原同步接口中的动作按重要性拆分为强依赖、弱依赖和后置任务。强依赖包括活动资格、重复提交幂等、核心单据创建；弱依赖包括客户标签、营销权益展示；后置任务包括商机分配、短信通知、CRM 同步、运营埋点。强依赖留在同步链路，弱依赖设置短超时和降级，后置任务事件化。&lt;/p&gt;
&lt;p&gt;第二步是状态机设计。投保意向不能只有“成功/失败”，而应有 &lt;code&gt;CREATED&lt;/code&gt;、&lt;code&gt;WAIT_ASSIGN&lt;/code&gt;、&lt;code&gt;ASSIGNED&lt;/code&gt;、&lt;code&gt;NOTIFIED&lt;/code&gt;、&lt;code&gt;SYNCED_CRM&lt;/code&gt;、&lt;code&gt;FAILED&lt;/code&gt; 等状态或子状态。异步消费者每完成一个动作，就推进对应状态。页面可以显示“已提交，正在分配顾问”，运营后台可以看到哪些环节失败。&lt;/p&gt;
&lt;p&gt;第三步是事件发布一致性。核心单据落库和事件发布必须绑定。可以用事务消息，也可以用本地消息表。对于团队可控性强的业务系统，本地消息表很常见：业务事务里写主单和消息记录，后台投递任务负责发送 MQ，发送成功后标记状态。&lt;/p&gt;
&lt;p&gt;第四步是消费者解耦。一个事件可以有多个消费者，但每个消费者只做自己的事情。商机分配消费者只负责代理人分配和状态推进；通知消费者只负责发送通知；CRM 消费者只负责外部同步。消费者之间不要直接互相调用，否则事件驱动会退化成隐藏的同步链路。&lt;/p&gt;
&lt;p&gt;第五步是补偿闭环。异步任务要有清晰的失败表、重试次数、错误原因、最后处理时间和人工触发入口。比如 CRM 同步失败不影响用户提交，但运营人员需要看到失败记录并可重新同步。商机分配失败则影响销售跟进，应设置更高优先级告警。&lt;/p&gt;
&lt;h2 id="关键伪代码或流程"&gt;关键伪代码或流程&lt;/h2&gt;
&lt;p&gt;同步改造前：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; SubmitResult &lt;span style="color:#a6e22e"&gt;submit&lt;/span&gt;(SubmitCommand command) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qualificationService.&lt;span style="color:#a6e22e"&gt;check&lt;/span&gt;(command);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Opportunity opportunity &lt;span style="color:#f92672"&gt;=&lt;/span&gt; opportunityService.&lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;(command);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Agent agent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; assignService.&lt;span style="color:#a6e22e"&gt;assign&lt;/span&gt;(opportunity);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; smsService.&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(agent, opportunity);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; crmService.&lt;span style="color:#a6e22e"&gt;sync&lt;/span&gt;(opportunity);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; metricService.&lt;span style="color:#a6e22e"&gt;record&lt;/span&gt;(opportunity);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; SubmitResult.&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt;(opportunity.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;事件驱动改造后：&lt;/p&gt;</description></item><item><title>线程池在保险销售高峰场景中的设计与调优</title><link>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%9C%A8%E4%BF%9D%E9%99%A9%E9%94%80%E5%94%AE%E9%AB%98%E5%B3%B0%E5%9C%BA%E6%99%AF%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E8%B0%83%E4%BC%98/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/resume-deep-dive/01-java-concurrency/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%9C%A8%E4%BF%9D%E9%99%A9%E9%94%80%E5%94%AE%E9%AB%98%E5%B3%B0%E5%9C%BA%E6%99%AF%E4%B8%AD%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E8%B0%83%E4%BC%98/</guid><description>&lt;h1 id="线程池在保险销售高峰场景中的设计与调优"&gt;线程池在保险销售高峰场景中的设计与调优&lt;/h1&gt;
&lt;h2 id="对应简历段落"&gt;对应简历段落&lt;/h2&gt;
&lt;p&gt;在保险销售活动系统中，参与销售活动、月底结算、商机流转等核心链路建设。针对活动高峰期用户集中投保、代理人批量跟进、保单状态异步回写、结算任务集中触发等场景，设计 RocketMQ 削峰、Sentinel 限流、线程池隔离和异步化处理方案，提升系统在流量突增下的稳定性。负责线程池参数调优、任务队列治理、拒绝策略设计、核心链路监控告警和故障排查，降低高峰期接口超时、任务堆积、数据库连接耗尽和跨业务相互拖垮的风险。&lt;/p&gt;
&lt;p&gt;这段简历看起来是在讲“用了线程池”，但面试官真正想确认的是：你是否理解线程池背后的资源模型，是否知道业务高峰为什么会把系统打垮，是否能把 RocketMQ、Sentinel、线程池、数据库连接池、下游 RPC 超时、幂等补偿这些能力串成一套完整的稳定性方案。线程池不是为了把代码改成异步那么简单，它本质上是在有限机器资源上做并发预算、排队策略、故障隔离和降级取舍。&lt;/p&gt;
&lt;h2 id="业务背景"&gt;业务背景&lt;/h2&gt;
&lt;p&gt;保险销售活动通常有几个明显特点。&lt;/p&gt;
&lt;p&gt;第一，流量具有强脉冲。比如开门红活动、月底冲刺、节假日前促销、产品停售前最后一波投保，用户和代理人会集中进入活动页、测算保费、提交投保意向、上传资料、查询核保进度。平时 QPS 可能只有几十，活动开始后短时间冲到几百甚至上千。&lt;/p&gt;
&lt;p&gt;第二，链路长且外部依赖多。一次销售动作可能包含活动资格校验、产品规则查询、客户画像查询、优惠权益计算、投保单创建、商机分配、代理人通知、短信触达、RocketMQ 消息发送、保司接口调用、CRM 状态同步等步骤。任何一个下游慢，都会把同步线程拖住。&lt;/p&gt;
&lt;p&gt;第三，业务有明显的优先级差异。活动页面查询和投保提交属于用户实时感知链路，必须优先保障。短信通知、商机打标签、运营报表、结算明细生成可以延迟。月底结算虽然不是用户实时请求，但数据规模大、持续时间长，如果不隔离，很容易抢占在线交易资源。&lt;/p&gt;
&lt;p&gt;第四，数据一致性要求高。保险业务不能简单丢任务。投保单、商机、佣金、结算状态都要可追踪、可重试、可补偿。线程池拒绝任务时不能只是抛异常，必须结合业务状态机、MQ 重试、失败表、人工补偿入口来设计。&lt;/p&gt;
&lt;p&gt;因此，项目里的线程池调优不是单点优化，而是围绕“哪些任务可以并行、哪些任务必须排队、哪些任务可以丢弃或降级、哪些任务必须持久化后异步执行”来做整体架构设计。&lt;/p&gt;
&lt;h2 id="问题为什么出现"&gt;问题为什么出现&lt;/h2&gt;
&lt;p&gt;高峰期系统出问题，通常不是因为单个接口写得慢，而是多个资源同时进入临界状态。&lt;/p&gt;
&lt;p&gt;最常见的问题是 Tomcat 工作线程被慢调用占满。比如活动提交流程里同步调用保司接口，保司在高峰期响应从 200ms 变成 3s。如果入口线程一直阻塞等待，下游稍慢就会导致 Web 容器线程耗尽，后续请求连进入业务代码的机会都没有。&lt;/p&gt;
&lt;p&gt;第二类问题是业务线程池被无界队列拖垮。很多项目喜欢用 &lt;code&gt;Executors.newFixedThreadPool&lt;/code&gt; 或 &lt;code&gt;newSingleThreadExecutor&lt;/code&gt;，默认队列是 &lt;code&gt;LinkedBlockingQueue&lt;/code&gt;，容量接近无限。高峰期任务生产速度大于消费速度时，队列持续增长，看似没有拒绝，实际上把压力转成了堆内存压力和延迟膨胀。等到 OOM 或 Full GC，系统已经很难自救。&lt;/p&gt;
&lt;p&gt;第三类问题是不同业务共用一个线程池。销售活动、商机流转、短信通知、结算任务如果都丢进同一个 &lt;code&gt;asyncExecutor&lt;/code&gt;，月底结算一旦批量提交几十万条任务，就可能把活动提交通知、保单状态回写、商机分配一起堵住。共用线程池会让低优先级任务拖垮高优先级链路。&lt;/p&gt;
&lt;p&gt;第四类问题是线程池数量调大后反而更慢。线程数不是越大越好。线程数过大时，CPU 上下文切换增加，数据库连接池被打满，下游服务被并发冲垮，锁竞争变重。比如线程池最大线程数配置为 300，但数据库连接池只有 50，最终 250 个线程只是在等待连接，既占内存又增加调度成本。&lt;/p&gt;
&lt;p&gt;第五类问题是 MQ 消费端没有限速。RocketMQ 本身能削峰，把入口流量先存起来，但如果消费者线程池无限拉取、无限并发处理，就只是把峰值从入口转移到消费端。消费端必须结合线程池容量、数据库吞吐、下游接口承载能力设置消费线程数、批量大小、重试策略和积压告警。&lt;/p&gt;
&lt;p&gt;第六类问题是缺少可观测性。很多故障排查时只看到接口超时，却不知道是线程池 active 打满、队列堆积、拒绝数飙升、DB 连接池等待、MQ 消费延迟还是 Sentinel 限流触发。没有指标，调参只能凭感觉。&lt;/p&gt;
&lt;h2 id="线程池核心原理"&gt;线程池核心原理&lt;/h2&gt;
&lt;p&gt;Java 线程池的核心类是 &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;。理解它要抓住几个参数：&lt;code&gt;corePoolSize&lt;/code&gt;、&lt;code&gt;maximumPoolSize&lt;/code&gt;、&lt;code&gt;workQueue&lt;/code&gt;、&lt;code&gt;keepAliveTime&lt;/code&gt;、&lt;code&gt;threadFactory&lt;/code&gt;、&lt;code&gt;RejectedExecutionHandler&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;提交任务时，线程池的大致流程是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当前运行线程数小于核心线程数，直接创建核心线程执行任务。&lt;/li&gt;
&lt;li&gt;核心线程已满，尝试把任务放入队列。&lt;/li&gt;
&lt;li&gt;队列也满，并且当前线程数小于最大线程数，创建非核心线程执行任务。&lt;/li&gt;
&lt;li&gt;线程数达到最大值且队列也满，执行拒绝策略。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里有一个很容易被忽略的点：如果使用无界队列，&lt;code&gt;maximumPoolSize&lt;/code&gt; 基本不会生效。因为任务会一直进入队列，很少走到创建非核心线程那一步。所以生产系统里通常不建议使用无界队列，尤其是承载外部流量或批处理任务时。&lt;/p&gt;</description></item><item><title/><link>https://zhouwy.top/posts/agentscope4j-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97---%E4%BB%8E%E7%8B%BC%E4%BA%BA%E6%9D%80%E9%A1%B9%E7%9B%AE%E6%8E%8C%E6%8F%A1%E5%A4%9A%E6%99%BA%E8%83%BD%E4%BD%93%E6%A1%86%E6%9E%B6/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zhouwy.top/posts/agentscope4j-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E6%8C%87%E5%8D%97---%E4%BB%8E%E7%8B%BC%E4%BA%BA%E6%9D%80%E9%A1%B9%E7%9B%AE%E6%8E%8C%E6%8F%A1%E5%A4%9A%E6%99%BA%E8%83%BD%E4%BD%93%E6%A1%86%E6%9E%B6/</guid><description>&lt;h1 id="agentscope4j-深度学习指南---从狼人杀项目掌握多智能体框架"&gt;AgentScope4J 深度学习指南 - 从狼人杀项目掌握多智能体框架&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;以狼人杀多智能体游戏为实战案例，系统学习 AgentScope4J 的核心概念，并补充 2026 年最新的 Harness Framework、长记忆、分布式会话与多 Agent 编排能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;AgentScope4J 是阿里巴巴开源的多智能体框架，提供了完整的 Agent 开发基础设施。本文以 &lt;a href="https://github.com/agentscope-ai/agentscope-java"&gt;werewolf-hitl&lt;/a&gt; 项目为案例，这是一个带人机交互（HITL）的狼人杀多智能体游戏，先从项目里实际出现的通信、结构化输出、人机交互与多智能体协作讲起，再延伸到 AgentScope Java 1.0.10 之后补强的 Nacos、A2A、长记忆、可观测与 1.1 系列 Harness Framework。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;学习路径：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Msg 消息体系 — 理解通信单元&lt;/li&gt;
&lt;li&gt;Agent 体系 — 理解代理机制&lt;/li&gt;
&lt;li&gt;工具调用与结构化输出 — 理解 LLM 交互&lt;/li&gt;
&lt;li&gt;WebUserInput 人机交互 — 理解异步等待&lt;/li&gt;
&lt;li&gt;MsgHub 多智能体通信 — 理解协作机制&lt;/li&gt;
&lt;li&gt;Hook 生命周期 — 理解执行拦截&lt;/li&gt;
&lt;li&gt;Pipeline 编排 — 理解多 Agent 组合&lt;/li&gt;
&lt;li&gt;Formatter、流式输出、Prompt、持久化 — 理解框架生产化基础&lt;/li&gt;
&lt;li&gt;A2A、MCP、RAG、多模态 — 理解扩展生态&lt;/li&gt;
&lt;li&gt;Harness Framework — 理解最近新增的工作区、记忆、会话、沙箱和子 Agent 运行时&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="一msg-消息体系"&gt;一、Msg 消息体系&lt;/h2&gt;
&lt;h3 id="11-msg-结构总览"&gt;1.1 Msg 结构总览&lt;/h3&gt;
&lt;p&gt;Msg 是 AgentScope 的&lt;strong&gt;唯一通信单元&lt;/strong&gt;。Agent 之间、Agent 与 Model 之间、Agent 与 Tool 之间，传递的都是 Msg。&lt;/p&gt;</description></item></channel></rss>