ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Gatsby Markdown Table Benchmark 深度解析:用海量 Markdown 表格页面压测节点创建与查询性能

Gatsby Markdown Table Benchmark 深度解析:用海量 Markdown 表格页面压测节点创建与查询性能 Gatsby Markdown Table Benchmark 深度解析用海量 Markdown 表格页面压测节点创建与查询性能【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby本篇文章围绕 Gatsby 官方仓库benchmarks/markdown_table基准测试站点展开完整梳理它的设计初衷、目录结构、数据生成与页面创建流程、运行方式与调参方法并结合仓库源码剖析其作为性能基准测试的局限性。读完本文你将掌握如何复现该基准、如何通过NUM_PAGES与MAX_NUM_ROWS控制压测规模以及为什么官方推荐改用markdown-slug与markdown-id这两个更贴近真实场景的衍生基准。一、基准测试的定位与设计初衷在 benchmarks/markdown_table/README.md 中官方明确说明这个基准测试最初是为了**基准测试 Gatsby 的节点创建node creation与查询运行query running**而创建的手段是创建大量由 Markdown 渲染出来的页面。简单来说它试图回答这样一个问题当站点包含数千个由 Markdown 内容生成的页面时Gatsby 的数据层node 创建、GraphQL 查询与页面构建HTML 生成需要多长时间、消耗多少内存。不过README 同时也坦诚地指出了该基准存在的三个设计问题这正是理解它价值的关键页面生成发生在测试过程之中数据是构建时在内存里生成的sourceNodes中直接createNode这给测量本身增加了额外开销并且会预热warm up节点与内存导致计时结果偏离真实场景重度依赖 Markdown 插件页面内容中包含大量 Markdown 表格表格解析由gatsby-transformer-remark处理占用了大量处理时间使基准结果主要反映的是插件开销而非 Gatsby 核心能力索引键选择README 指出该基准是按slug而非id进行索引的——需要说明的是从当前仓库源码看gatsby-node.js 中createPage的 context 实际传入的是id: step.toString()blank.js 的查询也以fakeMarkdown(id: { eq: $id })为键README 的历史描述与当前代码存在出入这一点恰好也是官方认为该基准不够有代表性的原因之一。正因为以上问题README 明确写道保留此基准主要是出于历史原因historical reasons并建议在需要更接近真实场景的基准时选择父级目录中的markdown-slug与markdown-id两个站点——它们做同样的事但不包含表格且分别按slug和id索引。二、站点结构与核心源码解析benchmarks/markdown_table是一个麻雀虽小、五脏俱全的完整 Gatsby 站点其文件布局如下benchmarks/markdown_table/ ├── gatsby-config.js # 插件配置 ├── gatsby-node.js # 节点创建与页面创建 ├── page-template.js # Markdown 内容生成模板 ├── package.json # 脚本与依赖 ├── scripts/ │ └──>const NUM_PAGES parseInt(process.env.NUM_PAGES || 5000, 10) const template require(./page-template) exports.sourceNodes ({ actions: { createNode } }) { // Create markdown nodes for (let step 0; step NUM_PAGES; step) { createNode({ id: step.toString(), parent: null, children: [], internal: { type: FakeMarkdown, mediaType: text/markdown, content: template(step), contentDigest: step.toString(), }, }) } }注意这里读取的默认值5000不设置环境变量时直接执行gatsby build会构建 5000 个页面详见下文运行方式。页面内容由 page-template.js 动态生成它使用了两个依赖faker生成随机化的英文单词、句子、段落gray-matter将title、slug序列化进 Markdown frontmatter。每个页面内容的 Markdown 结构固定为frontmatter随机标题 随机 slug→## Page #index标题 →### API小节中的一张随机行数的表格 →### More Detail小节中的随机段落。表格生成逻辑如下const MAX_NUM_ROWS parseInt(process.env.MAX_NUM_ROWS || 25, 10) module.exports index ${matter.stringify(, { title: faker.lorem.sentence(), slug: /${faker.helpers.slugify(faker.lorem.sentence())}, }).trim()} ## Page #${index} ### API |Name|Description|Required| |:--:|-----------|--------| ${new Array(faker.random.number(MAX_NUM_ROWS)) .fill(undefined) .map(() |${faker.lorem.word()}|${faker.lorem.sentence()}|${faker.random.boolean()}| .trim() ) .join(\n)} ### More Detail ${faker.lorem.paragraphs()} 由代码可见MAX_NUM_ROWS默认值为25表格实际行数在0 ~ MAX_NUM_ROWS之间随机每行由随机单词Name、随机句子Description和随机布尔值Required组成。这样的内容让gatsby-transformer-remark必须对每张表格进行真实解析从而把 Markdown 表格解析的开销纳入基准测量。2.2 页面创建createPages 与模板查询createPages同样循环NUM_PAGES次为每个节点创建一条路径为/path/${step}/的页面组件指向 src/templates/blank.js并把id放入 contextexports.createPages ({ actions: { createPage } }) { let step for (step 0; step NUM_PAGES; step) { createPage({ path: /path/${step}/, component: require.resolve(./src/templates/blank.js), context: { id: step.toString(), }, }) } }页面模板blank.js通过 GraphQL 按id精确查询对应的fakeMarkdown节点并取出经gatsby-transformer-remark转换后的 HTML用dangerouslySetInnerHTML渲染export default ({ data }) { const markdown data.fakeMarkdown.childMarkdownRemark return ( div h1{markdown.frontmatter.title}/h1 div dangerouslySetInnerHTML{{ __html: markdown.html }} / /div ) } export const query graphql query testing($id: String!) { fakeMarkdown(id: { eq: $id }) { childMarkdownRemark { frontmatter { title } html } } } 也就是说整个基准的负载链条是内存生成 N 个 FakeMarkdown 节点 → transformer-remark 将每篇 Markdown含表格解析成 HTML → 为每个节点创建页面 → 每个页面执行一次按 id 的 GraphQL 查询。任何一环的性能问题节点创建、表格解析、查询执行、页面生成都会反映到最终耗时上。首页 src/pages/index.js 只是一个输出 Hello world! 的静态页面用于占位不参与压测逻辑。2.3 插件配置与依赖清单gatsby-config.js 只启用两个插件module.exports { plugins: [gatsby-plugin-benchmark-reporting, gatsby-transformer-remark], }gatsby-transformer-remark把text/markdown类型的节点解析为 MarkdownRemark 节点产出html与 frontmatter 字段——它正是 README 中所说表格处理耗时的承担者gatsby-plugin-benchmark-reporting基准数据的采集与上报插件详见下文如何观测基准结果。package.json 中可以看到该基准所依赖的环境gatsby ^2.19.5、gatsby-transformer-remark ^2.6.48、faker ^4.1.0、gray-matter ^4.0.2、react ^16.12.0、react-dom ^16.12.0以及用于运行scripts/data-update.ts当前内容为空占位 noop见>NUM_PAGES25000 MAX_NUM_ROWS100 gatsby build即构建一个包含 2.5 万页、每页表格最多 100 行的站点。调大这两个参数可以放大节点创建、表格解析与查询运行的开销形成更强的压力调小则可以快速验证链路是否跑通。3.2 三种启动方式README 提供了两种执行路径其一是先安装依赖再构建# 首次运行只需安装一次依赖 yarn yarn build其二是使用封装好的 bench 脚本yarn bench查看 package.json 中的 scripts 定义yarn bench实际执行的是set -x; gatsby clean; NUM_PAGES${NUM_PAGES:-2000} gatsby build这里有两点值得注意gatsby clean每次 bench 都会先清空.cache与public目录确保从零开始构建避免增量构建缓存干扰测量结果默认值差异gatsby build直接运行时NUM_PAGES未设置则取gatsby-node.js中的默认值5000而yarn bench脚本自身又兜底了一个默认值2000即未设置NUM_PAGES时以 2000 构建。也就是说README 所说的默认 5k 页面针对的是直接gatsby build的情形而yarn bench的默认规模是 2000 页两者并不冲突但容易混淆建议始终显式传入NUM_PAGES以保证结果可复现。其余脚本clean、develop、serve与普通 Gatsby 站点一致分别用于清缓存、开发模式和预览构建产物。3.3 大页面规模下的内存提示在姊妹基准 markdown_slug/README.md 与 markdown_id/README.md 中官方给出了一个大规模构建时的实用建议当页面数量很大时可以通过node --max_old_space_size提高 Node.js 老生代内存上限例如node --max_old_space_size2000 node_modules/.bin/gatsby build页面量级越大节点图、查询结果与 HTML 产物占用的内存越多这一参数对markdown_table这种默认 5000 页、可扩展到 25000 页的基准同样适用。四、如何观测基准结果benchmark-reporting 插件的工作原理基准跑完后结果从哪来答案就在gatsby-plugin-benchmark-reporting。它的核心实现位于 packages/gatsby-plugin-benchmark-reporting/src/gatsby-node.js通过挂载onPreInit、onPreBootstrap、onPreBuild、onPostBuild四个生命周期钩子来采集数据。从源码可以看到它主要记录以下指标时间戳序列bootstrapTime插件加载时刻→benchmarkStart→preInit→preBootstrap→preBuild→postBuild→benchmarkEnd完整覆盖一次构建的各个阶段用于分析时间花在了哪个环节内存快照process.memoryUsage()的rss、heapTotal、heapUsed、external四项环境与版本Node.js 版本、Gatsby 版本、Gatsby CLI 版本、sharp与webpack版本、当前 git commit 哈希与提交时间、CI 标识产物统计public目录下 JS 总大小以及构建产物中 jpg/png/gif 等其他资源的数量构建规模counts.pages直接读取process.env.NUM_PAGES与本文基准的压测规模对应。数据去向有两种模式若设置了BENCHMARK_REPORTING_URL环境变量插件会把 JSON 数据 POST 到该地址并携带BENCHMARK_REPORTING_SECRET作为鉴权头若未设置或设为cli插件会在终端打印Gathered data: json后直接结束——本地复现基准时使用这种模式即可拿到全部测量结果。从源码还可以推断gatsby clean后全新构建会得到initial类型的构建而BENCHMARK_BUILD_TYPE_NEXT可指定后续构建类型如DATA_UPDATE这一机制服务于持续基准测试CI场景。五、为什么要换一个基准与 markdown_slug / markdown_id 的对比README 反复强调该基准的历史属性并指向两个替代者理解其中的对比有助于你选择正确的压测方案维度markdown_tablemarkdown_slug / markdown_id内容形态包含大量随机 Markdown 表格普通 Markdown 页面无表格数据来源构建时在sourceNodes中内存生成FakeMarkdown节点构建前由脚本预生成.md文件存放于根目录markdown-pages经gatsby-source-filesystem读取查询键按idREADME 历史描述为slug一个按slug索引一个按id索引站点基础自定义最小站点基于gatsby-starter-blog定位历史遗留供对照官方推荐的代表性基准从 markdown_slug/gatsby-node.js 与 markdown_id/gatsby-node.js 的实现差异可以直观看到两者的关键区别markdown_slug在createPages中仅把slug放进 context而markdown_id额外传入了id。两个基准的 README 明确指出按slug查询在写作当时慢于按id查询——这正是把二者分开建站、分别测量的原因也是markdown_table被认为按 slug 索引不够有代表性的背景。对比之下markdown_table的劣势一目了然它把内容生成和内容处理混在一次构建中计时被内容生成开销污染表格解析又让 transformer 插件成为瓶颈主导项掩盖了 Gatsby 核心节点/查询管线本身的真实表现。六、总结与使用建议benchmarks/markdown_table是一个值得收藏的历史样本它演示了在 Gatsby 中纯内存生成海量 Markdown 节点 → transformer 解析 → 批量建页 → 按键查询的完整压测链路其NUM_PAGES/MAX_NUM_ROWS双参数调压模型、gatsby clean冷启动测量、以及配套的gatsby-plugin-benchmark-reporting指标采集方案在今天仍有借鉴意义。但如果你需要的是能代表真实站点、结果可解释的 Markdown 类基准请按 README 的建议直接选择想测按slug索引的查询性能 → 参考 markdown_slug/README.md想测按id索引的查询性能 → 参考 markdown_id/README.md想对比两种索引键的性能差异 → 两个站点都跑一遍对比同一NUM_PAGES下的构建耗时与内存数据。无论选择哪一个都建议固定NUM_PAGES、先gatsby clean、并在本地关闭BENCHMARK_REPORTING_URL或设为cli来直接读取终端输出的 JSON 指标保证每次测量可比。【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表