云景阁 yunjing's personal website https://0x5143.github.io/ Mon, 02 Mar 2026 03:19:56 +0000 Mon, 02 Mar 2026 03:19:56 +0000 Jekyll v3.10.0 GitHub Agent + OpenSpec SDD 纯 Issue 驱动工作流整合方案 <h2 id="1-这是什么问题">1. 这是什么问题?</h2> <p>我希望探索一种将 <strong>GitHub Copilot Coding Agent</strong>、<strong>OpenSpec SDD(Spec-Driven Development)</strong> 和 <strong>GitHub Actions CI</strong> 整合在一起的工作流,实现:</p> <ul> <li>代码与文档分离(单仓库双目录)</li> <li>以 Issue 为核心驱动 SDD 全生命周期</li> <li>新增 Issue 自动触发 CI 生成 SDD 骨架</li> <li>SDD 完成后可自动分配给 Copilot Agent 实现</li> </ul> <p><strong>核心约束:必须可落地、可执行,零额外依赖。</strong></p> <hr /> <h2 id="2-核心结论--判断">2. 核心结论 / 判断</h2> <p><strong>纯 Issue 驱动 &gt; Project Board 同步展示。</strong></p> <p>经过多轮迭代验证,最终方案砍掉了 GitHub Project Board,理由是:</p> <ul> <li>Project Board 在此场景中仅作为”只读镜像”,无决策价值</li> <li>引入 Project 需要 PAT Token、GraphQL mutation、组织级权限等大量额外依赖</li> <li>Issue Label 本身就是天然的状态机,完全可以替代 Board 的状态流转</li> </ul> <p>砍掉后,整个方案:</p> <table> <thead> <tr> <th>维度</th> <th>含 Project Board</th> <th>纯 Issue(最终版)</th> </tr> </thead> <tbody> <tr> <td>Secrets</td> <td>1 PAT + 5 Variables</td> <td>0(仅 GITHUB_TOKEN)</td> </tr> <tr> <td>GraphQL 调用</td> <td>4 处 mutation</td> <td>0</td> </tr> <tr> <td>落地难度</td> <td>需 Org Admin 配合</td> <td>仓库 Maintainer 即可</td> </tr> <tr> <td>可移植性</td> <td>依赖组织级 Project</td> <td>任何仓库即开即用</td> </tr> </tbody> </table> <hr /> <h2 id="3-设计理念">3. 设计理念</h2> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Issue 是"第一公民",Label 是"状态机", Milestone 是"版本线”,PR 是"交付物"。 不需要一个额外的看板来"展示"这些已经存在的东西。 </code></pre></div></div> <hr /> <h2 id="4-前置条件">4. 前置条件</h2> <table> <thead> <tr> <th>#</th> <th>条件</th> <th>确认方式</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Node.js ≥ 20.19.0</td> <td><code class="language-plaintext highlighter-rouge">node -v</code></td> </tr> <tr> <td>2</td> <td>OpenSpec CLI</td> <td><code class="language-plaintext highlighter-rouge">npm install -g @fission-ai/openspec@latest</code></td> </tr> <tr> <td>3</td> <td>Copilot Coding Agent 已启用</td> <td>Repo → Settings → Copilot → Agent ✅</td> </tr> <tr> <td>4</td> <td>Actions 读写权限</td> <td>Repo → Settings → Actions → General → Read and write ✅</td> </tr> </tbody> </table> <blockquote> <p>不再需要 PAT Token、PROJECT_TOKEN Secret、任何 Project Variables。所有 Workflow 仅使用自动注入的 <code class="language-plaintext highlighter-rouge">GITHUB_TOKEN</code>。</p> </blockquote> <hr /> <h2 id="5-仓库目录结构单仓库双目录">5. 仓库目录结构(单仓库双目录)</h2> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>my-project/ ├── src/ # 💻 代码 │ └── ... ├── .openspec/ # 📄 SDD 文档 │ ├── config.json │ ├── specs/ # 源真相 (Source of Truth) │ │ └── &lt;capability&gt;/spec.md │ └── changes/ # 活跃变更 │ └── &lt;change-id&gt;/ │ ├── proposal.md │ ├── tasks.md │ ├── design.md (可选) │ └── specs/&lt;domain&gt;/spec.md ├── .github/ │ ├── workflows/ │ │ ├── 01-sdd-init.yml │ │ ├── 02-sdd-validate.yml │ │ ├── 03-sdd-implement.yml │ │ └── 04-sdd-archive.yml │ ├── ISSUE_TEMPLATE/ │ │ └── sdd-request.yml │ └── copilot-instructions.md └── README.md </code></pre></div></div> <p><strong>关键区分:</strong></p> <ul> <li><code class="language-plaintext highlighter-rouge">src/</code> — 纯代码,Copilot Agent 的工作区域</li> <li><code class="language-plaintext highlighter-rouge">.openspec/</code> — 纯文档,SDD Pipeline 的工作区域</li> <li><code class="language-plaintext highlighter-rouge">.github/</code> — 自动化胶水层</li> </ul> <hr /> <h2 id="6-label-状态机替代-project-board">6. Label 状态机(替代 Project Board)</h2> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Issue 生命周期: [sdd] ─── Workflow 01 ───▶ [sdd:spec-ing] │ 团队完善 SDD Review &amp; Merge PR │ ▼ [sdd:spec-ready] │ Workflow 03 创建实现 Issue ▼ [sdd:implementing] + [copilot-assigned] │ Copilot / 人工 提交 &amp; 合并实现 PR ▼ [sdd:done] Issue 自动关闭 </code></pre></div></div> <p><strong>所需 Labels:</strong></p> <table> <thead> <tr> <th>Label</th> <th>颜色</th> <th>说明</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">sdd</code></td> <td><code class="language-plaintext highlighter-rouge">#7057ff</code></td> <td>SDD 流程入口</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">sdd:spec-ing</code></td> <td><code class="language-plaintext highlighter-rouge">#fbca04</code></td> <td>SDD 文档编写中</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">sdd:spec-ready</code></td> <td><code class="language-plaintext highlighter-rouge">#0e8a16</code></td> <td>SDD 文档已就绪</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">sdd:implementing</code></td> <td><code class="language-plaintext highlighter-rouge">#1d76db</code></td> <td>代码实现中</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">sdd:done</code></td> <td><code class="language-plaintext highlighter-rouge">#0e8a16</code></td> <td>已完成并归档</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">copilot-assigned</code></td> <td><code class="language-plaintext highlighter-rouge">#8b5cf6</code></td> <td>已分配给 Copilot Agent</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">auto-implement</code></td> <td><code class="language-plaintext highlighter-rouge">#d93f0b</code></td> <td>SDD 合并后自动交给 Copilot</td> </tr> </tbody> </table> <p><strong>日常查询:</strong></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>所有 SDD 进行中: is:open label:sdd:spec-ing 等待实现: is:open label:sdd:spec-ready 实现中: is:open label:sdd:implementing Copilot 处理中: is:open label:copilot-assigned 已完成: label:sdd:done is:closed </code></pre></div></div> <hr /> <h2 id="7-两套命令体系避免混淆">7. 两套命令体系(避免混淆)</h2> <table> <thead> <tr> <th>Shell CLI(CI/CD 中使用)</th> <th>AI IDE 内 OPSX 指令(开发者在 IDE 中使用)</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">openspec init</code></td> <td><code class="language-plaintext highlighter-rouge">/opsx:new &lt;change&gt;</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">openspec validate</code></td> <td><code class="language-plaintext highlighter-rouge">/opsx:ff</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">openspec archive</code></td> <td><code class="language-plaintext highlighter-rouge">/opsx:continue</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">openspec --version</code></td> <td><code class="language-plaintext highlighter-rouge">/opsx:apply &lt;change&gt;</code></td> </tr> <tr> <td> </td> <td><code class="language-plaintext highlighter-rouge">/opsx:verify &lt;change&gt;</code></td> </tr> <tr> <td> </td> <td><code class="language-plaintext highlighter-rouge">/opsx:archive &lt;change&gt;</code></td> </tr> </tbody> </table> <blockquote> <p>⚠️ CI/CD 工作流中只能使用 Shell CLI,不能使用 <code class="language-plaintext highlighter-rouge">/opsx:</code> 指令。OPSX 指令是 AI 编码助手(Claude/Cursor/Windsurf)中的交互式命令。</p> </blockquote> <hr /> <h2 id="8-四个核心-workflow">8. 四个核心 Workflow</h2> <h3 id="81-workflow-01issuesdd--生成-sdd-骨架--创建-draft-pr">8.1 Workflow 01:Issue[sdd] → 生成 SDD 骨架 → 创建 Draft PR</h3> <p><strong>触发条件:</strong> Issue 被创建或被打上 <code class="language-plaintext highlighter-rouge">sdd</code> 标签</p> <p><strong>执行逻辑:</strong></p> <ol> <li>解析 Issue 模板中的 Change Name、Summary、验收标准</li> <li>幂等性检查(分支是否已存在)</li> <li>在 <code class="language-plaintext highlighter-rouge">.openspec/changes/&lt;change-id&gt;/</code> 生成 <code class="language-plaintext highlighter-rouge">proposal.md</code>、<code class="language-plaintext highlighter-rouge">tasks.md</code>、<code class="language-plaintext highlighter-rouge">specs/</code></li> <li>创建 <code class="language-plaintext highlighter-rouge">sdd/&lt;change-id&gt;</code> 分支,提交并推送</li> <li>创建 Draft PR</li> <li>Issue 标签从 <code class="language-plaintext highlighter-rouge">sdd</code> → <code class="language-plaintext highlighter-rouge">sdd:spec-ing</code></li> <li>在 Issue 中评论 SDD Pipeline 状态</li> </ol> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">01</span><span class="nv"> </span><span class="s">📐</span><span class="nv"> </span><span class="s">SDD</span><span class="nv"> </span><span class="s">Init"</span> <span class="na">on</span><span class="pi">:</span> <span class="na">issues</span><span class="pi">:</span> <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">opened</span><span class="pi">,</span> <span class="nv">labeled</span><span class="pi">]</span> <span class="na">permissions</span><span class="pi">:</span> <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span> <span class="na">issues</span><span class="pi">:</span> <span class="s">write</span> <span class="na">pull-requests</span><span class="pi">:</span> <span class="s">write</span> </code></pre></div></div> <h3 id="82-workflow-02sdd-文件变更--自动校验">8.2 Workflow 02:SDD 文件变更 → 自动校验</h3> <p><strong>触发条件:</strong> PR 中有 <code class="language-plaintext highlighter-rouge">.openspec/**</code> 文件变更</p> <p><strong>执行逻辑:</strong></p> <ol> <li>检查每个 change 目录的文件完整性(proposal.md、tasks.md、specs/)</li> <li>检查内容非空(proposal 至少 5 行,tasks 至少 2 个任务项)</li> <li>运行 <code class="language-plaintext highlighter-rouge">openspec validate --all</code>(如果已初始化)</li> <li>在 PR 中评论校验报告</li> <li>有缺失必需文件时 CI 失败</li> </ol> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">02</span><span class="nv"> </span><span class="s">✅</span><span class="nv"> </span><span class="s">SDD</span><span class="nv"> </span><span class="s">Validate"</span> <span class="na">on</span><span class="pi">:</span> <span class="na">pull_request</span><span class="pi">:</span> <span class="na">paths</span><span class="pi">:</span> <span class="pi">-</span> <span class="s1">'</span><span class="s">.openspec/**'</span> <span class="na">permissions</span><span class="pi">:</span> <span class="na">contents</span><span class="pi">:</span> <span class="s">read</span> <span class="na">pull-requests</span><span class="pi">:</span> <span class="s">write</span> </code></pre></div></div> <h3 id="83-workflow-03sdd-pr-合并--创建实现-issue--可选分配-copilot">8.3 Workflow 03:SDD PR 合并 → 创建实现 Issue → 可选分配 Copilot</h3> <p><strong>触发条件:</strong> <code class="language-plaintext highlighter-rouge">sdd/*</code> 分支的 PR 被合并到 main</p> <p><strong>执行逻辑:</strong></p> <ol> <li>从分支名提取 change-id</li> <li>读取 <code class="language-plaintext highlighter-rouge">tasks.md</code> 和 <code class="language-plaintext highlighter-rouge">proposal.md</code> 内容</li> <li>检查原始 SDD Issue 是否有 <code class="language-plaintext highlighter-rouge">auto-implement</code> 标签</li> <li>创建实现 Issue,body 中包含完整的 SDD 上下文</li> <li>如果 auto-implement,<code class="language-plaintext highlighter-rouge">assignees: ['copilot']</code> → 触发 Coding Agent</li> <li>SDD Issue 标签 → <code class="language-plaintext highlighter-rouge">sdd:spec-ready</code></li> <li>在 SDD Issue 中评论链接</li> </ol> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">03</span><span class="nv"> </span><span class="s">🤖</span><span class="nv"> </span><span class="s">SDD</span><span class="nv"> </span><span class="s">Merged</span><span class="nv"> </span><span class="s">→</span><span class="nv"> </span><span class="s">Implement"</span> <span class="na">on</span><span class="pi">:</span> <span class="na">pull_request</span><span class="pi">:</span> <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">closed</span><span class="pi">]</span> <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">main</span><span class="pi">]</span> <span class="na">permissions</span><span class="pi">:</span> <span class="na">contents</span><span class="pi">:</span> <span class="s">read</span> <span class="na">issues</span><span class="pi">:</span> <span class="s">write</span> </code></pre></div></div> <blockquote> <p><strong>关于 Copilot Agent 触发方式:</strong> 通过 REST API 将 Issue 的 assignee 设为 <code class="language-plaintext highlighter-rouge">"copilot"</code> 即可触发。Agent 会自动分析仓库、读取 SDD 文件、创建分支、编写代码、运行测试,最终提交一个 Draft PR。迭代反馈发生在 PR 评论中,而非 Issue 评论中。</p> </blockquote> <h3 id="84-workflow-04实现-pr-合并--归档-sdd--关闭-issue">8.4 Workflow 04:实现 PR 合并 → 归档 SDD → 关闭 Issue</h3> <p><strong>触发条件:</strong> <code class="language-plaintext highlighter-rouge">copilot/*</code> 分支或带 <code class="language-plaintext highlighter-rouge">sdd:implementing</code> 标签的 PR 被合并</p> <p><strong>执行逻辑:</strong></p> <ol> <li>从 PR body 中提取 change-id</li> <li>运行 <code class="language-plaintext highlighter-rouge">openspec archive &lt;change-id&gt; --yes</code>(CLI 失败时手动回退)</li> <li>合并 delta specs 到 <code class="language-plaintext highlighter-rouge">.openspec/specs/</code></li> <li>提交归档(commit message 含 <code class="language-plaintext highlighter-rouge">[skip ci]</code> 防止循环)</li> <li>所有关联 Issue 标签 → <code class="language-plaintext highlighter-rouge">sdd:done</code>,自动关闭</li> <li>评论归档完成</li> </ol> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">04</span><span class="nv"> </span><span class="s">📦</span><span class="nv"> </span><span class="s">Impl</span><span class="nv"> </span><span class="s">Merged</span><span class="nv"> </span><span class="s">→</span><span class="nv"> </span><span class="s">Archive"</span> <span class="na">on</span><span class="pi">:</span> <span class="na">pull_request</span><span class="pi">:</span> <span class="na">types</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">closed</span><span class="pi">]</span> <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">main</span><span class="pi">]</span> <span class="na">permissions</span><span class="pi">:</span> <span class="na">contents</span><span class="pi">:</span> <span class="s">write</span> <span class="na">issues</span><span class="pi">:</span> <span class="s">write</span> </code></pre></div></div> <hr /> <h2 id="9-完整生命周期时序">9. 完整生命周期时序</h2> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>👤 PM/Dev GitHub Actions 🤖 Copilot Agent ──────── ────────────── ──────────────── 1. New Issue [SDD] label: sdd │── issues.opened ─────▶│ │ 01-sdd-init ├ 幂等检查 ├ 生成 SDD 骨架 ├ 创建 sdd/ 分支 ├ 创建 Draft PR #2 ├ label → sdd:spec-ing └ 评论 Issue #1 ◀── 通知 ───────────────│ 2. 完善 SDD (PR #2) │── push ───────────────▶│ │ 02-sdd-validate ├ 文件检查 ├ openspec validate └ 发布报告 ◀── 报告 ───────────────│ 3. Merge SDD PR #2 │── merge ──────────────▶│ │ 03-sdd-implement ├ 解析 tasks.md ├ 创建 Issue #3 │ assignees: [copilot] ├ label #1 → sdd:spec-ready └ 评论 #1 │ │── Issue #3 ──────────────▶│ │ 分析仓库 │ 读取 SDD │ 创建分支 │ 编写代码 │ 运行测试 │◀── Draft PR #4 ──────────│ 4. Review &amp; Merge PR #4 │── merge ──────────────▶│ │ 04-sdd-archive ├ openspec archive ├ 合并 delta specs ├ commit [skip ci] ├ label → sdd:done ├ 关闭 Issue #1 &amp; #3 └ 评论: archived ◀── done ───────────────│ </code></pre></div></div> <hr /> <h2 id="10-关键设计决策与-review-记录">10. 关键设计决策与 Review 记录</h2> <h3 id="101-为什么砍掉-project-board">10.1 为什么砍掉 Project Board?</h3> <table> <thead> <tr> <th>问题</th> <th>详情</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">projects_v2_item</code> 仅组织级</td> <td>webhook 仅在组织级别生效,repo 级别不收该事件</td> </tr> <tr> <td>Actions 触发不稳定</td> <td>社区广泛报告延迟、丢失事件等问题</td> </tr> <tr> <td>权限要求高</td> <td>需要 Organization Admin + PAT (project scope)</td> </tr> <tr> <td>只读镜像无价值</td> <td>Board 仅同步展示已有 Issue 状态,不产生新决策</td> </tr> </tbody> </table> <h3 id="102-openspec-命令纠错">10.2 OpenSpec 命令纠错</h3> <table> <thead> <tr> <th>错误</th> <th>正确</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">npm install -g openspec</code></td> <td><code class="language-plaintext highlighter-rouge">npm install -g @fission-ai/openspec@latest</code></td> </tr> <tr> <td>目录 <code class="language-plaintext highlighter-rouge">openspec/</code></td> <td>目录 <code class="language-plaintext highlighter-rouge">.openspec/</code>(CLI 默认)</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/opsx:</code> 在 CI 中使用</td> <td><code class="language-plaintext highlighter-rouge">/opsx:</code> 仅在 AI IDE 中使用</td> </tr> </tbody> </table> <h3 id="103-copilot-agent-触发方式">10.3 Copilot Agent 触发方式</h3> <ul> <li>通过 REST API <code class="language-plaintext highlighter-rouge">assignees: ['copilot']</code> 触发</li> <li>Agent 自动创建 Draft PR,不在 Issue 中回复</li> <li>迭代反馈发生在 PR 评论中</li> <li>需要 Copilot Pro+/Business/Enterprise 订阅</li> <li>仓库 Settings → Copilot → Coding Agent 需提前启用</li> </ul> <h3 id="104-幂等性与健壮性">10.4 幂等性与健壮性</h3> <ul> <li>每个 Workflow 都有幂等性检查(分支已存在则 skip)</li> <li><code class="language-plaintext highlighter-rouge">openspec archive</code> CLI 失败时有手动回退方案</li> <li>归档提交使用 <code class="language-plaintext highlighter-rouge">[skip ci]</code> 防止无限循环</li> <li>Label 操作 <code class="language-plaintext highlighter-rouge">removeLabel</code> 使用 <code class="language-plaintext highlighter-rouge">.catch(() =&gt; {})</code> 容错</li> </ul> <hr /> <h2 id="11-已知限制">11. 已知限制</h2> <table> <thead> <tr> <th>#</th> <th>限制</th> <th>应对策略</th> </tr> </thead> <tbody> <tr> <td>1</td> <td><code class="language-plaintext highlighter-rouge">assignees: ['copilot']</code> 要求 Agent 已启用</td> <td>Workflow 中 <code class="language-plaintext highlighter-rouge">continue-on-error</code> + 回退为手动</td> </tr> <tr> <td>2</td> <td>OpenSpec CLI 仍在快速迭代</td> <td>archive 步骤有手动回退方案</td> </tr> <tr> <td>3</td> <td><code class="language-plaintext highlighter-rouge">[skip ci]</code> 提交不触发后续 workflow</td> <td>这是期望行为,防止循环</td> </tr> <tr> <td>4</td> <td>Copilot Agent 只适合中小型任务</td> <td>大型任务手动实现,不打 auto-implement</td> </tr> <tr> <td>5</td> <td>branch protection 可能阻止 bot push</td> <td>需配置允许 <code class="language-plaintext highlighter-rouge">github-actions[bot]</code></td> </tr> </tbody> </table> <hr /> <h2 id="12-日常使用速查">12. 日常使用速查</h2> <table> <thead> <tr> <th>你想做什么</th> <th>操作</th> </tr> </thead> <tbody> <tr> <td>发起新需求</td> <td>New Issue → 选 “SDD Request” 模板 → 自动触发</td> </tr> <tr> <td>查看 SDD 进行中</td> <td><code class="language-plaintext highlighter-rouge">is:open label:sdd:spec-ing</code></td> </tr> <tr> <td>查看等待实现</td> <td><code class="language-plaintext highlighter-rouge">is:open label:sdd:spec-ready</code></td> </tr> <tr> <td>查看实现中</td> <td><code class="language-plaintext highlighter-rouge">is:open label:sdd:implementing</code></td> </tr> <tr> <td>查看 Copilot 处理中</td> <td><code class="language-plaintext highlighter-rouge">is:open label:copilot-assigned</code></td> </tr> <tr> <td>查看已完成</td> <td><code class="language-plaintext highlighter-rouge">label:sdd:done is:closed</code></td> </tr> <tr> <td>本地编辑 SDD</td> <td><code class="language-plaintext highlighter-rouge">git checkout sdd/&lt;change-id&gt;</code> → 编辑 → push</td> </tr> <tr> <td>IDE 中用 OPSX</td> <td><code class="language-plaintext highlighter-rouge">/opsx:new &lt;id&gt;</code> → <code class="language-plaintext highlighter-rouge">/opsx:ff</code> → <code class="language-plaintext highlighter-rouge">/opsx:apply</code></td> </tr> </tbody> </table> <hr /> <h2 id="13-总结">13. 总结</h2> <p>这个方案的核心是:</p> <blockquote> <p><strong>Issue 是入口,Label 是状态机,SDD 是规格标准,Actions 是自动化引擎,Copilot Agent 是可选执行者。</strong></p> </blockquote> <p>它不依赖任何额外的 Token、Project Board 或组织级权限。Clone 仓库 → 跑初始化脚本 → 提 Issue → 自动运转。</p> <p>这不是一个理论方案,而是一个可以直接 copy-paste 到任何 GitHub 仓库中立即使用的工程实现。</p> <hr /> <h2 id="参考链接">参考链接</h2> <ul> <li><a href="https://github.com/Fission-AI/OpenSpec">OpenSpec - Fission-AI/OpenSpec</a></li> <li><a href="https://github.com/Fission-AI/OpenSpec/blob/main/docs/cli.md">OpenSpec CLI Reference</a></li> <li><a href="https://github.com/Fission-AI/OpenSpec/blob/main/docs/workflows.md">OpenSpec Workflow Docs</a></li> <li><a href="https://docs.github.com/copilot/how-tos/use-copilot-agents/coding-agent/assign-copilot-to-an-issue">GitHub Docs: Assign Copilot to an Issue</a></li> <li><a href="https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows">GitHub Docs: Events that trigger workflows</a></li> <li><a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions">GitHub Docs: Automating Projects using Actions</a></li> </ul> Mon, 02 Mar 2026 00:00:00 +0000 https://0x5143.github.io/2026/03/02/github-agent-openspec-sdd-workflow/ https://0x5143.github.io/2026/03/02/github-agent-openspec-sdd-workflow/ github-actions openspec sdd copilot agent-skill workflow-automation methodology 使用 GitHub Pages + Copilot Agent 构建个人知识库的可行性分析 <h2 id="1-这是什么问题">1. 这是什么问题?</h2> <p>我希望探索一种<strong>长期可维护、可演化</strong>的方式,用于记录个人思考,并借助 GitHub Copilot / Agent 能力,对这些知识进行新增与检索。</p> <p>核心设想是:</p> <ul> <li>使用 <code class="language-plaintext highlighter-rouge">github.io</code>(GitHub Pages)作为<strong>知识的最终载体</strong></li> <li>使用 Git 仓库作为<strong>思想的版本化存储</strong></li> <li>使用 Copilot + Agent Skill 辅助完成: <ul> <li>新增结构化文档</li> <li>从已有知识中检索与总结特定问题</li> </ul> </li> </ul> <p>问题在于:<br /> <strong>这个方案是否真的可行?边界在哪里?会踩哪些坑?</strong></p> <hr /> <h2 id="2-核心结论--判断">2. 核心结论 / 判断</h2> <p><strong>这个方案在工程层面是可行的,在方法论层面是“有条件成立”的。</strong></p> <p>它非常适合用来构建一个:</p> <ul> <li>面向长期的</li> <li>可追溯演进的</li> <li>以“已成型思考”为主的</li> </ul> <p><strong>个人知识库(而非灵感捕捉系统或第二大脑)。</strong></p> <p>GitHub Pages 负责“稳定呈现”,<br /> Copilot / Agent 负责“结构化整理与辅助回忆”,<br /> <strong>判断与洞察仍然必须由人来完成。</strong></p> <hr /> <h2 id="3-推理与依据">3. 推理与依据</h2> <h3 id="31-github-pages-作为知识载体的优势">3.1 GitHub Pages 作为知识载体的优势</h3> <ul> <li>全部内容为纯文本(Markdown)</li> <li>可版本控制、可 diff、可回滚</li> <li>不依赖第三方 SaaS 的生命周期</li> <li>思考过程本身可被保留(commit history)</li> </ul> <p>这使它非常适合作为<strong>长期思想资产</strong>,而不是短期笔记。</p> <hr /> <h3 id="32-copilot--agent-的真实能力定位">3.2 Copilot / Agent 的真实能力定位</h3> <p>Copilot 并不真正“理解你”,但它非常擅长:</p> <ul> <li>将非结构化输入整理成结构化文档</li> <li>按固定模板重写、补全、规范表达</li> <li>在给定上下文中,对已有内容进行总结与对比</li> </ul> <p>因此,它更像是一个:</p> <blockquote> <p><strong>高级编辑器 + 结构化助理</strong><br /> 而不是“自动产生新知识的智能体”。</p> </blockquote> <hr /> <h3 id="33-文件系统-vs-人类认知的天然冲突">3.3 文件系统 vs 人类认知的天然冲突</h3> <p>GitHub 的底层模型是:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 目录 / 文件 / 行 </code></pre></div></div> <p>而人的认知模型是:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 概念 / 关联 / 权重 / 上下文 </code></pre></div></div> <p>这意味着:</p> <ul> <li>知识之间的“关系”无法自动维护</li> <li>Copilot 无法天然知道哪些概念在体系中是核心</li> <li>写得越多,整体理解成本越高</li> </ul> <p>这构成了该方案的<strong>结构性上限</strong>。</p> <hr /> <h2 id="4-agent-skill-的设计思路">4. Agent Skill 的设计思路</h2> <p>为了避免“AI 注水型知识库”,需要对 Agent 进行<strong>强约束设计</strong>。</p> <h3 id="41-agent-skillkb-writer新增文档">4.1 Agent Skill:KB-Writer(新增文档)</h3> <p><strong>目标</strong><br /> 将零散想法转化为一个“可长期维护的知识节点”。</p> <p><strong>关键原则</strong></p> <ul> <li>不允许引入用户未表达的新观点</li> <li>强制输出结构化 Markdown</li> <li>明确结论、推理与适用边界</li> <li>信息不足时必须显式标注“不确定”</li> </ul> <p><strong>本质角色</strong><br /> 👉 一个“严谨的整理员”,而不是作者。</p> <hr /> <h3 id="42-agent-skillkb-query知识检索">4.2 Agent Skill:KB-Query(知识检索)</h3> <p><strong>目标</strong><br /> 在已有文档中,对某个问题进行一次<strong>可解释的回忆与总结</strong>。</p> <p><strong>输出必须包含</strong></p> <ul> <li>明确结论</li> <li>引用的具体文档</li> <li>不一致或冲突点</li> <li>适用前提与置信度说明</li> </ul> <p><strong>能力边界</strong></p> <ul> <li>无法保证全库无遗漏</li> <li>无法替代人工判断</li> <li>不应被当作搜索引擎或裁决者</li> </ul> <hr /> <h2 id="5-主要痛点与缺陷">5. 主要痛点与缺陷</h2> <h3 id="51-查询能力有限">5.1 查询能力有限</h3> <ul> <li>缺乏真正的语义索引</li> <li>跨主题检索容易遗漏</li> <li>Copilot 无法给出“结果完整性”的保证</li> </ul> <h3 id="52-写入摩擦随时间上升">5.2 写入摩擦随时间上升</h3> <ul> <li>目录与标签需要持续维护</li> <li>文档重构成本逐年增加</li> <li>容易在 6–12 个月后陷入结构债务</li> </ul> <h3 id="53-agent-不具备长期记忆">5.3 Agent 不具备长期记忆</h3> <ul> <li>每次理解都是“临时上下文”</li> <li>无法真正形成对个人知识体系的内化理解</li> </ul> <hr /> <h2 id="6-适用边界">6. 适用边界</h2> <h3 id="61-这个方案适合">6.1 这个方案<strong>适合</strong></h3> <ul> <li>成熟后的思考沉淀</li> <li>方法论、系统设计、模型分析</li> <li>希望公开、可检验的思想资产</li> </ul> <h3 id="62-这个方案不适合">6.2 这个方案<strong>不适合</strong></h3> <ul> <li>随手灵感记录</li> <li>高度碎片化的信息流</li> <li>期待 AI 自动产生洞察或决策</li> </ul> <hr /> <h2 id="7-实践建议避免一年后崩盘">7. 实践建议(避免一年后崩盘)</h2> <ul> <li>设置“缓冲区”(草稿 / issue / discussion)</li> <li>只有在想法稳定后才进入 <code class="language-plaintext highlighter-rouge">github.io</code></li> <li>Copilot 主要参与“整理阶段”,而非“思考阶段”</li> <li>把“发布”当作一次思考的完成标志</li> </ul> <hr /> <h2 id="8-总结">8. 总结</h2> <p>这个方案不是为了追求效率最大化,而是为了:</p> <blockquote> <p><strong>构建一个不会随工具消失的、可演化的思想结构。</strong></p> </blockquote> <p>GitHub Pages 是地基,<br /> Copilot 是工具,<br /> 而体系是否成立,最终取决于<strong>人是否保持判断力与自律</strong>。</p> Wed, 11 Feb 2026 00:00:00 +0000 https://0x5143.github.io/2026/02/11/github-pages-copilot-personal-knowledge-base/ https://0x5143.github.io/2026/02/11/github-pages-copilot-personal-knowledge-base/ personal-knowledge-base github-pages copilot agent-skill methodology Unity游戏开发问题排查思路 <ul> <li>服务器 <ul> <li>磁盘 <ul> <li>利用 <code class="language-plaintext highlighter-rouge">df -h</code> 获取磁盘空间状态</li> <li>利用 <code class="language-plaintext highlighter-rouge">ls -lh</code> 查看文件大小</li> <li>利用 <code class="language-plaintext highlighter-rouge">du -h</code> 查看文本夹大小</li> </ul> </li> <li>CPU <ul> <li>利用top查看进程状态 <ul> <li>获取CPU/内存使用率最高的进程pid、启动命令等信息</li> </ul> </li> <li>利用 <code class="language-plaintext highlighter-rouge">top -H</code> 查看线程状态 <ul> <li>获取CPU/内存使用率最高的线程信息</li> </ul> </li> </ul> </li> <li>内存 <ul> <li>利用 <code class="language-plaintext highlighter-rouge">free -h</code> 查看内存使用情况</li> </ul> </li> </ul> </li> <li>数据库 <ul> <li>慢SQL <ul> <li>利用explain执行计划优化SQL <ul> <li>利用索引提速</li> <li>小表驱动大表</li> </ul> </li> <li>利用 <code class="language-plaintext highlighter-rouge">show processlist</code> 然后kill终结慢查询</li> </ul> </li> <li>连接过多 <ul> <li>利用 <code class="language-plaintext highlighter-rouge">set global max_connections</code>增大连接数</li> <li>利用 <code class="language-plaintext highlighter-rouge">show processlist</code> 然后kill终结多余的连接</li> </ul> </li> <li>死锁 <ul> <li>事务隔离级别</li> <li>锁时序分析 <ul> <li>表锁</li> <li>页锁</li> <li>行锁 <ul> <li>Record Lock</li> <li>Gap Lock</li> <li>Next-Key Lock</li> </ul> </li> <li>意向锁</li> </ul> </li> </ul> </li> </ul> </li> <li>Redis <ul> <li>内存不足 <ul> <li>大key <ul> <li>rdbtools查询大key</li> <li>利用redis-cli查询大key</li> <li>利用debug object key查看key序列化后的大小</li> </ul> </li> <li>config set maxmemory临时增加内存</li> <li>指定内存淘汰机制</li> </ul> </li> <li>连接数过多 <ul> <li>config set maxclient临时增加最大连接数</li> <li>限制客户端最大连接数</li> </ul> </li> <li>慢命令 <ul> <li>config set slowlog-log-lower-than设置慢命令阙值</li> <li>config set slowlog-max-len设置最大慢命令记录保存数</li> <li>利用slowlog get查询慢命令</li> </ul> </li> <li>查询网络延迟 <ul> <li>redis-cli -latency查询延迟信息</li> </ul> </li> </ul> </li> <li>网络 <ul> <li>利用 <code class="language-plaintext highlighter-rouge">netstat</code> 查询统计网络状态</li> </ul> </li> <li>业务线</li> </ul> Mon, 07 Dec 2020 00:00:00 +0000 https://0x5143.github.io/2020/12/07/unity-game-development-troubleshooting-ideas/ https://0x5143.github.io/2020/12/07/unity-game-development-troubleshooting-ideas/ unity 记录在Unity的使用过程中,可能遇到的问题 <h1 id="概述">概述</h1> <p>以此文来记录平时学习使用Unity过程中碰到的问题,加强记忆避免再次发生。</p> <p>大部分问答来源于网上,本文只是记录与自己相关的,并根据Unity的最新LTS版本进行答案更新。</p> <h1 id="一assetbundle相关">一、AssetBundle相关</h1> <h3 id="q1-请问内置的shader怎么打包我用到了内置材质球不只是shader这时候在profiler中看到加载的结果会出现多份">Q1: 请问内置的shader怎么打包?我用到了内置材质球,不只是Shader,这时候在Profiler中看到加载的结果会出现多份。</h3> <blockquote> <p>通常有两种方式对内置的Shader进行打包:</p> <ol> <li>将其添加到Graphics Settings中的Always Included Shaders中,此时添加后的内置Shader就不会被打入AssetBundle包中;</li> <li>在Unity官方资源上下载内置的Shader,将其导入项目,并替换成非内置的材质球,从而可以直接通过脚本来控制其打包的方式。</li> </ol> </blockquote> <h3 id="q2项目在发布时player-setting中勾选的这个选项optimize-mesh-data对于已经打包并且放到了streaming-assets中的assetbundle文件有效果吗模型资源里有个optimize-mesh是否能达到同样的效果">Q2:项目在发布时,Player Setting中勾选的这个选项(Optimize Mesh Data),对于已经打包并且放到了Streaming Assets中的AssetBundle文件有效果吗?模型资源里有个Optimize Mesh,是否能达到同样的效果?</h3> <blockquote> <p>理论上 Optimize Mesh Data 是 Build Player 或者 Bundle 时才生效的,所以之前打好的 Bundle 就没效果了。另外,两个选项的效果是不同的,后者是调整面片排序的。</p> </blockquote> <h1 id="二资源使用">二、资源使用</h1> <h2 id="纹理相关">纹理相关</h2> <h3 id="q1同样的包同一个图集etc2格式在红米note1上会比酷派的内存会大四倍请问这是什么原因造成的如果不支持opengl-30会造成这么大的影响吗">Q1、同样的包同一个图集,ETC2格式,在红米Note1上会比酷派的内存会大四倍,请问这是什么原因造成的?如果不支持OpenGL 3.0,会造成这么大的影响吗?</h3> <blockquote> <p>ETC2 的格式理论上只在OpenGL ES 3.0 的设备上被支持,而在不被支持的设备上则会内部自动转成 RGBA32/ARGB32的格式,这对于 RGBA Compressed ETC2 8bits 的纹理就是放大了 4 倍。因此,如果希望在 OpenGL ES 2.0 的设备上对透明材质进行压缩,那么可以尝试使用分离 Alpha 通道的方式,用两个 ETC1 来进行压缩。</p> </blockquote> <h3 id="q2ios平台需要对图集做rgb和alpha通道的分离吗我发现在同样大小的图片正方形rgb-compressed-pvrtc-4bits和rgba-compressed-pvrtc-4bits两种格式占用内存是一样的如果把一张图片分成两张那么在ios平台是不是占用内存多一倍有透明通道的对于它的图集怎么处理会更好一点">Q2、iOS平台需要对图集做RGB和Alpha通道的分离吗?我发现在同样大小的图片(正方形),RGB Compressed PVRTC 4bits和RGBA Compressed PVRTC 4bits两种格式,占用内存是一样的,如果把一张图片分成两张,那么在iOS平台是不是占用内存多一倍?有透明通道的,对于它的图集怎么处理会更好一点?</h3> <blockquote> <p>通常iOS下是不需要做通道分离的,因为 iOS 通用的 PVRTC 格式支持 Alpha 通道。但目前也有团队反馈,在 iOS 上进行通道分离有助于减少失真,可以在一定程度上提高视觉效果,因此也可以尝试做一个对比。 如果发现占用内存是一样的,那么原始图片是RGB的。如果iOS上做通道分离,内存确实会增加一倍。UI的纹理在iOS下可以直接选择默认的 Compress,在打Atlas时会自动处理成 PVRTC,开发团队可以从Sprite packer窗口来看Atlas的压缩格式做个确认。</p> </blockquote> <h3 id="q3请问unity引擎中使用什么贴图压缩格式可以保证在占用内存相对较小的情况下true-color效果和原图相当同时在ios和android平台上图片的压缩格式分别用什么比较合适有什么需要注意的地方吗">Q3、请问Unity引擎中使用什么贴图压缩格式,可以保证在占用内存相对较小的情况下True Color效果和原图相当?同时在iOS和Android平台上图片的压缩格式分别用什么比较合适?有什么需要注意的地方吗?</h3> <blockquote> <p>目前来讲,并不存在一个所有GPU平台都支持硬件解压的压缩格式。 ETC1 和 PVRTC 分别是Android和iOS上我们最推荐的格式。 但对于透明纹理,ETC1不支持,而 PVRTC 则可能有较大失真,因此更推荐使用 RGBA 16。 一般来说建议直接使用 Unity 默认的压缩格式(即选择 Compressed 即可,不需要做特殊设置),Unity 会做如下处理:</p> <ol> <li>Android 上不带Alpha通道的图片采用 ETC1,带Alpha通道的图片采用True Color中的RGB16,TrueColor中的 RGBA16 会&gt;比 RGBA32 更节省空间,但图像的显示质量会差一些;</li> <li>iOS 上使用 PVRTC,但PVRTC格式要求纹理的长宽相等,且都是2的幂次(即POT,在ImportSettings中可以将NPOT的纹理自动转换成POT)。 另外,针对Android 上的带Alpha通道的图片,还有一种常见的做法,即把Alpha通道独立出来作为另一张纹理,从而将 RGB 部分和 Alpha 部分分别采用 ETC1来压缩,但渲染时就需要自定义的 Shader来处理。 同时,我们不建议直接使用 RGBA32 格式的纹理,因为它占用了很大的内存。一般建议使用 RGBA16 和 ETC 格式的纹理来进行加载。 如果转换到 RGBA16 格式时出现了类似“色阶”的颜色问题,则建议尽可能避免大量的过渡色使用。</li> </ol> </blockquote> <h2 id="网格相关">网格相关</h2> <h3 id="q1我有一个特效依赖了两个fbx我把这两个fbx的这个勾选去掉editor运行正常否则就会宕机请问这是什么原因">Q1、我有一个特效依赖了两个FBX。我把这两个FBX的这个勾选去掉,Editor运行正常。否则就会宕机,请问这是什么原因?</h3> <blockquote> <p>将FBX上的Read/Write Enabled关闭后,内存中便不再保存其Mesh的副本(只存在显存中),因此其属性就不可再被访问和修改。而粒子系统通常需要动态地修改其粒子的顶点属性。因此,理论上来说,供粒子系统使用的Mesh是需要开启Read/Write Enabled的,而在Editor下Mesh和Texture都是强制开启的,所以在真机上就会出现问题。</p> </blockquote> <h2 id="动画片段相关">动画片段相关</h2> <h2 id="音频相关">音频相关</h2> <h3 id="q1请问音频中的-quality-什么意思一般设置为多少合适我拖进去一首歌曲试了一下-在0-和-100-的情况下区别不大但是生成的音频文件大小差别很大">Q1、请问音频中的 Quality 什么意思?一般设置为多少合适?我拖进去一首歌曲,试了一下 在0 和 100 的情况下区别不大,但是生成的音频文件大小差别很大。</h3> <blockquote> <p>Quality 表示在压缩音频时的失真程度(实际上可以认为是压缩算法的一个参数),该值越大,压缩后的文件越大,但音质保留的越好。而对于其失真的程度是视音频数据以及内部的压缩算法而定的,确实会有区别不大的情况。该值的设置原则就是,在音质失真可接受的情况下,越小越好。</p> </blockquote> <h2 id="材质相关">材质相关</h2> <h2 id="shader相关">Shader相关</h2> <h3 id="q1以前端游时代材质根据pass不同光照环境不同可以离线预编译成shadercache运行时并不需要拼材质再实时编译只要加载二进制代码就好了那unity有没有做这件事呢我们是根据平台和环境预编译的shader">Q1、以前端游时代,材质根据Pass不同、光照环境不同可以离线预编译成ShaderCache,运行时并不需要拼材质再实时编译,只要加载二进制代码就好了。那Unity有没有做这件事呢?我们是根据平台和环境预编译的Shader。</h3> <blockquote> <p>对于支持 Binary Shader 加载的设备,在首次编译某个 Shader 的时候是会生成其对应的 Binary Shader Cache ,生成的 Binary 文件位于和 Application.persistantPath 并列的Cache 目录下。</p> </blockquote> <h3 id="q2相同效果前提下就性能而言shader-是用-vf-还是surface好">Q2、相同效果前提下,就性能而言,Shader 是用 V&amp;F 还是Surface好?</h3> <blockquote> <p>Surface生成的V&amp;F比较庞杂,分支较多,如果不注意 #pragma surface 参数的选择,容易出现不必要的开销。举例来说,如果直接用 Unity 5.x 中默认创建的 Surface Shader (默认参数为 #pragma surface surf Standard fullforwardshadows),那么 Shader 是会做 Physically based Standard Lighting 的,而这在移动端开销非常大,且并非必要。</p> </blockquote> <h2 id="字体相关">字体相关</h2> <h3 id="q1我们现在为了美观需要同时使用2套字体但是每增加一套字体就会内存增加50mb左右请问你们在优化其他unity游戏时怎么处理类似情况的">Q1、我们现在为了美观,需要同时使用2套字体。但是每增加一套字体,就会内存增加50MB左右。请问你们在优化其他Unity游戏时,怎么处理类似情况的?</h3> <blockquote> <p>如果美术字不多的话,建议使用单独的字体贴图来进行实现,从而来达到特定美术字显示的效果;如果美术字较多的话,那么仅能建议使用另外一个套字体来进行实现。一般来说,字体的内存量应该也是完全可以控制在10MB以下的。</p> </blockquote> <h2 id="粒子系统">粒子系统</h2> <h2 id="lightmap">Lightmap</h2> <h3 id="q1lightmap在pc上显示正常但是转到android平台上存在色差颜色普遍偏暗">Q1、Lightmap在PC上显示正常,但是转到Android平台上存在色差,颜色普遍偏暗。</h3> <blockquote> <p>一般来讲,有两种情况可能会导致色偏和亮度差异。</p> <ol> <li>Unity烘焙的Lightmap是32bit的HDR图,而移动设备通常不支持HDR图(32bit per channel),会按照LDR图(8bit per channel)的形式进行处理,因此会出现色偏问题。因此我们建议: <ul> <li>在移动平台下使用Mobile/Diffuse材质,可载入Standard Assets(Mobile) package获得。如果要获得更合适的效果,需要自行修改Lightmap的DecodeLightmap函数,该函数可在Unity\Editor\Data\CGIncludes\UnityCG.cginc文件中找到。需要说明的是,这种方法也不能达到与PC端完全一致的效果。</li> <li>如果需要PC和移动平台的显示效果一致,可以用图像编辑软体修改Lightmap為LDR格式,例如PNG(8bit per channel)。</li> <li>为了避免类似问题,请不要使用过于强烈的Light进行烘焙,因為Light的强度(Intensity)越高,色偏问题会越严重。若有阴影丢失时,可以尝试检查一下模型的Lightmapindex、Lightmapscaleoffset、UV2等影响Lightmap采样的一些参数。</li> </ul> </li> <li>另一种可能是存在过曝现象,可以尝试将playersettings -&gt; use direct3d 11关闭,看问题是否解决。</li> </ol> </blockquote> Sat, 18 Jul 2020 00:00:00 +0000 https://0x5143.github.io/2020/07/18/unity_qa/ https://0x5143.github.io/2020/07/18/unity_qa/ unity 分布式实时监控系统(不断更新) <p>在2010年, Google发表了一篇名为”Dapper, a Large-Scale Distributed Systems Tracing Infrastructure”的论文,在论文中介绍了Google在生产环境中大规模分布式系统下的跟踪系统Dapper的设计和使用经验。 实时监控告警和应用性能分析诊断平台,主要包括应用端监控和移动端监控等。</p> <h2 id="为什么需要跟踪系统">为什么需要跟踪系统</h2> <h3 id="故障快速定位">故障快速定位</h3> <p>快速的故障定位非常重要,一个良好设计的系统需要提供快速检测、隔离及修复问题的机制,而快速定位则是这个机制中的重要一环,由于缺乏快速定位的机制,当关键业务出现服务停止或者性能恶化,对业务有直接影响时,由于缺乏有效和快速的应对措施,往往由最终用户的反馈作为整个定位的开始一环,而此时已经是损失很难挽回的时间点了。</p> <h3 id="恶性循环的形成">恶性循环的形成</h3> <p>由于缺乏快速定位机制,那些长期存在的性能低下问题以及重复出现的问题很难精确定位,产生了大量的技术债务。而随着新的功能的不断添加,同样无法尽早或者预先发现问题导致问题也不断的累积。修改自身也可能会导致更多问题的引入,从而形成了恶性循环。</p> <h3 id="现状把握决策支持">现状把握&amp;决策支持</h3> <p>根据跟踪系统提供的数据可以提供及时和全面的性能报告,并且在此基础之上,能够做进一步的分析从而提供决策支持的所需数据,比如根据收集的信息可以对用户行为/模式分析:从而进一步地进行测量和管控。</p> <h2 id="启示1跟踪系统的两个重要需求">启示1:跟踪系统的两个重要需求</h2> <p>在Dapper的论文这样认为,有两个重要的需求需要予以满足:ubiquitous deployment和continuous monitoring。而这两个需求分别是对跟踪系统的作用范围(无死角跟踪)和运行方式(24小时不间断)做出了规范。</p> <h2 id="启示2跟踪系统的三大设计要点">启示2:跟踪系统的三大设计要点</h2> <h3 id="低损耗low-overhead">低损耗(Low overhead)</h3> <p>跟踪系统的接入不应该产生很高的性能损耗。具体要求多低,Dapper论文中使用的是negligible(微不足道的,可以忽略的), 如果从度量的角度来讲,pinpoint的实现是3%一下。</p> <h3 id="应用级透明application-level-transparency">应用级透明(Application-level transparency)</h3> <p>由于业务逻辑的不断增长,如果跟踪系统在设计上无法做到对于应用程序的较小侵入性,会导致大量的维护成本以及引入额外缺陷的机率大大提高。理想的做法是能达到论文中所提到的那样:程序员应该不需要意识到有追踪系统的存在。而在实际的实践中,比如引入zipkin或者pinpoint,或多或少还是需要一定的成本,至少跟踪系统的埋点不与业务逻辑产生紧密耦合还是可以实现的,但是完全无意识追踪系统的存在,在具体的实现上还是需要结合系统特点,不是一蹴而就的目标。</p> <h3 id="扩展性scalability">扩展性(Scalability)</h3> <p>至少跟踪系统应该要能适应未来几年的服务和集群的扩展。</p> <h2 id="启示3跟踪系统的kpi">启示3:跟踪系统的KPI</h2> <ul> <li>采样率:根据Google的实践经验,采样率效率1/16时便能避免明显延迟,性能损耗在实验误差范围之内。跟踪大量服务时,采样率调整到1/1024就有足够的跟踪数据,同时能保证性能损耗非常低。Dapper的第一个生产版本对所有进程采用1/1024的统一采样率,后来演进为可变采样率。</li> <li>损耗:跟踪系统自身的性能影响对于系统应该是能够忽略不计的,pinpoint控制在3%之内。</li> <li>实效性:跟踪数据产生之后,分析的速度需要及时快速,理想状况数据在一分钟之内能够统计出来,以便对于生产环境的异常状况作出快速反应。</li> </ul> <h2 id="启示5准确定位延迟问题">启示5:准确定位延迟问题</h2> <p>当一个系统涉及到多个子系统和多个开发团队的情况下,端到端的性能问题的根本原因定位可以通过跟踪系统来进行。Google通过使用Dapper描述了这种情况下的必需数据,其实践有如下经验:</p> <ul> <li>问题往往来源于一些意想不到的服务之间的交互,问题的纠正往往比较容易,而在Dapper引入之前发现较为困难</li> <li>简化跟踪的接口,使用唯一的追踪ID,便于集成和检测</li> </ul> <h2 id="启示8dapper系统的核心构成">启示8:Dapper系统的核心构成</h2> <p>Dapper是通过trace tree和span构建跟踪系统的。</p> <h3 id="span">span</h3> <p>span是用用于记录一个服务调用的过程的结构,一个典型的跟踪系统中,一次RPC调用会对应到一个的span上,dapper中定义了span相关的如下信息:</p> <p><img src="/files/2018/06/20180611204738876.png" alt="" title="A detailed view of a single span" /></p> <ul> <li>span名称:用于记录span的名称</li> <li>spanid:用于记录span的Id,一般用全局唯一的64位整数表示</li> <li>父spanid:父span的spanid,用于描述跟踪树结构</li> <li>事件信息:cs/cr/sr/ss四种事件类型,span不同的事件类型对应不同的时间戳,根据这些时间戳,可计算出不同阶段的耗时信息。</li> <li>annotation:一般,事件信息在annotation中存放,另外自定义的信息也可以与之关联</li> </ul> <table> <thead> <tr> <th>缩写</th> <th>全称</th> <th>说明</th> </tr> </thead> <tbody> <tr> <td>cs</td> <td>client send</td> <td>客户端/消费者发起请求</td> </tr> <tr> <td>cr</td> <td>client receive</td> <td>客户端/消费者接收到应答</td> </tr> <tr> <td>sr</td> <td>server receive</td> <td>服务端/生产者接收到请求</td> </tr> <tr> <td>ss</td> <td>server send</td> <td>服务端/生产者发送应答</td> </tr> </tbody> </table> <h3 id="trace-tree">trace tree</h3> <p>一个请求可能跟多个服务调用关联,每次服务的调用与一个span进行关联,而span之间通过父spanid进行连接,这样所组成的一个树形结构就是所谓的跟踪树,这个结构显示体现了某一请求的服务调用链的状况。比如论文中的例子即为一个典型的三层架构的例子:具体说明如下:</p> <p><img src="/files/2018/06/20180611204839950.png" alt="" title="The causal and temporal relationships between five spans in a Dapper trace tree." /></p> <table> <thead> <tr> <th>层次</th> <th>服务名称</th> <th>父span</th> <th>调用顺序</th> </tr> </thead> <tbody> <tr> <td>前端</td> <td>Frontend:A</td> <td>无</td> <td>1</td> </tr> <tr> <td>中间</td> <td>MiddleTier:B</td> <td>A</td> <td>2</td> </tr> <tr> <td>中间</td> <td>MiddleTier:C</td> <td>A</td> <td>3</td> </tr> <tr> <td>后端</td> <td>Backend:D</td> <td>C</td> <td>4</td> </tr> <tr> <td>后端</td> <td>Backend:E</td> <td>C</td> <td>5</td> </tr> </tbody> </table> <p><img src="/files/2018/06/20180611204853221.png" alt="" title="The path taken through a simple serving system on behalf of user request X. The letter-labeled nodes represent processes in a distributed system" /></p> <p>这样的一个树形结构,表现出来的调用顺序则是:A-&gt;B-&gt;C-&gt;D-&gt;E。这样的一个简洁的设计,就是dapper的主要构成,可以看出其与业务逻辑基本不相关的一个通用的模型,而在zipkin等的实现中,可以清晰地看到Dapper的整体设计思路。</p> <h2 id="启示9定位全局网络流量和使用率">启示9:定位全局网络流量和使用率</h2> <p>Dapper不是设计用来做链路级的监控的,但是在实践中发现,其比较适合去做集群之间活动性的应用及行为分析,显示集群之间最活跃的网络流量的应用级热点,与传统的网络相关的工具相比,最重要的特点是Dapper可以定位到应用程序级别的根本问题。</p> <h2 id="启示10dapper不太适合的场景">启示10:Dapper不太适合的场景</h2> <p>Dapper的模型的隐含前提是不同的子系统使用同一个被跟踪的请求所产生的连锁链式调用栈的情况。如果是多个追踪请求合并起来,而最终只使用其中的一个的情况则无法很好地对应。 Dapper可以找出性能瓶颈,但是并不一定能准确定位到根本原因,因为定位出很慢的结果往往是由其他请求造成的,而这则需要进一步的分析。 Dapper的设计主要是针对在线服务的系统,尤其是一个用户请求的系统行为,但是离线的情况下,则不能直接使用,需要一些人工的关联和干预行为。</p> <h2 id="dapper的实现">Dapper的实现</h2> <table> <thead> <tr> <th>名称</th> <th>开发商</th> <th>类型</th> <th>源码地址</th> </tr> </thead> <tbody> <tr> <td>zipkin</td> <td>twitter</td> <td>开源</td> <td><a href="https://github.com/openzipkin/zipkin">https://github.com/openzipkin/zipkin</a></td> </tr> <tr> <td>pinpoint</td> <td>naver</td> <td>开源</td> <td><a href="https://github.com/naver/pinpoint">https://github.com/naver/pinpoint</a></td> </tr> <tr> <td>appdash</td> <td>sourcegraph</td> <td>开源</td> <td><a href="https://github.com/sourcegraph/appdash">https://github.com/sourcegraph/appdash</a></td> </tr> <tr> <td>cat</td> <td>大众点评</td> <td>开源</td> <td><a href="https://github.com/dianping/cat">https://github.com/dianping/cat</a></td> </tr> <tr> <td>hydra</td> <td>京东</td> <td>开源</td> <td><a href="https://github.com/odenny/hydra">https://github.com/odenny/hydra</a></td> </tr> <tr> <td>鹰眼</td> <td>阿里巴巴</td> <td>闭源</td> <td>-</td> </tr> </tbody> </table> <h2 id="大众点评的cat实现参考">大众点评的cat实现参考</h2> <h3 id="客户端sdk设计">客户端SDK设计</h3> <table> <thead> <tr> <th>用途</th> <th>示例</th> <th>对应接口</th> </tr> </thead> <tbody> <tr> <td>一段代码执行时间</td> <td>url/sql响应时间</td> <td>Transaction</td> </tr> <tr> <td>一段代码执行次数</td> <td>Exception出现次数</td> <td>Event</td> </tr> <tr> <td>定时执行某些代码</td> <td>分钟粒度CPU,IO</td> <td>HeartBeat</td> </tr> <tr> <td>一个指标的变化值</td> <td>监控销售额</td> <td>Metric</td> </tr> </tbody> </table> <h3 id="消息生命周期">消息生命周期</h3> <p>构建、传输 、分析、存储、展示</p> <h2 id="参考链接">参考链接</h2> <p><a href="https://ai.google/research/pubs/pub36356">Dapper, a Large-Scale Distributed Systems Tracing Infrastructure</a><br /> <a href="http://www.infoq.com/cn/presentations/the-practice-of-open-source-distributed-monitoring-cat-system?utm_source=infoq&amp;utm_medium=videos_homepage&amp;utm_campaign=videos_row2">开源分布式监控 CAT 系统的高可用实践</a><br /> <a href="https://blog.csdn.net/liumiaocn/article/details/80657661">大规模分布式系统的跟踪系统:Dapper设计给我们的启示</a><br /> <a href="https://juejin.im/post/5a7a9e0af265da4e914b46f1">全链路监控(一):方案概述与比较</a></p> Wed, 23 May 2018 00:00:00 +0000 https://0x5143.github.io/2018/05/23/central_application_tracking/ https://0x5143.github.io/2018/05/23/central_application_tracking/ 监控 APM 理解架构之路(不断更新) <h3 id="软件架构的重要性">软件架构的重要性</h3> <ul> <li>软件架构能够满足系统的品质</li> <li>架构设计使受益人达成一致的目标</li> <li>架构设计能够支持计划编制过程</li> <li>架构设计对系统开发的指导性</li> <li>架构设计能够有效地管理复杂性</li> <li>架构设计为复用奠定了基础</li> <li>架构设计能够降低维护费用</li> <li>架构设计能够支持冲突分析</li> </ul> Sat, 28 Apr 2018 00:00:00 +0000 https://0x5143.github.io/2018/04/28/understanding_the_road_to_architecture/ https://0x5143.github.io/2018/04/28/understanding_the_road_to_architecture/ 架构 配置中心的设计与选型(未完成) <p><b>此文未完结,待补充完善</b></p> <h2 id="服务治理">服务治理</h2> <p>配置中心的配置管理和动态配置下发能力,使得它可以承担服务治理中的Control Plane的职责,它可以把服务治理相关的配置下发给实际的执行模块,比如Dubbo,或者是Spring Cloud集成的Netflix治理工具库,比如Hystrix以及Ribbon。</p> <p>服务治理的主要功能包括:</p> <ul> <li>注册/发现,服务实例启动后,将会自动通过Hawk Client向配置中心注册,运维可以在配置中心门户查看服务的实例列表,并进行实例级别的服务治理。</li> <li>负载均衡,调用其他服务时,可以灵活指定其负载均衡策略,如 round robin、hash、consistent hash、weight based round robin等等。</li> <li>路由,配置微服务的路由策略和路由规则,路由策略如本地优先,本地数据中心优先等等,路由规则如白名单,黑名单,流量引导,读写分离,前后端分离,灰度升级等等。</li> <li>限流,针对某个调用方限流对象进行QPS限流,分钟级或秒级,或针对所有微服务客户端进行限流。</li> <li>降级,针对某个降级对象,进行手动或者自动降级(容错),并制定降级策略,抛出异常或特定返回值。</li> <li>容错,针对某个微服务,设定调用超时与重试策略。</li> <li>熔断,针对某个微服务或者RPC方法,设定熔断的触发条件,可以强制熔断,或者自动熔断,也可以取消熔断,运维可以指定熔断参数,比如异常,错误码,失败次数比率,时间窗口,以及窗口请求书等等。</li> </ul> <h2 id="参考资料">参考资料</h2> <ul> <li><a href="http://dockone.io/article/2992">分布式配置中心架构与实战</a></li> </ul> Fri, 23 Feb 2018 00:00:00 +0000 https://0x5143.github.io/2018/02/23/config-center-design/ https://0x5143.github.io/2018/02/23/config-center-design/ 配置中心 Golang 网络通信协议的设计杂谈 <p>当设计网络通信协议时,有许许多多的情况和问题需要考虑。</p> <ol> <li> <p>是广播还是单播?</p> </li> <li> <p>消息应该是有状态还是无状态?</p> </li> <li> <p>协议是可靠服务还是不可靠服务?</p> </li> <li> <p>是否需要响应?</p> </li> <li> <p>您想要使用什么数据格式?</p> </li> <li> <p>消息流是使用突发性的还是稳定性的?<br /> Ethernet和Internet最好使用突发性消息流。</p> </li> <li> <p>有多流同步的需求?</p> </li> <li> <p>建立的是单独的应用还是需要给他人使用的库?</p> </li> </ol> Thu, 08 Feb 2018 00:00:00 +0000 https://0x5143.github.io/2018/02/08/network-protocol-design/ https://0x5143.github.io/2018/02/08/network-protocol-design/ 通信协议 微服务架构杂谈 <p>微服务架构需要关注的技术点</p> <ul> <li>基础设施自动化</li> <li>服务注册与服务发现</li> <li>服务路由</li> <li>服务熔断</li> <li>集中配置</li> <li>统一日志收集</li> <li>服务监控</li> </ul> <p>至少应包含以下功能</p> <ul> <li>服务注册与服务发现</li> <li>负载均衡</li> <li>消息总线,轻量级的MQ或HTTP</li> <li>日志审计,主要是日志的汇总、分类和查询</li> <li>监控和告警,主要是监控每个服务的状态,必要时生产告警</li> <li>部署和升级</li> <li>事件调度机制</li> <li>资源管理,如底层的虚拟机、物理机和网络管理</li> </ul> <p>另外一些可选的功能</p> <ul> <li>微服务统一代码框架,支持多种编程语言</li> <li>统一服务构建和打包</li> <li>统一服务测试</li> <li>微服务CI/CD流水线</li> <li>服务依赖关系管理</li> <li>统一问题追踪调试框架(调用链)</li> <li>灰度分布</li> <li>蓝绿部署</li> </ul> Thu, 08 Feb 2018 00:00:00 +0000 https://0x5143.github.io/2018/02/08/micro-service/ https://0x5143.github.io/2018/02/08/micro-service/ 微服务 刁钻而无意义的笔试题 <p>今天在网上看到有人发表了一篇关于一个无意义而刁钻的笔试题,如</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio&gt;</span><span class="cp"> </span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span> <span class="p">)</span> <span class="p">{</span> <span class="nl">http:</span><span class="c1">//www.yunjing.me</span> <span class="n">printf</span><span class="p">(</span><span class="s">"http://www.yunjing.me</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>让分析这段代码是否存在问题,若有则指出问题所在。</p> <p>知道猫腻后,就打算用Go语文来装一下,然后,结果很神奇。</p> <pre><code class="language-Go">package main func main() { http://www.yunjing.me println("http://www.yunjing.me") } </code></pre> <p>惊叹于Go语言的极简设计理念,这类刁钻的笔试题并无用武之地,Go编译时报错: <code class="language-plaintext highlighter-rouge">label http defined and not used</code></p> Tue, 09 Jan 2018 00:00:00 +0000 https://0x5143.github.io/2018/01/09/trick-label-http/ https://0x5143.github.io/2018/01/09/trick-label-http/ 面试题