ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

JBrowse2 多格式基因组浏览器配置与部署实战

2026/10/1 9:24:16 拓冰建站 浏览量
JBrowse2 多格式基因组浏览器配置与部署实战 1. 从 JBrowse 1 到 JBrowse 2这次换浏览器到底换了什么如果你手头有一份组装好的基因组、几个 GFF3 注释文件、几 GB 的 BAM 和一堆 BigWig 覆盖度曲线想在浏览器里让合作方自己拖着看那基本绕不开JBrowse2 基因组浏览器。我第一次真正把 JBrowse2 用到生产环境是因为一个多物种比较项目老版本的 JBrowse 1 在渲染同源区块和 Hi-C 热图时已经力不从心而合作方又希望能把 FASTA、GFF3、VCF、BAM、BigWig 这几种多种格式的基因组文件全部塞进同一个界面里切换着看。JBrowse 2 刚好提供了这种一个页面装下整个项目的能力所以我把整套配置从 JBrowse 1 迁了过来。先说清楚它是什么。JBrowse 2 是一个基于 Web 的基因组浏览器底层用 TypeScript React 重写前端渲染画布换成了更现代的图形层后端不依赖任何数据库——它直接读取静态文件。这一点极其关键你不需要装 MySQL、不需要跑一个 Java 服务只要有能提供 HTTP 静态文件访问的服务器nginx、apache、甚至python -m http.server把索引做对的 FASTA、GFF3、BAM 丢上去再写一份 config.json浏览器就能用。它和 JBrowse 1 的差别我用下来最直观的是这四条一是组装assembly概念被强化了参考序列本身成了一个可配置对象支持 FASTA、bgzip 压缩 FASTA、2bit、chrom.sizes 四种来源二是适配器adapter体系成了配置的核心同一类轨道可以挂不同的适配器来读不同格式三是视图变多了线性视图之外还有 Dotplot、Synteny共线性、SV Inspector、Spreadsheet 等做比较基因组时不用再开五个工具四是插件机制正规化了第三方想加功能直接写插件挂进 config 即可。谁适合看这篇分三类。第一类是生信工程师手里有组装和注释结果需要快速搭一个内部浏览平台给实验组用第二类是做群体遗传或比较基因组的研究者要同时看变异、覆盖度和共线性第三类是运维/IT 支持同学被要求把那个基因组网站部署到内网服务器上但对 FASTA 和 GFF3 的区别只有模糊印象。不管哪一类后面这套流程都是照着能跑通的路径写的我会把每个选择背后的理由讲明白也会把踩过的坑直接标出来——因为 JBrowse 2 的坑九成都在索引文件和配置文件这两处。2. 三条安装路线CLI、jbrowse-web、Desktop 该选哪条很多人卡在第一步不是因为不会装而是因为没想清楚自己要交付什么。JBrowse 2 官方其实给了好几条路命令行工具jbrowse/cli、静态包jbrowse-web、桌面端jbrowse-desktop还有一个 Python 侧的jbrowse-jupyter。它们不是替代关系是面向不同场景的。我踩过的第一个坑就是一开始用jbrowse create生成了静态包后来想加轨道又跑去手改 JSON改到第三遍才发现 CLI 已经能一行命令搞定——白白浪费了一个下午。2.1 用 jbrowse/cli 建站最推荐的主力方案这条路适合我要给团队搭一个长期可维护的浏览站点。核心思路是CLI 负责生成静态包和执行配置变更你负责准备文件最后交给 nginx 托管。# 环境Node 18 LTS 以上比较稳先确认版本 node -v npm -v # 全局装 CLI npm install -g jbrowse/cli # 确认可用 jbrowse --version # 生成一个空站点会下载一套预构建的前端资源 jbrowse create /data/web/jbrowse_site生成的目录里最需要记住的是config.json——它是整个站点的配置总入口。其余是打包好的 JS/CSS除非你要改源码否则一辈子不用动。CLI 的价值在于它把改配置这件事变成了命令。比如加组装、加轨道、加文本索引、启动本地管理服务都有对应子命令而且它会帮你校验参数、自动补默认值比手写 JSON 靠谱得多。我现在的习惯是能用 CLI 加的绝不手改 config.json只有 CLI 覆盖不到的字段比如某些视图的默认状态、trackDefaults 里的细粒度样式才手动补。2.2 jbrowse-web 静态包适合只读发布jbrowse create出来的东西本质就是 jbrowse-web。它的特点是纯静态、无后端逻辑、所有状态当前视图、打开的轨道、缩放位置都存在浏览器 localStorage 里。这意味着用户 A 拖到某个区间用户 B 打开页面不会看到同一个位置除非用分享会话生成一个带 session 的链接。没有服务端项目概念多个物种就得多份 config 或多份部署目录。如果你的场景是发布一个固定视图给外部看这条路最省事。但如果你需要多人在同一套配置上协作维护最好还是配一个管理端。2.3 jbrowse-desktop本地看大文件最舒服Desktop 是 Electron 打包的本地应用可以直接读本地磁盘路径不用起 HTTP 服务也没有跨域问题。我一般用它做两件事一是快速验货——拿到别人给的 BAM/GFF3先拖进 Desktop 看一眼格式和坐标对不对再决定怎么部署二是处理超大文件因为不经过网络缩放拖动的手感明显好于远程站点。要注意的是 Desktop 的配置文件和网页端不完全通用。Desktop 支持每个会话独立配置也有全局配置路径层级和网页端那份 config.json 有差异。我遇到过把网页端配置直接粘进 Desktop 结果轨道全红的情况后来才发现是相对路径的问题——Desktop 里的相对路径是相对于应用数据目录不是相对于你那份 config 文件。2.4 三条路线的取舍对照路线是否需要 Node是否可远程访问大文件表现适合场景jbrowse/cli 静态托管建站时需要是取决于网络和索引团队内部/对外发布的正式站点jbrowse-web 静态包不需要可直接下载发行包是同上只读展示、临时演示jbrowse-desktop不需要否本地最好本地检查文件、离线分析jbrowse-jupyter需要 Python否Notebook 内一般分析报告里嵌入式展示选型的判断逻辑其实很简单看数据放在哪。数据在服务器上、要让别人访问就走 CLI 静态托管数据在本机、只有你自己看走 Desktop数据在流程中间、要写进分析报告走 Jupyter 组件。2.5 一个容易忽略的前置动作无论走哪条路动手前先把文件清单列出来标注每个文件的格式、体积、是否已索引。我见过太多装好了发现没数据的情况最后都是索引文件缺失。所以我的习惯是先跑一遍清单检查ls -lh *.fa *.fa.gz *.fai *.gzi *.gff3.gz *.tbi *.bam *.bai *.bw 2/dev/null这条命令能让你一眼看出压缩 FASTA 有没有.fai和.gzi、GFF3 有没有.tbi、BAM 有没有.bai。缺哪个补哪个比装完再排查省事得多。3. 参考序列的三种来源FASTA、bgzip FASTA 与 2bit 怎么选组装assembly是 JBrowse2 配置里最不能出错的一块。它定义了参考序列是什么轨道坐标全都基于它。JBrowse2 支持好几种序列来源选错了不会报错只会让你在缩放时卡到怀疑人生。3.1 未压缩 FASTA .fai能跑但不建议上生产最小可用配置是一个.fa加上samtools faidx生成的.fai。适配器用IndexedFastaAdapter。它的问题很直接文件体积是压缩后的三到四倍浏览器每次要按字节偏移去拉序列网络传输量巨大。samtools faidx genome.fa # 产出 genome.fa.fai.fai是啥你可以把它理解成书的目录每一行记录一条染色体的名字、长度、该染色体序列在文件里的字节偏移、每行碱基数、每行字节数。有了它浏览器想取 chr3 第 1000000 到 1001000 这段就能直接算出字节位置去读而不用把整个 FASTA 下载下来。这就是索引式访问的全部秘密。3.2 bgzip FASTA .fai .gzi生产环境首选这是我最推荐的方案。要点是通过bgzipblock gzip分块压缩而不是普通 gzip。bgzip 把文件切成很多小块每块独立压缩所以支持随机跳转读取普通 gzip 是连续压缩流想读中间一段必须从头解压浏览器根本扛不住。# 安装 htslib 后一般会带上 bgzip bgzip -c genome.fa genome.fa.gz # 对 bgzip 后的文件建索引会同时产出 .fai 和 .gzi samtools faidx genome.fa.gz # 确认三个文件都在 ls -lh genome.fa.gz genome.fa.gz.fai genome.fa.gz.gzi这里有个特别容易搞混的点我必须强调.gzi不是普通 gzip 的索引。它记录的是 bgzip 压缩块在文件中的位置只有 bgzip 生成的文件才有。如果你用gzip -c genome.fa genome.fa.gz去做samtools faidx大概率会报错或者产不出有效的.gzi。我看到过有人反复检查配置最后发现问题只是压缩命令用错了。对应的配置片段长这样{ type: BgZipFastaAdapter, fastaLocation: { uri: data/genome.fa.gz }, faiLocation: { uri: data/genome.fa.gz.fai }, gziLocation: { uri: data/genome.fa.gz.gzi } }3.3 2bit 格式体积最小加载最快2bit 是 UCSC 发明的二进制序列格式把每个碱基压到 2 bit因为只有四种碱基外加一个 N 区域的掩码。它比 bgzip FASTA 还小、读取还快缺点是工具链支持不如 FASTA 通用很多下游软件不认。# UCSC 工具集中的 faToTwoBit faToTwoBit genome.fa genome.2bit # 顺便生成 chrom.sizes后面有用 twoBitInfo genome.2bit genome.chrom.sizes配置{ type: TwoBitAdapter, twoBitLocation: { uri: data/genome.2bit }, chromSizesLocation: { uri: data/genome.chrom.sizes } }注意chromSizesLocation不是必须的但如果提供了TwoBitAdapter就不用把整个 2bit 文件读进来算染色体长度首屏会快很多。这一步在小基因组上感觉不明显在人类级别基因组上差别是秒开和等十秒的区别。3.4 只有 chrom.sizes不给序列只要坐标轴还有一种情况你手上没有参考序列比如涉及授权问题不能公开但想展示注释和覆盖度。这时可以用ChromSizesAdapter{ type: ChromSizesAdapter, chromSizesLocation: { uri: data/genome.chrom.sizes } }这样浏览器会画出坐标轴和所有轨道只是序列那一行是空的。做展示页面时挺实用。3.5 refNameAliaseschr1、1、NC_000001 三种叫法打架怎么办这是 JBrowse2 配置里最隐蔽也最常踩的坑。你的 FASTA 里染色体叫chr1注释文件里叫1另一个测序批次里叫NC_000001.11。这三份数据放一起轨道要么什么都不显示要么报reference not found。解决办法是配一个别名文件# aliases.txt制表符分隔带表头 alias refName 1 chr1 NC_000001.11 chr1 2 chr2然后在 assembly 里挂上去{ refNameAliases: { adapter: { type: RefNameAliasAdapter, location: { uri: data/aliases.txt } } } }用 CLI 加组装时可以直接带参数jbrowse add-assembly genome.fa.gz \ --out /data/web/jbrowse_site \ --name myGenome \ --type BgZipFastaAdapter \ --refNameAliases aliases.txt \ --load copy--load copy会把文件复制进站点目录--load inPlace只写路径不搬文件。我推荐在跨目录、跨机器的场景下用copy因为路径问题能少一半如果文件已经在站点目录或另挂了对象存储就用inPlace并在 URI 里写绝对路径或 CDN 地址。注意别名文件要覆盖所有可能出现过的写法宁可多写几行。漏一个写法用户就会在某个轨道上看到空白然后来找你说网站坏了。4. 轨道与适配器把 GFF3、VCF、BAM、BigWig 一次配齐适配器adapter是 JBrowse2 配置的灵魂。轨道track定义这是什么、长什么样适配器定义数据从哪来、怎么读。同一个 GFF3 文件用Gff3Adapter和Gff3TabixAdapter加载性能差几十倍。4.1 各格式对应的适配器速查数据内容文件格式推荐适配器需要的索引基因/转录本注释GFF3 / GTFGff3TabixAdapter.tbibgzip tabix区间特征BEDBedTabixAdapter.tbi变异位点VCFVcfTabixAdapter.tbi 或 .csi比对结果BAM / CRAMBamAdapter / CramAdapter.bai / .crai覆盖度、信号BigWigBigWigAdapter无自带索引区间注释BigBedBigBedAdapter无自带索引共线性PAF / anchors对应的共线性适配器视格式而定三维互作.hic / .coolHicAdapter / MCoolAdapter无格式自带这张表建议贴在工位上。九成的配置失败都是选了不带索引的适配器 文件很大。4.2 注释轨道GFF3 必须 bgzip tabix原始 GFF3 如果是几十 MB 甚至 GB 级用Gff3Adapter会让浏览器把整个文件拉下来再解析基本等于不可用。正确做法# 排序tabix 要求按坐标有序 jbrowse sort-gff annotations.gff3 annotations.sorted.gff3 # bgzip 压缩 bgzip annotations.sorted.gff3 # 建 tabix 索引 tabix -p gff annotations.sorted.gff3.gz配置片段{ type: FeatureTrack, trackId: genes_myGenome, name: 基因注释, assemblyNames: [myGenome], adapter: { type: Gff3TabixAdapter, gffGzLocation: { uri: data/annotations.sorted.gff3.gz }, index: { location: { uri: data/annotations.sorted.gff3.gz.tbi } } } }CLI 一行搞定jbrowse add-track annotations.sorted.gff3.gz \ --out /data/web/jbrowse_site \ --assemblyNames myGenome \ --indexFile annotations.sorted.gff3.gz.tbi \ --name 基因注释 \ --load copy要提醒两件事。第一排序是硬要求。没排序的 GFF3 建 tabix 会失败或者索引建出来但查询返回空。我遇到过一份从实验室内部系统导出的 GFF3按 gene 分组排列的看起来整整齐齐但坐标是乱的tabix 建完看起来成功了实际随机查询全是空结果排查了一小时才发现。第二如果同一个物种有多份注释比如不同版本的基因集给它们起不同trackId否则后加的会覆盖前面的。4.3 变异轨道VCF 的索引选择bgzip variants.vcf tabix -p vcf variants.vcf.gz{ type: VariantTrack, trackId: variants_myGenome, name: 变异位点, assemblyNames: [myGenome], adapter: { type: VcfTabixAdapter, vcfGzLocation: { uri: data/variants.vcf.gz }, index: { location: { uri: data/variants.vcf.gz.tbi } } } }有个细节值得说人类染色体超过 5 亿碱基时.tbi索引会失效tabix 的格式限制必须用.csitabix -p vcf -C variants.vcf.gz # -C 生成 csi然后在配置里把 index 的 uri 指向.csi。这个坑我在一条拼接了多个 contig 的长序列上踩过.tbi建得出来查询也偶尔返回但拖动到某个区间就整条轨道报错。4.4 比对轨道BAM 与 CRAM 的差别BAM 配置相对简单{ type: AlignmentsTrack, trackId: reads_myGenome, name: 比对结果, assemblyNames: [myGenome], adapter: { type: BamAdapter, bamLocation: { uri: data/reads.bam }, index: { location: { uri: data/reads.bam.bai } } } }CRAM 多一个必填项sequenceAdapter。因为 CRAM 是参考序列差异压缩读的时候必须能拿到参考序列来还原{ type: AlignmentsTrack, trackId: cram_myGenome, name: CRAM 比对, assemblyNames: [myGenome], adapter: { type: CramAdapter, cramLocation: { uri: data/reads.cram }, craiLocation: { uri: data/reads.cram.crai }, sequenceAdapter: { type: BgZipFastaAdapter, fastaLocation: { uri: data/genome.fa.gz }, faiLocation: { uri: data/genome.fa.gz.fai }, gziLocation: { uri: data/genome.fa.gz.gzi } } } }把 CRAM 里那个sequenceAdapter理解成解码器需要的字典就好。忘了写轨道会直接报错还算好排查写错参考版本比如用了另一个组装它会读出来一堆错位的比对那就很隐蔽了所以要养成核对 FASTA 版本的习惯。4.5 BigWig 与 BigBed最省心的两种格式这两种是二进制带索引格式给个 URI 就能用不需要额外索引文件{ type: QuantitativeTrack, trackId: coverage_myGenome, name: 覆盖度, assemblyNames: [myGenome], adapter: { type: BigWigAdapter, bigWigLocation: { uri: data/coverage.bw } } }这也解释了为什么我建议把 bedGraph 转成 BigWig 再上传一个 5 GB 的 bedGraph 在网页上基本没法用转成 BigWig 可能只有 300 MB而且实时查询秒回。常用转换工具是bedGraphToBigWigUCSC或wigToBigWig。4.6 多轨道配置的实战顺序一个真实站点的建议顺序是先加 assembly序列再加参考注释轨道再加变异再加比对最后加各类信号轨道。原因很简单前面出问题会直接导致后面全部无法显示按依赖顺序做报错定位最清晰。5. config.json 的真实结构手改与 CLI 的边界在哪跑通几条轨道之后你迟早要直接读那份config.json。它其实不复杂但版本之间字段有过调整所以我在这一节会把结构讲清楚同时给出什么时候用 CLI、什么时候手改的判断标准。5.1 顶层字段一览{ assemblies: [], tracks: [], connections: [], defaultSession: {}, trackDefaults: {}, plugins: [] }assemblies参考序列定义前面那一大段。tracks所有轨道每条是一个对象数组元素。connections远程数据源连接比如连接到某个公开数据库或内部 API。defaultSession打开页面时默认长什么样——默认打开哪些视图、哪些轨道、定在哪个区间。这个字段是官网首页效果的关键。trackDefaults按轨道类型给默认样式比如所有 BAM 轨道的覆盖度颜色。plugins插件数组。管理端、第三方功能都靠它加载。部分较新版本把配置拆分成了每个数据集一份配置的形式字段名可能略有差异。判断方法很土但有效拿 CLI 生成一份空站点看它自带 config.json 长什么样那就是你这个版本的标准答案。5.2 defaultSession决定别人打开页面的第一眼这个字段我强烈建议认真配。默认情况下用户打开站点只看到一个空的线性视图还得自己点打开轨道。而配好defaultSession之后一进去就是排好的轨道、定位在目标区域{ defaultSession: { name: 默认视图, views: [ { type: LinearGenomeView, id: lgv-default, tracks: [ { type: ReferenceSequenceTrack, configuration: myGenome-ReferenceSequenceTrack, id: ref-track }, { type: FeatureTrack, configuration: genes_myGenome, id: genes-track } ] } ] } }最省事的做法是用 CLI 的set-default-session子命令把当前浏览器里的会话也就是你调好的状态导出成默认配置。手写这段很容易出错因为 track 的引用方式在不同版本里稍有区别。5.3 trackDefaults一次改样式全局生效{ trackDefaults: { alignments: { coverage: { color: #4a6fa5 } }, featureTracks: { displayMode: normal } } }这个字段在轨道很多的时候特别值。三十条 BAM 轨道一条一条改颜色是自虐写在这里一次搞定。5.4 手改 vs CLI 的判断标准我的经验是三条规则增删 assembly、track、connection优先用 CLI它会帮你生成合法的 trackId处理索引字段不容易写错结构。改样式、改默认会话、加插件可以直接手改 JSON但改完用python -m json.tool config.json校验语法JSON 不允许尾逗号一个多余的逗号会让整个站点白屏。批量操作写脚本处理。比如你有两百个 BigWig 要加用循环调 CLI 比手写两百个对象快得多也更不容易漏。# 批量加 BigWig 轨道 for f in bw/*.bw; do name$(basename $f .bw) jbrowse add-track $f \ --out /data/web/jbrowse_site \ --assemblyNames myGenome \ --name $name \ --load copy done5.5 一个真实的白屏复盘有次我部署完页面纯白控制台报 JSON 解析错误。原因是复制配置片段时留了一个尾逗号而这份 config 有四百多行肉眼扫了三遍没找到。最后是用一个简单办法定位的把 config 喂给 Python 的 json 模块让它报出行号。python3 -c import json;json.load(open(config.json))它直接告诉我第 312 行有问题。从那以后我改完配置必跑这一段三秒钟的事省下一次深夜排查。6. 索引、跨域与静态托管部署阶段最容易翻车的三处本地能跑了不代表线上能用。我统计过自己遇到的上线问题八成集中在这三块文件服务不支持范围请求、跨域缺头、以及压缩方式不对。6.1 范围请求为什么你的 BAM 加载一半卡死JBrowse2 读大文件用的是 HTTP Range 请求也就是只取文件的第 1024 到 2048 字节。如果服务器不支持 Range它对每个请求都返回整个文件一个 5 GB 的 BAM 会被反复全量下载浏览器内存直接爆掉。nginx 默认支持 Range一般不用配。但如果前面挂了反向代理、CDN 或某些对象存储网关可能就丢了。验证方法很直接curl -I -H Range: bytes0-99 https://your.site/data/reads.bam看返回码是不是206 Partial Content以及响应头有没有Content-Range。返回200就是没生效。6.2 跨域把数据放在另一个域名时的必需头如果 config 和数据不在同一个域名下比如站点在browser.example.com数据在data.example.com或对象存储必须让数据服务返回跨域头。nginx 大致这样配location /data/ { add_header Access-Control-Allow-Origin * always; add_header Access-Control-Allow-Methods GET, HEAD, OPTIONS always; add_header Access-Control-Allow-Headers Range, Content-Type always; add_header Access-Control-Expose-Headers Content-Range, Accept-Ranges, Content-Length always; types { application/gzip gz bgz; application/octet-stream bam bai cram crai tbi csi 2bit bw bb; } }两个要点Access-Control-Expose-Headers里必须带上Content-Range否则前端读不到分段信息Access-Control-Allow-Headers里必须允许Range否则预检请求就被拦了。我当初就是漏了Range浏览器控制台只给出一个笼统的跨域错误不点进 Network 面板看预检请求根本发现不了。6.3 MIME 类型别让服务器把 .gz 当文本有些服务器的 MIME 映射不给力.gz、.bam之类的文件会被当成 text/plain 返回。多数情况下浏览器能容忍但某些代理会做内容嗅探甚至改写导致读取失败。上面那段types就是干这个的顺手加上没坏处。6.4 目录结构与相对路径的规划我现在的标准结构是这样jbrowse_site/ ├── config.json ├── index.html ├── assets/ └── data/ ├── genome.fa.gz ├── genome.fa.gz.fai ├── genome.fa.gz.gzi ├── annotations.sorted.gff3.gz ├── annotations.sorted.gff3.gz.tbi ├── variants.vcf.gz ├── variants.vcf.gz.tbi └── coverage.bwconfig 里统一写data/xxx这种相对 URI。好处是整套站点可以整体打包搬走换域名、换路径都不用改配置。如果数据量特别大、要放对象存储就把 URI 换成完整地址同时把上一节的跨域头配好。6.5 内网部署与容器化的取舍内网纯静态部署是最省心的一个 nginx 容器挂载jbrowse_site目录就完事。如果是多团队共用、需要在线改配置可以起一个容器跑管理端把站点目录挂进去。但要注意管理端默认是无鉴权的别直接暴露在无保护的网络上至少加一层认证。7. 排查实录六个报错背后的真实原因这一节我把遇到过的问题按现象—排查链路—根因—修复写出来你可以照着复现这个思路。7.1 轨道显示空白控制台 404现象加了一条 GFF3 轨道界面显示轨道存在但没有任何特征。排查链路打开浏览器 Network 面板过滤文件名 → 看到.tbi请求返回 404 → 检查本地文件发现.tbi确实没生成因为压缩时用的是gzip而不是bgzip。根因适配器需要 tabix 索引索引缺失时它不会报错只是返回空数据。修复用 bgzip 重压、tabix 重建索引同时确认.tbi和.gz在同一个目录、URI 指向正确。7.2 序列行能显示但所有轨道都提示坐标找不到现象要点很明确——序列出现了注释轨道的特征一个都不显示偶尔报 reference 相关错误。排查链路对比 FASTA 的.fai里染色体的名字和 GFF3 第一列的名字。发现 FASTA 用chr1GFF3 用1。根因refName 不一致。修复加refNameAliases别名文件。加完之后如果还是不显示检查别名文件的分隔符——必须是制表符用空格分隔会被解析成一行。7.3 页面白屏什么都不显示现象本地改了 config 后直接白屏。排查链路控制台看是否有 JSON 解析错误 → 用python3 -c import json;json.load(open(config.json))定位行号。根因JSON 尾逗号或括号不匹配。修复删掉多余逗号重新加载页面。7.4 BAM 轨道加载缓慢拖动卡顿现象能显示但每次拖动都要等好几秒。排查链路Network 面板看.bam请求的响应大小发现每个请求都返回整个文件。根因服务器不支持 Range 请求。修复检查反向代理配置确认proxy_set_header Range $http_range;之类的转发头没被吃掉用curl -I -H Range: ...验证返回 206。7.5 长染色体上的变异轨道读取失败现象短 contig 上的变异正常显示超长染色体的某个区间报错。排查链路检查索引类型发现是.tbi。根因.tbi对大染色体有坐标范围限制。修复改用.csi索引并在配置里把 index 指向.csi文件。7.6 同一个 trackId 导致配置互相覆盖现象加了两条注释轨道只剩一条。排查链路直接搜 config.json 里的trackId发现两条重名。根因trackId在站点内要求唯一重复时后加的会覆盖或者加载异常。修复给每条轨道加唯一后缀比如genes_v1、genes_v2。用 CLI 加轨道时它一般会自动生成唯一 ID这也是我推荐 CLI 的原因之一。7.7 一份排查顺序清单遇到问题别乱翻配置按这个顺序走会快很多看 Network 面板有没有 404。索引文件优先怀疑。看有没有 CORS 报错检查预检请求和Range头。看数据是否真的返回了响应大小是否为 0。检查 refName 是否一致。检查 JSON 语法。检查trackId是否重复。检查适配器类型和文件格式是否匹配。这七步走完绝大部分问题都能定性。8. 进阶玩法文本搜索、共线性视图与插件扩展基础站点跑通之后可以往上加东西了。这部分是我觉得 JBrowse2 相对同类工具真正有优势的地方。8.1 给注释加全文检索用户最想要的功能往往不是能看而是能搜。输入一个基因名直接跳过去比手动找坐标友好太多。JBrowse2 支持基于 trix 的文本索引jbrowse text-index \ --out /data/web/jbrowse_site \ --tracks genes_myGenome \ --attributes Name,ID,Description跑完会在数据目录生成索引文件config 里对应的轨道会多出文本索引字段。要注意两点一是索引会额外占空间基因集很大时可能几百 MB二是改了注释文件之后索引必须重建否则搜出来的位置是旧的。我一般把重建索引写进数据更新脚本里跟文件替换绑在一起。8.2 Dotplot 与共线性视图比较基因组的主场做物种间比较、或者组装版本之间的比对时Dotplot 视图特别好用。数据源通常是 PAF 或者 anchors 文件jbrowse add-track alignment.paf \ --out /data/web/jbrowse_site \ --assemblyNames genomeA,genomeB \ --name A vs B 比对由于 JBrowse2 的共线性适配器在不同版本中命名和参数有过调整有的叫 PAF 相关适配器有的走 anchors我建议直接用jbrowse add-track --help看当前版本的可用类型或者参考官方配置手册里对应的 JSON 结构不要照抄旧版本的配置片段。这是我升级过一次版本之后学到的教训升级后共线性轨道的配置字段名变了站点里一半轨道直接消失。8.3 Hi-C 与三维互作数据.hic和.cool格式都有对应适配器配上后可以在线性视图下方展示互作热图。数据体积通常很大强烈建议放在支持 Range 的对象存储上并确认跨域头齐全。这类轨道的分辨率设置会影响加载量调得太细会导致每次移动都拉取大量数据实践中先从较粗分辨率开始需要细节再放大。8.4 插件把内部功能接进去插件是这个体系里最有意思的部分。它允许你把自定义的轨道类型、自定义渲染、甚至整套数据接入逻辑打包成 JS挂到 config 的plugins数组里。我见过几种典型用法接入内部 API让浏览器实时查询后端数据库而不是读静态文件。自定义渲染逻辑比如按某个分类字段给特征染色。加一个内部专用的侧边栏面板展示样本元信息。挂插件的方式大致是{ plugins: [ { name: MyInternalPlugin, url: plugins/my-plugin.js } ] }如果用的是管理端它会自动往plugins里写管理端插件的条目。这里有个小坑管理端启动后如果页面提示找不到对应插件检查plugins数组是不是被别的操作覆盖了——我遇到过用脚本重写 config 时把plugins整个清空的情况管理端就再也起不来了。8.5 版本升级先备份再升级后核对# 备份 cp -r /data/web/jbrowse_site /data/web/jbrowse_site.bak # 升级静态资源config.json 通常会保留但一定要核对 jbrowse upgrade --out /data/web/jbrowse_site升级之后必做三件事打开页面看有没有白屏、确认所有轨道还能加载、跑一遍文本搜索。我那次共线性轨道消失就是因为升级前没备份、升级后没核对最后花了半天才把配置修回来。8.6 性能上的一些实操经验最后分享几条在真实站点上总结的经验。轨道数量控制在二十条以内会让首屏舒服很多如果确实很多把它们分成多个defaultSession视图让用户按需打开而不是全部默认加载。比对轨道默认别开覆盖度模式BAM 的覆盖度计算在低倍缩放下开销很大只保留错配视图会顺滑得多。数据尽量同源部署跨域虽然能配通但每次请求多一次预检轨道多了之后累积的延迟相当明显。还有一点config.json 改动后一定要强制刷新浏览器清缓存或者用无痕窗口因为页面资源有缓存你可能改对了却看到旧效果白白怀疑自己。提示每次大改配置之前把 config.json 复制一份带日期的备份。这个习惯我已经保持了两年多回滚过至少五次。