[{"data":1,"prerenderedAt":11584},["ShallowReactive",2],{"post:\u002Fposts\u002Fmx-player-eac3-custom-codec":3,"posts:all":315},{"id":4,"title":5,"body":6,"date":301,"description":302,"extension":303,"meta":304,"navigation":305,"path":306,"seo":307,"stem":308,"tags":309,"__hash__":314},"posts\u002Fposts\u002Fmx-player-eac3-custom-codec.md","解决MX Player不支持 EAC3 格式和找不到自定义编解码器的问题（提示 3.1.0 ARMv8 NEON）",{"type":7,"value":8,"toc":290},"minimark",[9,13,21,24,31,35,38,41,45,48,54,57,60,155,158,161,164,167,173,192,195,211,214,217,220,223,229,232,238,241,245,248,254,257,260],[10,11,12],"p",{},"最近看美剧换了一家字幕组，用电脑看没问题，但是在手机上用 MX Player 看视频，画面正常但没声音，提示「不支持此音频格式（EAC3）」。",[10,14,15],{},[16,17],"img",{"alt":18,"src":19,"width":20},"MX Player 播放界面提示「不支持此音频格式（EAC3）」","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F01-eac3-not-support.jpg",900,[10,22,23],{},"去搜解决办法，说要装自定义编解码器。跟着教程下了一个装上，结果又提示「找不到自定义编解码器，请使用 3.1.0 ARMv8 NEON 版编解码器」。",[10,25,26],{},[16,27],{"alt":28,"src":29,"width":30},"解码器设置页弹窗：找不到自定义编解码器，请使用 3.1.0 ARMv8 NEON 版编解码器","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F02-codec-not-found.jpg",360,[32,33,34],"h2",{"id":34},"为什么会没声音",[10,36,37],{},"EAC3（Dolby Digital Plus）是杜比的专利音频格式，常见于 MKV 电影和流媒体片源。MX Player 因为授权费的问题，从 1.7.32 版本开始把 AC3、EAC3、DTS 这些格式的解码能力移除了。所以视频能播，声音出不来。",[10,39,40],{},"MX Player 支持加载自定义编解码器。社区有人用官方开源的 FFmpeg 代码重新编译了一份，把被删掉的格式加了回来。",[32,42,44],{"id":43},"编解码器版本和-app-版本不是一回事","编解码器版本和 App 版本不是一回事",[10,46,47],{},"MX Player 的自定义编解码器版本号和 App 版本号是两套独立编号。关于页面里我的 App 是 3.2.5，要的编解码器却是 3.1.0，两个数字对不上很正常，不能拿 App 版本号去下编解码器。",[10,49,50],{},[16,51],{"alt":52,"src":53,"width":30},"关于页面，显示 MX 播放器 版本 3.2.5 (ARMv8 NEON)","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F03-about-version.jpg",[10,55,56],{},"具体用哪个版本，以 MX Player 自己提示的为准。打开 MX Player，点右上角三个点 → 设置 → 解码器，滑到底部找到「自定义编解码器」，点进去就会显示需要哪个版本。我的提示是「请使用 3.1.0 ARMv8 NEON 版编解码器」，其中的「ARMv8 NEON」就是 neon64 架构。",[10,58,59],{},"各版本对应关系：",[61,62,63,79],"table",{},[64,65,66],"thead",{},[67,68,69,73,76],"tr",{},[70,71,72],"th",{},"MX Player 版本",[70,74,75],{},"编解码器版本",[70,77,78],{},"下载地址",[80,81,82,99,113,127,141],"tbody",{},[67,83,84,88,91],{},[85,86,87],"td",{},"3.0.13 ~ 3.2.5",[85,89,90],{},"3.1.0",[85,92,93],{},[94,95,96],"a",{"href":96,"rel":97},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases\u002Ftag\u002Fv3.1.0_r1",[98],"nofollow",[67,100,101,104,107],{},[85,102,103],{},"2.7.x",[85,105,106],{},"2.7.0",[85,108,109],{},[94,110,111],{"href":111,"rel":112},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases\u002Ftag\u002Fv2.7.0_r0",[98],[67,114,115,118,121],{},[85,116,117],{},"2.6.x",[85,119,120],{},"2.5.0",[85,122,123],{},[94,124,125],{"href":125,"rel":126},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases\u002Ftag\u002Fv2.5.0_r0",[98],[67,128,129,132,135],{},[85,130,131],{},"2.5.x \u002F 2.1.x",[85,133,134],{},"1.87.0",[85,136,137],{},[94,138,139],{"href":139,"rel":140},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases\u002Ftag\u002Fv1.87.0_r6",[98],[67,142,143,146,149],{},[85,144,145],{},"1.93.3 Pro \u002F 1.96.x",[85,147,148],{},"1.87.0（文件标 1.90.1）",[85,150,151],{},[94,152,153],{"href":153,"rel":154},"https:\u002F\u002Fgithub.com\u002FUSBhost\u002FMX_FFmpeg\u002Freleases\u002Ftag\u002F1.90.1v2",[98],[10,156,157],{},"两个仓库的区别：USBhost 是原作者（XDA 论坛的），更新停在 1.90.1v2；iDNA420 是后来接棒维护的，2.x 和 3.x 的版本都在那边。用新版 MX Player 的直接去 iDNA420。",[10,159,160],{},"每个 release 一般有 5 个文件：aio（全量）、neon64、neon、x86、x86_64。懒得查架构就下 aio。",[32,162,163],{"id":163},"下载编解码器",[10,165,166],{},"进上表里的 release 页面，点开 Assets，找到你要的那个 zip：",[10,168,169],{},[16,170],{"alt":171,"src":172,"width":20},"GitHub 上 v3.1.0_r1 的 release 页面，Assets 里列出 5 个 zip 文件","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F04-github-release.jpg",[174,175,176,185],"ul",{},[177,178,179,180,184],"li",{},"不确定架构 → 下 ",[181,182,183],"strong",{},"aio-v3.1.0_r1.zip","（全量包，MX Player 自动选，25MB 左右）",[177,186,187,188,191],{},"ARMv8 NEON（现在几乎所有手机都是这个）→ 下 ",[181,189,190],{},"neon64-3.1.0-build_1.zip","（6MB 左右）",[10,193,194],{},"懒得点页面，直接用直链：",[174,196,197,204],{},[177,198,199,200],{},"全量包：",[94,201,202],{"href":202,"rel":203},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases\u002Fdownload\u002Fv3.1.0_r1\u002Faio-v3.1.0_r1.zip",[98],[177,205,206,207],{},"neon64：",[94,208,209],{"href":209,"rel":210},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases\u002Fdownload\u002Fv3.1.0_r1\u002Fneon64-3.1.0-build_1.zip",[98],[10,212,213],{},"zip 不用解压，原样放到手机里就行，建议放 Download 目录，MX Player 会自动扫描到。",[32,215,216],{"id":216},"安装",[10,218,219],{},"打开 MX Player → 设置 → 解码器 → 自定义编解码器，点进去选择你刚下载的 zip 文件。",[10,221,222],{},"选好之后会弹出确认框，显示文件名、路径、大小，点「确定」。",[10,224,225],{},[16,226],{"alt":227,"src":228,"width":30},"确认使用自定义编解码器，文件名 neon64-3.1.0-build_1.zip","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F05-codec-confirm.jpg",[10,230,231],{},"然后提示「重启应用以重新载入编解码器」，点确定，MX Player 会自动重启。",[10,233,234],{},[16,235],{"alt":236,"src":237,"width":30},"提示「重启应用以重新载入编解码器。」","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F06-restart.jpg",[10,239,240],{},"再打开之前没声音的那个视频，应该就正常了。",[32,242,244],{"id":243},"装了还是没声音","装了还是没声音？",[10,246,247],{},"可以试试在播放界面切换解码器。播放时点右上角的解码器图标，在「硬件解码器」「硬件解码器+」「软件解码器」之间切换试试，有时候换一个就出声了。",[10,249,250],{},[16,251],{"alt":252,"src":253,"width":20},"选择解码器弹窗：硬件解码器 \u002F 硬件解码器+ \u002F 软件解码器","\u002Fimages\u002Fmx-player-eac3-custom-codec\u002F07-decoder-select.jpg",[10,255,256],{},"如果是 Android TV 上用，记得给 MX Player 开存储权限，不然它可能读不到你放的 zip 文件。",[32,258,259],{"id":259},"相关文档",[174,261,262,269,276,283],{},[177,263,264,265],{},"MX Player 官方说明「Audio does not play」：",[94,266,267],{"href":267,"rel":268},"https:\u002F\u002Fsupport.mxplayer.in\u002Fsupport\u002Fsolutions\u002Farticles\u002F43000608838-audio-does-not-play-",[98],[177,270,271,272],{},"自定义编解码器原始帖（XDA 论坛，2013 年开帖至今仍在更新）：",[94,273,274],{"href":274,"rel":275},"https:\u002F\u002Fxdaforums.com\u002Ft\u002Fmx-player-custom-codec-ac3-dts-mlp-truehd-etc.2156254\u002F",[98],[177,277,278,279],{},"编解码器仓库 iDNA420\u002FMX-FFmpeg（含 2.x \u002F 3.x）：",[94,280,281],{"href":281,"rel":282},"https:\u002F\u002Fgithub.com\u002FiDNA420\u002FMX-FFmpeg\u002Freleases",[98],[177,284,285,286],{},"编解码器仓库 USBhost\u002FMX_FFmpeg（原维护者）：",[94,287,288],{"href":288,"rel":289},"https:\u002F\u002Fgithub.com\u002FUSBhost\u002FMX_FFmpeg\u002Freleases",[98],{"title":291,"searchDepth":292,"depth":292,"links":293},"",3,[294,296,297,298,299,300],{"id":34,"depth":295,"text":34},2,{"id":43,"depth":295,"text":44},{"id":163,"depth":295,"text":163},{"id":216,"depth":295,"text":216},{"id":243,"depth":295,"text":244},{"id":259,"depth":295,"text":259},"2026-10-24","手机上看 MKV 美剧，画面正常却没声音，MX Player 提示不支持 EAC3 格式；照着网上的教程装了自定义编解码器，又报「找不到自定义编解码器，请使用 3.1.0 ARMv8 NEON 版」。这篇把两个报错串成一条线讲清楚，附各版本对应关系和下载地址。","md",{},true,"\u002Fposts\u002Fmx-player-eac3-custom-codec",{"title":5,"description":302},"posts\u002Fmx-player-eac3-custom-codec",[310,311,312,313],"MXPlayer","Android","播放器","避坑","nXCIzcJte9ZMGzijbdBnYMA6_f7uv-0rMIGfmSM3Qc4",[316,519,2010,2285,2675,3296,3521,3578,4294,5604,6877,7293,8039,8528,9471,9879,11441],{"id":4,"title":5,"body":317,"date":301,"description":302,"extension":303,"meta":516,"navigation":305,"path":306,"seo":517,"stem":308,"tags":518,"__hash__":314},{"type":7,"value":318,"toc":508},[319,321,325,327,331,333,335,337,339,341,345,347,349,418,420,422,424,426,430,440,442,454,456,458,460,462,466,468,472,474,476,478,482,484,486],[10,320,12],{},[10,322,323],{},[16,324],{"alt":18,"src":19,"width":20},[10,326,23],{},[10,328,329],{},[16,330],{"alt":28,"src":29,"width":30},[32,332,34],{"id":34},[10,334,37],{},[10,336,40],{},[32,338,44],{"id":43},[10,340,47],{},[10,342,343],{},[16,344],{"alt":52,"src":53,"width":30},[10,346,56],{},[10,348,59],{},[61,350,351,361],{},[64,352,353],{},[67,354,355,357,359],{},[70,356,72],{},[70,358,75],{},[70,360,78],{},[80,362,363,374,385,396,407],{},[67,364,365,367,369],{},[85,366,87],{},[85,368,90],{},[85,370,371],{},[94,372,96],{"href":96,"rel":373},[98],[67,375,376,378,380],{},[85,377,103],{},[85,379,106],{},[85,381,382],{},[94,383,111],{"href":111,"rel":384},[98],[67,386,387,389,391],{},[85,388,117],{},[85,390,120],{},[85,392,393],{},[94,394,125],{"href":125,"rel":395},[98],[67,397,398,400,402],{},[85,399,131],{},[85,401,134],{},[85,403,404],{},[94,405,139],{"href":139,"rel":406},[98],[67,408,409,411,413],{},[85,410,145],{},[85,412,148],{},[85,414,415],{},[94,416,153],{"href":153,"rel":417},[98],[10,419,157],{},[10,421,160],{},[32,423,163],{"id":163},[10,425,166],{},[10,427,428],{},[16,429],{"alt":171,"src":172,"width":20},[174,431,432,436],{},[177,433,179,434,184],{},[181,435,183],{},[177,437,187,438,191],{},[181,439,190],{},[10,441,194],{},[174,443,444,449],{},[177,445,199,446],{},[94,447,202],{"href":202,"rel":448},[98],[177,450,206,451],{},[94,452,209],{"href":209,"rel":453},[98],[10,455,213],{},[32,457,216],{"id":216},[10,459,219],{},[10,461,222],{},[10,463,464],{},[16,465],{"alt":227,"src":228,"width":30},[10,467,231],{},[10,469,470],{},[16,471],{"alt":236,"src":237,"width":30},[10,473,240],{},[32,475,244],{"id":243},[10,477,247],{},[10,479,480],{},[16,481],{"alt":252,"src":253,"width":20},[10,483,256],{},[32,485,259],{"id":259},[174,487,488,493,498,503],{},[177,489,264,490],{},[94,491,267],{"href":267,"rel":492},[98],[177,494,271,495],{},[94,496,274],{"href":274,"rel":497},[98],[177,499,278,500],{},[94,501,281],{"href":281,"rel":502},[98],[177,504,285,505],{},[94,506,288],{"href":288,"rel":507},[98],{"title":291,"searchDepth":292,"depth":292,"links":509},[510,511,512,513,514,515],{"id":34,"depth":295,"text":34},{"id":43,"depth":295,"text":44},{"id":163,"depth":295,"text":163},{"id":216,"depth":295,"text":216},{"id":243,"depth":295,"text":244},{"id":259,"depth":295,"text":259},{},{"title":5,"description":302},[310,311,312,313],{"id":520,"title":521,"body":522,"date":1999,"description":2000,"extension":303,"meta":2001,"navigation":305,"path":2002,"seo":2003,"stem":2004,"tags":2005,"__hash__":2009},"posts\u002Fposts\u002Fnuxt-dynamic-import-prefetch.md","懒加载没懒成？Nuxt 动态 import 自动 prefetch 的坑",{"type":7,"value":523,"toc":1984},[524,539,542,545,599,602,609,612,626,633,640,714,719,724,733,736,747,781,784,787,887,946,952,955,958,963,969,975,978,983,1059,1062,1065,1071,1087,1093,1096,1102,1120,1123,1128,1131,1137,1175,1190,1193,1199,1212,1215,1221,1246,1252,1262,1265,1268,1274,1280,1283,1286,1291,1341,1347,1350,1360,1364,1413,1419,1422,1435,1441,1445,1502,1505,1517,1522,1525,1670,1673,1684,1690,1697,1702,1708,1711,1714,1717,1789,1856,1859,1865,1868,1871,1877,1880,1918,1921,1924,1927,1930,1959,1962,1965,1968,1980],[10,525,526,527,532,533,538],{},"最近一直在做站点的性能优化。毕竟",[94,528,531],{"href":529,"rel":530},"https:\u002F\u002Fwww.easydebug.net",[98],"在线工具","和",[94,534,537],{"href":535,"rel":536},"https:\u002F\u002Fblog.easydebug.net",[98],"博客","都跑在阿里云的一台3M小水管机器上，换算下来理论峰值 384 KB\u002Fs，实际也就是 300 KB\u002Fs 的水平。",[10,540,541],{},"用户对速度的感知，几乎都集中在首次加载。所以重中之重是给它减负，把不需要执行的 JS 从这一步拿掉。毕竟首次加载多下 200 KB，用户就得多等大半秒。现在用不上，那就等真用到时再下。",[10,543,544],{},"标准操作就是：",[546,547,551],"pre",{"className":548,"code":549,"language":550,"meta":291,"style":291},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const { renderLua } = await import('~\u002Flib\u002Fformat-lua')\n","ts",[552,553,554],"code",{"__ignoreMap":291},[555,556,559,563,567,571,574,577,581,584,587,590,594,596],"span",{"class":557,"line":558},"line",1,[555,560,562],{"class":561},"spNyl","const",[555,564,566],{"class":565},"sMK4o"," {",[555,568,570],{"class":569},"sTEyZ"," renderLua ",[555,572,573],{"class":565},"}",[555,575,576],{"class":565}," =",[555,578,580],{"class":579},"s7zQu"," await",[555,582,583],{"class":565}," import",[555,585,586],{"class":569},"(",[555,588,589],{"class":565},"'",[555,591,593],{"class":592},"sfazB","~\u002Flib\u002Fformat-lua",[555,595,589],{"class":565},[555,597,598],{"class":569},")\n",[10,600,601],{},"改完后 F12 一看。不对劲。",[10,603,604,605,608],{},"我什么都没点，那个触发才下载的 JS 还是被下载了，只是 Priority = ",[552,606,607],{},"Lowest","。",[32,610,611],{"id":611},"背景",[10,613,614,615,618,619,622,623],{},"目前使用版本 Node ",[181,616,617],{},"v24.18.0","、Nuxt ",[181,620,621],{},"4.5.2"," 、pnpm ",[181,624,625],{},"11.15.1",[10,627,628,629,632],{},"Nuxt 项目打包之后，代码会被切成很多个 ",[552,630,631],{},".js"," 文件，也就是常说的 chunk。一个页面不会用到全部 chunk，所以打包器要在 HTML 里留几个标记，告诉浏览器「这个文件待会儿要用，先去拿来」。",[10,634,635,636,639],{},"负责留标记的是一类 ",[552,637,638],{},"\u003Clink>"," 标签，常用的有两种：",[546,641,645],{"className":642,"code":643,"language":644,"meta":291,"style":291},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Clink rel=\"modulepreload\" href=\"\u002F_nuxt\u002Fxxx.js\">\n\u003Clink rel=\"prefetch\" href=\"\u002F_nuxt\u002Fxxx.js\">\n","html",[552,646,647,685],{"__ignoreMap":291},[555,648,649,652,656,659,662,665,668,670,673,675,677,680,682],{"class":557,"line":558},[555,650,651],{"class":565},"\u003C",[555,653,655],{"class":654},"swJcz","link",[555,657,658],{"class":561}," rel",[555,660,661],{"class":565},"=",[555,663,664],{"class":565},"\"",[555,666,667],{"class":592},"modulepreload",[555,669,664],{"class":565},[555,671,672],{"class":561}," href",[555,674,661],{"class":565},[555,676,664],{"class":565},[555,678,679],{"class":592},"\u002F_nuxt\u002Fxxx.js",[555,681,664],{"class":565},[555,683,684],{"class":565},">\n",[555,686,687,689,691,693,695,697,700,702,704,706,708,710,712],{"class":557,"line":295},[555,688,651],{"class":565},[555,690,655],{"class":654},[555,692,658],{"class":561},[555,694,661],{"class":565},[555,696,664],{"class":565},[555,698,699],{"class":592},"prefetch",[555,701,664],{"class":565},[555,703,672],{"class":561},[555,705,661],{"class":565},[555,707,664],{"class":565},[555,709,679],{"class":592},[555,711,664],{"class":565},[555,713,684],{"class":565},[10,715,716,718],{},[552,717,667],{}," 的意思是「这个文件我现在就要用」。浏览器解析到这一行，立刻用高优先级去下载，跟 CSS、字体抢带宽。",[10,720,721,723],{},[552,722,699],{}," 的意思是「这个文件以后可能会用」。浏览器先把当前页面的事干完，等网络闲下来，再用低优先级慢慢下。",[10,725,726,727,729,730,732],{},"两个标签的区别不在「下不下」，而在「什么时候下、抢不抢带宽」。两者最终都会把文件拿到本地，只是 ",[552,728,667],{}," 要跟首次加载抢带宽，",[552,731,699],{}," 排在队尾。",[10,734,735],{},"这两个标签基本不用手写。Nuxt 在构建时会读代码里的 import 关系，自己决定往 HTML 里塞哪几个。",[10,737,738,739,742,743,746],{},"而我在这个模块上写的是 ",[552,740,741],{},"await import()","：既不是静态导入，也没手写 prefetch，首次加载也用不到它。可 ",[552,744,745],{},"generate"," 之后，它还是变成了 prefetch：",[546,748,750],{"className":642,"code":749,"language":644,"meta":291,"style":291},"\u003Clink rel=\"prefetch\" href=\"\u002F_nuxt\u002FDxxxxxxx.js\">\n",[552,751,752],{"__ignoreMap":291},[555,753,754,756,758,760,762,764,766,768,770,772,774,777,779],{"class":557,"line":558},[555,755,651],{"class":565},[555,757,655],{"class":654},[555,759,658],{"class":561},[555,761,661],{"class":565},[555,763,664],{"class":565},[555,765,699],{"class":592},[555,767,664],{"class":565},[555,769,672],{"class":561},[555,771,661],{"class":565},[555,773,664],{"class":565},[555,775,776],{"class":592},"\u002F_nuxt\u002FDxxxxxxx.js",[555,778,664],{"class":565},[555,780,684],{"class":565},[32,782,783],{"id":783},"复现",[10,785,786],{},"我新建了一个 Nuxt 项目，分别把动态导入的几种写法拆成几个独立页面，每个页面都配了一个体积相同的200 KB载荷。项目代码放在文章末尾了。",[61,788,789,799],{},[64,790,791],{},[67,792,793,796],{},[70,794,795],{},"页面",[70,797,798],{},"写法",[80,800,801,811,824,836,849,859,872],{},[67,802,803,808],{},[85,804,805],{},[552,806,807],{},"\u002Fclean\u002F",[85,809,810],{},"什么都不导入，基线",[67,812,813,818],{},[85,814,815],{},[552,816,817],{},"\u002Flink-modulepreload\u002F",[85,819,820,821],{},"顶层静态 ",[552,822,823],{},"import",[67,825,826,831],{},[85,827,828],{},[552,829,830],{},"\u002Flink-prefetch\u002F",[85,832,833],{},[552,834,835],{},"\u003Clink rel=\"prefetch\">",[67,837,838,843],{},[85,839,840],{},[552,841,842],{},"\u002Fawait-import-prefetch\u002F",[85,844,845,846,848],{},"函数内 ",[552,847,741],{},"（一级）",[67,850,851,856],{},[85,852,853],{},[552,854,855],{},"\u002Fawait-import-nested\u002F",[85,857,858],{},"页面 → 一级 mid → 二级载荷",[67,860,861,866],{},[85,862,863],{},[552,864,865],{},"\u002Fasync-component\u002F",[85,867,868,871],{},[552,869,870],{},"defineAsyncComponent","，无条件渲染",[67,873,874,879],{},[85,875,876],{},[552,877,878],{},"\u002Fasync-component-conditional\u002F",[85,880,881,883,884],{},[552,882,870],{}," + ",[552,885,886],{},"v-if",[546,888,892],{"className":889,"code":890,"language":891,"meta":291,"style":291},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm gen        # 生成载荷，必须先跑\npnpm generate\npnpm analyze\nnpx --yes http-server .output\u002Fpublic -p 6521 -c-1\n","bash",[552,893,894,907,914,921],{"__ignoreMap":291},[555,895,896,900,903],{"class":557,"line":558},[555,897,899],{"class":898},"sBMFI","pnpm",[555,901,902],{"class":592}," gen",[555,904,906],{"class":905},"sHwdD","        # 生成载荷，必须先跑\n",[555,908,909,911],{"class":557,"line":295},[555,910,899],{"class":898},[555,912,913],{"class":592}," generate\n",[555,915,916,918],{"class":557,"line":292},[555,917,899],{"class":898},[555,919,920],{"class":592}," analyze\n",[555,922,924,927,930,933,936,939,943],{"class":557,"line":923},4,[555,925,926],{"class":898},"npx",[555,928,929],{"class":592}," --yes",[555,931,932],{"class":592}," http-server",[555,934,935],{"class":592}," .output\u002Fpublic",[555,937,938],{"class":592}," -p",[555,940,942],{"class":941},"sbssI"," 6521",[555,944,945],{"class":592}," -c-1\n",[10,947,948],{},[16,949],{"alt":950,"src":951,"width":20},"服务器启动","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F0-npx.png",[32,953,954],{"id":954},"实测",[10,956,957],{},"先看基线。",[10,959,960,962],{},[552,961,807],{}," 什么都没导入，理论上应该最干净。实测下来它仍然下了两个文件：",[10,964,965],{},[16,966],{"alt":967,"src":968,"width":20},"clean 基线","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F1-clean.png",[10,970,971,972,974],{},"4.0 KB 和 3.7 KB，Priority 都是 ",[552,973,607],{},"。这是 Nuxt 内置的 error-404 和 error-500，被 prefetch 到了每一个页面上，七个页面一个不落。它们是 Nuxt 的固定开销，后面所有页面的数字里都包含这 7.7 KB。",[10,976,977],{},"然后看关键的那一页。",[10,979,980,982],{},[552,981,842],{}," 里，那个 200 KB 的载荷是这样导入的：",[546,984,986],{"className":548,"code":985,"language":550,"meta":291,"style":291},"async function load() {\n  const mod = await import('~\u002Flib\u002Fpayload-dynamic')\n  output.value = mod.payloadRender()\n}\n",[552,987,988,1006,1031,1054],{"__ignoreMap":291},[555,989,990,993,996,1000,1003],{"class":557,"line":558},[555,991,992],{"class":561},"async",[555,994,995],{"class":561}," function",[555,997,999],{"class":998},"s2Zo4"," load",[555,1001,1002],{"class":565},"()",[555,1004,1005],{"class":565}," {\n",[555,1007,1008,1011,1014,1016,1018,1020,1022,1024,1027,1029],{"class":557,"line":295},[555,1009,1010],{"class":561},"  const",[555,1012,1013],{"class":569}," mod",[555,1015,576],{"class":565},[555,1017,580],{"class":579},[555,1019,583],{"class":565},[555,1021,586],{"class":654},[555,1023,589],{"class":565},[555,1025,1026],{"class":592},"~\u002Flib\u002Fpayload-dynamic",[555,1028,589],{"class":565},[555,1030,598],{"class":654},[555,1032,1033,1036,1039,1042,1044,1046,1048,1051],{"class":557,"line":292},[555,1034,1035],{"class":569},"  output",[555,1037,1038],{"class":565},".",[555,1040,1041],{"class":569},"value",[555,1043,576],{"class":565},[555,1045,1013],{"class":569},[555,1047,1038],{"class":565},[555,1049,1050],{"class":998},"payloadRender",[555,1052,1053],{"class":654},"()\n",[555,1055,1056],{"class":557,"line":923},[555,1057,1058],{"class":565},"}\n",[10,1060,1061],{},"它挂在一个按钮上。用户不点，这段代码就不会执行。",[10,1063,1064],{},"打开页面，什么都不点，等三秒：",[10,1066,1067],{},[16,1068],{"alt":1069,"src":1070,"width":20},"零交互，178 KB 已被下走","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F4-1-await-import-prefetch.png",[10,1072,1073,1074,1077,1078,1081,1082,1077,1085,608],{},"那个 178 KB 的 chunk 已经在列表里了，",[552,1075,1076],{},"Type"," 是 ",[552,1079,1080],{},"javascript","，",[552,1083,1084],{},"Priority",[552,1086,607],{},[10,1088,1089,1090,1092],{},"我用 ",[552,1091,741],{}," 的本意是「用到才下」。但页面刚一空闲，浏览器就把它下完了。",[10,1094,1095],{},"点击按钮，同一个文件会以另一种身份再出现一次：",[10,1097,1098],{},[16,1099],{"alt":1100,"src":1101,"width":20},"点击后，同一个文件变成 High","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F4-2-await-import-prefetch.png",[10,1103,1104,1105,1108,1109,1077,1111,1081,1114,1116,1117,608],{},"同一个文件 ",[552,1106,1107],{},"Dxxxxxxx.js","，这次的 ",[552,1110,1076],{},[552,1112,1113],{},"script",[552,1115,1084],{}," 变成 ",[552,1118,1119],{},"High",[10,1121,1122],{},"这说明 prefetch 并未替代这次加载，它只是把文件提前放进了缓存。用户真正触发时，浏览器仍会走完整的加载流程。",[10,1124,1125],{},[181,1126,1127],{},"省下的是等待，没省下的是流量。",[32,1129,1130],{"id":1130},"异步组件也绕不开",[10,1132,1133,1134,1136],{},"那不用 ",[552,1135,741],{},"，改用异步组件呢？",[546,1138,1140],{"className":548,"code":1139,"language":550,"meta":291,"style":291},"const PayloadAsync = defineAsyncComponent(() => import('~\u002Fcomponents\u002FPayloadAsync.vue'))\n",[552,1141,1142],{"__ignoreMap":291},[555,1143,1144,1146,1149,1151,1154,1156,1158,1161,1163,1165,1167,1170,1172],{"class":557,"line":558},[555,1145,562],{"class":561},[555,1147,1148],{"class":569}," PayloadAsync ",[555,1150,661],{"class":565},[555,1152,1153],{"class":998}," defineAsyncComponent",[555,1155,586],{"class":569},[555,1157,1002],{"class":565},[555,1159,1160],{"class":561}," =>",[555,1162,583],{"class":565},[555,1164,586],{"class":569},[555,1166,589],{"class":565},[555,1168,1169],{"class":592},"~\u002Fcomponents\u002FPayloadAsync.vue",[555,1171,589],{"class":565},[555,1173,1174],{"class":569},"))\n",[546,1176,1178],{"className":642,"code":1177,"language":644,"meta":291,"style":291},"\u003CPayloadAsync \u002F>\n",[552,1179,1180],{"__ignoreMap":291},[555,1181,1182,1184,1187],{"class":557,"line":558},[555,1183,651],{"class":565},[555,1185,1186],{"class":654},"PayloadAsync",[555,1188,1189],{"class":565}," \u002F>\n",[10,1191,1192],{},"实测：",[10,1194,1195],{},[16,1196],{"alt":1197,"src":1198,"width":20},"异步组件：直接 modulepreload","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F6-async-component.png",[10,1200,1201,1202,1077,1204,1081,1206,1077,1208,608],{},"178 KB，",[552,1203,1076],{},[552,1205,1113],{},[552,1207,1084],{},[181,1209,1210],{},[552,1211,1119],{},[10,1213,1214],{},"比 prefetch 更狠。它不是等网络空闲，而是和 CSS、字体一起抢带宽。我的理解是：这个组件在服务端渲染时确实被渲染了，Nuxt 判断它「首次加载就要用」，于是用最高优先级预加载，省掉客户端水合时再等一次。",[10,1216,1217,1218,1220],{},"那给它加个 ",[552,1219,886],{},"，让它不参与初次渲染会怎样？",[546,1222,1224],{"className":642,"code":1223,"language":644,"meta":291,"style":291},"\u003CPayloadAsync v-if=\"show\" \u002F>\n",[552,1225,1226],{"__ignoreMap":291},[555,1227,1228,1230,1232,1235,1237,1239,1242,1244],{"class":557,"line":558},[555,1229,651],{"class":565},[555,1231,1186],{"class":654},[555,1233,1234],{"class":561}," v-if",[555,1236,661],{"class":565},[555,1238,664],{"class":565},[555,1240,1241],{"class":592},"show",[555,1243,664],{"class":565},[555,1245,1189],{"class":565},[10,1247,1248],{},[16,1249],{"alt":1250,"src":1251,"width":20},"v-if：降级为 prefetch，但流量没省","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F7-1-async-component-conditional.png",[10,1253,1254,1256,1257,1259,1260,608],{},[552,1255,1084],{}," 从 ",[552,1258,1119],{}," 降到了 ",[552,1261,607],{},[10,1263,1264],{},"但页面总传输量还是 373 KB，和上一个页面一模一样。我没点按钮，那 178 KB 已经下完了。",[10,1266,1267],{},"点一下按钮，它才真正被用上：",[10,1269,1270],{},[16,1271],{"alt":1272,"src":1273,"width":20},"点击后才渲染","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F7-2-async-component-conditional.png",[10,1275,1276,1277,1279],{},"所以 ",[552,1278,886],{}," 只解决了「抢首次加载的带宽」，没解决「白下」。异步组件这条路，绕不开。",[32,1281,1282],{"id":1282},"三组对照",[10,1284,1285],{},"七个页面里，有三组可以两两对比。对比出来的结论比单看一页更有意思。",[1287,1288,1290],"h3",{"id":1289},"一静态导入-vs-异步组件","一、静态导入 vs 异步组件",[61,1292,1293,1306],{},[64,1294,1295],{},[67,1296,1297,1299,1302,1304],{},[70,1298,795],{},[70,1300,1301],{},"载荷",[70,1303,1076],{},[70,1305,1084],{},[80,1307,1308,1325],{},[67,1309,1310,1314,1317,1321],{},[85,1311,1312],{},[552,1313,817],{},[85,1315,1316],{},"178 KB",[85,1318,1319],{},[552,1320,1113],{},[85,1322,1323],{},[552,1324,1119],{},[67,1326,1327,1331,1333,1337],{},[85,1328,1329],{},[552,1330,865],{},[85,1332,1316],{},[85,1334,1335],{},[552,1336,1113],{},[85,1338,1339],{},[552,1340,1119],{},[10,1342,1343],{},[16,1344],{"alt":1345,"src":1346,"width":20},"静态导入：modulepreload","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F2-link-modulepreload.png",[10,1348,1349],{},"两者情况一样。",[10,1351,1352,1353,1355,1356,1359],{},"静态导入走 ",[552,1354,667],{}," 理所当然。问题在于后面那一行：",[181,1357,1358],{},"一个叫「异步」的组件，只要它参与了初次渲染，Nuxt 就把它当首次加载的资源处理","，「异步」这两个字等于白叫。",[1287,1361,1363],{"id":1362},"二手写的-prefetch-vs-框架注入的-prefetch","二、手写的 prefetch vs 框架注入的 prefetch",[61,1365,1366,1378],{},[64,1367,1368],{},[67,1369,1370,1372,1374,1376],{},[70,1371,795],{},[70,1373,1301],{},[70,1375,1076],{},[70,1377,1084],{},[80,1379,1380,1397],{},[67,1381,1382,1386,1389,1393],{},[85,1383,1384],{},[552,1385,830],{},[85,1387,1388],{},"208 KB",[85,1390,1391],{},[552,1392,1080],{},[85,1394,1395],{},[552,1396,607],{},[67,1398,1399,1403,1405,1409],{},[85,1400,1401],{},[552,1402,842],{},[85,1404,1316],{},[85,1406,1407],{},[552,1408,1080],{},[85,1410,1411],{},[552,1412,607],{},[10,1414,1415],{},[16,1416],{"alt":1417,"src":1418,"width":20},"手写 prefetch","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F3-link-prefetch.png",[10,1420,1421],{},"这一组验证一件事：Nuxt 注入的 prefetch 标签，和手写的有没有区别。",[10,1423,1424,1425,1427,1428,1081,1430,1427,1432,1434],{},"结论是没有。两者的 ",[552,1426,1076],{}," 都是 ",[552,1429,1080],{},[552,1431,1084],{},[552,1433,607],{},"，加载时机也一致。",[10,1436,1437,1438,1440],{},"也就是说，Nuxt 注入的这一行，与手写的 ",[552,1439,835],{}," 等价，区别只在于前者由构建过程自动完成，后者由开发者显式声明。",[1287,1442,1444],{"id":1443},"三一级动态-import-vs-条件渲染的异步组件","三、一级动态 import vs 条件渲染的异步组件",[61,1446,1447,1462],{},[64,1448,1449],{},[67,1450,1451,1453,1455,1457,1459],{},[70,1452,795],{},[70,1454,1301],{},[70,1456,1076],{},[70,1458,1084],{},[70,1460,1461],{},"页面总传输",[80,1463,1464,1483],{},[67,1465,1466,1470,1472,1476,1480],{},[85,1467,1468],{},[552,1469,842],{},[85,1471,1316],{},[85,1473,1474],{},[552,1475,1080],{},[85,1477,1478],{},[552,1479,607],{},[85,1481,1482],{},"372 KB",[67,1484,1485,1489,1491,1495,1499],{},[85,1486,1487],{},[552,1488,878],{},[85,1490,1316],{},[85,1492,1493],{},[552,1494,1080],{},[85,1496,1497],{},[552,1498,607],{},[85,1500,1501],{},"373 KB",[10,1503,1504],{},"两条路的结果一致。",[10,1506,1507,1509,1510,883,1512,1514,1515,608],{},[552,1508,741],{}," 和 ",[552,1511,870],{},[552,1513,886],{}," 的区别只在语法层面。只要被导入的模块挂在「一级」，结果都是零交互下先下载 178 KB，仅优先级为 ",[552,1516,607],{},[10,1518,1519],{},[181,1520,1521],{},"更换写法并不能规避。",[32,1523,1524],{"id":1524},"七页放在一起看",[61,1526,1527,1541],{},[64,1528,1529],{},[67,1530,1531,1533,1535,1537,1539],{},[70,1532,795],{},[70,1534,1301],{},[70,1536,1076],{},[70,1538,1084],{},[70,1540,1461],{},[80,1542,1543,1559,1577,1596,1614,1634,1652],{},[67,1544,1545,1549,1552,1554,1556],{},[85,1546,1547],{},[552,1548,807],{},[85,1550,1551],{},"—",[85,1553,1551],{},[85,1555,1551],{},[85,1557,1558],{},"194 KB",[67,1560,1561,1565,1567,1571,1575],{},[85,1562,1563],{},[552,1564,817],{},[85,1566,1316],{},[85,1568,1569],{},[552,1570,1113],{},[85,1572,1573],{},[552,1574,1119],{},[85,1576,1482],{},[67,1578,1579,1583,1585,1589,1593],{},[85,1580,1581],{},[552,1582,830],{},[85,1584,1388],{},[85,1586,1587],{},[552,1588,1080],{},[85,1590,1591],{},[552,1592,607],{},[85,1594,1595],{},"402 KB",[67,1597,1598,1602,1604,1608,1612],{},[85,1599,1600],{},[552,1601,842],{},[85,1603,1316],{},[85,1605,1606],{},[552,1607,1080],{},[85,1609,1610],{},[552,1611,607],{},[85,1613,1482],{},[67,1615,1616,1620,1623,1627,1631],{},[85,1617,1618],{},[552,1619,855],{},[85,1621,1622],{},"0.6 KB",[85,1624,1625],{},[552,1626,1080],{},[85,1628,1629],{},[552,1630,607],{},[85,1632,1633],{},"195 KB",[67,1635,1636,1640,1642,1646,1650],{},[85,1637,1638],{},[552,1639,865],{},[85,1641,1316],{},[85,1643,1644],{},[552,1645,1113],{},[85,1647,1648],{},[552,1649,1119],{},[85,1651,1501],{},[67,1653,1654,1658,1660,1664,1668],{},[85,1655,1656],{},[552,1657,878],{},[85,1659,1316],{},[85,1661,1662],{},[552,1663,1080],{},[85,1665,1666],{},[552,1667,607],{},[85,1669,1501],{},[10,1671,1672],{},"几点结论：",[10,1674,1675,1681,1682,608],{},[181,1676,1677,1678,1680],{},"一级 ",[552,1679,741],{}," 的懒加载不成立。"," 零交互下 178 KB 即被下载，仅优先级为 ",[552,1683,607],{},[10,1685,1686,1689],{},[181,1687,1688],{},"二级动态导入成立。"," 页面总传输 195 KB，与基线基本持平。",[10,1691,1692],{},[181,1693,1694,1696],{},[552,1695,886],{}," 只改变优先级，不改变流量。",[10,1698,1699],{},[181,1700,1701],{},"异步组件无条件渲染时，行为等同于静态导入。",[10,1703,1704,1707],{},[181,1705,1706],{},"存在固定开销"," error-404 与 error-500 两个 chunk 被 prefetch 到全部七个页面，每页 7.7 KB。",[10,1709,1710],{},"对照引言中那台 3M 带宽的机器：178 KB 约合半秒以上的等待，7.7 KB 则是每个页面都要重复支付的一次成本。",[32,1712,1713],{"id":1713},"解决方案",[10,1715,1716],{},"页面不再直接导入那个 200 KB 的模块，而是导入一个很小的中间模块，中间模块内部再去导入载荷：",[546,1718,1720],{"className":548,"code":1719,"language":550,"meta":291,"style":291},"\u002F\u002F 页面\nasync function load() {\n  const mid = await import('~\u002Flib\u002Fnested-mid')\n  output.value = await mid.loadLeaf()\n}\n",[552,1721,1722,1727,1739,1763,1784],{"__ignoreMap":291},[555,1723,1724],{"class":557,"line":558},[555,1725,1726],{"class":905},"\u002F\u002F 页面\n",[555,1728,1729,1731,1733,1735,1737],{"class":557,"line":295},[555,1730,992],{"class":561},[555,1732,995],{"class":561},[555,1734,999],{"class":998},[555,1736,1002],{"class":565},[555,1738,1005],{"class":565},[555,1740,1741,1743,1746,1748,1750,1752,1754,1756,1759,1761],{"class":557,"line":292},[555,1742,1010],{"class":561},[555,1744,1745],{"class":569}," mid",[555,1747,576],{"class":565},[555,1749,580],{"class":579},[555,1751,583],{"class":565},[555,1753,586],{"class":654},[555,1755,589],{"class":565},[555,1757,1758],{"class":592},"~\u002Flib\u002Fnested-mid",[555,1760,589],{"class":565},[555,1762,598],{"class":654},[555,1764,1765,1767,1769,1771,1773,1775,1777,1779,1782],{"class":557,"line":923},[555,1766,1035],{"class":569},[555,1768,1038],{"class":565},[555,1770,1041],{"class":569},[555,1772,576],{"class":565},[555,1774,580],{"class":579},[555,1776,1745],{"class":569},[555,1778,1038],{"class":565},[555,1780,1781],{"class":998},"loadLeaf",[555,1783,1053],{"class":654},[555,1785,1787],{"class":557,"line":1786},5,[555,1788,1058],{"class":565},[546,1790,1792],{"className":548,"code":1791,"language":550,"meta":291,"style":291},"\u002F\u002F nested-mid.ts\nexport async function loadLeaf() {\n  const mod = await import('.\u002Fpayload-nested')\n  return mod.payloadRender()\n}\n",[552,1793,1794,1799,1816,1839,1852],{"__ignoreMap":291},[555,1795,1796],{"class":557,"line":558},[555,1797,1798],{"class":905},"\u002F\u002F nested-mid.ts\n",[555,1800,1801,1804,1807,1809,1812,1814],{"class":557,"line":295},[555,1802,1803],{"class":579},"export",[555,1805,1806],{"class":561}," async",[555,1808,995],{"class":561},[555,1810,1811],{"class":998}," loadLeaf",[555,1813,1002],{"class":565},[555,1815,1005],{"class":565},[555,1817,1818,1820,1822,1824,1826,1828,1830,1832,1835,1837],{"class":557,"line":292},[555,1819,1010],{"class":561},[555,1821,1013],{"class":569},[555,1823,576],{"class":565},[555,1825,580],{"class":579},[555,1827,583],{"class":565},[555,1829,586],{"class":654},[555,1831,589],{"class":565},[555,1833,1834],{"class":592},".\u002Fpayload-nested",[555,1836,589],{"class":565},[555,1838,598],{"class":654},[555,1840,1841,1844,1846,1848,1850],{"class":557,"line":923},[555,1842,1843],{"class":579},"  return",[555,1845,1013],{"class":569},[555,1847,1038],{"class":565},[555,1849,1050],{"class":998},[555,1851,1053],{"class":654},[555,1853,1854],{"class":557,"line":1786},[555,1855,1058],{"class":565},[10,1857,1858],{},"同样不做任何操作，等待三秒：",[10,1860,1861],{},[16,1862],{"alt":1863,"src":1864,"width":20},"二级：那个 178 KB 不出现","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F5-1-await-import-nested.png",[10,1866,1867],{},"一级那个中间模块（0.6 KB）仍被 prefetch，但二级的 178 KB 连请求都没有发出。",[10,1869,1870],{},"点击按钮后才会加载：",[10,1872,1873],{},[16,1874],{"alt":1875,"src":1876,"width":20},"点击后才加载","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F5-2-await-import-nested.png",[10,1878,1879],{},"再对比页面总传输量：",[61,1881,1882,1891],{},[64,1883,1884],{},[67,1885,1886,1888],{},[70,1887,795],{},[70,1889,1890],{},"总传输",[80,1892,1893,1902,1910],{},[67,1894,1895,1900],{},[85,1896,1897,1899],{},[552,1898,807],{},"（基线）",[85,1901,1558],{},[67,1903,1904,1908],{},[85,1905,1906],{},[552,1907,855],{},[85,1909,1633],{},[67,1911,1912,1916],{},[85,1913,1914],{},[552,1915,842],{},[85,1917,1482],{},[10,1919,1920],{},"二级嵌套的页面只比基线多出 1 KB，就是那个中转模块的体积。而一级动态导入的页面，多出 178 KB。",[10,1922,1923],{},"我目前的处理方式是把体积大的模块下移到二级。中间那层转发模块很小，即使被 prefetch 影响也有限。",[32,1925,1926],{"id":1926},"关于这个问题的记录",[10,1928,1929],{},"最后我查了一下 GitHub，发现这不是新问题：",[174,1931,1932,1943,1951],{},[177,1933,1934,1939,1940,1942],{},[94,1935,1938],{"href":1936,"rel":1937},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fissues\u002F18376",[98],"#18376"," Disable ",[552,1941,699],{}," for dynamic imports（2023-01-20 提出，2026-05-05 关闭）",[177,1944,1945,1950],{},[94,1946,1949],{"href":1947,"rel":1948},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fissues\u002F14584",[98],"#14584"," optimisations for prefetching chunks（2022-08-15 提出，2026-09-06 关闭）",[177,1952,1953,1958],{},[94,1954,1957],{"href":1955,"rel":1956},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F36391",[98],"#36391"," unified client prefetch scheduler（2026-09-23 合并，目标版本 5.x）",[10,1960,1961],{},"#18376 描述的场景和本文遇到的一模一样。上游近期把客户端预取的调度统一到了一套队列（#36391），但它调整的是预取的秩序，不是「要不要预取动态 import」，是否影响本文描述的现象尚不确定，需要持续观察。",[10,1963,1964],{},"在此之前，可行的做法仍是自行处理：把大体积模块放到第二层动态 import，第一层只保留一个转发用的空壳。",[32,1966,1967],{"id":1967},"相关代码",[10,1969,1970,1971,1975,1976,1979],{},"相关代码在 ",[94,1972,1973],{"href":1973,"rel":1974},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002Fblog-code-pool",[98]," 的 ",[552,1977,1978],{},"nuxt-dynamic-import-prefetch\u002F"," 目录下。",[1981,1982,1983],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":291,"searchDepth":292,"depth":292,"links":1985},[1986,1987,1988,1989,1990,1995,1996,1997,1998],{"id":611,"depth":295,"text":611},{"id":783,"depth":295,"text":783},{"id":954,"depth":295,"text":954},{"id":1130,"depth":295,"text":1130},{"id":1282,"depth":295,"text":1282,"children":1991},[1992,1993,1994],{"id":1289,"depth":292,"text":1290},{"id":1362,"depth":292,"text":1363},{"id":1443,"depth":292,"text":1444},{"id":1524,"depth":295,"text":1524},{"id":1713,"depth":295,"text":1713},{"id":1926,"depth":295,"text":1926},{"id":1967,"depth":295,"text":1967},"2026-10-03","给 Nuxt 页面做首次加载减负时，发现它会在构建时给一级动态 import 注入 prefetch 标签。用户零交互，整个 chunk 就已经下走了，await import 也就失效了。这篇用七个页面的实测把这个机制拆开，并给出一个真能省下流量的写法。",{},"\u002Fposts\u002Fnuxt-dynamic-import-prefetch",{"title":521,"description":2000},"posts\u002Fnuxt-dynamic-import-prefetch",[2006,2007,2008],"Nuxt","性能优化","前端","mqDD5y_LQyuy938Ubfgm_XRIUCnM_f5SOTgkZ2qFsY8",{"id":2011,"title":2012,"body":2013,"date":2273,"description":2274,"extension":303,"meta":2275,"navigation":305,"path":2276,"seo":2277,"stem":2278,"tags":2279,"__hash__":2284},"posts\u002Fposts\u002Feasyauth-2fa-webdav-s3-backup-client.md","换完手机，GitHub 把我挡在了门外",{"type":7,"value":2014,"toc":2261},[2015,2018,2021,2024,2027,2031,2034,2037,2063,2066,2069,2072,2104,2107,2110,2114,2117,2120,2127,2134,2140,2143,2146,2149,2153,2156,2170,2177,2180,2183,2186,2189,2196,2202,2205,2212,2215,2218,2221,2258],[10,2016,2017],{},"换完手机那天，GitHub 把我挡在了门外。",[10,2019,2020],{},"那天我坐在电脑前，屏幕上弹出来的还是那行熟悉的提示——请输入两步验证代码。我掏出手机打开验证器，列表是空的，一条都没有。换机之前我以为它会跟着微软账号一起同步回来，结果并没有。中间到底卡在哪一步断的，我到现在也说不清。",[10,2022,2023],{},"先翻恢复码，没存；翻邮箱，没有；翻笔记，还是没有。最后只能走申诉：填表、等回复、再补材料，一个账号折腾了好几天才算有个说法。",[10,2025,2026],{},"那几天我一直在想一件事：我的密码一个都没丢，锁是我自己加的，可钥匙不知道被我放哪儿去了。",[32,2028,2030],{"id":2029},"那些验证器本来也没打算让你把数据拿走","那些验证器，本来也没打算让你把数据拿走",[10,2032,2033],{},"先说清楚，把验证码放在手机上没有任何问题。Microsoft Authenticator 我用了很多年，平时很顺手，它也确实尽职。",[10,2035,2036],{},"问题是，一直到出事那天我才发现，我从来没搞清楚过几件事：我的数据存在哪里、上一次同步是什么时候成功的、以及万一要用的时候，我能不能把它拿出来。",[174,2038,2039,2045,2051,2057],{},[177,2040,2041,2044],{},[181,2042,2043],{},"Google Authenticator","：同步要连 Google 的服务。在国内能不能连上、什么时候能连上，基本看运气。它可以把账号扫码迁到另一个 App，但不会给你一份能自己保管的备份文件。",[177,2046,2047,2050],{},[181,2048,2049],{},"Microsoft Authenticator","：备份锁在微软账号里，跨系统恢复限制不少——在安卓上备份的，往 iPhone 上恢复往往就不行。我这次就是栽在这儿：换完机打开，一条都没有。",[177,2052,2053,2056],{},[181,2054,2055],{},"Authy","：注册要手机号，数据在它自己的云上。想手动导出一份，它不给这个选项。",[177,2058,2059,2062],{},[181,2060,2061],{},"把 2FA 顺手塞进密码管理器","（1Password、Bitwarden 之类）：确实方便，但等于把密码和验证码放进同一个篮子。篮子漏了一次，二次验证也就白开了——它的全部意义本来就在于\"再多一道锁\"。",[10,2064,2065],{},"绕一圈你会发现，这些方案都在回答同一个问题：\"我的验证码存在谁那儿？\" 而答案基本都是\"存我这儿就行，你不用管\"。",[32,2067,2068],{"id":2068},"我想要的东西其实很朴素",[10,2070,2071],{},"列出来就五条：",[2073,2074,2075,2081,2087,2093,2099],"ol",{},[177,2076,2077,2080],{},[181,2078,2079],{},"数据存在本地","，最好能用上系统的加密存储；",[177,2082,2083,2086],{},[181,2084,2085],{},"能备份到我本来就有的地方","——坚果云、自己的 NAS、对象存储，这些我早就在用了，不想再为它单独开个账号；",[177,2088,2089,2092],{},[181,2090,2091],{},"备份必须是加密的","，网盘服务商拿到也看不懂；",[177,2094,2095,2098],{},[181,2096,2097],{},"兼容标准","，能从别家导进来，也能导出去，别再添一个孤岛；",[177,2100,2101,608],{},[181,2102,2103],{},"换手机的时候，别再让我把几十个账号一个个重新扫一遍",[10,2105,2106],{},"按这几条筛，能同时满足的其实不多：有些方案得自己搭服务、平时开着浏览器用，而我想要的只是在手机上点一下。",[10,2108,2109],{},"后来我用的是 EasyAuth。",[32,2111,2113],{"id":2112},"easyauth-是什么","EasyAuth 是什么",[10,2115,2116],{},"一句话：一个开源的 2FA 验证器 App，本地存储、离线可用，并且支持把数据加密备份到你自己的 WebDAV 或 S3 存储。",[10,2118,2119],{},"它是 Flutter 写的，Apache 2.0 协议完全开源，代码公开可审计，没有账号体系、没有遥测、没有广告，也不需要 Google 服务框架。",[10,2121,2122,2123,2126],{},"目前能直接下载安装的是 ",[181,2124,2125],{},"Android 版 APK","（GitHub \u002F Gitee 上都有）。iOS、Windows、Linux 还没有预编译版本，源码是公开的，可以自己编译。",[10,2128,2129,2130,2133],{},"算法上是 TOTP（RFC 6238）和 HOTP（RFC 4226）两种标准，兼容 Google Authenticator 那套 ",[552,2131,2132],{},"otpauth:\u002F\u002F"," 协议和迁移二维码——意味着它能跟别家互相搬家，不是又一个只能进不能出的孤岛。",[10,2135,2136],{},[16,2137],{"alt":2138,"src":2139,"width":30},"EasyAuth 主界面：口令按发行方分卡片，右侧是倒计时环","\u002Fimages\u002Feasyauth-2fa-webdav-s3-backup-client\u002F01-home-list.jpg",[10,2141,2142],{},"界面很朴素：顶上一行搜索框，下面是口令卡片，每张卡片三行——口令名、发行方、当前六位数字，右边一圈倒计时。没有花活，也没有让我盯着看的动画。",[32,2144,2145],{"id":2145},"它把备份这件事做对了",[10,2147,2148],{},"如果只让我说一个推荐理由，就是备份。",[1287,2150,2152],{"id":2151},"备份存哪儿你说了算","备份存哪儿，你说了算",[10,2154,2155],{},"支持两类：",[174,2157,2158,2164],{},[177,2159,2160,2163],{},[181,2161,2162],{},"WebDAV","：坚果云、Nextcloud，以及群晖、威联通这些自建 NAS 基本都自带；",[177,2165,2166,2169],{},[181,2167,2168],{},"S3 兼容","：AWS S3、阿里云 OSS、腾讯云 COS、七牛云、MinIO、Cloudflare R2 这类对象存储。",[10,2171,2172],{},[16,2173],{"alt":2174,"src":2175,"width":2176},"备份参数配置","\u002Fimages\u002Feasyauth-2fa-webdav-s3-backup-client\u002F02-03-backup.jpg",750,[10,2178,2179],{},"关键在于\"你自己的\"这四个字：填的是你自己服务器的地址、你自己的密钥，备份文件进的是你自己的空间。哪天不想用了，文件也还在你自己的空间里，不会因为某个服务关停就跟着消失。",[1287,2181,2182],{"id":2182},"备份本身是加密的",[10,2184,2185],{},"备份文件用 AES-256-GCM 加密，密钥由你设置的备份密码经 PBKDF2-HMAC-SHA256 迭代 60 万次派生。换句话说，你的网盘账号就算被别人拿到了，他下载走那个 zip 也只是一堆乱码。",[1287,2187,2188],{"id":2188},"多版本可回退",[10,2190,2191,2192,2195],{},"备份文件名带时间戳，形如 ",[552,2193,2194],{},"backup_20260527_120413.zip","，默认在远端保留最近 10 份，超出的自动清理。也可以在 App 里直接看到远端有哪些备份、手动删掉不要的，不用专门登录服务器去收拾。",[10,2197,2198],{},[16,2199],{"alt":2200,"src":2201,"width":2176},"管理远端备份：直接在 App 里查看和删除","\u002Fimages\u002Feasyauth-2fa-webdav-s3-backup-client\u002F04-05-backup.jpg",[1287,2203,2204],{"id":2204},"恢复不会手滑",[10,2206,2207,2208,2211],{},"恢复是覆盖式的，所以它一定会先弹一次确认。密码错了、或者备份文件损坏，它会直接报错，",[181,2209,2210],{},"不会","动你已经存在的数据。这个细节挺重要——恢复本来就是出事的时候才会做的事，那个时刻最不该再出岔子。",[32,2213,2214],{"id":2214},"说在后面",[10,2216,2217],{},"2FA 的意义本来就是把钥匙多放一把在自己手里。既然如此，这把钥匙最好真的在自己手里。",[10,2219,2220],{},"对了，还得交代一句：EasyAuth 是我自己写的，起因就是开头那件事。它完全开源，用得上的话自取：",[174,2222,2223,2230,2237,2244,2251],{},[177,2224,2225,2226],{},"官网：",[94,2227,2228],{"href":2228,"rel":2229},"https:\u002F\u002Feasyauth.easydebug.net\u002F",[98],[177,2231,2232,2233],{},"GitHub 仓库：",[94,2234,2235],{"href":2235,"rel":2236},"https:\u002F\u002Fgithub.com\u002FEasyDebug-NET\u002FEasyAuth",[98],[177,2238,2239,2240],{},"GitHub 下载：",[94,2241,2242],{"href":2242,"rel":2243},"https:\u002F\u002Fgithub.com\u002FEasyDebug-NET\u002FEasyAuth\u002Freleases\u002Flatest",[98],[177,2245,2246,2247],{},"Gitee 仓库：",[94,2248,2249],{"href":2249,"rel":2250},"https:\u002F\u002Fgitee.com\u002FEasyDebug-NET\u002FEasyAuth",[98],[177,2252,2253,2254],{},"Gitee 下载：",[94,2255,2256],{"href":2256,"rel":2257},"https:\u002F\u002Fgitee.com\u002FEasyDebug-NET\u002FEasyAuth\u002Freleases\u002Flatest",[98],[10,2259,2260],{},"有问题欢迎在仓库里提 Issue。",{"title":291,"searchDepth":292,"depth":292,"links":2262},[2263,2264,2265,2266,2272],{"id":2029,"depth":295,"text":2030},{"id":2068,"depth":295,"text":2068},{"id":2112,"depth":295,"text":2113},{"id":2145,"depth":295,"text":2145,"children":2267},[2268,2269,2270,2271],{"id":2151,"depth":292,"text":2152},{"id":2182,"depth":292,"text":2182},{"id":2188,"depth":292,"text":2188},{"id":2204,"depth":292,"text":2204},{"id":2214,"depth":295,"text":2214},"2026-09-16","换完手机，验证器的备份没把数据带回来，验证码一条没剩，我被自己的 GitHub 挡在门外好几天。这篇讲清 2FA 的备份困境，以及我最后换上的开源方案 EasyAuth。",{},"\u002Fposts\u002Feasyauth-2fa-webdav-s3-backup-client",{"title":2012,"description":2274},"posts\u002Feasyauth-2fa-webdav-s3-backup-client",[2280,2281,2282,2283],"2FA","安全","开源","工具","H1ikHm1mlGKvt9T3Yc6ujXWITzfPwDp8GYNmVpwLD74",{"id":2286,"title":2287,"body":2288,"date":2664,"description":2665,"extension":303,"meta":2666,"navigation":305,"path":2667,"seo":2668,"stem":2669,"tags":2670,"__hash__":2674},"posts\u002Fposts\u002Fcentos7-firewalld-common-commands.md","CentOS7 Firewalld 防火墙常用命令",{"type":7,"value":2289,"toc":2651},[2290,2293,2309,2312,2326,2329,2343,2346,2360,2363,2377,2381,2414,2417,2453,2456,2590,2594,2616,2619,2633,2636,2648],[32,2291,2292],{"id":2292},"开启防火墙",[546,2294,2296],{"className":889,"code":2295,"language":891,"meta":291,"style":291},"systemctl start firewalld.service\n",[552,2297,2298],{"__ignoreMap":291},[555,2299,2300,2303,2306],{"class":557,"line":558},[555,2301,2302],{"class":898},"systemctl",[555,2304,2305],{"class":592}," start",[555,2307,2308],{"class":592}," firewalld.service\n",[32,2310,2311],{"id":2311},"关闭防火墙",[546,2313,2315],{"className":889,"code":2314,"language":891,"meta":291,"style":291},"systemctl stop firewalld.service\n",[552,2316,2317],{"__ignoreMap":291},[555,2318,2319,2321,2324],{"class":557,"line":558},[555,2320,2302],{"class":898},[555,2322,2323],{"class":592}," stop",[555,2325,2308],{"class":592},[32,2327,2328],{"id":2328},"查看防火墙状态",[546,2330,2332],{"className":889,"code":2331,"language":891,"meta":291,"style":291},"systemctl status firewalld.service\n",[552,2333,2334],{"__ignoreMap":291},[555,2335,2336,2338,2341],{"class":557,"line":558},[555,2337,2302],{"class":898},[555,2339,2340],{"class":592}," status",[555,2342,2308],{"class":592},[32,2344,2345],{"id":2345},"开启开机启动防火墙",[546,2347,2349],{"className":889,"code":2348,"language":891,"meta":291,"style":291},"systemctl enable firewalld.service\n",[552,2350,2351],{"__ignoreMap":291},[555,2352,2353,2355,2358],{"class":557,"line":558},[555,2354,2302],{"class":898},[555,2356,2357],{"class":592}," enable",[555,2359,2308],{"class":592},[32,2361,2362],{"id":2362},"关闭开机启动防火墙",[546,2364,2366],{"className":889,"code":2365,"language":891,"meta":291,"style":291},"systemctl disable firewalld.service\n",[552,2367,2368],{"__ignoreMap":291},[555,2369,2370,2372,2375],{"class":557,"line":558},[555,2371,2302],{"class":898},[555,2373,2374],{"class":592}," disable",[555,2376,2308],{"class":592},[32,2378,2380],{"id":2379},"开启某个端口80","开启某个端口（80）",[546,2382,2384],{"className":889,"code":2383,"language":891,"meta":291,"style":291},"firewall-cmd --zone=public --add-port=80\u002Ftcp --permanent   \u002F\u002F永久\nfirewall-cmd --zone=public --add-port=80\u002Ftcp               \u002F\u002F临时\n",[552,2385,2386,2403],{"__ignoreMap":291},[555,2387,2388,2391,2394,2397,2400],{"class":557,"line":558},[555,2389,2390],{"class":898},"firewall-cmd",[555,2392,2393],{"class":592}," --zone=public",[555,2395,2396],{"class":592}," --add-port=80\u002Ftcp",[555,2398,2399],{"class":592}," --permanent",[555,2401,2402],{"class":592},"   \u002F\u002F永久\n",[555,2404,2405,2407,2409,2411],{"class":557,"line":295},[555,2406,2390],{"class":898},[555,2408,2393],{"class":592},[555,2410,2396],{"class":592},[555,2412,2413],{"class":592},"               \u002F\u002F临时\n",[32,2415,2416],{"id":2416},"端口转发",[546,2418,2420],{"className":889,"code":2419,"language":891,"meta":291,"style":291},"firewall-cmd --zone=public --add-masquerade --permanent    \u002F\u002F打开IP地址伪装\n\u002F\u002F将8080转发到80\nfirewall-cmd --zone=public --add-forward-port=port=80:proto=tcp:toport=8080 --permanent\n",[552,2421,2422,2436,2441],{"__ignoreMap":291},[555,2423,2424,2426,2428,2431,2433],{"class":557,"line":558},[555,2425,2390],{"class":898},[555,2427,2393],{"class":592},[555,2429,2430],{"class":592}," --add-masquerade",[555,2432,2399],{"class":592},[555,2434,2435],{"class":592},"    \u002F\u002F打开IP地址伪装\n",[555,2437,2438],{"class":557,"line":295},[555,2439,2440],{"class":898},"\u002F\u002F将8080转发到80\n",[555,2442,2443,2445,2447,2450],{"class":557,"line":292},[555,2444,2390],{"class":898},[555,2446,2393],{"class":592},[555,2448,2449],{"class":592}," --add-forward-port=port=80:proto=tcp:toport=8080",[555,2451,2452],{"class":592}," --permanent\n",[32,2454,2455],{"id":2455},"创建黑名单",[546,2457,2459],{"className":889,"code":2458,"language":891,"meta":291,"style":291},"\u002F\u002F创建blacklist ipset\nfirewall-cmd --permanent --zone=public --new-ipset=blacklist --type=hash:ip\n\u002F\u002F封禁 blacklist\nfirewall-cmd --permanent --zone=public --add-rich-rule='rule source ipset=blacklist drop'\n\u002F\u002F查看 blacklist\nfirewall-cmd --ipset=blacklist --get-entries\n\u002F\u002F添加IP到黑名单\nfirewall-cmd --permanent --zone=public --ipset=blacklist --add-entry=212.237.51.36\nfirewall-cmd --permanent --zone=public --ipset=blacklist --add-entry=188.226.191.66\nfirewall-cmd --permanent --zone=public --ipset=blacklist --add-entry=80.211.137.182\nfirewall-cmd --permanent --zone=public --ipset=blacklist --add-entry=60.191.66.226\n",[552,2460,2461,2469,2483,2491,2510,2517,2528,2534,2548,2562,2576],{"__ignoreMap":291},[555,2462,2463,2466],{"class":557,"line":558},[555,2464,2465],{"class":898},"\u002F\u002F创建blacklist",[555,2467,2468],{"class":592}," ipset\n",[555,2470,2471,2473,2475,2477,2480],{"class":557,"line":295},[555,2472,2390],{"class":898},[555,2474,2399],{"class":592},[555,2476,2393],{"class":592},[555,2478,2479],{"class":592}," --new-ipset=blacklist",[555,2481,2482],{"class":592}," --type=hash:ip\n",[555,2484,2485,2488],{"class":557,"line":292},[555,2486,2487],{"class":898},"\u002F\u002F封禁",[555,2489,2490],{"class":592}," blacklist\n",[555,2492,2493,2495,2497,2499,2502,2504,2507],{"class":557,"line":923},[555,2494,2390],{"class":898},[555,2496,2399],{"class":592},[555,2498,2393],{"class":592},[555,2500,2501],{"class":592}," --add-rich-rule=",[555,2503,589],{"class":565},[555,2505,2506],{"class":592},"rule source ipset=blacklist drop",[555,2508,2509],{"class":565},"'\n",[555,2511,2512,2515],{"class":557,"line":1786},[555,2513,2514],{"class":898},"\u002F\u002F查看",[555,2516,2490],{"class":592},[555,2518,2520,2522,2525],{"class":557,"line":2519},6,[555,2521,2390],{"class":898},[555,2523,2524],{"class":592}," --ipset=blacklist",[555,2526,2527],{"class":592}," --get-entries\n",[555,2529,2531],{"class":557,"line":2530},7,[555,2532,2533],{"class":898},"\u002F\u002F添加IP到黑名单\n",[555,2535,2537,2539,2541,2543,2545],{"class":557,"line":2536},8,[555,2538,2390],{"class":898},[555,2540,2399],{"class":592},[555,2542,2393],{"class":592},[555,2544,2524],{"class":592},[555,2546,2547],{"class":592}," --add-entry=212.237.51.36\n",[555,2549,2551,2553,2555,2557,2559],{"class":557,"line":2550},9,[555,2552,2390],{"class":898},[555,2554,2399],{"class":592},[555,2556,2393],{"class":592},[555,2558,2524],{"class":592},[555,2560,2561],{"class":592}," --add-entry=188.226.191.66\n",[555,2563,2565,2567,2569,2571,2573],{"class":557,"line":2564},10,[555,2566,2390],{"class":898},[555,2568,2399],{"class":592},[555,2570,2393],{"class":592},[555,2572,2524],{"class":592},[555,2574,2575],{"class":592}," --add-entry=80.211.137.182\n",[555,2577,2579,2581,2583,2585,2587],{"class":557,"line":2578},11,[555,2580,2390],{"class":898},[555,2582,2399],{"class":592},[555,2584,2393],{"class":592},[555,2586,2524],{"class":592},[555,2588,2589],{"class":592}," --add-entry=60.191.66.226\n",[32,2591,2593],{"id":2592},"禁止被-ping丢弃-icmp-包","禁止被 PING（丢弃 ICMP 包）",[546,2595,2597],{"className":889,"code":2596,"language":891,"meta":291,"style":291},"firewall-cmd --permanent --zone=public --add-rich-rule='rule protocol value=icmp drop'  \n",[552,2598,2599],{"__ignoreMap":291},[555,2600,2601,2603,2605,2607,2609,2611,2614],{"class":557,"line":558},[555,2602,2390],{"class":898},[555,2604,2399],{"class":592},[555,2606,2393],{"class":592},[555,2608,2501],{"class":592},[555,2610,589],{"class":565},[555,2612,2613],{"class":592},"rule protocol value=icmp drop",[555,2615,2509],{"class":565},[32,2617,2618],{"id":2618},"查看所有规则",[546,2620,2622],{"className":889,"code":2621,"language":891,"meta":291,"style":291},"firewall-cmd --zone=public --list-all\n",[552,2623,2624],{"__ignoreMap":291},[555,2625,2626,2628,2630],{"class":557,"line":558},[555,2627,2390],{"class":898},[555,2629,2393],{"class":592},[555,2631,2632],{"class":592}," --list-all\n",[32,2634,2635],{"id":2635},"重载防火墙配置",[546,2637,2639],{"className":889,"code":2638,"language":891,"meta":291,"style":291},"firewall-cmd --reload\n",[552,2640,2641],{"__ignoreMap":291},[555,2642,2643,2645],{"class":557,"line":558},[555,2644,2390],{"class":898},[555,2646,2647],{"class":592}," --reload\n",[1981,2649,2650],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":291,"searchDepth":292,"depth":292,"links":2652},[2653,2654,2655,2656,2657,2658,2659,2660,2661,2662,2663],{"id":2292,"depth":295,"text":2292},{"id":2311,"depth":295,"text":2311},{"id":2328,"depth":295,"text":2328},{"id":2345,"depth":295,"text":2345},{"id":2362,"depth":295,"text":2362},{"id":2379,"depth":295,"text":2380},{"id":2416,"depth":295,"text":2416},{"id":2455,"depth":295,"text":2455},{"id":2592,"depth":295,"text":2593},{"id":2618,"depth":295,"text":2618},{"id":2635,"depth":295,"text":2635},"2025-01-24","firewalld 的日常操作速查：开关服务、放行端口、端口转发、ipset 黑名单、丢弃 ICMP、重载配置。",{},"\u002Fposts\u002Fcentos7-firewalld-common-commands",{"title":2287,"description":2665},"posts\u002Fcentos7-firewalld-common-commands",[2671,2672,2673],"CentOS","运维","防火墙","XjNJknbfYQqHxi8T6AI6ILwW7bCMzps4rKUK1ej2haI",{"id":2676,"title":2677,"body":2678,"date":3284,"description":3285,"extension":303,"meta":3286,"navigation":305,"path":3287,"seo":3288,"stem":3289,"tags":3290,"__hash__":3295},"posts\u002Fposts\u002Fdelayq-redis-delay-queue.md","DelayQ：用 Redis 和数据库做一个延时队列",{"type":7,"value":2679,"toc":3270},[2680,2683,2686,2689,2692,2695,2706,2709,2712,2715,2718,2724,2727,2733,2736,2762,2765,2769,2772,2778,2796,2799,2803,2818,2822,2828,2831,2873,2876,2879,2885,2888,2891,2894,2900,2962,2972,2977,2986,2989,3008,3018,3021,3028,3036,3039,3162,3165,3206,3212,3219,3223,3226,3246,3249,3252,3261,3264,3267],[10,2681,2682],{},"业务里总有一类需求，叫“过一会儿再做”。",[10,2684,2685],{},"订单创建后 30 分钟没付款就自动关单；下单 15 分钟后推一条提醒；接口调失败了，等 5 分钟再重试一次；会员到期前三天发一封续费通知。",[10,2687,2688],{},"这类需求拆开看，目标都很朴素：到点执行、失败了要能重试、最后还得看得见状态。难点从来不在“延”，而在“时”—— 谁来记着这件事，以及进程重启之后它还记得吗。",[10,2690,2691],{},"DelayQ 就是我为这件事写的一个中间件。",[32,2693,2694],{"id":2694},"先说为什么不用现成的",[10,2696,2697,2698,2701,2702,2705],{},"最省事的做法是在业务代码里 ",[552,2699,2700],{},"Thread.sleep()","，或者丢给 ",[552,2703,2704],{},"ScheduledExecutorService","。单机跑个小脚本没问题，可它撑不住两件事：进程一重启，内存里的任务就全没了；多起一个实例，两边各算各的。",[10,2707,2708],{},"换成定时任务扫库呢？那得先回答两个问题：多久扫一次 —— 扫得越勤精度越高、数据库压力也越大；以及怎么保证几个实例不会同时抢到同一个任务。这两个问题不难，但每次都要重新想一遍。",[10,2710,2711],{},"那上 MQ 呢？RabbitMQ 用 TTL + 死信队列能做延时，RocketMQ 有自带的延时消息。都是成熟方案，但它们的延时级别往往是固定的几档，而且通常还附带一整套运维成本。我需要的是“任意秒数 + 到点调一个 HTTP 接口”，为这一件事养一个 MQ，不太划算。",[10,2713,2714],{},"DelayQ 的设计参考了有赞延迟队列的思路：把“任务的元信息”和“待触发的时间线”拆开存，中间用一条扫描线连接。",[32,2716,2717],{"id":2717},"它是什么",[10,2719,2720,2721],{},"一句话：",[181,2722,2723],{},"基于 Redis 和数据库的延时队列中间件，Java 开发，到点之后去调用一个你指定的 RESTful 接口。",[10,2725,2726],{},"跑起来之后是这样的：",[10,2728,2729],{},[16,2730],{"alt":2731,"src":2732},"DelayQ 架构：Job Pool 与 Delay Bucket 在 Redis，Timer 和 Runner 是两个 Java 线程池","\u002Fimages\u002Fdelayq-redis-delay-queue\u002Farchitecture.png",[10,2734,2735],{},"四块职责：",[2073,2737,2738,2744,2750,2756],{},[177,2739,2740,2743],{},[181,2741,2742],{},"Job Pool"," —— 存放所有 Job 的元信息，落在 Redis Hash 和数据库里；",[177,2745,2746,2749],{},[181,2747,2748],{},"Delay Bucket"," —— 十组以时间为维度的有序队列（Redis ZSet），按 Job Id 判定存进哪个 Bucket，里面只放 Job Id；",[177,2751,2752,2755],{},[181,2753,2754],{},"Timer"," —— 实时扫描各个 Bucket，把 delay 时间已经到期的 Job 丢进对应的 Ready Queue；",[177,2757,2758,2761],{},[181,2759,2760],{},"Ready Queue"," —— 按任务类型（Topic）执行，执行完把任务状态回写进 Job Pool。它对应架构图右侧那个 Runner。",[10,2763,2764],{},"这里有个刻意的分工：用 Redis ZSet 存时间线，“找下一个该执行的任务”就是一次有序查询，不用把整个队列翻一遍；而真正的任务体（要调的 URL、header、body）放在 Job Pool 里，Bucket 里只留一个 Id。高频变动的那个结构因此始终很轻。",[32,2766,2768],{"id":2767},"它不假装自己是通用-mq","它不假装自己是通用 MQ",[10,2770,2771],{},"先把边界说清楚，省得你跑起来才发现不对路。",[10,2773,2774,2775],{},"DelayQ 只做一件事：",[181,2776,2777],{},"到点了，替你去调一个 HTTP 接口。",[174,2779,2780,2787,2790,2793],{},[177,2781,2782,2783,2786],{},"目前",[181,2784,2785],{},"只支持 Post 请求","，其他请求方式还在计划里；",[177,2788,2789],{},"数据库是用 PostgreSQL 开发的，理论上兼容 Oracle、MySQL、MariaDB，但这个“理论上”需要你自己验一遍；",[177,2791,2792],{},"它依赖 Redis 和一个数据库，不是一个零依赖的独立进程；",[177,2794,2795],{},"它不做消息的广播、分区、顺序保证，那些是 MQ 的活儿。",[32,2797,2798],{"id":2798},"三个真正用得上的细节",[1287,2800,2802],{"id":2801},"delay-和-ttr","delay 和 ttr",[10,2804,2805,2806,2809,2810,2813,2814,2817],{},"创建任务时有三个参数：",[552,2807,2808],{},"topic"," 是任务类型，",[552,2811,2812],{},"delay"," 是从现在起等多少秒再执行，",[552,2815,2816],{},"ttr"," 是超时时间，单位都是秒。",[1287,2819,2821],{"id":2820},"passrule怎么判断成功了","passrule：怎么判断“成功了”",[10,2823,2824,2825,608],{},"调远端接口最麻烦的是“它到底成没成”。HTTP 200 不代表业务成功，返回体里可能明明白白写着 ",[552,2826,2827],{},"\"code\": 0",[10,2829,2830],{},"DelayQ 的做法是让你给一条通过规则：",[546,2832,2836],{"className":2833,"code":2834,"language":2835,"meta":291,"style":291},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\"passrule\": { \"code\": \"1\" }\n","json",[552,2837,2838],{"__ignoreMap":291},[555,2839,2840,2842,2845,2847,2850,2853,2856,2858,2860,2863,2865,2868,2870],{"class":557,"line":558},[555,2841,664],{"class":565},[555,2843,2844],{"class":592},"passrule",[555,2846,664],{"class":565},[555,2848,2849],{"class":569},": ",[555,2851,2852],{"class":565},"{",[555,2854,2855],{"class":565}," \"",[555,2857,552],{"class":561},[555,2859,664],{"class":565},[555,2861,2862],{"class":565},":",[555,2864,2855],{"class":565},[555,2866,2867],{"class":592},"1",[555,2869,664],{"class":565},[555,2871,2872],{"class":565}," }\n",[10,2874,2875],{},"它拿远端服务返回的报文去比这条规则，对上了才算任务成功。比只看 HTTP 状态码靠谱得多 —— 任务状态是 success 还是 error，得由业务结果说了算。",[1287,2877,2878],{"id":2878},"任务是看得见的",[10,2880,2881],{},[16,2882],{"alt":2883,"src":2884},"任务列表：ID、Job 类型、延迟时间、超时时间、状态、预计执行时间、创建时间","\u002Fimages\u002Fdelayq-redis-delay-queue\u002Ftask-list.png",[10,2886,2887],{},"列表页把每个 Job 的类型、delay、ttr、状态（success \u002F error）、预计执行时间和创建时间都摆了出来，顶部可以按 ID、Job 类型、状态筛选。排查的时候不至于去翻日志猜 —— 这一点在我自己的使用里比想象中重要。",[32,2889,2890],{"id":2890},"跑起来",[10,2892,2893],{},"环境只要三样：Java 1.8+、Redis、一个数据库。",[10,2895,2896,2899],{},[181,2897,2898],{},"1. 建表。"," 仓库里给了 PostgreSQL 和 Oracle 两份 DDL，PostgreSQL 这份是：",[546,2901,2905],{"className":2902,"code":2903,"language":2904,"meta":291,"style":291},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","CREATE TABLE tb_delayq_job (\n    id int8 NOT NULL,\n    topic varchar(50) NULL,\n    delay int4 NULL,\n    ttr int4 NULL,\n    body varchar(1000) NULL,\n    status varchar(10) NULL,\n    execution_time int8 NULL,\n    create_time int8 NULL,\n    CONSTRAINT pk_tb_delayq_job_id PRIMARY KEY (id)\n);\n","sql",[552,2906,2907,2912,2917,2922,2927,2932,2937,2942,2947,2952,2957],{"__ignoreMap":291},[555,2908,2909],{"class":557,"line":558},[555,2910,2911],{},"CREATE TABLE tb_delayq_job (\n",[555,2913,2914],{"class":557,"line":295},[555,2915,2916],{},"    id int8 NOT NULL,\n",[555,2918,2919],{"class":557,"line":292},[555,2920,2921],{},"    topic varchar(50) NULL,\n",[555,2923,2924],{"class":557,"line":923},[555,2925,2926],{},"    delay int4 NULL,\n",[555,2928,2929],{"class":557,"line":1786},[555,2930,2931],{},"    ttr int4 NULL,\n",[555,2933,2934],{"class":557,"line":2519},[555,2935,2936],{},"    body varchar(1000) NULL,\n",[555,2938,2939],{"class":557,"line":2530},[555,2940,2941],{},"    status varchar(10) NULL,\n",[555,2943,2944],{"class":557,"line":2536},[555,2945,2946],{},"    execution_time int8 NULL,\n",[555,2948,2949],{"class":557,"line":2550},[555,2950,2951],{},"    create_time int8 NULL,\n",[555,2953,2954],{"class":557,"line":2564},[555,2955,2956],{},"    CONSTRAINT pk_tb_delayq_job_id PRIMARY KEY (id)\n",[555,2958,2959],{"class":557,"line":2578},[555,2960,2961],{},");\n",[10,2963,2964,2967,2968,2971],{},[181,2965,2966],{},"2. 改配置。"," 编辑 ",[552,2969,2970],{},"application-dev.yml","，填上 Redis 和数据库的连接信息。",[10,2973,2974],{},[181,2975,2976],{},"3. 启动。",[546,2978,2980],{"className":889,"code":2979,"language":891,"meta":291,"style":291},".\u002Fstartup.sh\n",[552,2981,2982],{"__ignoreMap":291},[555,2983,2984],{"class":557,"line":558},[555,2985,2979],{"class":898},[10,2987,2988],{},"或者直接跑 jar：",[546,2990,2992],{"className":889,"code":2991,"language":891,"meta":291,"style":291},"java -jar DelayQ.jar --spring.profiles.active=dev\n",[552,2993,2994],{"__ignoreMap":291},[555,2995,2996,2999,3002,3005],{"class":557,"line":558},[555,2997,2998],{"class":898},"java",[555,3000,3001],{"class":592}," -jar",[555,3003,3004],{"class":592}," DelayQ.jar",[555,3006,3007],{"class":592}," --spring.profiles.active=dev\n",[10,3009,3010,3013,3014,3017],{},[181,3011,3012],{},"4. 打开界面。"," 访问 ",[552,3015,3016],{},"http:\u002F\u002Flocalhost:16996\u002Fdelay-q\u002Findex","，就是上面那个任务列表。",[32,3019,3020],{"id":3020},"加一个任务",[10,3022,3023,3024,3027],{},"向 ",[552,3025,3026],{},"add"," 接口发一个 Post，查询参数就是前面说的三个：",[546,3029,3034],{"className":3030,"code":3032,"language":3033},[3031],"language-text","POST http:\u002F\u002Flocalhost:16996\u002Fdelay-q\u002Fadd?topic=HttpPostTask&delay=60&ttr=10\n","text",[552,3035,3032],{"__ignoreMap":291},[10,3037,3038],{},"请求体里写清楚“到点之后要干什么”：",[546,3040,3042],{"className":2833,"code":3041,"language":2835,"meta":291,"style":291},"{\n  \"url\": \"http:\u002F\u002F172.16.6.202:16996\u002Fdelay-q\u002Fadd?topic=PrintTask&delay=10&ttr=10\",\n  \"headers\": {\n    \"Content-Type\": \"application\u002Fjson\"\n  },\n  \"body\": \"\",\n  \"passrule\": {\n    \"code\": \"1\"\n  }\n}\n",[552,3043,3044,3049,3071,3084,3104,3109,3125,3137,3153,3158],{"__ignoreMap":291},[555,3045,3046],{"class":557,"line":558},[555,3047,3048],{"class":565},"{\n",[555,3050,3051,3054,3057,3059,3061,3063,3066,3068],{"class":557,"line":295},[555,3052,3053],{"class":565},"  \"",[555,3055,3056],{"class":561},"url",[555,3058,664],{"class":565},[555,3060,2862],{"class":565},[555,3062,2855],{"class":565},[555,3064,3065],{"class":592},"http:\u002F\u002F172.16.6.202:16996\u002Fdelay-q\u002Fadd?topic=PrintTask&delay=10&ttr=10",[555,3067,664],{"class":565},[555,3069,3070],{"class":565},",\n",[555,3072,3073,3075,3078,3080,3082],{"class":557,"line":292},[555,3074,3053],{"class":565},[555,3076,3077],{"class":561},"headers",[555,3079,664],{"class":565},[555,3081,2862],{"class":565},[555,3083,1005],{"class":565},[555,3085,3086,3089,3092,3094,3096,3098,3101],{"class":557,"line":923},[555,3087,3088],{"class":565},"    \"",[555,3090,3091],{"class":898},"Content-Type",[555,3093,664],{"class":565},[555,3095,2862],{"class":565},[555,3097,2855],{"class":565},[555,3099,3100],{"class":592},"application\u002Fjson",[555,3102,3103],{"class":565},"\"\n",[555,3105,3106],{"class":557,"line":1786},[555,3107,3108],{"class":565},"  },\n",[555,3110,3111,3113,3116,3118,3120,3123],{"class":557,"line":2519},[555,3112,3053],{"class":565},[555,3114,3115],{"class":561},"body",[555,3117,664],{"class":565},[555,3119,2862],{"class":565},[555,3121,3122],{"class":565}," \"\"",[555,3124,3070],{"class":565},[555,3126,3127,3129,3131,3133,3135],{"class":557,"line":2530},[555,3128,3053],{"class":565},[555,3130,2844],{"class":561},[555,3132,664],{"class":565},[555,3134,2862],{"class":565},[555,3136,1005],{"class":565},[555,3138,3139,3141,3143,3145,3147,3149,3151],{"class":557,"line":2536},[555,3140,3088],{"class":565},[555,3142,552],{"class":898},[555,3144,664],{"class":565},[555,3146,2862],{"class":565},[555,3148,2855],{"class":565},[555,3150,2867],{"class":592},[555,3152,3103],{"class":565},[555,3154,3155],{"class":557,"line":2550},[555,3156,3157],{"class":565},"  }\n",[555,3159,3160],{"class":557,"line":2564},[555,3161,1058],{"class":565},[10,3163,3164],{},"参数逐个说：",[174,3166,3167,3176,3181,3186,3191,3196,3201],{},[177,3168,3169,3171,3172,3175],{},[552,3170,2808],{},"（必填）：任务类型，",[552,3173,3174],{},"HttpPostTask"," 是内置的那一种；",[177,3177,3178,3180],{},[552,3179,2812],{},"（必填）：等待秒数；",[177,3182,3183,3185],{},[552,3184,2816],{},"（必填）：超时秒数；",[177,3187,3188,3190],{},[552,3189,3056],{},"（必填）：远端服务地址；",[177,3192,3193,3195],{},[552,3194,3077],{},"（可选）：请求头；",[177,3197,3198,3200],{},[552,3199,3115],{},"（可选）：POST 的报文；",[177,3202,3203,3205],{},[552,3204,2844],{},"（可选）：上面那条通过规则。",[10,3207,3208],{},[16,3209],{"alt":3210,"src":3211},"用 Postman 发一个 add 请求，响应里的 data 就是新 Job 的 ID","\u002Fimages\u002Fdelayq-redis-delay-queue\u002Fpostman-add-task.png",[10,3213,3214,3215,3218],{},"返回体里的 ",[552,3216,3217],{},"data"," 就是新建 Job 的 ID，拿它去列表页就能查到这条任务后面执行成什么样了。",[32,3220,3222],{"id":3221},"现在的样子和下一步","现在的样子，和下一步",[10,3224,3225],{},"它还算年轻，有几件事我自己清楚没做好，一并写在这里：",[2073,3227,3228,3234,3240],{},[177,3229,3230,3233],{},[181,3231,3232],{},"多实例部署时，多个 Timer 会同时扫描。"," 同一批 Bucket 被重复扫，白白消耗资源 —— 这是最该先解决的问题；",[177,3235,3236,3239],{},[181,3237,3238],{},"只支持 Post。"," 后续想加上更多请求方式；",[177,3241,3242,3245],{},[181,3243,3244],{},"界面功能还薄。"," 列表、筛选、删除够用，但离完善还有距离。",[32,3247,3248],{"id":3248},"最后",[10,3250,3251],{},"仓库在这里，Apache License 2.0，代码随便看、随便用：",[174,3253,3254],{},[177,3255,3256,3257],{},"Gitee：",[94,3258,3259],{"href":3259,"rel":3260},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002FDelayQ",[98],[10,3262,3263],{},"如果你也在纠结“这几万个到点要执行的任务该放哪儿”，可以先 clone 下来跑一遍 —— 建一张表、连上 Redis，就能看到任务在列表里一条条变成 success。",[10,3265,3266],{},"用着有问题或者有想法，欢迎直接提 Issue。",[1981,3268,3269],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":291,"searchDepth":292,"depth":292,"links":3271},[3272,3273,3274,3275,3280,3281,3282,3283],{"id":2694,"depth":295,"text":2694},{"id":2717,"depth":295,"text":2717},{"id":2767,"depth":295,"text":2768},{"id":2798,"depth":295,"text":2798,"children":3276},[3277,3278,3279],{"id":2801,"depth":292,"text":2802},{"id":2820,"depth":292,"text":2821},{"id":2878,"depth":292,"text":2878},{"id":2890,"depth":295,"text":2890},{"id":3020,"depth":295,"text":3020},{"id":3221,"depth":295,"text":3222},{"id":3248,"depth":295,"text":3248},"2022-02-17","一个基于 Redis 和数据库的延时队列中间件，Java 开发，到点之后替你调用一个 RESTful 接口。这篇讲清它的四块架构、设计取舍、已知短板，以及怎么跑起来。",{},"\u002Fposts\u002Fdelayq-redis-delay-queue",{"title":2677,"description":3285},"posts\u002Fdelayq-redis-delay-queue",[3291,3292,3293,2282,3294],"Java","Redis","消息队列","中间件","KyPhn29RAViYkNckjQzeXVv3VUQrM_4dQMlhMd249ZA",{"id":3297,"title":3298,"body":3299,"date":3511,"description":3512,"extension":303,"meta":3513,"navigation":305,"path":3514,"seo":3515,"stem":3516,"tags":3517,"__hash__":3520},"posts\u002Fposts\u002Fmysql-batch-rename-table-column.md","MySQL 批量修改表名列名大小写",{"type":7,"value":3300,"toc":3507},[3301,3308,3312,3378,3382,3497,3504],[10,3302,3303,3304,3307],{},"接手老库时经常会碰到表名列名大小写混乱的情况。手写 rename 语句太累，不如查 ",[552,3305,3306],{},"information_schema"," 让数据库自己生成一批 SQL，复制出来跑就行。",[32,3309,3311],{"id":3310},"批量表名大写改小写","批量表名：大写改小写",[546,3313,3315],{"className":2902,"code":3314,"language":2904,"meta":291,"style":291},"SELECT\n concat(\n     'rename table  '\n     , TABLE_NAME\n     , ' to '\n     , LOWER(TABLE_NAME)\n     ,' ;'\n ) AS 'SQL'\nFROM\n information_schema.TABLES t \nWHERE\n TABLE_SCHEMA = 'xxxxxx';\n",[552,3316,3317,3322,3327,3332,3337,3342,3347,3352,3357,3362,3367,3372],{"__ignoreMap":291},[555,3318,3319],{"class":557,"line":558},[555,3320,3321],{},"SELECT\n",[555,3323,3324],{"class":557,"line":295},[555,3325,3326],{}," concat(\n",[555,3328,3329],{"class":557,"line":292},[555,3330,3331],{},"     'rename table  '\n",[555,3333,3334],{"class":557,"line":923},[555,3335,3336],{},"     , TABLE_NAME\n",[555,3338,3339],{"class":557,"line":1786},[555,3340,3341],{},"     , ' to '\n",[555,3343,3344],{"class":557,"line":2519},[555,3345,3346],{},"     , LOWER(TABLE_NAME)\n",[555,3348,3349],{"class":557,"line":2530},[555,3350,3351],{},"     ,' ;'\n",[555,3353,3354],{"class":557,"line":2536},[555,3355,3356],{}," ) AS 'SQL'\n",[555,3358,3359],{"class":557,"line":2550},[555,3360,3361],{},"FROM\n",[555,3363,3364],{"class":557,"line":2564},[555,3365,3366],{}," information_schema.TABLES t \n",[555,3368,3369],{"class":557,"line":2578},[555,3370,3371],{},"WHERE\n",[555,3373,3375],{"class":557,"line":3374},12,[555,3376,3377],{}," TABLE_SCHEMA = 'xxxxxx';\n",[32,3379,3381],{"id":3380},"批量列名大写改小写","批量列名：大写改小写",[546,3383,3385],{"className":2902,"code":3384,"language":2904,"meta":291,"style":291},"SELECT\nconcat(\n    'alter table '\n    , TABLE_NAME\n    , ' change column '\n    , COLUMN_NAME\n    , ' '\n    , LOWER(COLUMN_NAME)\n    , ' '\n    , COLUMN_TYPE\n    , ' comment \\''\n    , TRIM(REPLACE(REPLACE(REPLACE(REPLACE(COLUMN_COMMENT,',',':'),'\"',''),CHAR(10),''),CHAR(13),''))\n     , '\\''\n     , ' '\n     , IF(COLUMN_DEFAULT IS NULL, '', concat(' default \\'',TRIM(COLUMN_DEFAULT),'\\'')) \n     , ';' \n) AS 'SQL'\nFROM\n    information_schema.COLUMNS t\nWHERE\n    TABLE_SCHEMA = 'xxxxxx';\n",[552,3386,3387,3391,3396,3401,3406,3411,3416,3421,3426,3430,3435,3440,3445,3451,3457,3463,3469,3475,3480,3486,3491],{"__ignoreMap":291},[555,3388,3389],{"class":557,"line":558},[555,3390,3321],{},[555,3392,3393],{"class":557,"line":295},[555,3394,3395],{},"concat(\n",[555,3397,3398],{"class":557,"line":292},[555,3399,3400],{},"    'alter table '\n",[555,3402,3403],{"class":557,"line":923},[555,3404,3405],{},"    , TABLE_NAME\n",[555,3407,3408],{"class":557,"line":1786},[555,3409,3410],{},"    , ' change column '\n",[555,3412,3413],{"class":557,"line":2519},[555,3414,3415],{},"    , COLUMN_NAME\n",[555,3417,3418],{"class":557,"line":2530},[555,3419,3420],{},"    , ' '\n",[555,3422,3423],{"class":557,"line":2536},[555,3424,3425],{},"    , LOWER(COLUMN_NAME)\n",[555,3427,3428],{"class":557,"line":2550},[555,3429,3420],{},[555,3431,3432],{"class":557,"line":2564},[555,3433,3434],{},"    , COLUMN_TYPE\n",[555,3436,3437],{"class":557,"line":2578},[555,3438,3439],{},"    , ' comment \\''\n",[555,3441,3442],{"class":557,"line":3374},[555,3443,3444],{},"    , TRIM(REPLACE(REPLACE(REPLACE(REPLACE(COLUMN_COMMENT,',',':'),'\"',''),CHAR(10),''),CHAR(13),''))\n",[555,3446,3448],{"class":557,"line":3447},13,[555,3449,3450],{},"     , '\\''\n",[555,3452,3454],{"class":557,"line":3453},14,[555,3455,3456],{},"     , ' '\n",[555,3458,3460],{"class":557,"line":3459},15,[555,3461,3462],{},"     , IF(COLUMN_DEFAULT IS NULL, '', concat(' default \\'',TRIM(COLUMN_DEFAULT),'\\'')) \n",[555,3464,3466],{"class":557,"line":3465},16,[555,3467,3468],{},"     , ';' \n",[555,3470,3472],{"class":557,"line":3471},17,[555,3473,3474],{},") AS 'SQL'\n",[555,3476,3478],{"class":557,"line":3477},18,[555,3479,3361],{},[555,3481,3483],{"class":557,"line":3482},19,[555,3484,3485],{},"    information_schema.COLUMNS t\n",[555,3487,3489],{"class":557,"line":3488},20,[555,3490,3371],{},[555,3492,3494],{"class":557,"line":3493},21,[555,3495,3496],{},"    TABLE_SCHEMA = 'xxxxxx';\n",[10,3498,3499,3500,3503],{},"把 ",[552,3501,3502],{},"xxxxxx"," 换成你的库名，执行一遍拿到生成的 SQL，再贴到客户端里跑。",[1981,3505,3506],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":291,"searchDepth":292,"depth":292,"links":3508},[3509,3510],{"id":3310,"depth":295,"text":3311},{"id":3380,"depth":295,"text":3381},"2021-10-24","用 information_schema 生成批量 rename table \u002F change column 语句，把大写的表名列名一次性改成小写。",{},"\u002Fposts\u002Fmysql-batch-rename-table-column",{"title":3298,"description":3512},"posts\u002Fmysql-batch-rename-table-column",[3518,3519],"MySQL","数据库","-CV8-Yzpnbia5MLvz84obkND3gxhEiqFrHloxkl01uU",{"id":3522,"title":3523,"body":3524,"date":3569,"description":3570,"extension":303,"meta":3571,"navigation":305,"path":3572,"seo":3573,"stem":3574,"tags":3575,"__hash__":3577},"posts\u002Fposts\u002Fwin7-windows-update-error-80072ee2.md","新装 Win7 后 Windows Update 报 80072EE2 \u002F 80072EFE",{"type":7,"value":3525,"toc":3565},[3526,3529,3532,3538,3541,3544,3560],[32,3527,3528],{"id":3528},"故障场景",[10,3530,3531],{},"最近有台老电脑重装 Win7 系统，装完以后无法安装补丁，要么就报错 80072EE2，要么就报 80072EFE。",[10,3533,3534],{},[16,3535],{"alt":3536,"src":3537},"Windows Update 报错 80072EE2","\u002Fimages\u002Fwin7-windows-update\u002Ferror-80072ee2.png",[32,3539,3540],{"id":3540},"解决方法",[10,3542,3543],{},"安装补丁：",[2073,3545,3546,3553],{},[177,3547,3548],{},[94,3549,3552],{"href":3550,"rel":3551},"https:\u002F\u002Fwww.catalog.update.microsoft.com\u002FSearch.aspx?q=KB3020369",[98],"Windows6.1-KB3020369",[177,3554,3555],{},[94,3556,3559],{"href":3557,"rel":3558},"https:\u002F\u002Fwww.catalog.update.microsoft.com\u002FSearch.aspx?q=KB3125574",[98],"Windows6.1-KB3125574",[10,3561,3562],{},[181,3563,3564],{},"一定要注意顺序：先安装 KB3020369，后安装 KB3125574。",{"title":291,"searchDepth":292,"depth":292,"links":3566},[3567,3568],{"id":3528,"depth":295,"text":3528},{"id":3540,"depth":295,"text":3540},"2021-07-31","老电脑重装 Win7 后装不上补丁，报这两个错误码。按顺序打两个补丁就能救回来。",{},"\u002Fposts\u002Fwin7-windows-update-error-80072ee2",{"title":3523,"description":3570},"posts\u002Fwin7-windows-update-error-80072ee2",[3576,2672],"Windows","h8Ykzh6MWVdSZDFJA2VzFukeIUACGG0_SBjSotldcTY",{"id":3579,"title":3580,"body":3581,"date":4283,"description":4284,"extension":303,"meta":4285,"navigation":305,"path":4286,"seo":4287,"stem":4288,"tags":4289,"__hash__":4293},"posts\u002Fposts\u002Fnacos-snowflake-worker-id.md","用 Nacos 分配 Snowflake 的 Worker ID",{"type":7,"value":3582,"toc":4277},[3583,3585,3588,3591,3599,3602,3608,3615,4260,4263,4275],[32,3584,611],{"id":611},[10,3586,3587],{},"Snowflake 唯一 ID 算法大家都很熟悉，他是 Twitter 公司提出来的算法。但是对于 workerId 和 datacenterId 的维护往往需要各项目自行设计。\n基本都是基于 zookeeper or 数据库 or Redis 的。\n本例就是用 Nacos 分配 worker id。",[32,3589,3590],{"id":3590},"源码",[10,3592,3593,3594],{},"仓库地址：",[94,3595,3598],{"href":3596,"rel":3597},"https:\u002F\u002Fgitee.com\u002FJiaXiaohei\u002Fsnowflake-nacos",[98],"gitee.com\u002FJiaXiaohei\u002Fsnowflake-nacos",[32,3600,3601],{"id":3601},"原理",[10,3603,3604,3605],{},"核心类：",[552,3606,3607],{},"SnowflakeIdGenerator.class",[10,3609,3610,3611,3614],{},"基本原理：基于 Nacos 的监听服务 ",[552,3612,3613],{},"namingService.subscribe","，监听服务下的实例列表变化。\n注册一个服务到 Nacos，如果服务列表发生变化，获取服务所有实例的 Ip 地址和端口，用本机 Ip 地址和端口判断本机在实例列表的 index。",[546,3616,3619],{"className":3617,"code":3618,"language":2998,"meta":291,"style":291},"language-java shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import com.alibaba.cloud.nacos.NacosDiscoveryProperties;\nimport com.alibaba.cloud.nacos.NacosServiceManager;\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.listener.AbstractEventListener;\nimport com.alibaba.nacos.api.naming.listener.Event;\nimport com.alibaba.nacos.api.naming.listener.NamingEvent;\nimport com.alibaba.nacos.api.naming.pojo.Instance;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport java.text.DecimalFormat;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n\u002F**\n * SnowflakeId + Nacos\n *\u002F\n@Component\npublic class SnowflakeIdGenerator {\n\n    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\n\n    @Autowired\n    private NacosServiceManager nacosServiceManager;\n\n    @Autowired\n    private NacosDiscoveryProperties nacosDiscoveryProperties;\n\n    private static SnowflakeIdWorker snowflakeIdWorker;\n\n    private static int nodeId;\n\n    @PostConstruct\n    public void run() throws Exception {\n        init();\n    }\n\n    \u002F**\n     * 获取雪花Id\n     *\n     * @return\n     *\u002F\n    public static long nextId() {\n        return snowflakeIdWorker.nextId();\n    }\n\n    \u002F**\n     * 获取当前节点Id\n     *\n     * @return\n     *\u002F\n    public static int nodeId() {\n        return nodeId;\n    }\n\n    \u002F**\n     * 获取当前服务所有节点 + 增加服务监听\n     *\n     * @throws NacosException\n     *\u002F\n    private void init() throws NacosException {\n        NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());\n        namingService.subscribe(nacosDiscoveryProperties.getService(), new AbstractEventListener() {\n            @Override\n            public void onEvent(Event event) {\n                if (-1 == nacosDiscoveryProperties.getPort()) {\n                    return;\n                }\n                nodeId = calcNodeId(((NamingEvent) event).getInstances());\n                if (nodeId > 1024) {\n                    throw new IllegalArgumentException(\"Worker & Datacenter Id calc results exceed 1024\");\n                }\n                long workerId = nodeId % 31;\n                long datacenterId = (long) Math.floor((float) nodeId \u002F 31);\n                logger.info(\"nodeId:\" + nodeId + \" workerId:\" + workerId + \" datacenterId:\" + datacenterId);\n                snowflakeIdWorker = new SnowflakeIdWorker(workerId, datacenterId);\n            }\n        });\n    }\n\n    \u002F**\n     * 用ip+port计算服务列表的索引\n     *\n     * @param instanceList\n     * @return\n     *\u002F\n    private int calcNodeId(List\u003CInstance> instanceList) {\n        List\u003CLong> ipPosrList = instanceList.stream()\n                .map(x -> dealIpPort(x.getIp(), x.getPort()))\n                .sorted(Comparator.naturalOrder())\n                .collect(Collectors.toList());\n        return ipPosrList.indexOf(dealIpPort(nacosDiscoveryProperties.getIp(), nacosDiscoveryProperties.getPort()));\n    }\n\n    \u002F**\n     * ip补0 + 端口号\n     *\n     * @param ip\n     * @param port\n     * @return\n     *\u002F\n    private static Long dealIpPort(String ip, int port) {\n        String[] ips = ip.split(\"\\\\.\");\n        StringBuilder sbr = new StringBuilder();\n        for (int i = 0; i \u003C ips.length; i++) {\n            sbr.append(new DecimalFormat(\"000\").format(Integer.parseInt(ips[i])));\n        }\n        return Long.parseLong(sbr.toString() + port);\n    }\n\n}\n",[552,3620,3621,3626,3631,3636,3641,3646,3651,3656,3661,3666,3671,3676,3681,3686,3691,3696,3701,3706,3711,3715,3720,3725,3731,3737,3743,3748,3754,3759,3765,3771,3776,3781,3787,3792,3798,3803,3809,3814,3820,3826,3832,3838,3843,3849,3855,3861,3867,3873,3879,3885,3890,3895,3900,3906,3911,3916,3921,3927,3933,3938,3943,3948,3954,3959,3965,3970,3976,3982,3988,3994,4000,4006,4012,4018,4024,4030,4036,4041,4047,4053,4059,4065,4071,4077,4082,4087,4092,4098,4103,4109,4114,4119,4125,4131,4137,4143,4149,4155,4160,4165,4170,4176,4181,4187,4193,4198,4203,4209,4215,4221,4227,4233,4239,4245,4250,4255],{"__ignoreMap":291},[555,3622,3623],{"class":557,"line":558},[555,3624,3625],{},"import com.alibaba.cloud.nacos.NacosDiscoveryProperties;\n",[555,3627,3628],{"class":557,"line":295},[555,3629,3630],{},"import com.alibaba.cloud.nacos.NacosServiceManager;\n",[555,3632,3633],{"class":557,"line":292},[555,3634,3635],{},"import com.alibaba.nacos.api.exception.NacosException;\n",[555,3637,3638],{"class":557,"line":923},[555,3639,3640],{},"import com.alibaba.nacos.api.naming.NamingService;\n",[555,3642,3643],{"class":557,"line":1786},[555,3644,3645],{},"import com.alibaba.nacos.api.naming.listener.AbstractEventListener;\n",[555,3647,3648],{"class":557,"line":2519},[555,3649,3650],{},"import com.alibaba.nacos.api.naming.listener.Event;\n",[555,3652,3653],{"class":557,"line":2530},[555,3654,3655],{},"import com.alibaba.nacos.api.naming.listener.NamingEvent;\n",[555,3657,3658],{"class":557,"line":2536},[555,3659,3660],{},"import com.alibaba.nacos.api.naming.pojo.Instance;\n",[555,3662,3663],{"class":557,"line":2550},[555,3664,3665],{},"import org.slf4j.Logger;\n",[555,3667,3668],{"class":557,"line":2564},[555,3669,3670],{},"import org.slf4j.LoggerFactory;\n",[555,3672,3673],{"class":557,"line":2578},[555,3674,3675],{},"import org.springframework.beans.factory.annotation.Autowired;\n",[555,3677,3678],{"class":557,"line":3374},[555,3679,3680],{},"import org.springframework.stereotype.Component;\n",[555,3682,3683],{"class":557,"line":3447},[555,3684,3685],{"emptyLinePlaceholder":305},"\n",[555,3687,3688],{"class":557,"line":3453},[555,3689,3690],{},"import javax.annotation.PostConstruct;\n",[555,3692,3693],{"class":557,"line":3459},[555,3694,3695],{},"import java.text.DecimalFormat;\n",[555,3697,3698],{"class":557,"line":3465},[555,3699,3700],{},"import java.util.Comparator;\n",[555,3702,3703],{"class":557,"line":3471},[555,3704,3705],{},"import java.util.List;\n",[555,3707,3708],{"class":557,"line":3477},[555,3709,3710],{},"import java.util.stream.Collectors;\n",[555,3712,3713],{"class":557,"line":3482},[555,3714,3685],{"emptyLinePlaceholder":305},[555,3716,3717],{"class":557,"line":3488},[555,3718,3719],{},"\u002F**\n",[555,3721,3722],{"class":557,"line":3493},[555,3723,3724],{}," * SnowflakeId + Nacos\n",[555,3726,3728],{"class":557,"line":3727},22,[555,3729,3730],{}," *\u002F\n",[555,3732,3734],{"class":557,"line":3733},23,[555,3735,3736],{},"@Component\n",[555,3738,3740],{"class":557,"line":3739},24,[555,3741,3742],{},"public class SnowflakeIdGenerator {\n",[555,3744,3746],{"class":557,"line":3745},25,[555,3747,3685],{"emptyLinePlaceholder":305},[555,3749,3751],{"class":557,"line":3750},26,[555,3752,3753],{},"    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\n",[555,3755,3757],{"class":557,"line":3756},27,[555,3758,3685],{"emptyLinePlaceholder":305},[555,3760,3762],{"class":557,"line":3761},28,[555,3763,3764],{},"    @Autowired\n",[555,3766,3768],{"class":557,"line":3767},29,[555,3769,3770],{},"    private NacosServiceManager nacosServiceManager;\n",[555,3772,3774],{"class":557,"line":3773},30,[555,3775,3685],{"emptyLinePlaceholder":305},[555,3777,3779],{"class":557,"line":3778},31,[555,3780,3764],{},[555,3782,3784],{"class":557,"line":3783},32,[555,3785,3786],{},"    private NacosDiscoveryProperties nacosDiscoveryProperties;\n",[555,3788,3790],{"class":557,"line":3789},33,[555,3791,3685],{"emptyLinePlaceholder":305},[555,3793,3795],{"class":557,"line":3794},34,[555,3796,3797],{},"    private static SnowflakeIdWorker snowflakeIdWorker;\n",[555,3799,3801],{"class":557,"line":3800},35,[555,3802,3685],{"emptyLinePlaceholder":305},[555,3804,3806],{"class":557,"line":3805},36,[555,3807,3808],{},"    private static int nodeId;\n",[555,3810,3812],{"class":557,"line":3811},37,[555,3813,3685],{"emptyLinePlaceholder":305},[555,3815,3817],{"class":557,"line":3816},38,[555,3818,3819],{},"    @PostConstruct\n",[555,3821,3823],{"class":557,"line":3822},39,[555,3824,3825],{},"    public void run() throws Exception {\n",[555,3827,3829],{"class":557,"line":3828},40,[555,3830,3831],{},"        init();\n",[555,3833,3835],{"class":557,"line":3834},41,[555,3836,3837],{},"    }\n",[555,3839,3841],{"class":557,"line":3840},42,[555,3842,3685],{"emptyLinePlaceholder":305},[555,3844,3846],{"class":557,"line":3845},43,[555,3847,3848],{},"    \u002F**\n",[555,3850,3852],{"class":557,"line":3851},44,[555,3853,3854],{},"     * 获取雪花Id\n",[555,3856,3858],{"class":557,"line":3857},45,[555,3859,3860],{},"     *\n",[555,3862,3864],{"class":557,"line":3863},46,[555,3865,3866],{},"     * @return\n",[555,3868,3870],{"class":557,"line":3869},47,[555,3871,3872],{},"     *\u002F\n",[555,3874,3876],{"class":557,"line":3875},48,[555,3877,3878],{},"    public static long nextId() {\n",[555,3880,3882],{"class":557,"line":3881},49,[555,3883,3884],{},"        return snowflakeIdWorker.nextId();\n",[555,3886,3888],{"class":557,"line":3887},50,[555,3889,3837],{},[555,3891,3893],{"class":557,"line":3892},51,[555,3894,3685],{"emptyLinePlaceholder":305},[555,3896,3898],{"class":557,"line":3897},52,[555,3899,3848],{},[555,3901,3903],{"class":557,"line":3902},53,[555,3904,3905],{},"     * 获取当前节点Id\n",[555,3907,3909],{"class":557,"line":3908},54,[555,3910,3860],{},[555,3912,3914],{"class":557,"line":3913},55,[555,3915,3866],{},[555,3917,3919],{"class":557,"line":3918},56,[555,3920,3872],{},[555,3922,3924],{"class":557,"line":3923},57,[555,3925,3926],{},"    public static int nodeId() {\n",[555,3928,3930],{"class":557,"line":3929},58,[555,3931,3932],{},"        return nodeId;\n",[555,3934,3936],{"class":557,"line":3935},59,[555,3937,3837],{},[555,3939,3941],{"class":557,"line":3940},60,[555,3942,3685],{"emptyLinePlaceholder":305},[555,3944,3946],{"class":557,"line":3945},61,[555,3947,3848],{},[555,3949,3951],{"class":557,"line":3950},62,[555,3952,3953],{},"     * 获取当前服务所有节点 + 增加服务监听\n",[555,3955,3957],{"class":557,"line":3956},63,[555,3958,3860],{},[555,3960,3962],{"class":557,"line":3961},64,[555,3963,3964],{},"     * @throws NacosException\n",[555,3966,3968],{"class":557,"line":3967},65,[555,3969,3872],{},[555,3971,3973],{"class":557,"line":3972},66,[555,3974,3975],{},"    private void init() throws NacosException {\n",[555,3977,3979],{"class":557,"line":3978},67,[555,3980,3981],{},"        NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());\n",[555,3983,3985],{"class":557,"line":3984},68,[555,3986,3987],{},"        namingService.subscribe(nacosDiscoveryProperties.getService(), new AbstractEventListener() {\n",[555,3989,3991],{"class":557,"line":3990},69,[555,3992,3993],{},"            @Override\n",[555,3995,3997],{"class":557,"line":3996},70,[555,3998,3999],{},"            public void onEvent(Event event) {\n",[555,4001,4003],{"class":557,"line":4002},71,[555,4004,4005],{},"                if (-1 == nacosDiscoveryProperties.getPort()) {\n",[555,4007,4009],{"class":557,"line":4008},72,[555,4010,4011],{},"                    return;\n",[555,4013,4015],{"class":557,"line":4014},73,[555,4016,4017],{},"                }\n",[555,4019,4021],{"class":557,"line":4020},74,[555,4022,4023],{},"                nodeId = calcNodeId(((NamingEvent) event).getInstances());\n",[555,4025,4027],{"class":557,"line":4026},75,[555,4028,4029],{},"                if (nodeId > 1024) {\n",[555,4031,4033],{"class":557,"line":4032},76,[555,4034,4035],{},"                    throw new IllegalArgumentException(\"Worker & Datacenter Id calc results exceed 1024\");\n",[555,4037,4039],{"class":557,"line":4038},77,[555,4040,4017],{},[555,4042,4044],{"class":557,"line":4043},78,[555,4045,4046],{},"                long workerId = nodeId % 31;\n",[555,4048,4050],{"class":557,"line":4049},79,[555,4051,4052],{},"                long datacenterId = (long) Math.floor((float) nodeId \u002F 31);\n",[555,4054,4056],{"class":557,"line":4055},80,[555,4057,4058],{},"                logger.info(\"nodeId:\" + nodeId + \" workerId:\" + workerId + \" datacenterId:\" + datacenterId);\n",[555,4060,4062],{"class":557,"line":4061},81,[555,4063,4064],{},"                snowflakeIdWorker = new SnowflakeIdWorker(workerId, datacenterId);\n",[555,4066,4068],{"class":557,"line":4067},82,[555,4069,4070],{},"            }\n",[555,4072,4074],{"class":557,"line":4073},83,[555,4075,4076],{},"        });\n",[555,4078,4080],{"class":557,"line":4079},84,[555,4081,3837],{},[555,4083,4085],{"class":557,"line":4084},85,[555,4086,3685],{"emptyLinePlaceholder":305},[555,4088,4090],{"class":557,"line":4089},86,[555,4091,3848],{},[555,4093,4095],{"class":557,"line":4094},87,[555,4096,4097],{},"     * 用ip+port计算服务列表的索引\n",[555,4099,4101],{"class":557,"line":4100},88,[555,4102,3860],{},[555,4104,4106],{"class":557,"line":4105},89,[555,4107,4108],{},"     * @param instanceList\n",[555,4110,4112],{"class":557,"line":4111},90,[555,4113,3866],{},[555,4115,4117],{"class":557,"line":4116},91,[555,4118,3872],{},[555,4120,4122],{"class":557,"line":4121},92,[555,4123,4124],{},"    private int calcNodeId(List\u003CInstance> instanceList) {\n",[555,4126,4128],{"class":557,"line":4127},93,[555,4129,4130],{},"        List\u003CLong> ipPosrList = instanceList.stream()\n",[555,4132,4134],{"class":557,"line":4133},94,[555,4135,4136],{},"                .map(x -> dealIpPort(x.getIp(), x.getPort()))\n",[555,4138,4140],{"class":557,"line":4139},95,[555,4141,4142],{},"                .sorted(Comparator.naturalOrder())\n",[555,4144,4146],{"class":557,"line":4145},96,[555,4147,4148],{},"                .collect(Collectors.toList());\n",[555,4150,4152],{"class":557,"line":4151},97,[555,4153,4154],{},"        return ipPosrList.indexOf(dealIpPort(nacosDiscoveryProperties.getIp(), nacosDiscoveryProperties.getPort()));\n",[555,4156,4158],{"class":557,"line":4157},98,[555,4159,3837],{},[555,4161,4163],{"class":557,"line":4162},99,[555,4164,3685],{"emptyLinePlaceholder":305},[555,4166,4168],{"class":557,"line":4167},100,[555,4169,3848],{},[555,4171,4173],{"class":557,"line":4172},101,[555,4174,4175],{},"     * ip补0 + 端口号\n",[555,4177,4179],{"class":557,"line":4178},102,[555,4180,3860],{},[555,4182,4184],{"class":557,"line":4183},103,[555,4185,4186],{},"     * @param ip\n",[555,4188,4190],{"class":557,"line":4189},104,[555,4191,4192],{},"     * @param port\n",[555,4194,4196],{"class":557,"line":4195},105,[555,4197,3866],{},[555,4199,4201],{"class":557,"line":4200},106,[555,4202,3872],{},[555,4204,4206],{"class":557,"line":4205},107,[555,4207,4208],{},"    private static Long dealIpPort(String ip, int port) {\n",[555,4210,4212],{"class":557,"line":4211},108,[555,4213,4214],{},"        String[] ips = ip.split(\"\\\\.\");\n",[555,4216,4218],{"class":557,"line":4217},109,[555,4219,4220],{},"        StringBuilder sbr = new StringBuilder();\n",[555,4222,4224],{"class":557,"line":4223},110,[555,4225,4226],{},"        for (int i = 0; i \u003C ips.length; i++) {\n",[555,4228,4230],{"class":557,"line":4229},111,[555,4231,4232],{},"            sbr.append(new DecimalFormat(\"000\").format(Integer.parseInt(ips[i])));\n",[555,4234,4236],{"class":557,"line":4235},112,[555,4237,4238],{},"        }\n",[555,4240,4242],{"class":557,"line":4241},113,[555,4243,4244],{},"        return Long.parseLong(sbr.toString() + port);\n",[555,4246,4248],{"class":557,"line":4247},114,[555,4249,3837],{},[555,4251,4253],{"class":557,"line":4252},115,[555,4254,3685],{"emptyLinePlaceholder":305},[555,4256,4258],{"class":557,"line":4257},116,[555,4259,1058],{},[32,4261,4262],{"id":4262},"参考",[10,4264,4265,4266,4269,4270],{},"雪花生成类 ",[552,4267,4268],{},"SnowflakeIdWorker.class"," 参考：\n",[94,4271,4274],{"href":4272,"rel":4273},"https:\u002F\u002Fblog.csdn.net\u002Flq18050010830\u002Farticle\u002Fdetails\u002F89845790",[98],"blog.csdn.net\u002Flq18050010830\u002Farticle\u002Fdetails\u002F89845790",[1981,4276,3506],{},{"title":291,"searchDepth":292,"depth":292,"links":4278},[4279,4280,4281,4282],{"id":611,"depth":295,"text":611},{"id":3590,"depth":295,"text":3590},{"id":3601,"depth":295,"text":3601},{"id":4262,"depth":295,"text":4262},"2021-07-27","借 Nacos 的服务实例列表动态算出 workerId 和 datacenterId，不用自己维护 zookeeper 或数据库。",{},"\u002Fposts\u002Fnacos-snowflake-worker-id",{"title":3580,"description":4284},"posts\u002Fnacos-snowflake-worker-id",[4290,4291,4292,3291],"Nacos","Snowflake","分布式","q6O68lgetHxAC0gydMk10d90i9PDmzbCotgRtwvrCXw",{"id":4295,"title":4296,"body":4297,"date":4391,"description":5596,"extension":303,"meta":5597,"navigation":305,"path":5598,"seo":5599,"stem":5600,"tags":5601,"__hash__":5603},"posts\u002Fposts\u002Fgitlab-ce-upgrade-11-to-13.md","记一次 GitLab-CE 跨版本从 11 升级到 13 踩的坑",{"type":7,"value":4298,"toc":5590},[4299,4302,4308,4311,4315,4324,4330,4332,4336,4339,4502,4505,4596,4598,4602,4605,4952,4955,5079,5082,5370,5373,5569,5572,5587],[1287,4300,4301],{"id":4301},"环境",[546,4303,4306],{"className":4304,"code":4305,"language":3033},[3031],"OS:CentOS 8\nHostname:HyperV-GitLab\n",[552,4307,4305],{"__ignoreMap":291},[4309,4310],"hr",{},[1287,4312,4314],{"id":4313},"_1不可跨大版本升级","1.不可跨大版本升级",[10,4316,4317,4318,4323],{},"必须先升级到当前大版本的最新版 在升级到下一个大版本\n参考",[94,4319,4322],{"href":4320,"rel":4321},"https:\u002F\u002Fdocs.gitlab.com\u002Fee\u002Fpolicy\u002Fmaintenance.html#upgrade-recommendations",[98],"官方网站","对于版本升级的说明",[10,4325,4326],{},[16,4327],{"alt":4328,"src":4329},"GitLab 版本升级路径","\u002Fimages\u002Fgitlab-ce-upgrade\u002Fupgrade-path.png",[4309,4331],{},[1287,4333,4335],{"id":4334},"_2pg_dump-aborting-because-of-server-version-mismatch","2.pg_dump: aborting because of server version mismatch",[10,4337,4338],{},"因为使用了外部数据源（PG），而且是 12 的高版本，GitLab 自带 pg_dump 版本只到 10 或者 11。在升级的备份部分，就备份失败，报错如下：",[546,4340,4342],{"className":889,"code":4341,"language":891,"meta":291,"style":291},"gitlab preinstall: Automatically backing up only the GitLab SQL database (excluding everything else!)\n2020-10-01 21:47:39 +0800 -- Dumping database ... \nDumping PostgreSQL database db_gitlab ... pg_dump: server version: 12.4; pg_dump version: 11.7\npg_dump: aborting because of server version mismatch\n[FAILED]\nrake aborted!\nBackup::Error: Backup failed\n",[552,4343,4344,4387,4412,4450,4472,4483,4491],{"__ignoreMap":291},[555,4345,4346,4349,4352,4355,4358,4361,4364,4367,4370,4373,4376,4379,4382,4385],{"class":557,"line":558},[555,4347,4348],{"class":898},"gitlab",[555,4350,4351],{"class":592}," preinstall:",[555,4353,4354],{"class":592}," Automatically",[555,4356,4357],{"class":592}," backing",[555,4359,4360],{"class":592}," up",[555,4362,4363],{"class":592}," only",[555,4365,4366],{"class":592}," the",[555,4368,4369],{"class":592}," GitLab",[555,4371,4372],{"class":592}," SQL",[555,4374,4375],{"class":592}," database",[555,4377,4378],{"class":569}," (excluding ",[555,4380,4381],{"class":592},"everything",[555,4383,4384],{"class":592}," else!",[555,4386,598],{"class":569},[555,4388,4389,4392,4395,4398,4401,4404,4406,4409],{"class":557,"line":295},[555,4390,4391],{"class":898},"2020-10-01",[555,4393,4394],{"class":592}," 21:47:39",[555,4396,4397],{"class":592}," +0800",[555,4399,4400],{"class":592}," --",[555,4402,4403],{"class":592}," Dumping",[555,4405,4375],{"class":592},[555,4407,4408],{"class":592}," ...",[555,4410,4411],{"class":569}," \n",[555,4413,4414,4417,4420,4422,4425,4427,4430,4433,4436,4439,4442,4445,4447],{"class":557,"line":292},[555,4415,4416],{"class":898},"Dumping",[555,4418,4419],{"class":592}," PostgreSQL",[555,4421,4375],{"class":592},[555,4423,4424],{"class":592}," db_gitlab",[555,4426,4408],{"class":592},[555,4428,4429],{"class":592}," pg_dump:",[555,4431,4432],{"class":592}," server",[555,4434,4435],{"class":592}," version:",[555,4437,4438],{"class":941}," 12.4",[555,4440,4441],{"class":565},";",[555,4443,4444],{"class":898}," pg_dump",[555,4446,4435],{"class":592},[555,4448,4449],{"class":941}," 11.7\n",[555,4451,4452,4455,4458,4461,4464,4466,4469],{"class":557,"line":923},[555,4453,4454],{"class":898},"pg_dump:",[555,4456,4457],{"class":592}," aborting",[555,4459,4460],{"class":592}," because",[555,4462,4463],{"class":592}," of",[555,4465,4432],{"class":592},[555,4467,4468],{"class":592}," version",[555,4470,4471],{"class":592}," mismatch\n",[555,4473,4474,4477,4480],{"class":557,"line":1786},[555,4475,4476],{"class":565},"[",[555,4478,4479],{"class":569},"FAILED",[555,4481,4482],{"class":565},"]\n",[555,4484,4485,4488],{"class":557,"line":2519},[555,4486,4487],{"class":898},"rake",[555,4489,4490],{"class":592}," aborted!\n",[555,4492,4493,4496,4499],{"class":557,"line":2530},[555,4494,4495],{"class":898},"Backup::Error:",[555,4497,4498],{"class":592}," Backup",[555,4500,4501],{"class":592}," failed\n",[10,4503,4504],{},"解决方法：",[546,4506,4508],{"className":889,"code":4507,"language":891,"meta":291,"style":291},"1.安装postgresql12的客户端\ndnf install -y https:\u002F\u002Fdownload.postgresql.org\u002Fpub\u002Frepos\u002Fyum\u002Freporpms\u002FEL-8-x86_64\u002Fpgdg-redhat-repo-latest.noarch.rpm\ndnf -qy module disable postgresql\ndnf install -y postgresql12\n\n2.重新建立pg_dump的软连接\ncd \u002Fopt\u002Fgitlab\u002Fembedded\u002Fbin\nmv pg_dump pg_dump.bak\nln -s \u002Fusr\u002Fbin\u002Fpg_dump \u002Fopt\u002Fgitlab\u002Fembedded\u002Fbin\u002Fpg_dump\n",[552,4509,4510,4515,4529,4544,4555,4559,4564,4572,4582],{"__ignoreMap":291},[555,4511,4512],{"class":557,"line":558},[555,4513,4514],{"class":898},"1.安装postgresql12的客户端\n",[555,4516,4517,4520,4523,4526],{"class":557,"line":295},[555,4518,4519],{"class":898},"dnf",[555,4521,4522],{"class":592}," install",[555,4524,4525],{"class":592}," -y",[555,4527,4528],{"class":592}," https:\u002F\u002Fdownload.postgresql.org\u002Fpub\u002Frepos\u002Fyum\u002Freporpms\u002FEL-8-x86_64\u002Fpgdg-redhat-repo-latest.noarch.rpm\n",[555,4530,4531,4533,4536,4539,4541],{"class":557,"line":292},[555,4532,4519],{"class":898},[555,4534,4535],{"class":592}," -qy",[555,4537,4538],{"class":592}," module",[555,4540,2374],{"class":592},[555,4542,4543],{"class":592}," postgresql\n",[555,4545,4546,4548,4550,4552],{"class":557,"line":923},[555,4547,4519],{"class":898},[555,4549,4522],{"class":592},[555,4551,4525],{"class":592},[555,4553,4554],{"class":592}," postgresql12\n",[555,4556,4557],{"class":557,"line":1786},[555,4558,3685],{"emptyLinePlaceholder":305},[555,4560,4561],{"class":557,"line":2519},[555,4562,4563],{"class":898},"2.重新建立pg_dump的软连接\n",[555,4565,4566,4569],{"class":557,"line":2530},[555,4567,4568],{"class":998},"cd",[555,4570,4571],{"class":592}," \u002Fopt\u002Fgitlab\u002Fembedded\u002Fbin\n",[555,4573,4574,4577,4579],{"class":557,"line":2536},[555,4575,4576],{"class":898},"mv",[555,4578,4444],{"class":592},[555,4580,4581],{"class":592}," pg_dump.bak\n",[555,4583,4584,4587,4590,4593],{"class":557,"line":2550},[555,4585,4586],{"class":898},"ln",[555,4588,4589],{"class":592}," -s",[555,4591,4592],{"class":592}," \u002Fusr\u002Fbin\u002Fpg_dump",[555,4594,4595],{"class":592}," \u002Fopt\u002Fgitlab\u002Fembedded\u002Fbin\u002Fpg_dump\n",[4309,4597],{},[1287,4599,4601],{"id":4600},"_3error-preingitlab-ce-13111-ce0el8x86_64-scriptlet-failed-exit-status-1","3.error: %prein(gitlab-ce-13.1.11-ce.0.el8.x86_64) scriptlet failed, exit status 1",[10,4603,4604],{},"GitLab 一开始就用 Yum\u002Fdnf 安装，后面升级自然也一直用 Yum 命令升级；只到从 13.1.4 升级到 13.1.11 版本时，一直提示",[546,4606,4608],{"className":889,"code":4607,"language":891,"meta":291,"style":291},"[root@HyperV-GitLab opt]# yum update gitlab-ce-13.1.11\n上次元数据过期检查：6:53:15 前，执行于 2020年10月01日 星期四 15时40分48秒。\n依赖关系解决。\n==================================================================================================================================\n 软件包                       架构                      版本                                   仓库                          大小\n==================================================================================================================================\n升级:\n gitlab-ce                    x86_64                    13.1.11-ce.0.el8                       gitlab-ce                    716 M\n\n事务概要\n==================================================================================================================================\n升级  1 软件包\n\n总下载：716 M\n确定吗？[y\u002FN]： y\n下载软件包：\ngitlab-ce-13.1.11-ce.0.el8.x86_64.rpm                                                             3.7 MB\u002Fs | 716 MB     03:13    \n----------------------------------------------------------------------------------------------------------------------------------\n总计                                                                                              3.7 MB\u002Fs | 716 MB     03:13     \n运行事务检查\n事务检查成功。\n运行事务测试\n事务测试成功。\n运行事务\n  准备中  :                                                                                                                   1\u002F1 \n  运行脚本: gitlab-ce-13.1.11-ce.0.el8.x86_64                                                                                 1\u002F2 \n错误：%prein(gitlab-ce-13.1.11-ce.0.el8.x86_64) 脚本执行失败，退出状态码为 1\n\nError in PREIN scriptlet in rpm package gitlab-ce\n  验证    : gitlab-ce-13.1.11-ce.0.el8.x86_64                                                                                 1\u002F2 \n  验证    : gitlab-ce-13.1.4-ce.0.el8.x86_64                                                                                  2\u002F2 \n\n失败:\n  gitlab-ce-13.1.4-ce.0.el8.x86_64                                gitlab-ce-13.1.11-ce.0.el8.x86_64                               \n\n错误：事务失败\n[root@HyperV-GitLab opt]# \n",[552,4609,4610,4623,4640,4645,4650,4667,4671,4676,4696,4700,4705,4709,4720,4724,4731,4739,4744,4770,4775,4796,4801,4806,4811,4816,4821,4834,4847,4855,4859,4884,4898,4912,4916,4921,4932,4936,4941],{"__ignoreMap":291},[555,4611,4612,4614,4617,4620],{"class":557,"line":558},[555,4613,4476],{"class":565},[555,4615,4616],{"class":569},"root@HyperV-GitLab opt",[555,4618,4619],{"class":565},"]",[555,4621,4622],{"class":569},"# yum update gitlab-ce-13.1.11\n",[555,4624,4625,4628,4631,4634,4637],{"class":557,"line":295},[555,4626,4627],{"class":898},"上次元数据过期检查：6:53:15",[555,4629,4630],{"class":592}," 前，执行于",[555,4632,4633],{"class":592}," 2020年10月01日",[555,4635,4636],{"class":592}," 星期四",[555,4638,4639],{"class":592}," 15时40分48秒。\n",[555,4641,4642],{"class":557,"line":292},[555,4643,4644],{"class":898},"依赖关系解决。\n",[555,4646,4647],{"class":557,"line":923},[555,4648,4649],{"class":592},"==================================================================================================================================\n",[555,4651,4652,4655,4658,4661,4664],{"class":557,"line":1786},[555,4653,4654],{"class":898}," 软件包",[555,4656,4657],{"class":592},"                       架构",[555,4659,4660],{"class":592},"                      版本",[555,4662,4663],{"class":592},"                                   仓库",[555,4665,4666],{"class":592},"                          大小\n",[555,4668,4669],{"class":557,"line":2519},[555,4670,4649],{"class":592},[555,4672,4673],{"class":557,"line":2530},[555,4674,4675],{"class":898},"升级:\n",[555,4677,4678,4681,4684,4687,4690,4693],{"class":557,"line":2536},[555,4679,4680],{"class":898}," gitlab-ce",[555,4682,4683],{"class":592},"                    x86_64",[555,4685,4686],{"class":592},"                    13.1.11-ce.0.el8",[555,4688,4689],{"class":592},"                       gitlab-ce",[555,4691,4692],{"class":941},"                    716",[555,4694,4695],{"class":592}," M\n",[555,4697,4698],{"class":557,"line":2550},[555,4699,3685],{"emptyLinePlaceholder":305},[555,4701,4702],{"class":557,"line":2564},[555,4703,4704],{"class":898},"事务概要\n",[555,4706,4707],{"class":557,"line":2578},[555,4708,4649],{"class":592},[555,4710,4711,4714,4717],{"class":557,"line":3374},[555,4712,4713],{"class":898},"升级",[555,4715,4716],{"class":941},"  1",[555,4718,4719],{"class":592}," 软件包\n",[555,4721,4722],{"class":557,"line":3447},[555,4723,3685],{"emptyLinePlaceholder":305},[555,4725,4726,4729],{"class":557,"line":3453},[555,4727,4728],{"class":898},"总下载：716",[555,4730,4695],{"class":592},[555,4732,4733,4736],{"class":557,"line":3459},[555,4734,4735],{"class":898},"确定吗？[y\u002FN]：",[555,4737,4738],{"class":592}," y\n",[555,4740,4741],{"class":557,"line":3465},[555,4742,4743],{"class":898},"下载软件包：\n",[555,4745,4746,4749,4752,4755,4758,4761,4764,4767],{"class":557,"line":3471},[555,4747,4748],{"class":898},"gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm",[555,4750,4751],{"class":941},"                                                             3.7",[555,4753,4754],{"class":592}," MB\u002Fs",[555,4756,4757],{"class":565}," |",[555,4759,4760],{"class":898}," 716",[555,4762,4763],{"class":592}," MB",[555,4765,4766],{"class":592},"     03:13",[555,4768,4769],{"class":569},"    \n",[555,4771,4772],{"class":557,"line":3477},[555,4773,4774],{"class":898},"----------------------------------------------------------------------------------------------------------------------------------\n",[555,4776,4777,4780,4783,4785,4787,4789,4791,4793],{"class":557,"line":3482},[555,4778,4779],{"class":898},"总计",[555,4781,4782],{"class":941},"                                                                                              3.7",[555,4784,4754],{"class":592},[555,4786,4757],{"class":565},[555,4788,4760],{"class":898},[555,4790,4763],{"class":592},[555,4792,4766],{"class":592},[555,4794,4795],{"class":569},"     \n",[555,4797,4798],{"class":557,"line":3488},[555,4799,4800],{"class":898},"运行事务检查\n",[555,4802,4803],{"class":557,"line":3493},[555,4804,4805],{"class":898},"事务检查成功。\n",[555,4807,4808],{"class":557,"line":3727},[555,4809,4810],{"class":898},"运行事务测试\n",[555,4812,4813],{"class":557,"line":3733},[555,4814,4815],{"class":898},"事务测试成功。\n",[555,4817,4818],{"class":557,"line":3739},[555,4819,4820],{"class":898},"运行事务\n",[555,4822,4823,4826,4829,4832],{"class":557,"line":3745},[555,4824,4825],{"class":898},"  准备中",[555,4827,4828],{"class":592},"  :",[555,4830,4831],{"class":592},"                                                                                                                   1\u002F1",[555,4833,4411],{"class":569},[555,4835,4836,4839,4842,4845],{"class":557,"line":3750},[555,4837,4838],{"class":898},"  运行脚本:",[555,4840,4841],{"class":592}," gitlab-ce-13.1.11-ce.0.el8.x86_64",[555,4843,4844],{"class":592},"                                                                                 1\u002F2",[555,4846,4411],{"class":569},[555,4848,4849,4852],{"class":557,"line":3756},[555,4850,4851],{"class":898},"错误：%prein(gitlab-ce-13.1.11-ce.0.el8.x86_64",[555,4853,4854],{"class":569},") 脚本执行失败，退出状态码为 1\n",[555,4856,4857],{"class":557,"line":3761},[555,4858,3685],{"emptyLinePlaceholder":305},[555,4860,4861,4864,4867,4870,4873,4875,4878,4881],{"class":557,"line":3767},[555,4862,4863],{"class":898},"Error",[555,4865,4866],{"class":592}," in",[555,4868,4869],{"class":592}," PREIN",[555,4871,4872],{"class":592}," scriptlet",[555,4874,4866],{"class":592},[555,4876,4877],{"class":592}," rpm",[555,4879,4880],{"class":592}," package",[555,4882,4883],{"class":592}," gitlab-ce\n",[555,4885,4886,4889,4892,4894,4896],{"class":557,"line":3773},[555,4887,4888],{"class":898},"  验证",[555,4890,4891],{"class":592},"    :",[555,4893,4841],{"class":592},[555,4895,4844],{"class":592},[555,4897,4411],{"class":569},[555,4899,4900,4902,4904,4907,4910],{"class":557,"line":3778},[555,4901,4888],{"class":898},[555,4903,4891],{"class":592},[555,4905,4906],{"class":592}," gitlab-ce-13.1.4-ce.0.el8.x86_64",[555,4908,4909],{"class":592},"                                                                                  2\u002F2",[555,4911,4411],{"class":569},[555,4913,4914],{"class":557,"line":3783},[555,4915,3685],{"emptyLinePlaceholder":305},[555,4917,4918],{"class":557,"line":3789},[555,4919,4920],{"class":898},"失败:\n",[555,4922,4923,4926,4929],{"class":557,"line":3794},[555,4924,4925],{"class":898},"  gitlab-ce-13.1.4-ce.0.el8.x86_64",[555,4927,4928],{"class":592},"                                gitlab-ce-13.1.11-ce.0.el8.x86_64",[555,4930,4931],{"class":569},"                               \n",[555,4933,4934],{"class":557,"line":3800},[555,4935,3685],{"emptyLinePlaceholder":305},[555,4937,4938],{"class":557,"line":3805},[555,4939,4940],{"class":898},"错误：事务失败\n",[555,4942,4943,4945,4947,4949],{"class":557,"line":3811},[555,4944,4476],{"class":565},[555,4946,4616],{"class":569},[555,4948,4619],{"class":565},[555,4950,4951],{"class":569},"#\n",[10,4953,4954],{},"手动下载 rpm 包安装，也有相同的报错信息：",[546,4956,4958],{"className":889,"code":4957,"language":891,"meta":291,"style":291},"[root@HyperV-GitLab opt]# rpm -Uvh gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm \n警告：gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm: 头V4 RSA\u002FSHA256 Signature, 密钥 ID f27eab47: NOKEY\nVerifying...                          ################################# [100%]\n准备中...                          ################################# [100%]\n错误：%prein(gitlab-ce-13.1.11-ce.0.el8.x86_64) 脚本执行失败，退出状态码为 1\n错误：gitlab-ce-13.1.11-ce.0.el8.x86_64: 安裝 已失败\n错误：gitlab-ce-13.1.4-ce.0.el8.x86_64: 删除 已跳过\n\nOR\nerror: prein(gitlab-ce-13.1.11-ce.0.el8.x86_64)scriptlet failed, exit status 1\n",[552,4959,4960,4971,4997,5005,5012,5018,5029,5040,5044,5049],{"__ignoreMap":291},[555,4961,4962,4964,4966,4968],{"class":557,"line":558},[555,4963,4476],{"class":565},[555,4965,4616],{"class":569},[555,4967,4619],{"class":565},[555,4969,4970],{"class":569},"# rpm -Uvh gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm \n",[555,4972,4973,4976,4979,4982,4985,4988,4991,4994],{"class":557,"line":295},[555,4974,4975],{"class":898},"警告：gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm:",[555,4977,4978],{"class":592}," 头V4",[555,4980,4981],{"class":592}," RSA\u002FSHA256",[555,4983,4984],{"class":592}," Signature,",[555,4986,4987],{"class":592}," 密钥",[555,4989,4990],{"class":592}," ID",[555,4992,4993],{"class":592}," f27eab47:",[555,4995,4996],{"class":592}," NOKEY\n",[555,4998,4999,5002],{"class":557,"line":292},[555,5000,5001],{"class":898},"Verifying...",[555,5003,5004],{"class":905},"                          ################################# [100%]\n",[555,5006,5007,5010],{"class":557,"line":923},[555,5008,5009],{"class":898},"准备中...",[555,5011,5004],{"class":905},[555,5013,5014,5016],{"class":557,"line":1786},[555,5015,4851],{"class":898},[555,5017,4854],{"class":569},[555,5019,5020,5023,5026],{"class":557,"line":2519},[555,5021,5022],{"class":898},"错误：gitlab-ce-13.1.11-ce.0.el8.x86_64:",[555,5024,5025],{"class":592}," 安裝",[555,5027,5028],{"class":592}," 已失败\n",[555,5030,5031,5034,5037],{"class":557,"line":2530},[555,5032,5033],{"class":898},"错误：gitlab-ce-13.1.4-ce.0.el8.x86_64:",[555,5035,5036],{"class":592}," 删除",[555,5038,5039],{"class":592}," 已跳过\n",[555,5041,5042],{"class":557,"line":2536},[555,5043,3685],{"emptyLinePlaceholder":305},[555,5045,5046],{"class":557,"line":2550},[555,5047,5048],{"class":898},"OR\n",[555,5050,5051,5054,5057,5059,5062,5065,5068,5071,5074,5076],{"class":557,"line":2564},[555,5052,5053],{"class":898},"error:",[555,5055,5056],{"class":592}," prein",[555,5058,586],{"class":565},[555,5060,5061],{"class":898},"gitlab-ce-13.1.11-ce.0.el8.x86_64",[555,5063,5064],{"class":565},")",[555,5066,5067],{"class":592},"scriptlet",[555,5069,5070],{"class":592}," failed,",[555,5072,5073],{"class":592}," exit",[555,5075,2340],{"class":592},[555,5077,5078],{"class":941}," 1\n",[10,5080,5081],{},"用 rpm -Uvvh 查看安装详情，发现错误如下：",[546,5083,5085],{"className":889,"code":5084,"language":891,"meta":291,"style":291},"+ upgrade_check\n++ gitlab-ctl upgrade-check 13.1.4 13.13.1\n\u002Fbin\u002Fbash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)\n+ ctl_results='[2020-10-01T21:17:45+08:00] WARN: Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.\nI don'\\''t know that command.\nomnibus-ctl: command (subcommand)\ncheck-config\n\n.........\n\nContainer Registry Commands:\n  registry-garbage-collect\n    Run Container Registry garbage collection.'\n+ found='[2020-10-01T21:17:45+08:00] WARN: Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.'\n+ '[' '[2020-10-01T21:17:45+08:00] WARN: Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.' '!=' 'I don'\\''t know that command.' ']'\n+ exit 1\nD: %prein(gitlab-ce-13.1.11-ce.0.el8.x86_64): waitpid(35960) rc 35960 status 100\nerror: %prein(gitlab-ce-13.1.11-ce.0.el8.x86_64) scriptlet failed, exit status 1\nufdio:       6 reads,  8219028 total bytes in 0.001505 secs\nerror: gitlab-ce-13.1.11-ce.0.el8.x86_64: install failed\n",[552,5086,5087,5095,5112,5138,5150,5165,5170,5175,5179,5184,5188,5193,5198,5205,5219,5264,5272,5309,5331,5359],{"__ignoreMap":291},[555,5088,5089,5092],{"class":557,"line":558},[555,5090,5091],{"class":898},"+",[555,5093,5094],{"class":592}," upgrade_check\n",[555,5096,5097,5100,5103,5106,5109],{"class":557,"line":295},[555,5098,5099],{"class":898},"++",[555,5101,5102],{"class":592}," gitlab-ctl",[555,5104,5105],{"class":592}," upgrade-check",[555,5107,5108],{"class":941}," 13.1.4",[555,5110,5111],{"class":941}," 13.13.1\n",[555,5113,5114,5117,5120,5123,5126,5129,5132,5135],{"class":557,"line":292},[555,5115,5116],{"class":898},"\u002Fbin\u002Fbash:",[555,5118,5119],{"class":592}," warning:",[555,5121,5122],{"class":592}," setlocale:",[555,5124,5125],{"class":592}," LC_ALL:",[555,5127,5128],{"class":592}," cannot",[555,5130,5131],{"class":592}," change",[555,5133,5134],{"class":592}," locale",[555,5136,5137],{"class":569}," (en_US.UTF-8)\n",[555,5139,5140,5142,5145,5147],{"class":557,"line":923},[555,5141,5091],{"class":898},[555,5143,5144],{"class":592}," ctl_results=",[555,5146,589],{"class":565},[555,5148,5149],{"class":592},"[2020-10-01T21:17:45+08:00] WARN: Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.\n",[555,5151,5152,5155,5157,5160,5162],{"class":557,"line":1786},[555,5153,5154],{"class":592},"I don",[555,5156,589],{"class":565},[555,5158,5159],{"class":569},"\\'",[555,5161,589],{"class":565},[555,5163,5164],{"class":592},"t know that command.\n",[555,5166,5167],{"class":557,"line":2519},[555,5168,5169],{"class":592},"omnibus-ctl: command (subcommand)\n",[555,5171,5172],{"class":557,"line":2530},[555,5173,5174],{"class":592},"check-config\n",[555,5176,5177],{"class":557,"line":2536},[555,5178,3685],{"emptyLinePlaceholder":305},[555,5180,5181],{"class":557,"line":2550},[555,5182,5183],{"class":592},".........\n",[555,5185,5186],{"class":557,"line":2564},[555,5187,3685],{"emptyLinePlaceholder":305},[555,5189,5190],{"class":557,"line":2578},[555,5191,5192],{"class":592},"Container Registry Commands:\n",[555,5194,5195],{"class":557,"line":3374},[555,5196,5197],{"class":592},"  registry-garbage-collect\n",[555,5199,5200,5203],{"class":557,"line":3447},[555,5201,5202],{"class":592},"    Run Container Registry garbage collection.",[555,5204,2509],{"class":565},[555,5206,5207,5209,5212,5214,5217],{"class":557,"line":3453},[555,5208,5091],{"class":898},[555,5210,5211],{"class":592}," found=",[555,5213,589],{"class":565},[555,5215,5216],{"class":592},"[2020-10-01T21:17:45+08:00] WARN: Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.",[555,5218,2509],{"class":565},[555,5220,5221,5223,5226,5228,5230,5232,5234,5236,5238,5241,5243,5245,5247,5249,5251,5253,5256,5258,5260,5262],{"class":557,"line":3459},[555,5222,5091],{"class":898},[555,5224,5225],{"class":565}," '",[555,5227,4476],{"class":592},[555,5229,589],{"class":565},[555,5231,5225],{"class":565},[555,5233,5216],{"class":592},[555,5235,589],{"class":565},[555,5237,5225],{"class":565},[555,5239,5240],{"class":592},"!=",[555,5242,589],{"class":565},[555,5244,5225],{"class":565},[555,5246,5154],{"class":592},[555,5248,589],{"class":565},[555,5250,5159],{"class":569},[555,5252,589],{"class":565},[555,5254,5255],{"class":592},"t know that command.",[555,5257,589],{"class":565},[555,5259,5225],{"class":565},[555,5261,4619],{"class":592},[555,5263,2509],{"class":565},[555,5265,5266,5268,5270],{"class":557,"line":3465},[555,5267,5091],{"class":898},[555,5269,5073],{"class":592},[555,5271,5078],{"class":941},[555,5273,5274,5277,5280,5282,5284,5286,5288,5291,5293,5296,5298,5301,5304,5306],{"class":557,"line":3471},[555,5275,5276],{"class":898},"D:",[555,5278,5279],{"class":592}," %prein",[555,5281,586],{"class":565},[555,5283,5061],{"class":898},[555,5285,5064],{"class":565},[555,5287,2862],{"class":592},[555,5289,5290],{"class":592}," waitpid",[555,5292,586],{"class":565},[555,5294,5295],{"class":898},"35960",[555,5297,5064],{"class":565},[555,5299,5300],{"class":592}," rc",[555,5302,5303],{"class":941}," 35960",[555,5305,2340],{"class":592},[555,5307,5308],{"class":941}," 100\n",[555,5310,5311,5313,5315,5317,5319,5321,5323,5325,5327,5329],{"class":557,"line":3477},[555,5312,5053],{"class":898},[555,5314,5279],{"class":592},[555,5316,586],{"class":565},[555,5318,5061],{"class":898},[555,5320,5064],{"class":565},[555,5322,4872],{"class":592},[555,5324,5070],{"class":592},[555,5326,5073],{"class":592},[555,5328,2340],{"class":592},[555,5330,5078],{"class":941},[555,5332,5333,5336,5339,5342,5345,5348,5351,5353,5356],{"class":557,"line":3482},[555,5334,5335],{"class":898},"ufdio:",[555,5337,5338],{"class":941},"       6",[555,5340,5341],{"class":592}," reads,",[555,5343,5344],{"class":941},"  8219028",[555,5346,5347],{"class":592}," total",[555,5349,5350],{"class":592}," bytes",[555,5352,4866],{"class":592},[555,5354,5355],{"class":941}," 0.001505",[555,5357,5358],{"class":592}," secs\n",[555,5360,5361,5363,5366,5368],{"class":557,"line":3488},[555,5362,5053],{"class":898},[555,5364,5365],{"class":592}," gitlab-ce-13.1.11-ce.0.el8.x86_64:",[555,5367,4522],{"class":592},[555,5369,4501],{"class":592},[10,5371,5372],{},"可能是因为我安装系统的时候选择了中文，系统里竟然没有英文语言包？！\nlocale -a 如下：",[546,5374,5376],{"className":889,"code":5375,"language":891,"meta":291,"style":291},"[root@HyperV-GitLab ~]# vi \u002Fetc\u002Fenvironment\n[root@HyperV-GitLab ~]# locale -a\nlocale: Cannot set LC_CTYPE to default locale: No such file or directory\nlocale: Cannot set LC_MESSAGES to default locale: No such file or directory\nlocale: Cannot set LC_COLLATE to default locale: No such file or directory\nC\nC.utf8\nPOSIX\nzh_CN\nzh_CN.gb18030\nzh_CN.gbk\nzh_CN.utf8\nzh_HK\nzh_HK.utf8\nzh_SG\nzh_SG.gbk\nzh_SG.utf8\nzh_TW\nzh_TW.euctw\nzh_TW.utf8\n",[552,5377,5378,5391,5402,5440,5467,5494,5499,5504,5509,5514,5519,5524,5529,5534,5539,5544,5549,5554,5559,5564],{"__ignoreMap":291},[555,5379,5380,5382,5385,5388],{"class":557,"line":558},[555,5381,4476],{"class":565},[555,5383,5384],{"class":569},"root@HyperV-GitLab ",[555,5386,5387],{"class":565},"~]",[555,5389,5390],{"class":569},"# vi \u002Fetc\u002Fenvironment\n",[555,5392,5393,5395,5397,5399],{"class":557,"line":295},[555,5394,4476],{"class":565},[555,5396,5384],{"class":569},[555,5398,5387],{"class":565},[555,5400,5401],{"class":569},"# locale -a\n",[555,5403,5404,5407,5410,5413,5416,5419,5422,5425,5428,5431,5434,5437],{"class":557,"line":292},[555,5405,5406],{"class":898},"locale:",[555,5408,5409],{"class":592}," Cannot",[555,5411,5412],{"class":592}," set",[555,5414,5415],{"class":592}," LC_CTYPE",[555,5417,5418],{"class":592}," to",[555,5420,5421],{"class":592}," default",[555,5423,5424],{"class":592}," locale:",[555,5426,5427],{"class":592}," No",[555,5429,5430],{"class":592}," such",[555,5432,5433],{"class":592}," file",[555,5435,5436],{"class":592}," or",[555,5438,5439],{"class":592}," directory\n",[555,5441,5442,5444,5446,5448,5451,5453,5455,5457,5459,5461,5463,5465],{"class":557,"line":923},[555,5443,5406],{"class":898},[555,5445,5409],{"class":592},[555,5447,5412],{"class":592},[555,5449,5450],{"class":592}," LC_MESSAGES",[555,5452,5418],{"class":592},[555,5454,5421],{"class":592},[555,5456,5424],{"class":592},[555,5458,5427],{"class":592},[555,5460,5430],{"class":592},[555,5462,5433],{"class":592},[555,5464,5436],{"class":592},[555,5466,5439],{"class":592},[555,5468,5469,5471,5473,5475,5478,5480,5482,5484,5486,5488,5490,5492],{"class":557,"line":1786},[555,5470,5406],{"class":898},[555,5472,5409],{"class":592},[555,5474,5412],{"class":592},[555,5476,5477],{"class":592}," LC_COLLATE",[555,5479,5418],{"class":592},[555,5481,5421],{"class":592},[555,5483,5424],{"class":592},[555,5485,5427],{"class":592},[555,5487,5430],{"class":592},[555,5489,5433],{"class":592},[555,5491,5436],{"class":592},[555,5493,5439],{"class":592},[555,5495,5496],{"class":557,"line":2519},[555,5497,5498],{"class":898},"C\n",[555,5500,5501],{"class":557,"line":2530},[555,5502,5503],{"class":898},"C.utf8\n",[555,5505,5506],{"class":557,"line":2536},[555,5507,5508],{"class":898},"POSIX\n",[555,5510,5511],{"class":557,"line":2550},[555,5512,5513],{"class":898},"zh_CN\n",[555,5515,5516],{"class":557,"line":2564},[555,5517,5518],{"class":898},"zh_CN.gb18030\n",[555,5520,5521],{"class":557,"line":2578},[555,5522,5523],{"class":898},"zh_CN.gbk\n",[555,5525,5526],{"class":557,"line":3374},[555,5527,5528],{"class":898},"zh_CN.utf8\n",[555,5530,5531],{"class":557,"line":3447},[555,5532,5533],{"class":898},"zh_HK\n",[555,5535,5536],{"class":557,"line":3453},[555,5537,5538],{"class":898},"zh_HK.utf8\n",[555,5540,5541],{"class":557,"line":3459},[555,5542,5543],{"class":898},"zh_SG\n",[555,5545,5546],{"class":557,"line":3465},[555,5547,5548],{"class":898},"zh_SG.gbk\n",[555,5550,5551],{"class":557,"line":3471},[555,5552,5553],{"class":898},"zh_SG.utf8\n",[555,5555,5556],{"class":557,"line":3477},[555,5557,5558],{"class":898},"zh_TW\n",[555,5560,5561],{"class":557,"line":3482},[555,5562,5563],{"class":898},"zh_TW.euctw\n",[555,5565,5566],{"class":557,"line":3488},[555,5567,5568],{"class":898},"zh_TW.utf8\n",[10,5570,5571],{},"解决方法：\n安装英文语言包",[546,5573,5575],{"className":889,"code":5574,"language":891,"meta":291,"style":291},"yum install langpacks-en.noarch\n",[552,5576,5577],{"__ignoreMap":291},[555,5578,5579,5582,5584],{"class":557,"line":558},[555,5580,5581],{"class":898},"yum",[555,5583,4522],{"class":592},[555,5585,5586],{"class":592}," langpacks-en.noarch\n",[1981,5588,5589],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":291,"searchDepth":292,"depth":292,"links":5591},[5592,5593,5594,5595],{"id":4301,"depth":292,"text":4301},{"id":4313,"depth":292,"text":4314},{"id":4334,"depth":292,"text":4335},{"id":4600,"depth":292,"text":4601},"从 11 升到 13 的路上踩了三个坑：不能跨大版本、pg_dump 版本不匹配，以及系统没装英文语言包导致 rpm 安装脚本失败。",{},"\u002Fposts\u002Fgitlab-ce-upgrade-11-to-13",{"title":4296,"description":5596},"posts\u002Fgitlab-ce-upgrade-11-to-13",[5602,2672,2671],"GitLab","7OR6js5u1xKuSqCjnlh6LVlCEpRSzWCW0urRoqaJ980",{"id":5605,"title":5606,"body":5607,"date":6868,"description":6869,"extension":303,"meta":6870,"navigation":305,"path":6871,"seo":6872,"stem":6873,"tags":6874,"__hash__":6876},"posts\u002Fposts\u002Fjar2pom-maven-dependency.md","基于 Maven 中心库，通过 Jar 包生成 POM 的 dependency",{"type":7,"value":5608,"toc":6860},[5609,5611,5630,5633,5636,5642,5661,5664,6483,6487,6494,6503,6506,6674,6681,6701,6729,6735,6738,6741,6828,6830,6847,6858],[32,5610,611],{"id":611},[10,5612,5613,5614,5619,5620,5625,5626,608],{},"之前写过一篇",[94,5615,5618],{"href":5616,"rel":5617},"https:\u002F\u002Fblog.csdn.net\u002FJustinJia91\u002Farticle\u002Fdetails\u002F79605682",[98],"通过 Jar 包生成 Maven POM.xml 的 dependency","，方便将非 Maven 项目转换为 Maven 项目。\n原项目对 Jar 的搜索基于 Nexus2 的相关接口开发，随着很多仓库从 Nexus2 升级到 Nexus3 或者其他服务，原项目渐渐不可用了。\n新代码将 Nexus2 接口改为对接 ",[94,5621,5624],{"href":5622,"rel":5623},"https:\u002F\u002Fsearch.maven.org\u002F",[98],"Maven 中心库","。站内另留了一份 ",[94,5627,5629],{"href":5628},"\u002Fposts\u002Fjar2pom-nexus2-dependency","Nexus2 版的代码",[32,5631,5632],{"id":5632},"项目结构",[10,5634,5635],{},"整个项目只有 6 个 Java 文件 —— 一个主类，加上五个用来接中心库返回值的实体类：",[546,5637,5640],{"className":5638,"code":5639,"language":3033},[3031],"jar2pom\n├── pom.xml\n└── src\u002Fmain\u002Fjava\u002Fcom\u002Fjxh\u002Ftool\n    ├── Jar2pom.java              # 主类：遍历目录里的 jar、查坐标、打印 dependency\n    └── entity\n        ├── SolrSearch.java       # 响应根节点\n        ├── ResponseHeader.java   # 状态码与耗时\n        ├── Response.java         # 结果集\n        ├── Docs.java             # 单条坐标\n        └── Spellcheck.java       # 拼写建议\n",[552,5641,5639],{"__ignoreMap":291},[10,5643,5644,5645,5648,5649,5652,5653,5656,5657,5660],{},"Maven 坐标是 ",[552,5646,5647],{},"cn.jxh.tool:jar2pom:2.0-SNAPSHOT","，依赖只有三个：",[552,5650,5651],{},"httpclient"," 4.5.5 负责发请求，",[552,5654,5655],{},"fastjson"," 1.2.68 负责解 JSON，",[552,5658,5659],{},"dom4j"," 负责拼 XML。README 里记的开发环境是 JDK 1.7 + Maven 3.5 + IDEA 社区版。",[32,5662,5663],{"id":5663},"核心代码",[546,5665,5667],{"className":3617,"code":5666,"language":2998,"meta":291,"style":291},"    public static void main(String[] args) {\n        \u002F\u002F先通过Jar的SHA1查询 如果不存在则解析Manifest查询\n        File libs = new File(\"D:\\\\lib\");\n        for (File jar : libs.listFiles()) {\n\n            System.out.println(\"\u003C!--  \" + jar.getName() + \" -->\");\n\n            Docs doc = getPomByChecksum(jar);\n            if (null != doc) {\n                System.out.println(\"\u003C!--  Search by Checksum -->\");\n                System.out.println(assemblePomElement(doc).asXML());\n                continue;\n            }\n\n            doc = getPomByManifest(jar);\n            if (null != doc) {\n                System.out.println(\"\u003C!--  Search by Manifest -->\");\n                System.out.println(assemblePomElement(doc).asXML());\n                continue;\n            }\n\n            System.out.println(\"\u003C!--  No data was found -->\");\n\n            System.out.println();\n        }\n\n    }\n\n    \u002F**\n     * 通过Jar SHA1返回Pom dependency\n     *\n     * @param file\n     * @return\n     *\u002F\n    public static Docs getPomByChecksum(File file) {\n        Docs docs = null;\n\n        String checkSum = getCheckSum(file, \"SHA1\");\n        String jsonString = doGet(nexusUrl + \"&q=1:\" + checkSum);\n        SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n\n        if (solrSearch.getResponseHeader().getStatus() == 0 && solrSearch.getResponse().getDocs().size() > 0) {\n            docs = solrSearch.getResponse().getDocs().get(0);\n        }\n        return docs;\n    }\n\n\n    \u002F**\n     * 通过Jar Manifest返回Pom dependency\n     *\n     * @param file\n     * @return\n     *\u002F\n    public static Docs getPomByManifest(File file) {\n        Docs docs = null;\n\n        try {\n            JarFile jarfile = new JarFile(file);\n            Manifest mainmanifest = jarfile.getManifest();\n            jarfile.close();\n            if (null == mainmanifest) {\n                return null;\n            }\n            String a = null, v = null;\n            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Extension-Name\"))) {\n                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Extension-Name\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Title\"))) {\n                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Title\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Title\"))) {\n                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Title\"));\n            }\n            if (a != null && a.length() != 0) {\n                a = a.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n            }\n            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Bundle-Version\"))) {\n                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Bundle-Version\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Version\"))) {\n                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Version\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Version\"))) {\n                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Version\"));\n            }\n            if (v != null && v.length() != 0) {\n                v = v.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n            }\n            String jsonString = doGet(nexusUrl + \"&q=a:\" + a + \"%20v:\" + v);\n            SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n\n            if (solrSearch.getResponseHeader().getStatus() == 0) {\n                docs = solrSearch.getResponse().getDocs().get(0);\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return docs;\n    }\n\n    \u002F**\n     * 组装 dependency\n     *\n     * @param docs\n     * @return\n     *\u002F\n    public static Element assemblePomElement(Docs docs) {\n        Element dependency = new DOMElement(\"dependency\");\n        if (docs != null) {\n            dependency.addAttribute(\"groupId\", docs.getG());\n            dependency.addAttribute(\"artifactId\", docs.getA());\n            dependency.addAttribute(\"version\", docs.getV());\n        }\n        return dependency;\n    }\n\n    \u002F**\n     * 发起Get请求\n     *\n     * @param url\n     * @return\n     *\u002F\n    public static String doGet(String url) {\n        String srtResult = null;\n        CloseableHttpClient httpCilent = HttpClients.createDefault();\n        HttpGet httpGet = new HttpGet(url);\n        try {\n            HttpResponse httpResponse = httpCilent.execute(httpGet);\n            srtResult = EntityUtils.toString(httpResponse.getEntity());\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                httpCilent.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return srtResult;\n    }\n\n    \u002F**\n     * 计算CheckSum\n     *\n     * @param file\n     * @param algorithm SHA1 or MD5\n     * @return\n     *\u002F\n    public static String getCheckSum(File file, String algorithm) {\n        if (!file.isFile()) {\n            return null;\n        }\n        MessageDigest digest = null;\n        FileInputStream in = null;\n        byte buffer[] = new byte[1024];\n        int len;\n        try {\n            digest = MessageDigest.getInstance(algorithm);\n            in = new FileInputStream(file);\n            while ((len = in.read(buffer, 0, 1024)) != -1) {\n                digest.update(buffer, 0, len);\n            }\n            in.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n        BigInteger bigInt = new BigInteger(1, digest.digest());\n        return bigInt.toString(16);\n    }\n",[552,5668,5669,5674,5679,5684,5689,5693,5698,5702,5707,5712,5717,5722,5727,5731,5735,5740,5744,5749,5753,5757,5761,5765,5770,5774,5779,5783,5787,5791,5795,5799,5804,5808,5813,5817,5821,5826,5831,5835,5840,5845,5850,5854,5859,5864,5868,5873,5877,5881,5885,5889,5894,5898,5902,5906,5910,5915,5919,5923,5928,5933,5938,5943,5948,5953,5957,5962,5967,5972,5977,5982,5987,5992,5996,6001,6006,6010,6015,6020,6025,6030,6035,6040,6044,6049,6054,6058,6063,6068,6072,6077,6082,6086,6091,6096,6100,6104,6108,6112,6116,6121,6125,6130,6134,6138,6143,6148,6153,6158,6163,6168,6172,6177,6181,6185,6189,6194,6198,6204,6209,6214,6220,6226,6232,6238,6243,6249,6255,6260,6265,6271,6277,6283,6289,6295,6300,6305,6311,6316,6321,6326,6332,6337,6342,6348,6353,6358,6364,6370,6376,6381,6387,6393,6399,6405,6410,6416,6422,6428,6434,6439,6445,6451,6456,6461,6466,6472,6478],{"__ignoreMap":291},[555,5670,5671],{"class":557,"line":558},[555,5672,5673],{},"    public static void main(String[] args) {\n",[555,5675,5676],{"class":557,"line":295},[555,5677,5678],{},"        \u002F\u002F先通过Jar的SHA1查询 如果不存在则解析Manifest查询\n",[555,5680,5681],{"class":557,"line":292},[555,5682,5683],{},"        File libs = new File(\"D:\\\\lib\");\n",[555,5685,5686],{"class":557,"line":923},[555,5687,5688],{},"        for (File jar : libs.listFiles()) {\n",[555,5690,5691],{"class":557,"line":1786},[555,5692,3685],{"emptyLinePlaceholder":305},[555,5694,5695],{"class":557,"line":2519},[555,5696,5697],{},"            System.out.println(\"\u003C!--  \" + jar.getName() + \" -->\");\n",[555,5699,5700],{"class":557,"line":2530},[555,5701,3685],{"emptyLinePlaceholder":305},[555,5703,5704],{"class":557,"line":2536},[555,5705,5706],{},"            Docs doc = getPomByChecksum(jar);\n",[555,5708,5709],{"class":557,"line":2550},[555,5710,5711],{},"            if (null != doc) {\n",[555,5713,5714],{"class":557,"line":2564},[555,5715,5716],{},"                System.out.println(\"\u003C!--  Search by Checksum -->\");\n",[555,5718,5719],{"class":557,"line":2578},[555,5720,5721],{},"                System.out.println(assemblePomElement(doc).asXML());\n",[555,5723,5724],{"class":557,"line":3374},[555,5725,5726],{},"                continue;\n",[555,5728,5729],{"class":557,"line":3447},[555,5730,4070],{},[555,5732,5733],{"class":557,"line":3453},[555,5734,3685],{"emptyLinePlaceholder":305},[555,5736,5737],{"class":557,"line":3459},[555,5738,5739],{},"            doc = getPomByManifest(jar);\n",[555,5741,5742],{"class":557,"line":3465},[555,5743,5711],{},[555,5745,5746],{"class":557,"line":3471},[555,5747,5748],{},"                System.out.println(\"\u003C!--  Search by Manifest -->\");\n",[555,5750,5751],{"class":557,"line":3477},[555,5752,5721],{},[555,5754,5755],{"class":557,"line":3482},[555,5756,5726],{},[555,5758,5759],{"class":557,"line":3488},[555,5760,4070],{},[555,5762,5763],{"class":557,"line":3493},[555,5764,3685],{"emptyLinePlaceholder":305},[555,5766,5767],{"class":557,"line":3727},[555,5768,5769],{},"            System.out.println(\"\u003C!--  No data was found -->\");\n",[555,5771,5772],{"class":557,"line":3733},[555,5773,3685],{"emptyLinePlaceholder":305},[555,5775,5776],{"class":557,"line":3739},[555,5777,5778],{},"            System.out.println();\n",[555,5780,5781],{"class":557,"line":3745},[555,5782,4238],{},[555,5784,5785],{"class":557,"line":3750},[555,5786,3685],{"emptyLinePlaceholder":305},[555,5788,5789],{"class":557,"line":3756},[555,5790,3837],{},[555,5792,5793],{"class":557,"line":3761},[555,5794,3685],{"emptyLinePlaceholder":305},[555,5796,5797],{"class":557,"line":3767},[555,5798,3848],{},[555,5800,5801],{"class":557,"line":3773},[555,5802,5803],{},"     * 通过Jar SHA1返回Pom dependency\n",[555,5805,5806],{"class":557,"line":3778},[555,5807,3860],{},[555,5809,5810],{"class":557,"line":3783},[555,5811,5812],{},"     * @param file\n",[555,5814,5815],{"class":557,"line":3789},[555,5816,3866],{},[555,5818,5819],{"class":557,"line":3794},[555,5820,3872],{},[555,5822,5823],{"class":557,"line":3800},[555,5824,5825],{},"    public static Docs getPomByChecksum(File file) {\n",[555,5827,5828],{"class":557,"line":3805},[555,5829,5830],{},"        Docs docs = null;\n",[555,5832,5833],{"class":557,"line":3811},[555,5834,3685],{"emptyLinePlaceholder":305},[555,5836,5837],{"class":557,"line":3816},[555,5838,5839],{},"        String checkSum = getCheckSum(file, \"SHA1\");\n",[555,5841,5842],{"class":557,"line":3822},[555,5843,5844],{},"        String jsonString = doGet(nexusUrl + \"&q=1:\" + checkSum);\n",[555,5846,5847],{"class":557,"line":3828},[555,5848,5849],{},"        SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n",[555,5851,5852],{"class":557,"line":3834},[555,5853,3685],{"emptyLinePlaceholder":305},[555,5855,5856],{"class":557,"line":3840},[555,5857,5858],{},"        if (solrSearch.getResponseHeader().getStatus() == 0 && solrSearch.getResponse().getDocs().size() > 0) {\n",[555,5860,5861],{"class":557,"line":3845},[555,5862,5863],{},"            docs = solrSearch.getResponse().getDocs().get(0);\n",[555,5865,5866],{"class":557,"line":3851},[555,5867,4238],{},[555,5869,5870],{"class":557,"line":3857},[555,5871,5872],{},"        return docs;\n",[555,5874,5875],{"class":557,"line":3863},[555,5876,3837],{},[555,5878,5879],{"class":557,"line":3869},[555,5880,3685],{"emptyLinePlaceholder":305},[555,5882,5883],{"class":557,"line":3875},[555,5884,3685],{"emptyLinePlaceholder":305},[555,5886,5887],{"class":557,"line":3881},[555,5888,3848],{},[555,5890,5891],{"class":557,"line":3887},[555,5892,5893],{},"     * 通过Jar Manifest返回Pom dependency\n",[555,5895,5896],{"class":557,"line":3892},[555,5897,3860],{},[555,5899,5900],{"class":557,"line":3897},[555,5901,5812],{},[555,5903,5904],{"class":557,"line":3902},[555,5905,3866],{},[555,5907,5908],{"class":557,"line":3908},[555,5909,3872],{},[555,5911,5912],{"class":557,"line":3913},[555,5913,5914],{},"    public static Docs getPomByManifest(File file) {\n",[555,5916,5917],{"class":557,"line":3918},[555,5918,5830],{},[555,5920,5921],{"class":557,"line":3923},[555,5922,3685],{"emptyLinePlaceholder":305},[555,5924,5925],{"class":557,"line":3929},[555,5926,5927],{},"        try {\n",[555,5929,5930],{"class":557,"line":3935},[555,5931,5932],{},"            JarFile jarfile = new JarFile(file);\n",[555,5934,5935],{"class":557,"line":3940},[555,5936,5937],{},"            Manifest mainmanifest = jarfile.getManifest();\n",[555,5939,5940],{"class":557,"line":3945},[555,5941,5942],{},"            jarfile.close();\n",[555,5944,5945],{"class":557,"line":3950},[555,5946,5947],{},"            if (null == mainmanifest) {\n",[555,5949,5950],{"class":557,"line":3956},[555,5951,5952],{},"                return null;\n",[555,5954,5955],{"class":557,"line":3961},[555,5956,4070],{},[555,5958,5959],{"class":557,"line":3967},[555,5960,5961],{},"            String a = null, v = null;\n",[555,5963,5964],{"class":557,"line":3972},[555,5965,5966],{},"            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Extension-Name\"))) {\n",[555,5968,5969],{"class":557,"line":3978},[555,5970,5971],{},"                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Extension-Name\"));\n",[555,5973,5974],{"class":557,"line":3984},[555,5975,5976],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Title\"))) {\n",[555,5978,5979],{"class":557,"line":3990},[555,5980,5981],{},"                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Title\"));\n",[555,5983,5984],{"class":557,"line":3996},[555,5985,5986],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Title\"))) {\n",[555,5988,5989],{"class":557,"line":4002},[555,5990,5991],{},"                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Title\"));\n",[555,5993,5994],{"class":557,"line":4008},[555,5995,4070],{},[555,5997,5998],{"class":557,"line":4014},[555,5999,6000],{},"            if (a != null && a.length() != 0) {\n",[555,6002,6003],{"class":557,"line":4020},[555,6004,6005],{},"                a = a.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n",[555,6007,6008],{"class":557,"line":4026},[555,6009,4070],{},[555,6011,6012],{"class":557,"line":4032},[555,6013,6014],{},"            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Bundle-Version\"))) {\n",[555,6016,6017],{"class":557,"line":4038},[555,6018,6019],{},"                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Bundle-Version\"));\n",[555,6021,6022],{"class":557,"line":4043},[555,6023,6024],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Version\"))) {\n",[555,6026,6027],{"class":557,"line":4049},[555,6028,6029],{},"                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Version\"));\n",[555,6031,6032],{"class":557,"line":4055},[555,6033,6034],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Version\"))) {\n",[555,6036,6037],{"class":557,"line":4061},[555,6038,6039],{},"                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Version\"));\n",[555,6041,6042],{"class":557,"line":4067},[555,6043,4070],{},[555,6045,6046],{"class":557,"line":4073},[555,6047,6048],{},"            if (v != null && v.length() != 0) {\n",[555,6050,6051],{"class":557,"line":4079},[555,6052,6053],{},"                v = v.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n",[555,6055,6056],{"class":557,"line":4084},[555,6057,4070],{},[555,6059,6060],{"class":557,"line":4089},[555,6061,6062],{},"            String jsonString = doGet(nexusUrl + \"&q=a:\" + a + \"%20v:\" + v);\n",[555,6064,6065],{"class":557,"line":4094},[555,6066,6067],{},"            SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n",[555,6069,6070],{"class":557,"line":4100},[555,6071,3685],{"emptyLinePlaceholder":305},[555,6073,6074],{"class":557,"line":4105},[555,6075,6076],{},"            if (solrSearch.getResponseHeader().getStatus() == 0) {\n",[555,6078,6079],{"class":557,"line":4111},[555,6080,6081],{},"                docs = solrSearch.getResponse().getDocs().get(0);\n",[555,6083,6084],{"class":557,"line":4116},[555,6085,4070],{},[555,6087,6088],{"class":557,"line":4121},[555,6089,6090],{},"        } catch (IOException e) {\n",[555,6092,6093],{"class":557,"line":4127},[555,6094,6095],{},"            e.printStackTrace();\n",[555,6097,6098],{"class":557,"line":4133},[555,6099,4238],{},[555,6101,6102],{"class":557,"line":4139},[555,6103,5872],{},[555,6105,6106],{"class":557,"line":4145},[555,6107,3837],{},[555,6109,6110],{"class":557,"line":4151},[555,6111,3685],{"emptyLinePlaceholder":305},[555,6113,6114],{"class":557,"line":4157},[555,6115,3848],{},[555,6117,6118],{"class":557,"line":4162},[555,6119,6120],{},"     * 组装 dependency\n",[555,6122,6123],{"class":557,"line":4167},[555,6124,3860],{},[555,6126,6127],{"class":557,"line":4172},[555,6128,6129],{},"     * @param docs\n",[555,6131,6132],{"class":557,"line":4178},[555,6133,3866],{},[555,6135,6136],{"class":557,"line":4183},[555,6137,3872],{},[555,6139,6140],{"class":557,"line":4189},[555,6141,6142],{},"    public static Element assemblePomElement(Docs docs) {\n",[555,6144,6145],{"class":557,"line":4195},[555,6146,6147],{},"        Element dependency = new DOMElement(\"dependency\");\n",[555,6149,6150],{"class":557,"line":4200},[555,6151,6152],{},"        if (docs != null) {\n",[555,6154,6155],{"class":557,"line":4205},[555,6156,6157],{},"            dependency.addAttribute(\"groupId\", docs.getG());\n",[555,6159,6160],{"class":557,"line":4211},[555,6161,6162],{},"            dependency.addAttribute(\"artifactId\", docs.getA());\n",[555,6164,6165],{"class":557,"line":4217},[555,6166,6167],{},"            dependency.addAttribute(\"version\", docs.getV());\n",[555,6169,6170],{"class":557,"line":4223},[555,6171,4238],{},[555,6173,6174],{"class":557,"line":4229},[555,6175,6176],{},"        return dependency;\n",[555,6178,6179],{"class":557,"line":4235},[555,6180,3837],{},[555,6182,6183],{"class":557,"line":4241},[555,6184,3685],{"emptyLinePlaceholder":305},[555,6186,6187],{"class":557,"line":4247},[555,6188,3848],{},[555,6190,6191],{"class":557,"line":4252},[555,6192,6193],{},"     * 发起Get请求\n",[555,6195,6196],{"class":557,"line":4257},[555,6197,3860],{},[555,6199,6201],{"class":557,"line":6200},117,[555,6202,6203],{},"     * @param url\n",[555,6205,6207],{"class":557,"line":6206},118,[555,6208,3866],{},[555,6210,6212],{"class":557,"line":6211},119,[555,6213,3872],{},[555,6215,6217],{"class":557,"line":6216},120,[555,6218,6219],{},"    public static String doGet(String url) {\n",[555,6221,6223],{"class":557,"line":6222},121,[555,6224,6225],{},"        String srtResult = null;\n",[555,6227,6229],{"class":557,"line":6228},122,[555,6230,6231],{},"        CloseableHttpClient httpCilent = HttpClients.createDefault();\n",[555,6233,6235],{"class":557,"line":6234},123,[555,6236,6237],{},"        HttpGet httpGet = new HttpGet(url);\n",[555,6239,6241],{"class":557,"line":6240},124,[555,6242,5927],{},[555,6244,6246],{"class":557,"line":6245},125,[555,6247,6248],{},"            HttpResponse httpResponse = httpCilent.execute(httpGet);\n",[555,6250,6252],{"class":557,"line":6251},126,[555,6253,6254],{},"            srtResult = EntityUtils.toString(httpResponse.getEntity());\n",[555,6256,6258],{"class":557,"line":6257},127,[555,6259,6090],{},[555,6261,6263],{"class":557,"line":6262},128,[555,6264,6095],{},[555,6266,6268],{"class":557,"line":6267},129,[555,6269,6270],{},"        } finally {\n",[555,6272,6274],{"class":557,"line":6273},130,[555,6275,6276],{},"            try {\n",[555,6278,6280],{"class":557,"line":6279},131,[555,6281,6282],{},"                httpCilent.close();\n",[555,6284,6286],{"class":557,"line":6285},132,[555,6287,6288],{},"            } catch (IOException e) {\n",[555,6290,6292],{"class":557,"line":6291},133,[555,6293,6294],{},"                e.printStackTrace();\n",[555,6296,6298],{"class":557,"line":6297},134,[555,6299,4070],{},[555,6301,6303],{"class":557,"line":6302},135,[555,6304,4238],{},[555,6306,6308],{"class":557,"line":6307},136,[555,6309,6310],{},"        return srtResult;\n",[555,6312,6314],{"class":557,"line":6313},137,[555,6315,3837],{},[555,6317,6319],{"class":557,"line":6318},138,[555,6320,3685],{"emptyLinePlaceholder":305},[555,6322,6324],{"class":557,"line":6323},139,[555,6325,3848],{},[555,6327,6329],{"class":557,"line":6328},140,[555,6330,6331],{},"     * 计算CheckSum\n",[555,6333,6335],{"class":557,"line":6334},141,[555,6336,3860],{},[555,6338,6340],{"class":557,"line":6339},142,[555,6341,5812],{},[555,6343,6345],{"class":557,"line":6344},143,[555,6346,6347],{},"     * @param algorithm SHA1 or MD5\n",[555,6349,6351],{"class":557,"line":6350},144,[555,6352,3866],{},[555,6354,6356],{"class":557,"line":6355},145,[555,6357,3872],{},[555,6359,6361],{"class":557,"line":6360},146,[555,6362,6363],{},"    public static String getCheckSum(File file, String algorithm) {\n",[555,6365,6367],{"class":557,"line":6366},147,[555,6368,6369],{},"        if (!file.isFile()) {\n",[555,6371,6373],{"class":557,"line":6372},148,[555,6374,6375],{},"            return null;\n",[555,6377,6379],{"class":557,"line":6378},149,[555,6380,4238],{},[555,6382,6384],{"class":557,"line":6383},150,[555,6385,6386],{},"        MessageDigest digest = null;\n",[555,6388,6390],{"class":557,"line":6389},151,[555,6391,6392],{},"        FileInputStream in = null;\n",[555,6394,6396],{"class":557,"line":6395},152,[555,6397,6398],{},"        byte buffer[] = new byte[1024];\n",[555,6400,6402],{"class":557,"line":6401},153,[555,6403,6404],{},"        int len;\n",[555,6406,6408],{"class":557,"line":6407},154,[555,6409,5927],{},[555,6411,6413],{"class":557,"line":6412},155,[555,6414,6415],{},"            digest = MessageDigest.getInstance(algorithm);\n",[555,6417,6419],{"class":557,"line":6418},156,[555,6420,6421],{},"            in = new FileInputStream(file);\n",[555,6423,6425],{"class":557,"line":6424},157,[555,6426,6427],{},"            while ((len = in.read(buffer, 0, 1024)) != -1) {\n",[555,6429,6431],{"class":557,"line":6430},158,[555,6432,6433],{},"                digest.update(buffer, 0, len);\n",[555,6435,6437],{"class":557,"line":6436},159,[555,6438,4070],{},[555,6440,6442],{"class":557,"line":6441},160,[555,6443,6444],{},"            in.close();\n",[555,6446,6448],{"class":557,"line":6447},161,[555,6449,6450],{},"        } catch (Exception e) {\n",[555,6452,6454],{"class":557,"line":6453},162,[555,6455,6095],{},[555,6457,6459],{"class":557,"line":6458},163,[555,6460,6375],{},[555,6462,6464],{"class":557,"line":6463},164,[555,6465,4238],{},[555,6467,6469],{"class":557,"line":6468},165,[555,6470,6471],{},"        BigInteger bigInt = new BigInteger(1, digest.digest());\n",[555,6473,6475],{"class":557,"line":6474},166,[555,6476,6477],{},"        return bigInt.toString(16);\n",[555,6479,6481],{"class":557,"line":6480},167,[555,6482,3837],{},[32,6484,6486],{"id":6485},"solr-的响应是怎么变成对象的","Solr 的响应是怎么变成对象的",[10,6488,6489,6490,6493],{},"中心库这套接口底下是 Solr，回给你的是一坨嵌套 JSON。代码没有一层层 ",[552,6491,6492],{},"getJSONObject()"," 去抠字段，而是让 fastjson 直接把整段响应收进对象：",[546,6495,6497],{"className":3617,"code":6496,"language":2998,"meta":291,"style":291},"SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n",[552,6498,6499],{"__ignoreMap":291},[555,6500,6501],{"class":557,"line":558},[555,6502,6496],{},[10,6504,6505],{},"映射规则就是「字段名对上即可」，所以五个实体类都很薄：",[61,6507,6508,6524],{},[64,6509,6510],{},[67,6511,6512,6515,6518,6521],{},[70,6513,6514],{},"类",[70,6516,6517],{},"对应节点",[70,6519,6520],{},"字段",[70,6522,6523],{},"实际用到",[80,6525,6526,6551,6576,6602,6655],{},[67,6527,6528,6533,6536,6548],{},[85,6529,6530],{},[552,6531,6532],{},"SolrSearch",[85,6534,6535],{},"根节点",[85,6537,6538,6541,6542,6541,6545],{},[552,6539,6540],{},"responseHeader"," \u002F ",[552,6543,6544],{},"response",[552,6546,6547],{},"spellcheck",[85,6549,6550],{},"三个容器",[67,6552,6553,6558,6562,6570],{},[85,6554,6555],{},[552,6556,6557],{},"ResponseHeader",[85,6559,6560],{},[552,6561,6540],{},[85,6563,6564,6541,6567],{},[552,6565,6566],{},"status",[552,6568,6569],{},"QTime",[85,6571,6572,6575],{},[552,6573,6574],{},"status == 0"," 即查询成功",[67,6577,6578,6583,6587,6598],{},[85,6579,6580],{},[552,6581,6582],{},"Response",[85,6584,6585],{},[552,6586,6544],{},[85,6588,6589,6541,6592,6541,6595],{},[552,6590,6591],{},"numFound",[552,6593,6594],{},"start",[552,6596,6597],{},"docs",[85,6599,6600],{},[552,6601,6597],{},[67,6603,6604,6609,6615,6647],{},[85,6605,6606],{},[552,6607,6608],{},"Docs",[85,6610,6611,6614],{},[552,6612,6613],{},"docs[]"," 的每一项",[85,6616,6617,6541,6620,6541,6623,6541,6625,6541,6628,6541,6630,6541,6633,6541,6636,6541,6639,6541,6641,6541,6644],{},[552,6618,6619],{},"id",[552,6621,6622],{},"g",[552,6624,94],{},[552,6626,6627],{},"v",[552,6629,10],{},[552,6631,6632],{},"latestVersion",[552,6634,6635],{},"versionCount",[552,6637,6638],{},"timestamp",[552,6640,3033],{},[552,6642,6643],{},"ec",[552,6645,6646],{},"repositoryId",[85,6648,6649,6541,6651,6541,6653],{},[552,6650,6622],{},[552,6652,94],{},[552,6654,6627],{},[67,6656,6657,6662,6666,6671],{},[85,6658,6659],{},[552,6660,6661],{},"Spellcheck",[85,6663,6664],{},[552,6665,6547],{},[85,6667,6668],{},[552,6669,6670],{},"suggestions",[85,6672,6673],{},"没用上",[10,6675,6676,6677,6680],{},"能落进 ",[552,6678,6679],{},"dependency"," 的只有三个字段，正好是 Solr 那份命名里的单字母缩写：",[546,6682,6684],{"className":3617,"code":6683,"language":2998,"meta":291,"style":291},"dependency.addAttribute(\"groupId\", docs.getG());\ndependency.addAttribute(\"artifactId\", docs.getA());\ndependency.addAttribute(\"version\", docs.getV());\n",[552,6685,6686,6691,6696],{"__ignoreMap":291},[555,6687,6688],{"class":557,"line":558},[555,6689,6690],{},"dependency.addAttribute(\"groupId\", docs.getG());\n",[555,6692,6693],{"class":557,"line":295},[555,6694,6695],{},"dependency.addAttribute(\"artifactId\", docs.getA());\n",[555,6697,6698],{"class":557,"line":292},[555,6699,6700],{},"dependency.addAttribute(\"version\", docs.getV());\n",[10,6702,6703,6541,6705,6541,6707,6709,6710,6712,6713,6715,6716,6718,6719,6721,6722,6724,6725,6728],{},[552,6704,6622],{},[552,6706,94],{},[552,6708,6627],{}," 之外的那些 —— ",[552,6711,6632],{},"（最新版本）、",[552,6714,10],{},"（打包类型）、",[552,6717,6643],{},"（该版本下有哪些文件）、",[552,6720,6635],{},"（版本总数）、",[552,6723,6646],{},"（命中哪个仓库）、",[552,6726,6727],{},"Spellcheck.suggestions","（拼写建议）—— 都解析了但没用。类的骨架留在那里，将来想加「取最新版」或者「提示拼写」，字段是现成的。",[10,6730,6731,6732,608],{},"用实体类而不是手写 JSON 路径，好处很直白：解析一行就完成，后面取值都是 getter，字段名写错了编译器直接报，而不是运行时静默接到一个 ",[552,6733,6734],{},"null",[32,6736,6737],{"id":6737},"它什么时候查不出结果",[10,6739,6740],{},"这个工具解决的是「一批本地 jar，不知道 Maven 坐标」。但它依赖两个前提：jar 公开发布过，且 Manifest 没被改过。下面这几种情况它就交不出来，用之前心里有个数：",[2073,6742,6743,6753,6763,6794,6808],{},[177,6744,6745,6748,6749,6752],{},[181,6746,6747],{},"jar 压根不在 Maven 中心库。"," 中心库只收公开发布过的构件。国内厂商私服的包、公司内部工具包，以及因为授权原因不能上中心库的（典型是 Oracle 的 JDBC 驱动），SHA1 查不到，Manifest 也拼不出可用坐标，最后只打印一行 ",[552,6750,6751],{},"No data was found","，得自己手写。",[177,6754,6755,6758,6759,6762],{},[181,6756,6757],{},"只查中心库一个源。"," ",[552,6760,6761],{},"nexusUrl"," 是写死的中心库地址，查不了公司私服（Nexus3、Artifactory）。要换成自家仓库，改这个常量和对应的查询参数即可 —— 2018 那版就是从 Nexus2 这么换过来的，前提是私服也提供可搜索接口。",[177,6764,6765,6768,6769,6772,6773,6772,6776,6779,6780,6772,6783,6772,6786,6789,6790,6793],{},[181,6766,6767],{},"Manifest 里的名字不等于 Maven 坐标。"," 回退这条路，artifactId 是拿 ",[552,6770,6771],{},"Extension-Name"," → ",[552,6774,6775],{},"Implementation-Title",[552,6777,6778],{},"Specification-Title"," 依次取的，version 取 ",[552,6781,6782],{},"Bundle-Version",[552,6784,6785],{},"Implementation-Version",[552,6787,6788],{},"Specification-Version","。很多 jar 这几个字段是空的，或者写的是「XXX SDK」这种给人看的名字，搜出来的结果就不保证对；被改过包名、重新打过的 jar，SHA1 也会落空，最后都只能走到这条「猜」的路上。所以输出里标着 ",[552,6791,6792],{},"Search by Manifest"," 的那几条，建议开 Maven 仓库页面核一眼。",[177,6795,6796,6799,6800,6803,6804,6807],{},[181,6797,6798],{},"命中多条时只取第一条。"," 无论走哪条路，最后都是 ",[552,6801,6802],{},"getDocs().get(0)","，没有按版本排序再挑这类逻辑 —— 查询串里本来就带了 ",[552,6805,6806],{},"rows=1","，只打算要一条。",[177,6809,6810,6758,6813,6816,6817,1081,6820,6823,6824,6827],{},[181,6811,6812],{},"用法上的硬编码。",[552,6814,6815],{},"main()"," 里待处理目录写死成 ",[552,6818,6819],{},"D:\\lib",[552,6821,6822],{},"libs.listFiles()"," 只扫一层、也不筛后缀，换成别的路径、或者 ",[552,6825,6826],{},"lib"," 下面还有子目录，都得先改代码；结果是打在标准输出里的，得自己从控制台复制出来。",[32,6829,78],{"id":78},[174,6831,6832,6839],{},[177,6833,3256,6834],{},[94,6835,6838],{"href":6836,"rel":6837},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002Fjar2pom",[98],"gitee.com\u002FMegawatt\u002Fjar2pom",[177,6840,6841,6842],{},"CSDN：",[94,6843,6846],{"href":6844,"rel":6845},"https:\u002F\u002Fdownload.csdn.net\u002Fdownload\u002FJustinJia91\u002F12427956",[98],"download.csdn.net\u002Fdownload\u002FJustinJia91\u002F12427956",[10,6848,6849,6850,6853,6854,6857],{},"早年的仓库链接挂在 ",[552,6851,6852],{},"JiaXiaohei"," 这个账号名下，后来改成了 ",[552,6855,6856],{},"Megawatt","，老链接会自动跳过去，外部引用不用改。",[1981,6859,3506],{},{"title":291,"searchDepth":292,"depth":292,"links":6861},[6862,6863,6864,6865,6866,6867],{"id":611,"depth":295,"text":611},{"id":5632,"depth":295,"text":5632},{"id":5663,"depth":295,"text":5663},{"id":6485,"depth":295,"text":6486},{"id":6737,"depth":295,"text":6737},{"id":78,"depth":295,"text":78},"2020-05-16","把非 Maven 项目转成 Maven 时，得给一堆本地 jar 补 dependency。这个工具先用 jar 的 SHA1 去 Maven 中心库反查坐标，查不到再退回解析 Manifest。这篇带上项目结构、响应映射，以及它查不出结果的几种情况。",{},"\u002Fposts\u002Fjar2pom-maven-dependency",{"title":5606,"description":6869},"posts\u002Fjar2pom-maven-dependency",[6875,3291,2283],"Maven","My_pim5hwWNwMx0ud2asveYDDFzIcbblQ3loiVpZU38",{"id":6878,"title":6879,"body":6880,"date":7284,"description":7285,"extension":303,"meta":7286,"navigation":305,"path":7287,"seo":7288,"stem":7289,"tags":7290,"__hash__":7292},"posts\u002Fposts\u002Fflutter-android-sdkmanager-not-found.md","解决 Flutter 的 Android sdkmanager tool not found",{"type":7,"value":6881,"toc":7278},[6882,6884,6890,6893,6896,6902,7222,7224,7235,7240,7246,7251,7257,7262,7265,7268,7275],[32,6883,4301],{"id":4301},[546,6885,6888],{"className":6886,"code":6887,"language":3033},[3031],"Win10\nFlutter 1.12.13+hotfix.8\nAndroid Studio 3.6.1\n",[552,6889,6887],{"__ignoreMap":291},[32,6891,6892],{"id":6892},"问题描述",[10,6894,6895],{},"最近因为显卡 Boom 了，索性重装了系统。\n装完 Flutter 以后 doctor 通不过，一直找不到 sdkmanager。",[10,6897,6898],{},[16,6899],{"alt":6900,"src":6901},"flutter doctor 输出","\u002Fimages\u002Fflutter-android-sdkmanager\u002F01-flutter-doctor.png",[546,6903,6905],{"className":889,"code":6904,"language":891,"meta":291,"style":291},"> flutter doctor\nDoctor summary (to see all details, run flutter doctor -v):\n[√] Flutter (Channel stable, v1.12.13+hotfix.8, on Microsoft Windows [Version 10.0.18363.720], locale\n    zh-CN)\n[!] Android toolchain - develop for Android devices (Android SDK version 29.0.3)\n    X Android license status unknown.\n      Try re-installing or updating your Android SDK Manager.\n      See https:\u002F\u002Fdeveloper.android.com\u002Fstudio\u002F#downloads or visit https:\u002F\u002Fflutter.dev\u002Fsetup\u002F#android-setup\n      for detailed instructions.\n[√] Android Studio (version 3.6)\n[√] Connected device (1 available)\n\n! Doctor found issues in 1 category.\n\n> flutter doctor --android-licenses\nAndroid sdkmanager tool not found (D:\\Android\\Sdk\\tools\\bin\\sdkmanager).\nTry re-installing or updating your Android SDK,\nvisit https:\u002F\u002Fflutter.dev\u002Fsetup\u002F#android-setup for detailed instructions.\n\n> \n",[552,6906,6907,6915,6950,6989,6996,7024,7040,7063,7079,7087,7106,7124,7128,7150,7154,7161,7179,7197,7214,7218],{"__ignoreMap":291},[555,6908,6909,6912],{"class":557,"line":558},[555,6910,6911],{"class":565},">",[555,6913,6914],{"class":569}," flutter doctor\n",[555,6916,6917,6920,6923,6926,6929,6932,6935,6938,6941,6944,6947],{"class":557,"line":295},[555,6918,6919],{"class":898},"Doctor",[555,6921,6922],{"class":592}," summary",[555,6924,6925],{"class":569}," (to ",[555,6927,6928],{"class":592},"see",[555,6930,6931],{"class":592}," all",[555,6933,6934],{"class":592}," details,",[555,6936,6937],{"class":592}," run",[555,6939,6940],{"class":592}," flutter",[555,6942,6943],{"class":592}," doctor",[555,6945,6946],{"class":592}," -v",[555,6948,6949],{"class":569},"):\n",[555,6951,6952,6954,6957,6959,6962,6965,6968,6971,6974,6977,6980,6983,6986],{"class":557,"line":292},[555,6953,4476],{"class":565},[555,6955,6956],{"class":569},"√",[555,6958,4619],{"class":565},[555,6960,6961],{"class":569}," Flutter (",[555,6963,6964],{"class":898},"Channel",[555,6966,6967],{"class":592}," stable,",[555,6969,6970],{"class":592}," v1.12.13+hotfix.8,",[555,6972,6973],{"class":592}," on",[555,6975,6976],{"class":592}," Microsoft",[555,6978,6979],{"class":592}," Windows",[555,6981,6982],{"class":569}," [Version ",[555,6984,6985],{"class":592},"10.0.18363.720],",[555,6987,6988],{"class":592}," locale\n",[555,6990,6991,6994],{"class":557,"line":923},[555,6992,6993],{"class":898},"    zh-CN",[555,6995,598],{"class":569},[555,6997,6998,7001,7004,7007,7010,7012,7014,7017,7019,7022],{"class":557,"line":1786},[555,6999,7000],{"class":565},"[!]",[555,7002,7003],{"class":569}," Android toolchain - develop ",[555,7005,7006],{"class":579},"for",[555,7008,7009],{"class":569}," Android devices ",[555,7011,586],{"class":565},[555,7013,311],{"class":898},[555,7015,7016],{"class":592}," SDK",[555,7018,4468],{"class":592},[555,7020,7021],{"class":941}," 29.0.3",[555,7023,598],{"class":565},[555,7025,7026,7029,7032,7035,7037],{"class":557,"line":2519},[555,7027,7028],{"class":898},"    X",[555,7030,7031],{"class":592}," Android",[555,7033,7034],{"class":592}," license",[555,7036,2340],{"class":592},[555,7038,7039],{"class":592}," unknown.\n",[555,7041,7042,7045,7048,7050,7053,7056,7058,7060],{"class":557,"line":2530},[555,7043,7044],{"class":898},"      Try",[555,7046,7047],{"class":592}," re-installing",[555,7049,5436],{"class":592},[555,7051,7052],{"class":592}," updating",[555,7054,7055],{"class":592}," your",[555,7057,7031],{"class":592},[555,7059,7016],{"class":592},[555,7061,7062],{"class":592}," Manager.\n",[555,7064,7065,7068,7071,7073,7076],{"class":557,"line":2536},[555,7066,7067],{"class":898},"      See",[555,7069,7070],{"class":592}," https:\u002F\u002Fdeveloper.android.com\u002Fstudio\u002F#downloads",[555,7072,5436],{"class":592},[555,7074,7075],{"class":592}," visit",[555,7077,7078],{"class":592}," https:\u002F\u002Fflutter.dev\u002Fsetup\u002F#android-setup\n",[555,7080,7081,7084],{"class":557,"line":2550},[555,7082,7083],{"class":579},"      for",[555,7085,7086],{"class":569}," detailed instructions.\n",[555,7088,7089,7091,7093,7095,7098,7101,7104],{"class":557,"line":2564},[555,7090,4476],{"class":565},[555,7092,6956],{"class":569},[555,7094,4619],{"class":565},[555,7096,7097],{"class":569}," Android Studio (",[555,7099,7100],{"class":898},"version",[555,7102,7103],{"class":941}," 3.6",[555,7105,598],{"class":569},[555,7107,7108,7110,7112,7114,7117,7119,7122],{"class":557,"line":2578},[555,7109,4476],{"class":565},[555,7111,6956],{"class":569},[555,7113,4619],{"class":565},[555,7115,7116],{"class":569}," Connected device (",[555,7118,2867],{"class":898},[555,7120,7121],{"class":592}," available",[555,7123,598],{"class":569},[555,7125,7126],{"class":557,"line":3374},[555,7127,3685],{"emptyLinePlaceholder":305},[555,7129,7130,7133,7136,7139,7142,7144,7147],{"class":557,"line":3447},[555,7131,7132],{"class":565},"!",[555,7134,7135],{"class":898}," Doctor",[555,7137,7138],{"class":592}," found",[555,7140,7141],{"class":592}," issues",[555,7143,4866],{"class":592},[555,7145,7146],{"class":941}," 1",[555,7148,7149],{"class":592}," category.\n",[555,7151,7152],{"class":557,"line":3453},[555,7153,3685],{"emptyLinePlaceholder":305},[555,7155,7156,7158],{"class":557,"line":3459},[555,7157,6911],{"class":565},[555,7159,7160],{"class":569}," flutter doctor --android-licenses\n",[555,7162,7163,7165,7168,7171,7174,7176],{"class":557,"line":3465},[555,7164,311],{"class":898},[555,7166,7167],{"class":592}," sdkmanager",[555,7169,7170],{"class":592}," tool",[555,7172,7173],{"class":592}," not",[555,7175,7138],{"class":592},[555,7177,7178],{"class":569}," (D:\\Android\\Sdk\\tools\\bin\\sdkmanager).\n",[555,7180,7181,7184,7186,7188,7190,7192,7194],{"class":557,"line":3471},[555,7182,7183],{"class":898},"Try",[555,7185,7047],{"class":592},[555,7187,5436],{"class":592},[555,7189,7052],{"class":592},[555,7191,7055],{"class":592},[555,7193,7031],{"class":592},[555,7195,7196],{"class":592}," SDK,\n",[555,7198,7199,7202,7205,7208,7211],{"class":557,"line":3477},[555,7200,7201],{"class":898},"visit",[555,7203,7204],{"class":592}," https:\u002F\u002Fflutter.dev\u002Fsetup\u002F#android-setup",[555,7206,7207],{"class":592}," for",[555,7209,7210],{"class":592}," detailed",[555,7212,7213],{"class":592}," instructions.\n",[555,7215,7216],{"class":557,"line":3482},[555,7217,3685],{"emptyLinePlaceholder":305},[555,7219,7220],{"class":557,"line":3488},[555,7221,684],{"class":565},[32,7223,3540],{"id":3540},[10,7225,7226,7227,7230,7231,7234],{},"在 Android Studio 中启动 ",[181,7228,7229],{},"SDK Manager","，去掉选项 ",[181,7232,7233],{},"Hide Obsolete Packages","：",[10,7236,7237],{},[16,7238],{"alt":7229,"src":7239},"\u002Fimages\u002Fflutter-android-sdkmanager\u002F02-sdk-manager.png",[10,7241,7242,7243,7234],{},"选择安装 ",[181,7244,7245],{},"Android SDK Tools (Obsolete)",[10,7247,7248],{},[16,7249],{"alt":7245,"src":7250},"\u002Fimages\u002Fflutter-android-sdkmanager\u002F03-sdk-tools-obsolete.png",[10,7252,7253,7254,7234],{},"点击 ",[181,7255,7256],{},"Apply",[10,7258,7259],{},[16,7260],{"alt":7256,"src":7261},"\u002Fimages\u002Fflutter-android-sdkmanager\u002F04-apply.png",[10,7263,7264],{},"安装完成。",[32,7266,7267],{"id":7267},"原因",[10,7269,7270,7271,7274],{},"不知道为啥 ",[181,7272,7273],{},"Android SDK Tools"," 被废弃了，但是好像 Flutter 还需要其提供支持。",[1981,7276,7277],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":291,"searchDepth":292,"depth":292,"links":7279},[7280,7281,7282,7283],{"id":4301,"depth":295,"text":4301},{"id":6892,"depth":295,"text":6892},{"id":3540,"depth":295,"text":3540},{"id":7267,"depth":295,"text":7267},"2020-03-30","重装系统后 flutter doctor 一直报 Android license status unknown，找不到 sdkmanager。根因是被废弃的 Android SDK Tools 没装。",{},"\u002Fposts\u002Fflutter-android-sdkmanager-not-found",{"title":6879,"description":7285},"posts\u002Fflutter-android-sdkmanager-not-found",[7291,311],"Flutter","89rDwOF_d8Tw5wvZBBPNTmf8oT3xfyx82CIoQ11E9Dw",{"id":7294,"title":7295,"body":7296,"date":8030,"description":8031,"extension":303,"meta":8032,"navigation":305,"path":8033,"seo":8034,"stem":8035,"tags":8036,"__hash__":8038},"posts\u002Fposts\u002Fcentos-yum-pgadmin4-python3-missing.md","解决 CentOS yum 安装 pgadmin4 报 nothing provides python3-*** 的问题",{"type":7,"value":7297,"toc":8024},[7298,7300,7306,7308,7311,7317,7991,7993,7996,7998,8004,8021],[32,7299,4301],{"id":4301},[546,7301,7304],{"className":7302,"code":7303,"language":3033},[3031],"CentOS8 (最小安装)\nPython3 (yum\u002Fdnf安装)\n",[552,7305,7303],{"__ignoreMap":291},[32,7307,6892],{"id":6892},[10,7309,7310],{},"安装时提示缺少 python3-alembic 等包，\n但是安装了 python3 仍然提示找不到包。",[10,7312,7313],{},[16,7314],{"alt":7315,"src":7316},"yum 安装报错","\u002Fimages\u002Fcentos-yum-pgadmin4\u002Ferror.png",[546,7318,7320],{"className":889,"code":7319,"language":891,"meta":291,"style":291},"[root@HyperV-PSQL ~]# yum -y install pgadmin4\n上次元数据过期检查：0:11:32 前，执行于 2020年03月10日 星期二 11时35分24秒。\n错误：\n 问题: package pgadmin4-4.19-1.rhel8.x86_64 requires pgadmin4-web, but none of the providers can be installed\n  - cannot install the best candidate for the job\n  - nothing provides python3-alembic needed by pgadmin4-web-4.16-1.rhel8.noarch\n  - nothing provides pgadmin4-python3-unittest2 needed by pgadmin4-web-4.16-1.rhel8.noarch\n  - nothing provides python3-blinker >= 1.4 needed by pgadmin4-web-4.16-1.rhel8.noarch\n  - nothing provides python3-beautifulsoup4 >= 4.4.1 needed by pgadmin4-web-4.16-1.rhel8.noarch\n  - nothing provides python3-flask-sqlalchemy >= 2.3.2 needed by pgadmin4-web-4.16-1.rhel8.noarch\n  - nothing provides python3-alembic needed by pgadmin4-web-4.16-2.rhel8.noarch\n  - nothing provides python3-unittest2 needed by pgadmin4-web-4.16-2.rhel8.noarch\n  - nothing provides python3-blinker >= 1.4 needed by pgadmin4-web-4.16-2.rhel8.noarch\n  - nothing provides python3-beautifulsoup4 >= 4.4.1 needed by pgadmin4-web-4.16-2.rhel8.noarch\n  - nothing provides python3-alembic needed by pgadmin4-web-4.17-1.rhel8.noarch\n  - nothing provides python3-unittest2 needed by pgadmin4-web-4.17-1.rhel8.noarch\n  - nothing provides python3-blinker >= 1.4 needed by pgadmin4-web-4.17-1.rhel8.noarch\n  - nothing provides python3-beautifulsoup4 >= 4.4.1 needed by pgadmin4-web-4.17-1.rhel8.noarch\n  - nothing provides python3-alembic needed by pgadmin4-web-4.17-2.rhel8.noarch\n  - nothing provides python3-unittest2 needed by pgadmin4-web-4.17-2.rhel8.noarch\n  - nothing provides python3-bcrypt needed by pgadmin4-web-4.17-2.rhel8.noarch\n  - nothing provides python3-pynacl needed by pgadmin4-web-4.17-2.rhel8.noarch\n  - nothing provides python3-blinker >= 1.4 needed by pgadmin4-web-4.17-2.rhel8.noarch\n  - nothing provides python3-beautifulsoup4 >= 4.4.1 needed by pgadmin4-web-4.17-2.rhel8.noarch\n  - nothing provides python3-alembic needed by pgadmin4-web-4.18-1.rhel8.noarch\n  - nothing provides python3-unittest2 needed by pgadmin4-web-4.18-1.rhel8.noarch\n  - nothing provides python3-bcrypt needed by pgadmin4-web-4.18-1.rhel8.noarch\n  - nothing provides python3-pynacl needed by pgadmin4-web-4.18-1.rhel8.noarch\n  - nothing provides python3-blinker >= 1.4 needed by pgadmin4-web-4.18-1.rhel8.noarch\n  - nothing provides python3-beautifulsoup4 >= 4.4.1 needed by pgadmin4-web-4.18-1.rhel8.noarch\n  - nothing provides python3-bcrypt needed by pgadmin4-web-4.19-1.rhel8.noarch\n  - nothing provides python3-pynacl needed by pgadmin4-web-4.19-1.rhel8.noarch\n  - nothing provides python3-blinker >= 1.4 needed by pgadmin4-web-4.19-1.rhel8.noarch\n(尝试添加 '--skip-broken' 来跳过无法安装的软件包 或 '--nobest' 来不只使用最佳选择的软件包)\n",[552,7321,7322,7334,7350,7355,7393,7417,7439,7456,7481,7505,7529,7546,7563,7585,7607,7624,7640,7662,7684,7701,7717,7734,7751,7773,7795,7812,7828,7844,7860,7882,7904,7921,7937,7959],{"__ignoreMap":291},[555,7323,7324,7326,7329,7331],{"class":557,"line":558},[555,7325,4476],{"class":565},[555,7327,7328],{"class":569},"root@HyperV-PSQL ",[555,7330,5387],{"class":565},[555,7332,7333],{"class":569},"# yum -y install pgadmin4\n",[555,7335,7336,7339,7341,7344,7347],{"class":557,"line":295},[555,7337,7338],{"class":898},"上次元数据过期检查：0:11:32",[555,7340,4630],{"class":592},[555,7342,7343],{"class":592}," 2020年03月10日",[555,7345,7346],{"class":592}," 星期二",[555,7348,7349],{"class":592}," 11时35分24秒。\n",[555,7351,7352],{"class":557,"line":292},[555,7353,7354],{"class":898},"错误：\n",[555,7356,7357,7360,7362,7365,7368,7371,7374,7377,7379,7381,7384,7387,7390],{"class":557,"line":923},[555,7358,7359],{"class":898}," 问题:",[555,7361,4880],{"class":592},[555,7363,7364],{"class":592}," pgadmin4-4.19-1.rhel8.x86_64",[555,7366,7367],{"class":592}," requires",[555,7369,7370],{"class":592}," pgadmin4-web,",[555,7372,7373],{"class":592}," but",[555,7375,7376],{"class":592}," none",[555,7378,4463],{"class":592},[555,7380,4366],{"class":592},[555,7382,7383],{"class":592}," providers",[555,7385,7386],{"class":592}," can",[555,7388,7389],{"class":592}," be",[555,7391,7392],{"class":592}," installed\n",[555,7394,7395,7398,7400,7402,7404,7407,7410,7412,7414],{"class":557,"line":1786},[555,7396,7397],{"class":898},"  -",[555,7399,5128],{"class":592},[555,7401,4522],{"class":592},[555,7403,4366],{"class":592},[555,7405,7406],{"class":592}," best",[555,7408,7409],{"class":592}," candidate",[555,7411,7207],{"class":592},[555,7413,4366],{"class":592},[555,7415,7416],{"class":592}," job\n",[555,7418,7419,7421,7424,7427,7430,7433,7436],{"class":557,"line":2519},[555,7420,7397],{"class":898},[555,7422,7423],{"class":592}," nothing",[555,7425,7426],{"class":592}," provides",[555,7428,7429],{"class":592}," python3-alembic",[555,7431,7432],{"class":592}," needed",[555,7434,7435],{"class":592}," by",[555,7437,7438],{"class":592}," pgadmin4-web-4.16-1.rhel8.noarch\n",[555,7440,7441,7443,7445,7447,7450,7452,7454],{"class":557,"line":2530},[555,7442,7397],{"class":898},[555,7444,7423],{"class":592},[555,7446,7426],{"class":592},[555,7448,7449],{"class":592}," pgadmin4-python3-unittest2",[555,7451,7432],{"class":592},[555,7453,7435],{"class":592},[555,7455,7438],{"class":592},[555,7457,7458,7460,7462,7464,7467,7470,7472,7475,7477,7479],{"class":557,"line":2536},[555,7459,7397],{"class":898},[555,7461,7423],{"class":592},[555,7463,7426],{"class":592},[555,7465,7466],{"class":592}," python3-blinker",[555,7468,7469],{"class":565}," >",[555,7471,661],{"class":592},[555,7473,7474],{"class":941}," 1.4",[555,7476,7432],{"class":592},[555,7478,7435],{"class":592},[555,7480,7438],{"class":592},[555,7482,7483,7485,7487,7489,7492,7494,7496,7499,7501,7503],{"class":557,"line":2550},[555,7484,7397],{"class":898},[555,7486,7423],{"class":592},[555,7488,7426],{"class":592},[555,7490,7491],{"class":592}," python3-beautifulsoup4",[555,7493,7469],{"class":565},[555,7495,661],{"class":592},[555,7497,7498],{"class":941}," 4.4.1",[555,7500,7432],{"class":592},[555,7502,7435],{"class":592},[555,7504,7438],{"class":592},[555,7506,7507,7509,7511,7513,7516,7518,7520,7523,7525,7527],{"class":557,"line":2564},[555,7508,7397],{"class":898},[555,7510,7423],{"class":592},[555,7512,7426],{"class":592},[555,7514,7515],{"class":592}," python3-flask-sqlalchemy",[555,7517,7469],{"class":565},[555,7519,661],{"class":592},[555,7521,7522],{"class":941}," 2.3.2",[555,7524,7432],{"class":592},[555,7526,7435],{"class":592},[555,7528,7438],{"class":592},[555,7530,7531,7533,7535,7537,7539,7541,7543],{"class":557,"line":2578},[555,7532,7397],{"class":898},[555,7534,7423],{"class":592},[555,7536,7426],{"class":592},[555,7538,7429],{"class":592},[555,7540,7432],{"class":592},[555,7542,7435],{"class":592},[555,7544,7545],{"class":592}," pgadmin4-web-4.16-2.rhel8.noarch\n",[555,7547,7548,7550,7552,7554,7557,7559,7561],{"class":557,"line":3374},[555,7549,7397],{"class":898},[555,7551,7423],{"class":592},[555,7553,7426],{"class":592},[555,7555,7556],{"class":592}," python3-unittest2",[555,7558,7432],{"class":592},[555,7560,7435],{"class":592},[555,7562,7545],{"class":592},[555,7564,7565,7567,7569,7571,7573,7575,7577,7579,7581,7583],{"class":557,"line":3447},[555,7566,7397],{"class":898},[555,7568,7423],{"class":592},[555,7570,7426],{"class":592},[555,7572,7466],{"class":592},[555,7574,7469],{"class":565},[555,7576,661],{"class":592},[555,7578,7474],{"class":941},[555,7580,7432],{"class":592},[555,7582,7435],{"class":592},[555,7584,7545],{"class":592},[555,7586,7587,7589,7591,7593,7595,7597,7599,7601,7603,7605],{"class":557,"line":3453},[555,7588,7397],{"class":898},[555,7590,7423],{"class":592},[555,7592,7426],{"class":592},[555,7594,7491],{"class":592},[555,7596,7469],{"class":565},[555,7598,661],{"class":592},[555,7600,7498],{"class":941},[555,7602,7432],{"class":592},[555,7604,7435],{"class":592},[555,7606,7545],{"class":592},[555,7608,7609,7611,7613,7615,7617,7619,7621],{"class":557,"line":3459},[555,7610,7397],{"class":898},[555,7612,7423],{"class":592},[555,7614,7426],{"class":592},[555,7616,7429],{"class":592},[555,7618,7432],{"class":592},[555,7620,7435],{"class":592},[555,7622,7623],{"class":592}," pgadmin4-web-4.17-1.rhel8.noarch\n",[555,7625,7626,7628,7630,7632,7634,7636,7638],{"class":557,"line":3465},[555,7627,7397],{"class":898},[555,7629,7423],{"class":592},[555,7631,7426],{"class":592},[555,7633,7556],{"class":592},[555,7635,7432],{"class":592},[555,7637,7435],{"class":592},[555,7639,7623],{"class":592},[555,7641,7642,7644,7646,7648,7650,7652,7654,7656,7658,7660],{"class":557,"line":3471},[555,7643,7397],{"class":898},[555,7645,7423],{"class":592},[555,7647,7426],{"class":592},[555,7649,7466],{"class":592},[555,7651,7469],{"class":565},[555,7653,661],{"class":592},[555,7655,7474],{"class":941},[555,7657,7432],{"class":592},[555,7659,7435],{"class":592},[555,7661,7623],{"class":592},[555,7663,7664,7666,7668,7670,7672,7674,7676,7678,7680,7682],{"class":557,"line":3477},[555,7665,7397],{"class":898},[555,7667,7423],{"class":592},[555,7669,7426],{"class":592},[555,7671,7491],{"class":592},[555,7673,7469],{"class":565},[555,7675,661],{"class":592},[555,7677,7498],{"class":941},[555,7679,7432],{"class":592},[555,7681,7435],{"class":592},[555,7683,7623],{"class":592},[555,7685,7686,7688,7690,7692,7694,7696,7698],{"class":557,"line":3482},[555,7687,7397],{"class":898},[555,7689,7423],{"class":592},[555,7691,7426],{"class":592},[555,7693,7429],{"class":592},[555,7695,7432],{"class":592},[555,7697,7435],{"class":592},[555,7699,7700],{"class":592}," pgadmin4-web-4.17-2.rhel8.noarch\n",[555,7702,7703,7705,7707,7709,7711,7713,7715],{"class":557,"line":3488},[555,7704,7397],{"class":898},[555,7706,7423],{"class":592},[555,7708,7426],{"class":592},[555,7710,7556],{"class":592},[555,7712,7432],{"class":592},[555,7714,7435],{"class":592},[555,7716,7700],{"class":592},[555,7718,7719,7721,7723,7725,7728,7730,7732],{"class":557,"line":3493},[555,7720,7397],{"class":898},[555,7722,7423],{"class":592},[555,7724,7426],{"class":592},[555,7726,7727],{"class":592}," python3-bcrypt",[555,7729,7432],{"class":592},[555,7731,7435],{"class":592},[555,7733,7700],{"class":592},[555,7735,7736,7738,7740,7742,7745,7747,7749],{"class":557,"line":3727},[555,7737,7397],{"class":898},[555,7739,7423],{"class":592},[555,7741,7426],{"class":592},[555,7743,7744],{"class":592}," python3-pynacl",[555,7746,7432],{"class":592},[555,7748,7435],{"class":592},[555,7750,7700],{"class":592},[555,7752,7753,7755,7757,7759,7761,7763,7765,7767,7769,7771],{"class":557,"line":3733},[555,7754,7397],{"class":898},[555,7756,7423],{"class":592},[555,7758,7426],{"class":592},[555,7760,7466],{"class":592},[555,7762,7469],{"class":565},[555,7764,661],{"class":592},[555,7766,7474],{"class":941},[555,7768,7432],{"class":592},[555,7770,7435],{"class":592},[555,7772,7700],{"class":592},[555,7774,7775,7777,7779,7781,7783,7785,7787,7789,7791,7793],{"class":557,"line":3739},[555,7776,7397],{"class":898},[555,7778,7423],{"class":592},[555,7780,7426],{"class":592},[555,7782,7491],{"class":592},[555,7784,7469],{"class":565},[555,7786,661],{"class":592},[555,7788,7498],{"class":941},[555,7790,7432],{"class":592},[555,7792,7435],{"class":592},[555,7794,7700],{"class":592},[555,7796,7797,7799,7801,7803,7805,7807,7809],{"class":557,"line":3745},[555,7798,7397],{"class":898},[555,7800,7423],{"class":592},[555,7802,7426],{"class":592},[555,7804,7429],{"class":592},[555,7806,7432],{"class":592},[555,7808,7435],{"class":592},[555,7810,7811],{"class":592}," pgadmin4-web-4.18-1.rhel8.noarch\n",[555,7813,7814,7816,7818,7820,7822,7824,7826],{"class":557,"line":3750},[555,7815,7397],{"class":898},[555,7817,7423],{"class":592},[555,7819,7426],{"class":592},[555,7821,7556],{"class":592},[555,7823,7432],{"class":592},[555,7825,7435],{"class":592},[555,7827,7811],{"class":592},[555,7829,7830,7832,7834,7836,7838,7840,7842],{"class":557,"line":3756},[555,7831,7397],{"class":898},[555,7833,7423],{"class":592},[555,7835,7426],{"class":592},[555,7837,7727],{"class":592},[555,7839,7432],{"class":592},[555,7841,7435],{"class":592},[555,7843,7811],{"class":592},[555,7845,7846,7848,7850,7852,7854,7856,7858],{"class":557,"line":3761},[555,7847,7397],{"class":898},[555,7849,7423],{"class":592},[555,7851,7426],{"class":592},[555,7853,7744],{"class":592},[555,7855,7432],{"class":592},[555,7857,7435],{"class":592},[555,7859,7811],{"class":592},[555,7861,7862,7864,7866,7868,7870,7872,7874,7876,7878,7880],{"class":557,"line":3767},[555,7863,7397],{"class":898},[555,7865,7423],{"class":592},[555,7867,7426],{"class":592},[555,7869,7466],{"class":592},[555,7871,7469],{"class":565},[555,7873,661],{"class":592},[555,7875,7474],{"class":941},[555,7877,7432],{"class":592},[555,7879,7435],{"class":592},[555,7881,7811],{"class":592},[555,7883,7884,7886,7888,7890,7892,7894,7896,7898,7900,7902],{"class":557,"line":3773},[555,7885,7397],{"class":898},[555,7887,7423],{"class":592},[555,7889,7426],{"class":592},[555,7891,7491],{"class":592},[555,7893,7469],{"class":565},[555,7895,661],{"class":592},[555,7897,7498],{"class":941},[555,7899,7432],{"class":592},[555,7901,7435],{"class":592},[555,7903,7811],{"class":592},[555,7905,7906,7908,7910,7912,7914,7916,7918],{"class":557,"line":3778},[555,7907,7397],{"class":898},[555,7909,7423],{"class":592},[555,7911,7426],{"class":592},[555,7913,7727],{"class":592},[555,7915,7432],{"class":592},[555,7917,7435],{"class":592},[555,7919,7920],{"class":592}," pgadmin4-web-4.19-1.rhel8.noarch\n",[555,7922,7923,7925,7927,7929,7931,7933,7935],{"class":557,"line":3783},[555,7924,7397],{"class":898},[555,7926,7423],{"class":592},[555,7928,7426],{"class":592},[555,7930,7744],{"class":592},[555,7932,7432],{"class":592},[555,7934,7435],{"class":592},[555,7936,7920],{"class":592},[555,7938,7939,7941,7943,7945,7947,7949,7951,7953,7955,7957],{"class":557,"line":3789},[555,7940,7397],{"class":898},[555,7942,7423],{"class":592},[555,7944,7426],{"class":592},[555,7946,7466],{"class":592},[555,7948,7469],{"class":565},[555,7950,661],{"class":592},[555,7952,7474],{"class":941},[555,7954,7432],{"class":592},[555,7956,7435],{"class":592},[555,7958,7920],{"class":592},[555,7960,7961,7963,7966,7968,7971,7973,7976,7979,7981,7984,7986,7989],{"class":557,"line":3794},[555,7962,586],{"class":565},[555,7964,7965],{"class":898},"尝试添加",[555,7967,5225],{"class":565},[555,7969,7970],{"class":592},"--skip-broken",[555,7972,589],{"class":565},[555,7974,7975],{"class":592}," 来跳过无法安装的软件包",[555,7977,7978],{"class":592}," 或",[555,7980,5225],{"class":565},[555,7982,7983],{"class":592},"--nobest",[555,7985,589],{"class":565},[555,7987,7988],{"class":592}," 来不只使用最佳选择的软件包",[555,7990,598],{"class":565},[32,7992,7267],{"id":7267},[10,7994,7995],{},"官方的 rpm repository 没有提供对应的包。",[32,7997,3540],{"id":3540},[10,7999,8000,8001,7234],{},"安装 ",[181,8002,8003],{},"EPEL",[546,8005,8007],{"className":889,"code":8006,"language":891,"meta":291,"style":291},"yum install epel-release -y\n",[552,8008,8009],{"__ignoreMap":291},[555,8010,8011,8013,8015,8018],{"class":557,"line":558},[555,8012,5581],{"class":898},[555,8014,4522],{"class":592},[555,8016,8017],{"class":592}," epel-release",[555,8019,8020],{"class":592}," -y\n",[1981,8022,8023],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":291,"searchDepth":292,"depth":292,"links":8025},[8026,8027,8028,8029],{"id":4301,"depth":295,"text":4301},{"id":6892,"depth":295,"text":6892},{"id":7267,"depth":295,"text":7267},{"id":3540,"depth":295,"text":3540},"2020-03-13","CentOS 8 上装 pgadmin4 一直缺 python3-alembic 之类的包，但 python3 明明装了。原因是官方 rpm 仓库没提供这些包，得先装 EPEL。",{},"\u002Fposts\u002Fcentos-yum-pgadmin4-python3-missing",{"title":7295,"description":8031},"posts\u002Fcentos-yum-pgadmin4-python3-missing",[2671,8037,2672],"PostgreSQL","aVJQ5MrUa_hpNZ0bTIwLxk-DJDKyebCmCM9GG4FrjaA",{"id":8040,"title":8041,"body":8042,"date":8518,"description":8519,"extension":303,"meta":8520,"navigation":305,"path":8521,"seo":8522,"stem":8523,"tags":8524,"__hash__":8527},"posts\u002Fposts\u002Fmacpro-gpu-switch.md","MacPro GPU Switch：用 JavaFX 做一个 macOS 显卡切换工具",{"type":7,"value":8043,"toc":8503},[8044,8047,8050,8053,8109,8112,8115,8118,8124,8127,8130,8133,8136,8143,8163,8166,8169,8173,8179,8185,8195,8232,8235,8239,8254,8261,8265,8268,8282,8293,8307,8325,8328,8350,8354,8360,8385,8388,8394,8398,8430,8432,8435,8438,8449,8464,8467,8481,8484,8486,8489,8497,8500],[10,8045,8046],{},"MacBook Pro 15 寸是台双显卡的机器：一块 Intel 核显，一块 Radeon 独显。",[10,8048,8049],{},"用久了会撞上一个很具体的烦恼 —— 你没法决定现在用哪块。",[10,8051,8052],{},"macOS 的系统设置里，关于显卡只有「自动切换图形卡模式」这一个勾选框，它管的是“要不要自动切”，管不了“现在锁在哪一块”。真正能决定这件事的，是一条电源管理命令：",[546,8054,8056],{"className":889,"code":8055,"language":891,"meta":291,"style":291},"sudo pmset -a GPUSwitch 0   # 整合（只用核显）\nsudo pmset -a GPUSwitch 1   # 高性能（只用独显）\nsudo pmset -a GPUSwitch 2   # 自动\n",[552,8057,8058,8078,8093],{"__ignoreMap":291},[555,8059,8060,8063,8066,8069,8072,8075],{"class":557,"line":558},[555,8061,8062],{"class":898},"sudo",[555,8064,8065],{"class":592}," pmset",[555,8067,8068],{"class":592}," -a",[555,8070,8071],{"class":592}," GPUSwitch",[555,8073,8074],{"class":941}," 0",[555,8076,8077],{"class":905},"   # 整合（只用核显）\n",[555,8079,8080,8082,8084,8086,8088,8090],{"class":557,"line":295},[555,8081,8062],{"class":898},[555,8083,8065],{"class":592},[555,8085,8068],{"class":592},[555,8087,8071],{"class":592},[555,8089,7146],{"class":941},[555,8091,8092],{"class":905},"   # 高性能（只用独显）\n",[555,8094,8095,8097,8099,8101,8103,8106],{"class":557,"line":292},[555,8096,8062],{"class":898},[555,8098,8065],{"class":592},[555,8100,8068],{"class":592},[555,8102,8071],{"class":592},[555,8104,8105],{"class":941}," 2",[555,8107,8108],{"class":905},"   # 自动\n",[10,8110,8111],{},"能用，但每次都得开终端、敲一遍、输一次密码。为了两秒钟的切换走完这一套，太长了。",[10,8113,8114],{},"MacPro GPU Switch 就是把这条命令包进一个窗口：选核显就一直用核显，选独显就一直用独显，选自动就交回给系统。",[32,8116,8117],{"id":8117},"它长这样",[10,8119,8120],{},[16,8121],{"alt":8122,"src":8123},"MacPro GPU Switch 主界面：左侧大字显示当前模式，右侧填 Root 密码并选择模式","\u002Fimages\u002Fmacpro-gpu-switch\u002Fmain-window.png",[10,8125,8126],{},"界面很直白：左边大字写着当前处于哪个模式，颜色也跟着变（整合是青色，高性能是橙色，自动是蓝色）；右边填 Root 密码，下面三个单选框对应三种模式；底部是「关于」「应用」「关闭」。",[10,8128,8129],{},"勾上「记住密码」，下次打开就不用再输。",[10,8131,8132],{},"它做的事就四步：读当前状态 → 你选一个新模式 → 点「应用」→ 它替你跑那条 sudo 命令，再把新状态读回来显示。",[32,8134,8135],{"id":8135},"三种模式",[10,8137,8138,8139,8142],{},"对应 ",[552,8140,8141],{},"GPUSwitch"," 的三个值：",[2073,8144,8145,8151,8157],{},[177,8146,8147,8150],{},[181,8148,8149],{},"整合","（0）—— 只用核显，最省电最凉快，重活干不了；",[177,8152,8153,8156],{},[181,8154,8155],{},"高性能","（1）—— 只用独显，性能拉满，代价是发热和续航；",[177,8158,8159,8162],{},[181,8160,8161],{},"自动","（2）—— 交给系统判断，也就是 macOS 默认的那套逻辑。",[32,8164,8165],{"id":8165},"实现上其实只有三件事",[10,8167,8168],{},"代码量很小，核心就是三次 Shell 调用。",[1287,8170,8172],{"id":8171},"一读懂当前状态","一、读懂当前状态",[10,8174,8175,8178],{},[552,8176,8177],{},"pmset -g"," 会打印一大串电源管理配置，显卡那一行长这样：",[546,8180,8183],{"className":8181,"code":8182,"language":3033},[3031],"gpuswitch             0\n",[552,8184,8182],{"__ignoreMap":291},[10,8186,8187,8188,8190,8191,8194],{},"所以读状态的逻辑就是：跑一遍 ",[552,8189,8177],{},"，逐行找以 ",[552,8192,8193],{},"gpuswitch"," 开头的那行，把数字抠出来。",[546,8196,8198],{"className":3617,"code":8197,"language":2998,"meta":291,"style":291},"String[] command = {\"\u002Fbin\u002Fsh\", \"-c\", \"pmset -g\"};\n\nfor (String process : processList) {\n    if (process.startsWith(\"gpuswitch\")) {\n        i = Integer.parseInt(process.replaceAll(\"gpuswitch| \", \"\"));\n    }\n}\n",[552,8199,8200,8205,8209,8214,8219,8224,8228],{"__ignoreMap":291},[555,8201,8202],{"class":557,"line":558},[555,8203,8204],{},"String[] command = {\"\u002Fbin\u002Fsh\", \"-c\", \"pmset -g\"};\n",[555,8206,8207],{"class":557,"line":295},[555,8208,3685],{"emptyLinePlaceholder":305},[555,8210,8211],{"class":557,"line":292},[555,8212,8213],{},"for (String process : processList) {\n",[555,8215,8216],{"class":557,"line":923},[555,8217,8218],{},"    if (process.startsWith(\"gpuswitch\")) {\n",[555,8220,8221],{"class":557,"line":1786},[555,8222,8223],{},"        i = Integer.parseInt(process.replaceAll(\"gpuswitch| \", \"\"));\n",[555,8225,8226],{"class":557,"line":2519},[555,8227,3837],{},[555,8229,8230],{"class":557,"line":2530},[555,8231,1058],{},[10,8233,8234],{},"拿到 0 \u002F 1 \u002F 2，再去点亮界面上对应的那个单选框。",[1287,8236,8238],{"id":8237},"二切换","二、切换",[546,8240,8242],{"className":3617,"code":8241,"language":2998,"meta":291,"style":291},"String[] command = {\"\u002Fbin\u002Fbash\", \"-c\",\n    \"echo \\\"\" + password + \"\\\" | sudo -S pmset -a GPUSwitch \" + status};\n",[552,8243,8244,8249],{"__ignoreMap":291},[555,8245,8246],{"class":557,"line":558},[555,8247,8248],{},"String[] command = {\"\u002Fbin\u002Fbash\", \"-c\",\n",[555,8250,8251],{"class":557,"line":295},[555,8252,8253],{},"    \"echo \\\"\" + password + \"\\\" | sudo -S pmset -a GPUSwitch \" + status};\n",[10,8255,8256,8257,8260],{},"关键在 ",[552,8258,8259],{},"sudo -S"," 这个参数。它让 sudo 从标准输入读密码 —— 不加它，sudo 会去找终端要密码，而一个图形界面程序没有终端，命令会直接卡在那里。把密码 echo 进去、管道喂给 sudo，就能在没有终端的环境里把这条命令跑完。",[1287,8262,8264],{"id":8263},"三验证密码","三、验证密码",[10,8266,8267],{},"切显卡之前得先知道密码对不对，否则用户点「应用」只会撞上一个莫名其妙的失败。",[546,8269,8271],{"className":3617,"code":8270,"language":2998,"meta":291,"style":291},"String[] command = {\"\u002Fbin\u002Fbash\", \"-c\",\n    \"echo \\\"\" + password + \"\\\" | sudo -S whoami\"};\n",[552,8272,8273,8277],{"__ignoreMap":291},[555,8274,8275],{"class":557,"line":558},[555,8276,8248],{},[555,8278,8279],{"class":557,"line":295},[555,8280,8281],{},"    \"echo \\\"\" + password + \"\\\" | sudo -S whoami\"};\n",[10,8283,8284,8285,8288,8289,8292],{},"跑一次 ",[552,8286,8287],{},"whoami","，输出的第一行是 ",[552,8290,8291],{},"root","，就说明密码正确。",[10,8294,8295,8296,8298,8299,8302,8303,8306],{},"用 ",[552,8297,8287],{}," 而不是 ",[552,8300,8301],{},"sudo -v","，好处是它验证权限的方式和后面那条 ",[552,8304,8305],{},"pmset"," 完全一致，同时又没有任何副作用 —— 密码错了也不会误伤配置。",[10,8308,8309,8310,8313,8314,8317,8318,1509,8321,8324],{},"写正式代码之前，我先在 ",[552,8311,8312],{},"src\u002Ftest"," 下扔了两个带 ",[552,8315,8316],{},"main"," 的小类，把命令单独跑通再搬进工具类。目录里留下的 ",[552,8319,8320],{},"CheckPassword",[552,8322,8323],{},"RunPmset"," 就是当时的验证代码。",[1287,8326,8327],{"id":8327},"界面部分",[10,8329,8330,8331,1975,8334,8337,8338,8341,8342,8345,8346,8349],{},"JavaFX + FXML，没什么花活。三个 ",[552,8332,8333],{},"RadioButton",[552,8335,8336],{},"userData"," 里分别挂着 0 \u002F 1 \u002F 2，用 ",[552,8339,8340],{},"ToggleGroup"," 保证互斥；窗口初始化时先读一次当前状态，用 ",[552,8343,8344],{},"fire()"," 把对应的那个点亮。为了避免用户还没选模式就点「应用」，",[552,8347,8348],{},"applyApp()"," 里也做了一次空判断。",[32,8351,8353],{"id":8352},"记住密码是怎么存的","「记住密码」是怎么存的",[10,8355,8356,8357,7234],{},"勾上「记住密码」之后，密码不会明文落盘，而是先加密再写进程序目录下的 ",[552,8358,8359],{},"conf.properties",[2073,8361,8362,8371],{},[177,8363,8364,7234,8367,8370],{},[181,8365,8366],{},"算法",[552,8368,8369],{},"AES\u002FECB\u002FPKCS5Padding","，密文再转成 URL-safe 的 Base64 保存；",[177,8372,8373,8376,8377,8380,8381,8384],{},[181,8374,8375],{},"密钥不是写死的常量","，而是每台机器独有的一个标识 —— 跑一遍 ",[552,8378,8379],{},"system_profiler SPHardwareDataType","，抓 ",[552,8382,8383],{},"Hardware UUID:"," 那一行，去掉空格和连字符，正好 32 位十六进制，直接拿来当 AES-256 的密钥。",[10,8386,8387],{},"配置文件里只存两样东西：一个「是否记住密码」的开关，和密文本身。",[10,8389,8390,8391,8393],{},"取 Hardware UUID 而不是写死一个字符串，好处是凭据跟机器绑在一起 —— 把 ",[552,8392,8359],{}," 拷到别的机器上，密文就解不开了。反过来，换机器或者重装系统之后，之前记住的密码会失效，重新输一次就行。",[32,8395,8397],{"id":8396},"支持范围和一句要紧的警告","支持范围，和一句要紧的警告",[174,8399,8400,8407,8414,8421],{},[177,8401,8402,8403,8406],{},"实际验证过的只有一台：",[181,8404,8405],{},"MacBook Pro（15-inch，2018）+ macOS Mojave 10.14.3","；",[177,8408,8409,8410,8413],{},"理论上所有能用 ",[552,8411,8412],{},"sudo pmset -a GPUSwitch"," 切换显卡的型号都能用，欢迎大家补充自己的型号；",[177,8415,8416,8417,8420],{},"⚠️ ",[181,8418,8419],{},"只有核显的机器切勿尝试。"," 这是 README 里唯一用感叹号写的一句，我原样搬过来 —— 没有独显的机器上执行这条命令没有意义，也没人验证过会发生什么；",[177,8422,8423,8424,1509,8426,8429],{},"它只在 macOS 上有意义，依赖 ",[552,8425,8305],{},[552,8427,8428],{},"system_profiler"," 这两个系统命令。",[32,8431,2890],{"id":2890},[10,8433,8434],{},"开发环境是当年的那一套：Apache Maven 3.6、JDK 8、IntelliJ IDEA 2018.3。",[10,8436,8437],{},"当年 Oracle JDK 8 自带 JavaFX，不用另外下 SDK —— 这也是我选 JavaFX 而不是拉一堆第三方 UI 库的原因：对一个这么小的工具来说，零额外依赖比什么都重要。",[10,8439,8440,8441,8444,8445,8448],{},"跑起来很简单：clone 下来用 IDEA 打开，运行 ",[552,8442,8443],{},"cn.jxh.switcher.Main"," 就行。想打包成 macOS 的原生应用，pom 里已经配好了 JavaFX 的 Maven 插件（zenjava 8.8.3，应用名 ",[552,8446,8447],{},"MacProGPUSwitch","）。",[10,8450,8451,8452,8455,8456,8459,8460,8463],{},"版本号那处设计顺带提一句：",[552,8453,8454],{},"app.properties"," 里放的不是写死的数字，而是 ",[552,8457,8458],{},"${project.version}","，打包时由 Maven 的 resource filtering 替换成真实版本。改 pom 里的 ",[552,8461,8462],{},"\u003Cversion>"," 一处，界面「关于」里显示的版本号就跟着走。",[32,8465,8466],{"id":8466},"版本",[174,8468,8469,8475],{},[177,8470,8471,8474],{},[181,8472,8473],{},"1.0.0","（2019-02-10）：第一个版本，能读状态、能切换；",[177,8476,8477,8480],{},[181,8478,8479],{},"1.0.1","（2019-02-12）：加上 Root 密码验证，以及「记住密码」。",[10,8482,8483],{},"写完这篇的时候，正好是 1.0.1。",[32,8485,3248],{"id":3248},[10,8487,8488],{},"仓库在这里，Apache License 2.0：",[174,8490,8491],{},[177,8492,3256,8493],{},[94,8494,8495],{"href":8495,"rel":8496},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002FMacProGPUSwitch",[98],[10,8498,8499],{},"这是个很小的工具，解决的问题也很窄。但如果你也是 2018 款 MacBook Pro 的用户，被那块随时会醒过来的独显烦过，它可能正好是你要的那个开关。",[1981,8501,8502],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":291,"searchDepth":292,"depth":292,"links":8504},[8505,8506,8507,8513,8514,8515,8516,8517],{"id":8117,"depth":295,"text":8117},{"id":8135,"depth":295,"text":8135},{"id":8165,"depth":295,"text":8165,"children":8508},[8509,8510,8511,8512],{"id":8171,"depth":292,"text":8172},{"id":8237,"depth":292,"text":8238},{"id":8263,"depth":292,"text":8264},{"id":8327,"depth":292,"text":8327},{"id":8352,"depth":295,"text":8353},{"id":8396,"depth":295,"text":8397},{"id":2890,"depth":295,"text":2890},{"id":8466,"depth":295,"text":8466},{"id":3248,"depth":295,"text":3248},"2019-02-12","一个 JavaFX 写的小工具，把 macOS 里只能靠 sudo pmset 切换的显卡模式搬进了图形界面。这篇讲清它解决什么问题、三行核心代码怎么调 pmset，以及「记住密码」是怎么存下来的。",{},"\u002Fposts\u002Fmacpro-gpu-switch",{"title":8041,"description":8519},"posts\u002Fmacpro-gpu-switch",[3291,8525,8526,2282,2283],"JavaFX","macOS","U6I7kwzEWkjBx0gtZXPbqcmzLWn3SmGI4ZAHFQMmdgY",{"id":8529,"title":5618,"body":8530,"date":9464,"description":9465,"extension":303,"meta":9466,"navigation":305,"path":5628,"seo":9467,"stem":9468,"tags":9469,"__hash__":9470},"posts\u002Fposts\u002Fjar2pom-nexus2-dependency.md",{"type":7,"value":8531,"toc":9460},[8532,8541,9386,9390,9449,9452,9458],[8533,8534,8535],"blockquote",{},[10,8536,8537,8538,608],{},"此版本基于 Nexus OSS 2 相关接口。\n基于 Maven 中心库的版本请",[94,8539,8540],{"href":6871},"点这里",[546,8542,8544],{"className":3617,"code":8543,"language":2998,"meta":291,"style":291},"package com.jxh.tool;\n\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.apache.http.impl.client.HttpClients;\nimport org.apache.http.util.EntityUtils;\nimport org.dom4j.Document;\nimport org.dom4j.DocumentException;\nimport org.dom4j.DocumentHelper;\nimport org.dom4j.Element;\nimport org.dom4j.dom.DOMElement;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.util.jar.*;\n\n\u002F**\n * 通过Jar包SHA1或MD5生成Pom文件\n *\n * @author JiaXiaohei\n * @date 2018-03-16\n *\u002F\npublic class Jar2pom {\n\n    \u002F**\n     * Maven库\n     *\u002F\n    \u002F\u002Fpublic static final String nexusUrl = \"http:\u002F\u002Fmaven.aliyun.com\u002Fnexus\u002Fservice\u002Flocal\u002Flucene\u002Fsearch\";\n    public static final String nexusUrl = \"http:\u002F\u002F59.110.144.164\u002Fnexus\u002Fservice\u002Flocal\u002Flucene\u002Fsearch\";\n\n\n    public static void main(String[] args) {\n    \u002F\u002F先通过Jar的SHA1查询 如果不存在则解析Manifest查询\n        File libs = new File(\"D:\\\\lib\");\n        for (File jar : libs.listFiles()) {\n            System.out.println(\"\u003C!--  \" + jar.getName() + \" -->\");\n            if (!getPomByChecksum(jar).isTextOnly()) {\n                System.out.println(\"\u003C!--  Search by Checksum -->\");\n                System.out.println(getPomByChecksum(jar).asXML());\n            } else if (!getPomByManifest(jar).isTextOnly()) {\n                System.out.println(\"\u003C!--  Search by Manifest -->\");\n                System.out.println(getPomByManifest(jar).asXML());\n            } else {\n                System.out.println(\"\u003C!--  No data was found -->\");\n            }\n            System.out.println();\n        }\n\n    }\n\n    \u002F**\n     * 通过Jar SHA1返回Pom dependency\n     *\n     * @param file\n     * @return\n     *\u002F\n    public static Element getPomByChecksum(File file) {\n        String checkSum = getCheckSum(file, \"SHA1\");\n        String xml = doGet(nexusUrl + \"?sha1=\" + checkSum);\n        return assemblePomElement(xml);\n    }\n\n    \u002F**\n     * 通过Jar Manifest返回Pom dependency\n     *\n     * @param file\n     * @return\n     *\u002F\n    public static Element getPomByManifest(File file) {\n        try {\n            JarFile jarfile = new JarFile(file);\n            Manifest mainmanifest = jarfile.getManifest();\n            jarfile.close();\n            if (null == mainmanifest) {\n                return new DOMElement(\"dependency\");\n            }\n            String a = null, v = null;\n            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Extension-Name\"))) {\n                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Extension-Name\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Title\"))) {\n                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Title\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Title\"))) {\n                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Title\"));\n            }\n            if (a != null && a.length() != 0) {\n                a = a.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n            }\n            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Bundle-Version\"))) {\n                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Bundle-Version\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Version\"))) {\n                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Version\"));\n            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Version\"))) {\n                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Version\"));\n            }\n            if (v != null && v.length() != 0) {\n                v = v.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n            }\n            String xml = doGet(nexusUrl + \"?a=\" + a + \"&v=\" + v);\n            return assemblePomElement(xml);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return new DOMElement(\"dependency\");\n    }\n\n    \u002F**\n     * 解析获取的XML 组装dependency\n     *\n     * @param xml\n     * @return\n     *\u002F\n    public static Element assemblePomElement(String xml) {\n        Element dependency = new DOMElement(\"dependency\");\n\n        if (xml != null && xml.length() != 0) {\n            try {\n                Document document = DocumentHelper.parseText(xml);\n                Element dataElement = document.getRootElement().element(\"data\");\n                if (dataElement.getText() != null && dataElement.getText().length() != 0) {\n                    Element artifactElement = dataElement.element(\"artifact\");\n                    dependency.add((Element) artifactElement.element(\"groupId\").clone());\n                    dependency.add((Element) artifactElement.element(\"artifactId\").clone());\n                    dependency.add((Element) artifactElement.element(\"version\").clone());\n                }\n            } catch (DocumentException e) {\n                e.printStackTrace();\n            }\n        }\n        return dependency;\n    }\n\n    \u002F**\n     * 发起Get请求\n     *\n     * @param url\n     * @return\n     *\u002F\n    public static String doGet(String url) {\n        String srtResult = null;\n        CloseableHttpClient httpCilent = HttpClients.createDefault();\n        HttpGet httpGet = new HttpGet(url);\n        try {\n            HttpResponse httpResponse = httpCilent.execute(httpGet);\n            srtResult = EntityUtils.toString(httpResponse.getEntity());\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                httpCilent.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return srtResult;\n    }\n\n    \u002F**\n     * 计算CheckSum\n     *\n     * @param file\n     * @param algorithm SHA1 or MD5\n     * @return\n     *\u002F\n    public static String getCheckSum(File file, String algorithm) {\n        if (!file.isFile()) {\n            return null;\n        }\n        MessageDigest digest = null;\n        FileInputStream in = null;\n        byte buffer[] = new byte[1024];\n        int len;\n        try {\n            digest = MessageDigest.getInstance(algorithm);\n            in = new FileInputStream(file);\n            while ((len = in.read(buffer, 0, 1024)) != -1) {\n                digest.update(buffer, 0, len);\n            }\n            in.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n        BigInteger bigInt = new BigInteger(1, digest.digest());\n        return bigInt.toString(16);\n    }\n\n}\n",[552,8545,8546,8551,8555,8560,8565,8570,8575,8580,8585,8590,8595,8600,8605,8609,8614,8619,8624,8629,8634,8639,8643,8647,8652,8657,8662,8667,8671,8676,8680,8684,8689,8693,8698,8703,8707,8711,8715,8720,8724,8728,8732,8737,8741,8746,8751,8755,8760,8765,8770,8774,8778,8782,8786,8790,8794,8798,8802,8806,8810,8814,8818,8823,8827,8832,8837,8841,8845,8849,8853,8857,8861,8865,8869,8874,8878,8882,8886,8890,8894,8899,8903,8907,8911,8915,8919,8923,8927,8931,8935,8939,8943,8947,8951,8955,8959,8963,8967,8971,8975,8979,8983,8987,8992,8997,9001,9005,9009,9014,9018,9022,9026,9031,9035,9040,9044,9048,9053,9057,9061,9066,9070,9075,9080,9085,9090,9095,9100,9105,9109,9114,9118,9122,9126,9130,9134,9138,9142,9146,9150,9154,9158,9162,9166,9170,9174,9178,9182,9186,9190,9194,9198,9202,9206,9210,9214,9218,9222,9226,9230,9234,9238,9242,9246,9250,9254,9258,9262,9266,9271,9276,9281,9286,9291,9296,9301,9306,9311,9316,9321,9326,9331,9336,9341,9346,9351,9356,9361,9366,9371,9376,9381],{"__ignoreMap":291},[555,8547,8548],{"class":557,"line":558},[555,8549,8550],{},"package com.jxh.tool;\n",[555,8552,8553],{"class":557,"line":295},[555,8554,3685],{"emptyLinePlaceholder":305},[555,8556,8557],{"class":557,"line":292},[555,8558,8559],{},"import org.apache.http.HttpResponse;\n",[555,8561,8562],{"class":557,"line":923},[555,8563,8564],{},"import org.apache.http.client.methods.HttpGet;\n",[555,8566,8567],{"class":557,"line":1786},[555,8568,8569],{},"import org.apache.http.impl.client.CloseableHttpClient;\n",[555,8571,8572],{"class":557,"line":2519},[555,8573,8574],{},"import org.apache.http.impl.client.HttpClients;\n",[555,8576,8577],{"class":557,"line":2530},[555,8578,8579],{},"import org.apache.http.util.EntityUtils;\n",[555,8581,8582],{"class":557,"line":2536},[555,8583,8584],{},"import org.dom4j.Document;\n",[555,8586,8587],{"class":557,"line":2550},[555,8588,8589],{},"import org.dom4j.DocumentException;\n",[555,8591,8592],{"class":557,"line":2564},[555,8593,8594],{},"import org.dom4j.DocumentHelper;\n",[555,8596,8597],{"class":557,"line":2578},[555,8598,8599],{},"import org.dom4j.Element;\n",[555,8601,8602],{"class":557,"line":3374},[555,8603,8604],{},"import org.dom4j.dom.DOMElement;\n",[555,8606,8607],{"class":557,"line":3447},[555,8608,3685],{"emptyLinePlaceholder":305},[555,8610,8611],{"class":557,"line":3453},[555,8612,8613],{},"import java.io.File;\n",[555,8615,8616],{"class":557,"line":3459},[555,8617,8618],{},"import java.io.FileInputStream;\n",[555,8620,8621],{"class":557,"line":3465},[555,8622,8623],{},"import java.io.IOException;\n",[555,8625,8626],{"class":557,"line":3471},[555,8627,8628],{},"import java.math.BigInteger;\n",[555,8630,8631],{"class":557,"line":3477},[555,8632,8633],{},"import java.security.MessageDigest;\n",[555,8635,8636],{"class":557,"line":3482},[555,8637,8638],{},"import java.util.jar.*;\n",[555,8640,8641],{"class":557,"line":3488},[555,8642,3685],{"emptyLinePlaceholder":305},[555,8644,8645],{"class":557,"line":3493},[555,8646,3719],{},[555,8648,8649],{"class":557,"line":3727},[555,8650,8651],{}," * 通过Jar包SHA1或MD5生成Pom文件\n",[555,8653,8654],{"class":557,"line":3733},[555,8655,8656],{}," *\n",[555,8658,8659],{"class":557,"line":3739},[555,8660,8661],{}," * @author JiaXiaohei\n",[555,8663,8664],{"class":557,"line":3745},[555,8665,8666],{}," * @date 2018-03-16\n",[555,8668,8669],{"class":557,"line":3750},[555,8670,3730],{},[555,8672,8673],{"class":557,"line":3756},[555,8674,8675],{},"public class Jar2pom {\n",[555,8677,8678],{"class":557,"line":3761},[555,8679,3685],{"emptyLinePlaceholder":305},[555,8681,8682],{"class":557,"line":3767},[555,8683,3848],{},[555,8685,8686],{"class":557,"line":3773},[555,8687,8688],{},"     * Maven库\n",[555,8690,8691],{"class":557,"line":3778},[555,8692,3872],{},[555,8694,8695],{"class":557,"line":3783},[555,8696,8697],{},"    \u002F\u002Fpublic static final String nexusUrl = \"http:\u002F\u002Fmaven.aliyun.com\u002Fnexus\u002Fservice\u002Flocal\u002Flucene\u002Fsearch\";\n",[555,8699,8700],{"class":557,"line":3789},[555,8701,8702],{},"    public static final String nexusUrl = \"http:\u002F\u002F59.110.144.164\u002Fnexus\u002Fservice\u002Flocal\u002Flucene\u002Fsearch\";\n",[555,8704,8705],{"class":557,"line":3794},[555,8706,3685],{"emptyLinePlaceholder":305},[555,8708,8709],{"class":557,"line":3800},[555,8710,3685],{"emptyLinePlaceholder":305},[555,8712,8713],{"class":557,"line":3805},[555,8714,5673],{},[555,8716,8717],{"class":557,"line":3811},[555,8718,8719],{},"    \u002F\u002F先通过Jar的SHA1查询 如果不存在则解析Manifest查询\n",[555,8721,8722],{"class":557,"line":3816},[555,8723,5683],{},[555,8725,8726],{"class":557,"line":3822},[555,8727,5688],{},[555,8729,8730],{"class":557,"line":3828},[555,8731,5697],{},[555,8733,8734],{"class":557,"line":3834},[555,8735,8736],{},"            if (!getPomByChecksum(jar).isTextOnly()) {\n",[555,8738,8739],{"class":557,"line":3840},[555,8740,5716],{},[555,8742,8743],{"class":557,"line":3845},[555,8744,8745],{},"                System.out.println(getPomByChecksum(jar).asXML());\n",[555,8747,8748],{"class":557,"line":3851},[555,8749,8750],{},"            } else if (!getPomByManifest(jar).isTextOnly()) {\n",[555,8752,8753],{"class":557,"line":3857},[555,8754,5748],{},[555,8756,8757],{"class":557,"line":3863},[555,8758,8759],{},"                System.out.println(getPomByManifest(jar).asXML());\n",[555,8761,8762],{"class":557,"line":3869},[555,8763,8764],{},"            } else {\n",[555,8766,8767],{"class":557,"line":3875},[555,8768,8769],{},"                System.out.println(\"\u003C!--  No data was found -->\");\n",[555,8771,8772],{"class":557,"line":3881},[555,8773,4070],{},[555,8775,8776],{"class":557,"line":3887},[555,8777,5778],{},[555,8779,8780],{"class":557,"line":3892},[555,8781,4238],{},[555,8783,8784],{"class":557,"line":3897},[555,8785,3685],{"emptyLinePlaceholder":305},[555,8787,8788],{"class":557,"line":3902},[555,8789,3837],{},[555,8791,8792],{"class":557,"line":3908},[555,8793,3685],{"emptyLinePlaceholder":305},[555,8795,8796],{"class":557,"line":3913},[555,8797,3848],{},[555,8799,8800],{"class":557,"line":3918},[555,8801,5803],{},[555,8803,8804],{"class":557,"line":3923},[555,8805,3860],{},[555,8807,8808],{"class":557,"line":3929},[555,8809,5812],{},[555,8811,8812],{"class":557,"line":3935},[555,8813,3866],{},[555,8815,8816],{"class":557,"line":3940},[555,8817,3872],{},[555,8819,8820],{"class":557,"line":3945},[555,8821,8822],{},"    public static Element getPomByChecksum(File file) {\n",[555,8824,8825],{"class":557,"line":3950},[555,8826,5839],{},[555,8828,8829],{"class":557,"line":3956},[555,8830,8831],{},"        String xml = doGet(nexusUrl + \"?sha1=\" + checkSum);\n",[555,8833,8834],{"class":557,"line":3961},[555,8835,8836],{},"        return assemblePomElement(xml);\n",[555,8838,8839],{"class":557,"line":3967},[555,8840,3837],{},[555,8842,8843],{"class":557,"line":3972},[555,8844,3685],{"emptyLinePlaceholder":305},[555,8846,8847],{"class":557,"line":3978},[555,8848,3848],{},[555,8850,8851],{"class":557,"line":3984},[555,8852,5893],{},[555,8854,8855],{"class":557,"line":3990},[555,8856,3860],{},[555,8858,8859],{"class":557,"line":3996},[555,8860,5812],{},[555,8862,8863],{"class":557,"line":4002},[555,8864,3866],{},[555,8866,8867],{"class":557,"line":4008},[555,8868,3872],{},[555,8870,8871],{"class":557,"line":4014},[555,8872,8873],{},"    public static Element getPomByManifest(File file) {\n",[555,8875,8876],{"class":557,"line":4020},[555,8877,5927],{},[555,8879,8880],{"class":557,"line":4026},[555,8881,5932],{},[555,8883,8884],{"class":557,"line":4032},[555,8885,5937],{},[555,8887,8888],{"class":557,"line":4038},[555,8889,5942],{},[555,8891,8892],{"class":557,"line":4043},[555,8893,5947],{},[555,8895,8896],{"class":557,"line":4049},[555,8897,8898],{},"                return new DOMElement(\"dependency\");\n",[555,8900,8901],{"class":557,"line":4055},[555,8902,4070],{},[555,8904,8905],{"class":557,"line":4061},[555,8906,5961],{},[555,8908,8909],{"class":557,"line":4067},[555,8910,5966],{},[555,8912,8913],{"class":557,"line":4073},[555,8914,5971],{},[555,8916,8917],{"class":557,"line":4079},[555,8918,5976],{},[555,8920,8921],{"class":557,"line":4084},[555,8922,5981],{},[555,8924,8925],{"class":557,"line":4089},[555,8926,5986],{},[555,8928,8929],{"class":557,"line":4094},[555,8930,5991],{},[555,8932,8933],{"class":557,"line":4100},[555,8934,4070],{},[555,8936,8937],{"class":557,"line":4105},[555,8938,6000],{},[555,8940,8941],{"class":557,"line":4111},[555,8942,6005],{},[555,8944,8945],{"class":557,"line":4116},[555,8946,4070],{},[555,8948,8949],{"class":557,"line":4121},[555,8950,6014],{},[555,8952,8953],{"class":557,"line":4127},[555,8954,6019],{},[555,8956,8957],{"class":557,"line":4133},[555,8958,6024],{},[555,8960,8961],{"class":557,"line":4139},[555,8962,6029],{},[555,8964,8965],{"class":557,"line":4145},[555,8966,6034],{},[555,8968,8969],{"class":557,"line":4151},[555,8970,6039],{},[555,8972,8973],{"class":557,"line":4157},[555,8974,4070],{},[555,8976,8977],{"class":557,"line":4162},[555,8978,6048],{},[555,8980,8981],{"class":557,"line":4167},[555,8982,6053],{},[555,8984,8985],{"class":557,"line":4172},[555,8986,4070],{},[555,8988,8989],{"class":557,"line":4178},[555,8990,8991],{},"            String xml = doGet(nexusUrl + \"?a=\" + a + \"&v=\" + v);\n",[555,8993,8994],{"class":557,"line":4183},[555,8995,8996],{},"            return assemblePomElement(xml);\n",[555,8998,8999],{"class":557,"line":4189},[555,9000,6090],{},[555,9002,9003],{"class":557,"line":4195},[555,9004,6095],{},[555,9006,9007],{"class":557,"line":4200},[555,9008,4238],{},[555,9010,9011],{"class":557,"line":4205},[555,9012,9013],{},"        return new DOMElement(\"dependency\");\n",[555,9015,9016],{"class":557,"line":4211},[555,9017,3837],{},[555,9019,9020],{"class":557,"line":4217},[555,9021,3685],{"emptyLinePlaceholder":305},[555,9023,9024],{"class":557,"line":4223},[555,9025,3848],{},[555,9027,9028],{"class":557,"line":4229},[555,9029,9030],{},"     * 解析获取的XML 组装dependency\n",[555,9032,9033],{"class":557,"line":4235},[555,9034,3860],{},[555,9036,9037],{"class":557,"line":4241},[555,9038,9039],{},"     * @param xml\n",[555,9041,9042],{"class":557,"line":4247},[555,9043,3866],{},[555,9045,9046],{"class":557,"line":4252},[555,9047,3872],{},[555,9049,9050],{"class":557,"line":4257},[555,9051,9052],{},"    public static Element assemblePomElement(String xml) {\n",[555,9054,9055],{"class":557,"line":6200},[555,9056,6147],{},[555,9058,9059],{"class":557,"line":6206},[555,9060,3685],{"emptyLinePlaceholder":305},[555,9062,9063],{"class":557,"line":6211},[555,9064,9065],{},"        if (xml != null && xml.length() != 0) {\n",[555,9067,9068],{"class":557,"line":6216},[555,9069,6276],{},[555,9071,9072],{"class":557,"line":6222},[555,9073,9074],{},"                Document document = DocumentHelper.parseText(xml);\n",[555,9076,9077],{"class":557,"line":6228},[555,9078,9079],{},"                Element dataElement = document.getRootElement().element(\"data\");\n",[555,9081,9082],{"class":557,"line":6234},[555,9083,9084],{},"                if (dataElement.getText() != null && dataElement.getText().length() != 0) {\n",[555,9086,9087],{"class":557,"line":6240},[555,9088,9089],{},"                    Element artifactElement = dataElement.element(\"artifact\");\n",[555,9091,9092],{"class":557,"line":6245},[555,9093,9094],{},"                    dependency.add((Element) artifactElement.element(\"groupId\").clone());\n",[555,9096,9097],{"class":557,"line":6251},[555,9098,9099],{},"                    dependency.add((Element) artifactElement.element(\"artifactId\").clone());\n",[555,9101,9102],{"class":557,"line":6257},[555,9103,9104],{},"                    dependency.add((Element) artifactElement.element(\"version\").clone());\n",[555,9106,9107],{"class":557,"line":6262},[555,9108,4017],{},[555,9110,9111],{"class":557,"line":6267},[555,9112,9113],{},"            } catch (DocumentException e) {\n",[555,9115,9116],{"class":557,"line":6273},[555,9117,6294],{},[555,9119,9120],{"class":557,"line":6279},[555,9121,4070],{},[555,9123,9124],{"class":557,"line":6285},[555,9125,4238],{},[555,9127,9128],{"class":557,"line":6291},[555,9129,6176],{},[555,9131,9132],{"class":557,"line":6297},[555,9133,3837],{},[555,9135,9136],{"class":557,"line":6302},[555,9137,3685],{"emptyLinePlaceholder":305},[555,9139,9140],{"class":557,"line":6307},[555,9141,3848],{},[555,9143,9144],{"class":557,"line":6313},[555,9145,6193],{},[555,9147,9148],{"class":557,"line":6318},[555,9149,3860],{},[555,9151,9152],{"class":557,"line":6323},[555,9153,6203],{},[555,9155,9156],{"class":557,"line":6328},[555,9157,3866],{},[555,9159,9160],{"class":557,"line":6334},[555,9161,3872],{},[555,9163,9164],{"class":557,"line":6339},[555,9165,6219],{},[555,9167,9168],{"class":557,"line":6344},[555,9169,6225],{},[555,9171,9172],{"class":557,"line":6350},[555,9173,6231],{},[555,9175,9176],{"class":557,"line":6355},[555,9177,6237],{},[555,9179,9180],{"class":557,"line":6360},[555,9181,5927],{},[555,9183,9184],{"class":557,"line":6366},[555,9185,6248],{},[555,9187,9188],{"class":557,"line":6372},[555,9189,6254],{},[555,9191,9192],{"class":557,"line":6378},[555,9193,6090],{},[555,9195,9196],{"class":557,"line":6383},[555,9197,6095],{},[555,9199,9200],{"class":557,"line":6389},[555,9201,6270],{},[555,9203,9204],{"class":557,"line":6395},[555,9205,6276],{},[555,9207,9208],{"class":557,"line":6401},[555,9209,6282],{},[555,9211,9212],{"class":557,"line":6407},[555,9213,6288],{},[555,9215,9216],{"class":557,"line":6412},[555,9217,6294],{},[555,9219,9220],{"class":557,"line":6418},[555,9221,4070],{},[555,9223,9224],{"class":557,"line":6424},[555,9225,4238],{},[555,9227,9228],{"class":557,"line":6430},[555,9229,6310],{},[555,9231,9232],{"class":557,"line":6436},[555,9233,3837],{},[555,9235,9236],{"class":557,"line":6441},[555,9237,3685],{"emptyLinePlaceholder":305},[555,9239,9240],{"class":557,"line":6447},[555,9241,3848],{},[555,9243,9244],{"class":557,"line":6453},[555,9245,6331],{},[555,9247,9248],{"class":557,"line":6458},[555,9249,3860],{},[555,9251,9252],{"class":557,"line":6463},[555,9253,5812],{},[555,9255,9256],{"class":557,"line":6468},[555,9257,6347],{},[555,9259,9260],{"class":557,"line":6474},[555,9261,3866],{},[555,9263,9264],{"class":557,"line":6480},[555,9265,3872],{},[555,9267,9269],{"class":557,"line":9268},168,[555,9270,6363],{},[555,9272,9274],{"class":557,"line":9273},169,[555,9275,6369],{},[555,9277,9279],{"class":557,"line":9278},170,[555,9280,6375],{},[555,9282,9284],{"class":557,"line":9283},171,[555,9285,4238],{},[555,9287,9289],{"class":557,"line":9288},172,[555,9290,6386],{},[555,9292,9294],{"class":557,"line":9293},173,[555,9295,6392],{},[555,9297,9299],{"class":557,"line":9298},174,[555,9300,6398],{},[555,9302,9304],{"class":557,"line":9303},175,[555,9305,6404],{},[555,9307,9309],{"class":557,"line":9308},176,[555,9310,5927],{},[555,9312,9314],{"class":557,"line":9313},177,[555,9315,6415],{},[555,9317,9319],{"class":557,"line":9318},178,[555,9320,6421],{},[555,9322,9324],{"class":557,"line":9323},179,[555,9325,6427],{},[555,9327,9329],{"class":557,"line":9328},180,[555,9330,6433],{},[555,9332,9334],{"class":557,"line":9333},181,[555,9335,4070],{},[555,9337,9339],{"class":557,"line":9338},182,[555,9340,6444],{},[555,9342,9344],{"class":557,"line":9343},183,[555,9345,6450],{},[555,9347,9349],{"class":557,"line":9348},184,[555,9350,6095],{},[555,9352,9354],{"class":557,"line":9353},185,[555,9355,6375],{},[555,9357,9359],{"class":557,"line":9358},186,[555,9360,4238],{},[555,9362,9364],{"class":557,"line":9363},187,[555,9365,6471],{},[555,9367,9369],{"class":557,"line":9368},188,[555,9370,6477],{},[555,9372,9374],{"class":557,"line":9373},189,[555,9375,3837],{},[555,9377,9379],{"class":557,"line":9378},190,[555,9380,3685],{"emptyLinePlaceholder":305},[555,9382,9384],{"class":557,"line":9383},191,[555,9385,1058],{},[32,9387,9389],{"id":9388},"依赖-jar","依赖 Jar",[546,9391,9395],{"className":9392,"code":9393,"language":9394,"meta":291,"style":291},"language-xml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","        \u003Cdependency>\n            \u003CgroupId>org.apache.httpcomponents\u003C\u002FgroupId>\n            \u003CartifactId>httpclient\u003C\u002FartifactId>\n            \u003Cversion>4.5.5\u003C\u002Fversion>\n        \u003C\u002Fdependency>\n\n        \u003Cdependency>\n            \u003CgroupId>org.dom4j\u003C\u002FgroupId>\n            \u003CartifactId>dom4j\u003C\u002FartifactId>\n            \u003Cversion>[2.1.1,)\u003C\u002Fversion>\n        \u003C\u002Fdependency>\n","xml",[552,9396,9397,9402,9407,9412,9417,9422,9426,9430,9435,9440,9445],{"__ignoreMap":291},[555,9398,9399],{"class":557,"line":558},[555,9400,9401],{},"        \u003Cdependency>\n",[555,9403,9404],{"class":557,"line":295},[555,9405,9406],{},"            \u003CgroupId>org.apache.httpcomponents\u003C\u002FgroupId>\n",[555,9408,9409],{"class":557,"line":292},[555,9410,9411],{},"            \u003CartifactId>httpclient\u003C\u002FartifactId>\n",[555,9413,9414],{"class":557,"line":923},[555,9415,9416],{},"            \u003Cversion>4.5.5\u003C\u002Fversion>\n",[555,9418,9419],{"class":557,"line":1786},[555,9420,9421],{},"        \u003C\u002Fdependency>\n",[555,9423,9424],{"class":557,"line":2519},[555,9425,3685],{"emptyLinePlaceholder":305},[555,9427,9428],{"class":557,"line":2530},[555,9429,9401],{},[555,9431,9432],{"class":557,"line":2536},[555,9433,9434],{},"            \u003CgroupId>org.dom4j\u003C\u002FgroupId>\n",[555,9436,9437],{"class":557,"line":2550},[555,9438,9439],{},"            \u003CartifactId>dom4j\u003C\u002FartifactId>\n",[555,9441,9442],{"class":557,"line":2564},[555,9443,9444],{},"            \u003Cversion>[2.1.1,)\u003C\u002Fversion>\n",[555,9446,9447],{"class":557,"line":2578},[555,9448,9421],{},[32,9450,9451],{"id":9451},"开发环境",[546,9453,9456],{"className":9454,"code":9455,"language":3033},[3031],"jdk1.7.0_80\napache-maven-3.5.0\nIntelliJ IDEA Community Edition 2017.1.4 x64\n",[552,9457,9455],{"__ignoreMap":291},[1981,9459,3506],{},{"title":291,"searchDepth":292,"depth":292,"links":9461},[9462,9463],{"id":9388,"depth":295,"text":9389},{"id":9451,"depth":295,"text":9451},"2018-03-18","给非 Maven 项目的一堆本地 jar 生成 dependency。用 jar 的 SHA1 去 Nexus OSS 2 的 lucene 接口反查坐标，查不到再退回解析 Manifest。",{},{"title":5618,"description":9465},"posts\u002Fjar2pom-nexus2-dependency",[6875,3291,2283],"N6S1Bh1OtcM7yjACIQns3iLoKMfkeLyq1DuPPB856kg",{"id":9472,"title":9473,"body":9474,"date":9869,"description":9870,"extension":303,"meta":9871,"navigation":305,"path":9872,"seo":9873,"stem":9874,"tags":9875,"__hash__":9878},"posts\u002Fposts\u002Foracle-sort-positive-negative.md","ORACLE 正负数分开排序 SQL",{"type":7,"value":9475,"toc":9863},[9476,9479,9508,9511,9551,9621,9625,9645,9670,9747,9751,9769,9791,9861],[32,9477,9478],{"id":9478},"表结构",[546,9480,9482],{"className":2902,"code":9481,"language":2904,"meta":291,"style":291},"create table TEST_ORDER\n(\n  t_name  VARCHAR2(5),\n  t_value NUMBER(3)\n);\n",[552,9483,9484,9489,9494,9499,9504],{"__ignoreMap":291},[555,9485,9486],{"class":557,"line":558},[555,9487,9488],{},"create table TEST_ORDER\n",[555,9490,9491],{"class":557,"line":295},[555,9492,9493],{},"(\n",[555,9495,9496],{"class":557,"line":292},[555,9497,9498],{},"  t_name  VARCHAR2(5),\n",[555,9500,9501],{"class":557,"line":923},[555,9502,9503],{},"  t_value NUMBER(3)\n",[555,9505,9506],{"class":557,"line":1786},[555,9507,2961],{},[32,9509,9510],{"id":9510},"表数据",[546,9512,9514],{"className":2902,"code":9513,"language":2904,"meta":291,"style":291},"insert into TEST_ORDER (t_name, t_value) values ('A', 10);\ninsert into TEST_ORDER (t_name, t_value) values ('B', 30);\ninsert into TEST_ORDER (t_name, t_value) values ('C', 20);\ninsert into TEST_ORDER (t_name, t_value) values ('D', 40);\ninsert into TEST_ORDER (t_name, t_value) values ('E', -10);\ninsert into TEST_ORDER (t_name, t_value) values ('F', -30);\ninsert into TEST_ORDER (t_name, t_value) values ('G', -20);\n",[552,9515,9516,9521,9526,9531,9536,9541,9546],{"__ignoreMap":291},[555,9517,9518],{"class":557,"line":558},[555,9519,9520],{},"insert into TEST_ORDER (t_name, t_value) values ('A', 10);\n",[555,9522,9523],{"class":557,"line":295},[555,9524,9525],{},"insert into TEST_ORDER (t_name, t_value) values ('B', 30);\n",[555,9527,9528],{"class":557,"line":292},[555,9529,9530],{},"insert into TEST_ORDER (t_name, t_value) values ('C', 20);\n",[555,9532,9533],{"class":557,"line":923},[555,9534,9535],{},"insert into TEST_ORDER (t_name, t_value) values ('D', 40);\n",[555,9537,9538],{"class":557,"line":1786},[555,9539,9540],{},"insert into TEST_ORDER (t_name, t_value) values ('E', -10);\n",[555,9542,9543],{"class":557,"line":2519},[555,9544,9545],{},"insert into TEST_ORDER (t_name, t_value) values ('F', -30);\n",[555,9547,9548],{"class":557,"line":2530},[555,9549,9550],{},"insert into TEST_ORDER (t_name, t_value) values ('G', -20);\n",[61,9552,9553,9563],{},[64,9554,9555],{},[67,9556,9557,9560],{},[70,9558,9559],{},"T_NAME",[70,9561,9562],{},"T_VALUE",[80,9564,9565,9573,9581,9589,9597,9605,9613],{},[67,9566,9567,9570],{},[85,9568,9569],{},"A",[85,9571,9572],{},"10",[67,9574,9575,9578],{},[85,9576,9577],{},"B",[85,9579,9580],{},"30",[67,9582,9583,9586],{},[85,9584,9585],{},"C",[85,9587,9588],{},"20",[67,9590,9591,9594],{},[85,9592,9593],{},"D",[85,9595,9596],{},"40",[67,9598,9599,9602],{},[85,9600,9601],{},"E",[85,9603,9604],{},"-10",[67,9606,9607,9610],{},[85,9608,9609],{},"F",[85,9611,9612],{},"-30",[67,9614,9615,9618],{},[85,9616,9617],{},"G",[85,9619,9620],{},"-20",[32,9622,9624],{"id":9623},"先正后负-正数升序-负数降序","先正后负 正数升序 负数降序",[546,9626,9628],{"className":2902,"code":9627,"language":2904,"meta":291,"style":291},"SELECT T_NAME, T_VALUE\n  FROM TEST_ORDER\n ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC;\n",[552,9629,9630,9635,9640],{"__ignoreMap":291},[555,9631,9632],{"class":557,"line":558},[555,9633,9634],{},"SELECT T_NAME, T_VALUE\n",[555,9636,9637],{"class":557,"line":295},[555,9638,9639],{},"  FROM TEST_ORDER\n",[555,9641,9642],{"class":557,"line":292},[555,9643,9644],{}," ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC;\n",[546,9646,9648],{"className":2902,"code":9647,"language":2904,"meta":291,"style":291},"SELECT T_NAME,\n       T_VALUE,\n       RANK() OVER(ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC) AS RANKING\n  FROM TEST_ORDER;\n",[552,9649,9650,9655,9660,9665],{"__ignoreMap":291},[555,9651,9652],{"class":557,"line":558},[555,9653,9654],{},"SELECT T_NAME,\n",[555,9656,9657],{"class":557,"line":295},[555,9658,9659],{},"       T_VALUE,\n",[555,9661,9662],{"class":557,"line":292},[555,9663,9664],{},"       RANK() OVER(ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC) AS RANKING\n",[555,9666,9667],{"class":557,"line":923},[555,9668,9669],{},"  FROM TEST_ORDER;\n",[61,9671,9672,9683],{},[64,9673,9674],{},[67,9675,9676,9678,9680],{},[70,9677,9559],{},[70,9679,9562],{},[70,9681,9682],{},"RANKING",[80,9684,9685,9693,9702,9711,9720,9729,9738],{},[67,9686,9687,9689,9691],{},[85,9688,9569],{},[85,9690,9572],{},[85,9692,2867],{},[67,9694,9695,9697,9699],{},[85,9696,9585],{},[85,9698,9588],{},[85,9700,9701],{},"2",[67,9703,9704,9706,9708],{},[85,9705,9577],{},[85,9707,9580],{},[85,9709,9710],{},"3",[67,9712,9713,9715,9717],{},[85,9714,9593],{},[85,9716,9596],{},[85,9718,9719],{},"4",[67,9721,9722,9724,9726],{},[85,9723,9601],{},[85,9725,9604],{},[85,9727,9728],{},"5",[67,9730,9731,9733,9735],{},[85,9732,9617],{},[85,9734,9620],{},[85,9736,9737],{},"6",[67,9739,9740,9742,9744],{},[85,9741,9609],{},[85,9743,9612],{},[85,9745,9746],{},"7",[32,9748,9750],{"id":9749},"先正后负-正数降序-负数升序","先正后负 正数降序 负数升序",[546,9752,9754],{"className":2902,"code":9753,"language":2904,"meta":291,"style":291},"SELECT T_NAME, T_VALUE\n  FROM TEST_ORDER\n ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC;\n",[552,9755,9756,9760,9764],{"__ignoreMap":291},[555,9757,9758],{"class":557,"line":558},[555,9759,9634],{},[555,9761,9762],{"class":557,"line":295},[555,9763,9639],{},[555,9765,9766],{"class":557,"line":292},[555,9767,9768],{}," ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC;\n",[546,9770,9772],{"className":2902,"code":9771,"language":2904,"meta":291,"style":291},"SELECT T_NAME,\n       T_VALUE,\n       RANK() OVER(ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC) AS RANKING\n  FROM TEST_ORDER;\n",[552,9773,9774,9778,9782,9787],{"__ignoreMap":291},[555,9775,9776],{"class":557,"line":558},[555,9777,9654],{},[555,9779,9780],{"class":557,"line":295},[555,9781,9659],{},[555,9783,9784],{"class":557,"line":292},[555,9785,9786],{},"       RANK() OVER(ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC) AS RANKING\n",[555,9788,9789],{"class":557,"line":923},[555,9790,9669],{},[61,9792,9793,9803],{},[64,9794,9795],{},[67,9796,9797,9799,9801],{},[70,9798,9559],{},[70,9800,9562],{},[70,9802,9682],{},[80,9804,9805,9813,9821,9829,9837,9845,9853],{},[67,9806,9807,9809,9811],{},[85,9808,9593],{},[85,9810,9596],{},[85,9812,2867],{},[67,9814,9815,9817,9819],{},[85,9816,9577],{},[85,9818,9580],{},[85,9820,9701],{},[67,9822,9823,9825,9827],{},[85,9824,9585],{},[85,9826,9588],{},[85,9828,9710],{},[67,9830,9831,9833,9835],{},[85,9832,9569],{},[85,9834,9572],{},[85,9836,9719],{},[67,9838,9839,9841,9843],{},[85,9840,9609],{},[85,9842,9612],{},[85,9844,9728],{},[67,9846,9847,9849,9851],{},[85,9848,9617],{},[85,9850,9620],{},[85,9852,9737],{},[67,9854,9855,9857,9859],{},[85,9856,9601],{},[85,9858,9604],{},[85,9860,9746],{},[1981,9862,3506],{},{"title":291,"searchDepth":292,"depth":292,"links":9864},[9865,9866,9867,9868],{"id":9478,"depth":295,"text":9478},{"id":9510,"depth":295,"text":9510},{"id":9623,"depth":295,"text":9624},{"id":9749,"depth":295,"text":9750},"2017-09-12","用 SIGN() 把正数和负数分成两组，再各自按绝对值升序或降序排。附 RANK() 版本。",{},"\u002Fposts\u002Foracle-sort-positive-negative",{"title":9473,"description":9870},"posts\u002Foracle-sort-positive-negative",[9876,9877],"Oracle","SQL","M56SlfAqK0uBBScuf5UIwsQ--405GEtCa-zRbdbpBfo",{"id":9880,"title":9881,"body":9882,"date":11430,"description":11431,"extension":303,"meta":11432,"navigation":305,"path":11433,"seo":11434,"stem":11435,"tags":11436,"__hash__":11440},"posts\u002Fposts\u002Fcentos7-hadoop-spark-cluster-setup.md","CentOS7 下 Hadoop 2.7.3 + Spark 2.1.0 集群环境搭建",{"type":7,"value":9883,"toc":11393},[9884,9886,9935,9939,9995,9997,10018,10022,10035,10100,10104,10108,10128,10132,10135,10148,10157,10160,10172,10176,10219,10223,10254,10258,10278,10281,10285,10288,10304,10307,10341,10345,10348,10374,10378,10390,10393,10417,10421,10426,10521,10526,10541,10546,10573,10578,10631,10636,10721,10726,10742,10809,10814,10886,10890,10906,10910,10913,10917,10926,10930,10939,10943,10958,10962,10965,10977,10981,10993,10996,11019,11022,11026,11064,11069,11084,11148,11152,11164,11168,11171,11175,11184,11188,11190,11194,11197,11359,11361,11390],[32,9885,4301],{"id":4301},[61,9887,9888,9901],{},[64,9889,9890],{},[67,9891,9892,9895,9898],{},[70,9893,9894],{},"主机名",[70,9896,9897],{},"ip",[70,9899,9900],{},"进程",[80,9902,9903,9914,9925],{},[67,9904,9905,9908,9911],{},[85,9906,9907],{},"nn.hadoop.data.example.net",[85,9909,9910],{},"172.16.156.220",[85,9912,9913],{},"NameNode、Master、ResourceManager、SecondaryNameNode、JobHistoryServer",[67,9915,9916,9919,9922],{},[85,9917,9918],{},"dn1.hadoop.data.example.net",[85,9920,9921],{},"172.16.156.221",[85,9923,9924],{},"NodeManager、DataNode、Worker",[67,9926,9927,9930,9933],{},[85,9928,9929],{},"dn2.hadoop.data.example.net",[85,9931,9932],{},"172.16.156.222",[85,9934,9924],{},[1287,9936,9938],{"id":9937},"yum-安装如下包可能有部分包用不到","yum 安装如下包（可能有部分包用不到）",[546,9940,9942],{"className":889,"code":9941,"language":891,"meta":291,"style":291},"yum install pcre-devel openssl openssl-devel openssh-clients htop gcc zlib lrzsz zip unzip vim telnet-server ncurses wget net-tools\n",[552,9943,9944],{"__ignoreMap":291},[555,9945,9946,9948,9950,9953,9956,9959,9962,9965,9968,9971,9974,9977,9980,9983,9986,9989,9992],{"class":557,"line":558},[555,9947,5581],{"class":898},[555,9949,4522],{"class":592},[555,9951,9952],{"class":592}," pcre-devel",[555,9954,9955],{"class":592}," openssl",[555,9957,9958],{"class":592}," openssl-devel",[555,9960,9961],{"class":592}," openssh-clients",[555,9963,9964],{"class":592}," htop",[555,9966,9967],{"class":592}," gcc",[555,9969,9970],{"class":592}," zlib",[555,9972,9973],{"class":592}," lrzsz",[555,9975,9976],{"class":592}," zip",[555,9978,9979],{"class":592}," unzip",[555,9981,9982],{"class":592}," vim",[555,9984,9985],{"class":592}," telnet-server",[555,9987,9988],{"class":592}," ncurses",[555,9990,9991],{"class":592}," wget",[555,9993,9994],{"class":592}," net-tools\n",[1287,9996,2311],{"id":2311},[546,9998,10000],{"className":889,"code":9999,"language":891,"meta":291,"style":291},"systemctl stop firewalld.service\nsystemctl disable firewalld.service\n",[552,10001,10002,10010],{"__ignoreMap":291},[555,10003,10004,10006,10008],{"class":557,"line":558},[555,10005,2302],{"class":898},[555,10007,2323],{"class":592},[555,10009,2308],{"class":592},[555,10011,10012,10014,10016],{"class":557,"line":295},[555,10013,2302],{"class":898},[555,10015,2374],{"class":592},[555,10017,2308],{"class":592},[1287,10019,10021],{"id":10020},"配置-hosts-文件","配置 hosts 文件",[546,10023,10025],{"className":889,"code":10024,"language":891,"meta":291,"style":291},"vi \u002Fetc\u002Fhosts\n",[552,10026,10027],{"__ignoreMap":291},[555,10028,10029,10032],{"class":557,"line":558},[555,10030,10031],{"class":898},"vi",[555,10033,10034],{"class":592}," \u002Fetc\u002Fhosts\n",[546,10036,10038],{"className":889,"code":10037,"language":891,"meta":291,"style":291},"127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4\n::1         localhost localhost.localdomain localhost6 localhost6.localdomain6\n\n172.16.156.220  nn.hadoop.data.example.net\n172.16.156.221  dn1.hadoop.data.example.net\n172.16.156.222  dn2.hadoop.data.example.net\n",[552,10039,10040,10057,10075,10079,10086,10093],{"__ignoreMap":291},[555,10041,10042,10045,10048,10051,10054],{"class":557,"line":558},[555,10043,10044],{"class":898},"127.0.0.1",[555,10046,10047],{"class":592},"   localhost",[555,10049,10050],{"class":592}," localhost.localdomain",[555,10052,10053],{"class":592}," localhost4",[555,10055,10056],{"class":592}," localhost4.localdomain4\n",[555,10058,10059,10061,10064,10067,10069,10072],{"class":557,"line":295},[555,10060,2862],{"class":998},[555,10062,10063],{"class":592},":1",[555,10065,10066],{"class":592},"         localhost",[555,10068,10050],{"class":592},[555,10070,10071],{"class":592}," localhost6",[555,10073,10074],{"class":592}," localhost6.localdomain6\n",[555,10076,10077],{"class":557,"line":292},[555,10078,3685],{"emptyLinePlaceholder":305},[555,10080,10081,10083],{"class":557,"line":923},[555,10082,9910],{"class":898},[555,10084,10085],{"class":592},"  nn.hadoop.data.example.net\n",[555,10087,10088,10090],{"class":557,"line":1786},[555,10089,9921],{"class":898},[555,10091,10092],{"class":592},"  dn1.hadoop.data.example.net\n",[555,10094,10095,10097],{"class":557,"line":2519},[555,10096,9932],{"class":898},[555,10098,10099],{"class":592},"  dn2.hadoop.data.example.net\n",[32,10101,10103],{"id":10102},"安装-jdk-和-scala","安装 JDK 和 Scala",[1287,10105,10107],{"id":10106},"_0创建文件夹","0.创建文件夹",[546,10109,10111],{"className":889,"code":10110,"language":891,"meta":291,"style":291},"mkdir \u002Fapp\u002Fjava\nmkdir \u002Fapp\u002Fscala\n",[552,10112,10113,10121],{"__ignoreMap":291},[555,10114,10115,10118],{"class":557,"line":558},[555,10116,10117],{"class":898},"mkdir",[555,10119,10120],{"class":592}," \u002Fapp\u002Fjava\n",[555,10122,10123,10125],{"class":557,"line":295},[555,10124,10117],{"class":898},[555,10126,10127],{"class":592}," \u002Fapp\u002Fscala\n",[1287,10129,10131],{"id":10130},"_1下载","1.下载",[10,10133,10134],{},"下载 JDK",[546,10136,10138],{"className":889,"code":10137,"language":891,"meta":291,"style":291},"wget http:\u002F\u002Fdownload.oracle.com\u002Fotn-pub\u002Fjava\u002Fjdk\u002F8u121-b13\u002Fe9e7ea248e2c4826b92b3f075a80e441\u002Fjdk-8u121-linux-x64.tar.gz\n",[552,10139,10140],{"__ignoreMap":291},[555,10141,10142,10145],{"class":557,"line":558},[555,10143,10144],{"class":898},"wget",[555,10146,10147],{"class":592}," http:\u002F\u002Fdownload.oracle.com\u002Fotn-pub\u002Fjava\u002Fjdk\u002F8u121-b13\u002Fe9e7ea248e2c4826b92b3f075a80e441\u002Fjdk-8u121-linux-x64.tar.gz\n",[10,10149,10150,10151,10156],{},"如果失效，",[94,10152,10155],{"href":10153,"rel":10154},"http:\u002F\u002Fwww.oracle.com\u002Ftechnetwork\u002Fjava\u002Fjavase\u002Fdownloads\u002Findex.html",[98],"点这里下载"," 并上传至服务器",[10,10158,10159],{},"下载 Scala",[546,10161,10163],{"className":889,"code":10162,"language":891,"meta":291,"style":291},"wget http:\u002F\u002Fdownloads.lightbend.com\u002Fscala\u002F2.12.1\u002Fscala-2.12.1.tgz\n",[552,10164,10165],{"__ignoreMap":291},[555,10166,10167,10169],{"class":557,"line":558},[555,10168,10144],{"class":898},[555,10170,10171],{"class":592}," http:\u002F\u002Fdownloads.lightbend.com\u002Fscala\u002F2.12.1\u002Fscala-2.12.1.tgz\n",[1287,10173,10175],{"id":10174},"_2移动解压","2.移动&解压",[546,10177,10179],{"className":889,"code":10178,"language":891,"meta":291,"style":291},"mv jdk-8u121-linux-x64.tar.gz \u002Fapp\u002Fjava\ntar -zxvf jdk-8u121-linux-x64.tar.gz\nmv scala-2.12.1.tgz \u002Fapp\u002Fscala\ntar -zxvf scala-2.12.1.tgz\n",[552,10180,10181,10190,10201,10210],{"__ignoreMap":291},[555,10182,10183,10185,10188],{"class":557,"line":558},[555,10184,4576],{"class":898},[555,10186,10187],{"class":592}," jdk-8u121-linux-x64.tar.gz",[555,10189,10120],{"class":592},[555,10191,10192,10195,10198],{"class":557,"line":295},[555,10193,10194],{"class":898},"tar",[555,10196,10197],{"class":592}," -zxvf",[555,10199,10200],{"class":592}," jdk-8u121-linux-x64.tar.gz\n",[555,10202,10203,10205,10208],{"class":557,"line":292},[555,10204,4576],{"class":898},[555,10206,10207],{"class":592}," scala-2.12.1.tgz",[555,10209,10127],{"class":592},[555,10211,10212,10214,10216],{"class":557,"line":923},[555,10213,10194],{"class":898},[555,10215,10197],{"class":592},[555,10217,10218],{"class":592}," scala-2.12.1.tgz\n",[1287,10220,10222],{"id":10221},"_3授权","3.授权",[546,10224,10226],{"className":889,"code":10225,"language":891,"meta":291,"style":291},"chmod -R 775 \u002Fapp\u002F\nchown -R hadoop \u002Fapp\u002F\n",[552,10227,10228,10242],{"__ignoreMap":291},[555,10229,10230,10233,10236,10239],{"class":557,"line":558},[555,10231,10232],{"class":898},"chmod",[555,10234,10235],{"class":592}," -R",[555,10237,10238],{"class":941}," 775",[555,10240,10241],{"class":592}," \u002Fapp\u002F\n",[555,10243,10244,10247,10249,10252],{"class":557,"line":295},[555,10245,10246],{"class":898},"chown",[555,10248,10235],{"class":592},[555,10250,10251],{"class":592}," hadoop",[555,10253,10241],{"class":592},[32,10255,10257],{"id":10256},"创建-hadoop-用户","创建 hadoop 用户",[546,10259,10261],{"className":889,"code":10260,"language":891,"meta":291,"style":291},"useradd hadoop\npasswd hadoop\n",[552,10262,10263,10271],{"__ignoreMap":291},[555,10264,10265,10268],{"class":557,"line":558},[555,10266,10267],{"class":898},"useradd",[555,10269,10270],{"class":592}," hadoop\n",[555,10272,10273,10276],{"class":557,"line":295},[555,10274,10275],{"class":898},"passwd",[555,10277,10270],{"class":592},[10,10279,10280],{},"如无特殊说明 以后均为 hadoop 用户操作",[32,10282,10284],{"id":10283},"ssh-免密登录","SSH 免密登录",[10,10286,10287],{},"生成秘钥：~\u002F.ssh\u002Fid_rsa 和 ~\u002F.ssh\u002Fid_rsa.pub",[546,10289,10291],{"className":889,"code":10290,"language":891,"meta":291,"style":291},"ssh-keygen -t rsa \n",[552,10292,10293],{"__ignoreMap":291},[555,10294,10295,10298,10301],{"class":557,"line":558},[555,10296,10297],{"class":898},"ssh-keygen",[555,10299,10300],{"class":592}," -t",[555,10302,10303],{"class":592}," rsa\n",[10,10305,10306],{},"拷贝公钥到其他机器上",[546,10308,10310],{"className":889,"code":10309,"language":891,"meta":291,"style":291},"ssh-copy-id -i nn.hadoop.data.example.net\nssh-copy-id -i dn1.hadoop.data.example.net\nssh-copy-id -i dn2.hadoop.data.example.net\n",[552,10311,10312,10323,10332],{"__ignoreMap":291},[555,10313,10314,10317,10320],{"class":557,"line":558},[555,10315,10316],{"class":898},"ssh-copy-id",[555,10318,10319],{"class":592}," -i",[555,10321,10322],{"class":592}," nn.hadoop.data.example.net\n",[555,10324,10325,10327,10329],{"class":557,"line":295},[555,10326,10316],{"class":898},[555,10328,10319],{"class":592},[555,10330,10331],{"class":592}," dn1.hadoop.data.example.net\n",[555,10333,10334,10336,10338],{"class":557,"line":292},[555,10335,10316],{"class":898},[555,10337,10319],{"class":592},[555,10339,10340],{"class":592}," dn2.hadoop.data.example.net\n",[32,10342,10344],{"id":10343},"安装-hadoop","安装 Hadoop",[1287,10346,10107],{"id":10347},"_0创建文件夹-1",[546,10349,10351],{"className":889,"code":10350,"language":891,"meta":291,"style":291},"mkdir \u002Fapp\u002Fhadoop\u002Fdata\nmkdir \u002Fapp\u002Fhadoop\u002Fname\nmkdir \u002Fapp\u002Fhadoop\u002Ftmp\n",[552,10352,10353,10360,10367],{"__ignoreMap":291},[555,10354,10355,10357],{"class":557,"line":558},[555,10356,10117],{"class":898},[555,10358,10359],{"class":592}," \u002Fapp\u002Fhadoop\u002Fdata\n",[555,10361,10362,10364],{"class":557,"line":295},[555,10363,10117],{"class":898},[555,10365,10366],{"class":592}," \u002Fapp\u002Fhadoop\u002Fname\n",[555,10368,10369,10371],{"class":557,"line":292},[555,10370,10117],{"class":898},[555,10372,10373],{"class":592}," \u002Fapp\u002Fhadoop\u002Ftmp\n",[1287,10375,10377],{"id":10376},"_1下载-hadoop","1.下载 hadoop",[546,10379,10381],{"className":889,"code":10380,"language":891,"meta":291,"style":291},"wget http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.cgi\u002Fhadoop\u002Fcommon\u002Fhadoop-2.7.3\u002Fhadoop-2.7.3.tar.gz\n",[552,10382,10383],{"__ignoreMap":291},[555,10384,10385,10387],{"class":557,"line":558},[555,10386,10144],{"class":898},[555,10388,10389],{"class":592}," http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.cgi\u002Fhadoop\u002Fcommon\u002Fhadoop-2.7.3\u002Fhadoop-2.7.3.tar.gz\n",[1287,10391,10175],{"id":10392},"_2移动解压-1",[546,10394,10396],{"className":889,"code":10395,"language":891,"meta":291,"style":291},"mv hadoop-2.7.3.tar.gz \u002Fapp\u002Fhadoop\ntar -zxvf hadoop-2.7.3.tar.gz\n",[552,10397,10398,10408],{"__ignoreMap":291},[555,10399,10400,10402,10405],{"class":557,"line":558},[555,10401,4576],{"class":898},[555,10403,10404],{"class":592}," hadoop-2.7.3.tar.gz",[555,10406,10407],{"class":592}," \u002Fapp\u002Fhadoop\n",[555,10409,10410,10412,10414],{"class":557,"line":295},[555,10411,10194],{"class":898},[555,10413,10197],{"class":592},[555,10415,10416],{"class":592}," hadoop-2.7.3.tar.gz\n",[1287,10418,10420],{"id":10419},"_3修改配置文件","3.修改配置文件",[10,10422,10423],{},[181,10424,10425],{},"\u002Fetc\u002Fprofile（root 权限）",[546,10427,10429],{"className":889,"code":10428,"language":891,"meta":291,"style":291},"HADOOP_HOME=\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\nexport HADOOP_INSTALL=$HADOOP_HOME\nexport HADOOP_MAPRED_HOME=$HADOOP_HOME\nexport HADOOP_COMMON_HOME=$HADOOP_HOME\nexport HADOOP_HDFS_HOME=$HADOOP_HOME\nexport YARN_HOME=$HADOOP_HOME\nexport HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME\u002Flib\u002Fnative\nexport PATH=$PATH:$HADOOP_HOME\u002Fsbin:$HADOOP_HOME\u002Fbin\n",[552,10430,10431,10441,10453,10464,10475,10486,10497,10509],{"__ignoreMap":291},[555,10432,10433,10436,10438],{"class":557,"line":558},[555,10434,10435],{"class":569},"HADOOP_HOME",[555,10437,661],{"class":565},[555,10439,10440],{"class":592},"\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\n",[555,10442,10443,10445,10448,10450],{"class":557,"line":295},[555,10444,1803],{"class":561},[555,10446,10447],{"class":569}," HADOOP_INSTALL",[555,10449,661],{"class":565},[555,10451,10452],{"class":569},"$HADOOP_HOME\n",[555,10454,10455,10457,10460,10462],{"class":557,"line":292},[555,10456,1803],{"class":561},[555,10458,10459],{"class":569}," HADOOP_MAPRED_HOME",[555,10461,661],{"class":565},[555,10463,10452],{"class":569},[555,10465,10466,10468,10471,10473],{"class":557,"line":923},[555,10467,1803],{"class":561},[555,10469,10470],{"class":569}," HADOOP_COMMON_HOME",[555,10472,661],{"class":565},[555,10474,10452],{"class":569},[555,10476,10477,10479,10482,10484],{"class":557,"line":1786},[555,10478,1803],{"class":561},[555,10480,10481],{"class":569}," HADOOP_HDFS_HOME",[555,10483,661],{"class":565},[555,10485,10452],{"class":569},[555,10487,10488,10490,10493,10495],{"class":557,"line":2519},[555,10489,1803],{"class":561},[555,10491,10492],{"class":569}," YARN_HOME",[555,10494,661],{"class":565},[555,10496,10452],{"class":569},[555,10498,10499,10501,10504,10506],{"class":557,"line":2530},[555,10500,1803],{"class":561},[555,10502,10503],{"class":569}," HADOOP_COMMON_LIB_NATIVE_DIR",[555,10505,661],{"class":565},[555,10507,10508],{"class":569},"$HADOOP_HOME\u002Flib\u002Fnative\n",[555,10510,10511,10513,10516,10518],{"class":557,"line":2536},[555,10512,1803],{"class":561},[555,10514,10515],{"class":569}," PATH",[555,10517,661],{"class":565},[555,10519,10520],{"class":569},"$PATH:$HADOOP_HOME\u002Fsbin:$HADOOP_HOME\u002Fbin\n",[10,10522,10523],{},[181,10524,10525],{},"slaves",[546,10527,10529],{"className":889,"code":10528,"language":891,"meta":291,"style":291},"dn1.hadoop.data.example.net\ndn2.hadoop.data.example.net\n",[552,10530,10531,10536],{"__ignoreMap":291},[555,10532,10533],{"class":557,"line":558},[555,10534,10535],{"class":898},"dn1.hadoop.data.example.net\n",[555,10537,10538],{"class":557,"line":295},[555,10539,10540],{"class":898},"dn2.hadoop.data.example.net\n",[10,10542,10543],{},[181,10544,10545],{},"hadoop-env.sh",[546,10547,10549],{"className":889,"code":10548,"language":891,"meta":291,"style":291},"# export JAVA_HOME=${JAVA_HOME}\n改为\nexport JAVA_HOME=\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\u002F\n",[552,10550,10551,10556,10561],{"__ignoreMap":291},[555,10552,10553],{"class":557,"line":558},[555,10554,10555],{"class":905},"# export JAVA_HOME=${JAVA_HOME}\n",[555,10557,10558],{"class":557,"line":295},[555,10559,10560],{"class":898},"改为\n",[555,10562,10563,10565,10568,10570],{"class":557,"line":292},[555,10564,1803],{"class":561},[555,10566,10567],{"class":569}," JAVA_HOME",[555,10569,661],{"class":565},[555,10571,10572],{"class":569},"\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\u002F\n",[10,10574,10575],{},[181,10576,10577],{},"core-site.xml",[546,10579,10581],{"className":9392,"code":10580,"language":9394,"meta":291,"style":291},"\u003Cconfiguration>\n    \u003Cproperty>\n    \u003Cname>fs.defaultFS\u003C\u002Fname>\n    \u003Cvalue>hdfs:\u002F\u002Fnn.hadoop.data.example.net:9000\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n    \u003Cname>hadoop.tmp.dir\u003C\u002Fname>\n    \u003Cvalue>\u002Fapp\u002Fhadoop\u002Ftmp\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n\u003C\u002Fconfiguration>\n",[552,10582,10583,10588,10593,10598,10603,10608,10612,10617,10622,10626],{"__ignoreMap":291},[555,10584,10585],{"class":557,"line":558},[555,10586,10587],{},"\u003Cconfiguration>\n",[555,10589,10590],{"class":557,"line":295},[555,10591,10592],{},"    \u003Cproperty>\n",[555,10594,10595],{"class":557,"line":292},[555,10596,10597],{},"    \u003Cname>fs.defaultFS\u003C\u002Fname>\n",[555,10599,10600],{"class":557,"line":923},[555,10601,10602],{},"    \u003Cvalue>hdfs:\u002F\u002Fnn.hadoop.data.example.net:9000\u003C\u002Fvalue>\n",[555,10604,10605],{"class":557,"line":1786},[555,10606,10607],{},"    \u003C\u002Fproperty>\n",[555,10609,10610],{"class":557,"line":2519},[555,10611,10592],{},[555,10613,10614],{"class":557,"line":2530},[555,10615,10616],{},"    \u003Cname>hadoop.tmp.dir\u003C\u002Fname>\n",[555,10618,10619],{"class":557,"line":2536},[555,10620,10621],{},"    \u003Cvalue>\u002Fapp\u002Fhadoop\u002Ftmp\u003C\u002Fvalue>\n",[555,10623,10624],{"class":557,"line":2550},[555,10625,10607],{},[555,10627,10628],{"class":557,"line":2564},[555,10629,10630],{},"\u003C\u002Fconfiguration>\n",[10,10632,10633],{},[181,10634,10635],{},"hdfs-site.xml",[546,10637,10639],{"className":9392,"code":10638,"language":9394,"meta":291,"style":291},"\u003Cconfiguration>\n    \u003Cproperty>\n        \u003Cname>dfs.namenode.secondary.http-address\u003C\u002Fname>\n        \u003Cvalue>nn.hadoop.data.example.net:50090\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n        \u003Cname>dfs.namenode.name.dir\u003C\u002Fname>\n        \u003Cvalue>\u002Fapp\u002Fhadoop\u002Fname\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n        \u003Cname>dfs.replication\u003C\u002Fname>\n        \u003Cvalue>1\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n        \u003Cname>dfs.datanode.data.dir\u003C\u002Fname>\n        \u003Cvalue>\u002Fapp\u002Fhadoop\u002Fdata\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n\u003C\u002Fconfiguration>\n",[552,10640,10641,10645,10649,10654,10659,10663,10667,10672,10677,10681,10685,10690,10695,10699,10703,10708,10713,10717],{"__ignoreMap":291},[555,10642,10643],{"class":557,"line":558},[555,10644,10587],{},[555,10646,10647],{"class":557,"line":295},[555,10648,10592],{},[555,10650,10651],{"class":557,"line":292},[555,10652,10653],{},"        \u003Cname>dfs.namenode.secondary.http-address\u003C\u002Fname>\n",[555,10655,10656],{"class":557,"line":923},[555,10657,10658],{},"        \u003Cvalue>nn.hadoop.data.example.net:50090\u003C\u002Fvalue>\n",[555,10660,10661],{"class":557,"line":1786},[555,10662,10607],{},[555,10664,10665],{"class":557,"line":2519},[555,10666,10592],{},[555,10668,10669],{"class":557,"line":2530},[555,10670,10671],{},"        \u003Cname>dfs.namenode.name.dir\u003C\u002Fname>\n",[555,10673,10674],{"class":557,"line":2536},[555,10675,10676],{},"        \u003Cvalue>\u002Fapp\u002Fhadoop\u002Fname\u003C\u002Fvalue>\n",[555,10678,10679],{"class":557,"line":2550},[555,10680,10607],{},[555,10682,10683],{"class":557,"line":2564},[555,10684,10592],{},[555,10686,10687],{"class":557,"line":2578},[555,10688,10689],{},"        \u003Cname>dfs.replication\u003C\u002Fname>\n",[555,10691,10692],{"class":557,"line":3374},[555,10693,10694],{},"        \u003Cvalue>1\u003C\u002Fvalue>\n",[555,10696,10697],{"class":557,"line":3447},[555,10698,10607],{},[555,10700,10701],{"class":557,"line":3453},[555,10702,10592],{},[555,10704,10705],{"class":557,"line":3459},[555,10706,10707],{},"        \u003Cname>dfs.datanode.data.dir\u003C\u002Fname>\n",[555,10709,10710],{"class":557,"line":3465},[555,10711,10712],{},"        \u003Cvalue>\u002Fapp\u002Fhadoop\u002Fdata\u003C\u002Fvalue>\n",[555,10714,10715],{"class":557,"line":3471},[555,10716,10607],{},[555,10718,10719],{"class":557,"line":3477},[555,10720,10630],{},[10,10722,10723],{},[181,10724,10725],{},"mapred-site.xml",[546,10727,10729],{"className":889,"code":10728,"language":891,"meta":291,"style":291},"cp mapred-site.xml.template mapred-site.xml\n",[552,10730,10731],{"__ignoreMap":291},[555,10732,10733,10736,10739],{"class":557,"line":558},[555,10734,10735],{"class":898},"cp",[555,10737,10738],{"class":592}," mapred-site.xml.template",[555,10740,10741],{"class":592}," mapred-site.xml\n",[546,10743,10745],{"className":9392,"code":10744,"language":9394,"meta":291,"style":291},"\u003Cconfiguration>\n    \u003Cproperty>\n        \u003Cname>mapreduce.framework.name\u003C\u002Fname>\n        \u003Cvalue>yarn\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n        \u003Cname>mapreduce.jobhistory.address\u003C\u002Fname>\n        \u003Cvalue>nn.hadoop.data.example.net:10020\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n        \u003Cname>mapreduce.jobhistory.webapp.address\u003C\u002Fname>\n        \u003Cvalue>nn.hadoop.data.example.net:19888\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n\u003C\u002Fconfiguration>\n",[552,10746,10747,10751,10755,10760,10765,10769,10773,10778,10783,10787,10791,10796,10801,10805],{"__ignoreMap":291},[555,10748,10749],{"class":557,"line":558},[555,10750,10587],{},[555,10752,10753],{"class":557,"line":295},[555,10754,10592],{},[555,10756,10757],{"class":557,"line":292},[555,10758,10759],{},"        \u003Cname>mapreduce.framework.name\u003C\u002Fname>\n",[555,10761,10762],{"class":557,"line":923},[555,10763,10764],{},"        \u003Cvalue>yarn\u003C\u002Fvalue>\n",[555,10766,10767],{"class":557,"line":1786},[555,10768,10607],{},[555,10770,10771],{"class":557,"line":2519},[555,10772,10592],{},[555,10774,10775],{"class":557,"line":2530},[555,10776,10777],{},"        \u003Cname>mapreduce.jobhistory.address\u003C\u002Fname>\n",[555,10779,10780],{"class":557,"line":2536},[555,10781,10782],{},"        \u003Cvalue>nn.hadoop.data.example.net:10020\u003C\u002Fvalue>\n",[555,10784,10785],{"class":557,"line":2550},[555,10786,10607],{},[555,10788,10789],{"class":557,"line":2564},[555,10790,10592],{},[555,10792,10793],{"class":557,"line":2578},[555,10794,10795],{},"        \u003Cname>mapreduce.jobhistory.webapp.address\u003C\u002Fname>\n",[555,10797,10798],{"class":557,"line":3374},[555,10799,10800],{},"        \u003Cvalue>nn.hadoop.data.example.net:19888\u003C\u002Fvalue>\n",[555,10802,10803],{"class":557,"line":3447},[555,10804,10607],{},[555,10806,10807],{"class":557,"line":3453},[555,10808,10630],{},[10,10810,10811],{},[181,10812,10813],{},"yarn-site.xml",[546,10815,10817],{"className":9392,"code":10816,"language":9394,"meta":291,"style":291},"\u003Cconfiguration>\n\u003C!-- Site specific YARN configuration properties -->\n    \u003Cproperty>\n        \u003Cname>yarn.resourcemanager.hostname\u003C\u002Fname>\n        \u003Cvalue>nn.hadoop.data.example.net\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n        \u003Cname>yarn.nodemanager.aux-services\u003C\u002Fname>\n        \u003Cvalue>mapreduce_shuffle\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n    \u003Cproperty>\n         \u003Cname>yarn.nodemanager.aux-services.mapreduce.shuffle.class\u003C\u002Fname>\n         \u003Cvalue>org.apache.hadoop.mapred.ShuffleHandler\u003C\u002Fvalue>\n    \u003C\u002Fproperty>\n\u003C\u002Fconfiguration>\n",[552,10818,10819,10823,10828,10832,10837,10842,10846,10850,10855,10860,10864,10868,10873,10878,10882],{"__ignoreMap":291},[555,10820,10821],{"class":557,"line":558},[555,10822,10587],{},[555,10824,10825],{"class":557,"line":295},[555,10826,10827],{},"\u003C!-- Site specific YARN configuration properties -->\n",[555,10829,10830],{"class":557,"line":292},[555,10831,10592],{},[555,10833,10834],{"class":557,"line":923},[555,10835,10836],{},"        \u003Cname>yarn.resourcemanager.hostname\u003C\u002Fname>\n",[555,10838,10839],{"class":557,"line":1786},[555,10840,10841],{},"        \u003Cvalue>nn.hadoop.data.example.net\u003C\u002Fvalue>\n",[555,10843,10844],{"class":557,"line":2519},[555,10845,10607],{},[555,10847,10848],{"class":557,"line":2530},[555,10849,10592],{},[555,10851,10852],{"class":557,"line":2536},[555,10853,10854],{},"        \u003Cname>yarn.nodemanager.aux-services\u003C\u002Fname>\n",[555,10856,10857],{"class":557,"line":2550},[555,10858,10859],{},"        \u003Cvalue>mapreduce_shuffle\u003C\u002Fvalue>\n",[555,10861,10862],{"class":557,"line":2564},[555,10863,10607],{},[555,10865,10866],{"class":557,"line":2578},[555,10867,10592],{},[555,10869,10870],{"class":557,"line":3374},[555,10871,10872],{},"         \u003Cname>yarn.nodemanager.aux-services.mapreduce.shuffle.class\u003C\u002Fname>\n",[555,10874,10875],{"class":557,"line":3447},[555,10876,10877],{},"         \u003Cvalue>org.apache.hadoop.mapred.ShuffleHandler\u003C\u002Fvalue>\n",[555,10879,10880],{"class":557,"line":3453},[555,10881,10607],{},[555,10883,10884],{"class":557,"line":3459},[555,10885,10630],{},[1287,10887,10889],{"id":10888},"_4格式化-namenode","4.格式化 namenode",[546,10891,10893],{"className":889,"code":10892,"language":891,"meta":291,"style":291},"hadoop namenode -format\n",[552,10894,10895],{"__ignoreMap":291},[555,10896,10897,10900,10903],{"class":557,"line":558},[555,10898,10899],{"class":898},"hadoop",[555,10901,10902],{"class":592}," namenode",[555,10904,10905],{"class":592}," -format\n",[1287,10907,10909],{"id":10908},"_5复制文件到其他机器","5.复制文件到其他机器",[10,10911,10912],{},"将 \u002Fapp\u002Fhadoop（包括 data、name、tmp 和配置好的 hadoop）复制到其他机器。",[1287,10914,10916],{"id":10915},"_6启动-dfs","6.启动 dfs",[546,10918,10920],{"className":889,"code":10919,"language":891,"meta":291,"style":291},"start-dfs.sh\n",[552,10921,10922],{"__ignoreMap":291},[555,10923,10924],{"class":557,"line":558},[555,10925,10919],{"class":898},[1287,10927,10929],{"id":10928},"_7启动-yarn","7.启动 yarn",[546,10931,10933],{"className":889,"code":10932,"language":891,"meta":291,"style":291},"start-yarn.sh\n",[552,10934,10935],{"__ignoreMap":291},[555,10936,10937],{"class":557,"line":558},[555,10938,10932],{"class":898},[1287,10940,10942],{"id":10941},"_8启动-jobhistory","8.启动 jobhistory",[546,10944,10946],{"className":889,"code":10945,"language":891,"meta":291,"style":291},"mr-jobhistory-daemon.sh start historyserver\n",[552,10947,10948],{"__ignoreMap":291},[555,10949,10950,10953,10955],{"class":557,"line":558},[555,10951,10952],{"class":898},"mr-jobhistory-daemon.sh",[555,10954,2305],{"class":592},[555,10956,10957],{"class":592}," historyserver\n",[32,10959,10961],{"id":10960},"安装-spark2","安装 Spark2",[1287,10963,10107],{"id":10964},"_0创建文件夹-2",[546,10966,10968],{"className":889,"code":10967,"language":891,"meta":291,"style":291},"mkdir \u002Fapp\u002Fspark\n",[552,10969,10970],{"__ignoreMap":291},[555,10971,10972,10974],{"class":557,"line":558},[555,10973,10117],{"class":898},[555,10975,10976],{"class":592}," \u002Fapp\u002Fspark\n",[1287,10978,10980],{"id":10979},"_1下载-spark2","1.下载 Spark2",[546,10982,10984],{"className":889,"code":10983,"language":891,"meta":291,"style":291},"wget http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.lua\u002Fspark\u002Fspark-2.1.0\u002Fspark-2.1.0-bin-hadoop2.7.tgz\n",[552,10985,10986],{"__ignoreMap":291},[555,10987,10988,10990],{"class":557,"line":558},[555,10989,10144],{"class":898},[555,10991,10992],{"class":592}," http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.lua\u002Fspark\u002Fspark-2.1.0\u002Fspark-2.1.0-bin-hadoop2.7.tgz\n",[1287,10994,10175],{"id":10995},"_2移动解压-2",[546,10997,10999],{"className":889,"code":10998,"language":891,"meta":291,"style":291},"mv spark-2.1.0-bin-hadoop2.7.tgz \u002Fapp\u002Fspark\ntar -zxvf spark-2.1.0-bin-hadoop2.7.tgz\n",[552,11000,11001,11010],{"__ignoreMap":291},[555,11002,11003,11005,11008],{"class":557,"line":558},[555,11004,4576],{"class":898},[555,11006,11007],{"class":592}," spark-2.1.0-bin-hadoop2.7.tgz",[555,11009,10976],{"class":592},[555,11011,11012,11014,11016],{"class":557,"line":295},[555,11013,10194],{"class":898},[555,11015,10197],{"class":592},[555,11017,11018],{"class":592}," spark-2.1.0-bin-hadoop2.7.tgz\n",[1287,11020,10420],{"id":11021},"_3修改配置文件-1",[10,11023,11024],{},[181,11025,10425],{},[546,11027,11029],{"className":889,"code":11028,"language":891,"meta":291,"style":291},"export SPARK_HOME=\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\nexport PATH=\"$SPARK_HOME\u002Fbin:$PATH\"\n",[552,11030,11031,11043],{"__ignoreMap":291},[555,11032,11033,11035,11038,11040],{"class":557,"line":558},[555,11034,1803],{"class":561},[555,11036,11037],{"class":569}," SPARK_HOME",[555,11039,661],{"class":565},[555,11041,11042],{"class":569},"\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\n",[555,11044,11045,11047,11049,11051,11053,11056,11059,11062],{"class":557,"line":295},[555,11046,1803],{"class":561},[555,11048,10515],{"class":569},[555,11050,661],{"class":565},[555,11052,664],{"class":565},[555,11054,11055],{"class":569},"$SPARK_HOME",[555,11057,11058],{"class":592},"\u002Fbin:",[555,11060,11061],{"class":569},"$PATH",[555,11063,3103],{"class":565},[10,11065,11066],{},[181,11067,11068],{},"spark-env.sh",[546,11070,11072],{"className":889,"code":11071,"language":891,"meta":291,"style":291},"cp spark-env.sh.template spark-env.sh\n",[552,11073,11074],{"__ignoreMap":291},[555,11075,11076,11078,11081],{"class":557,"line":558},[555,11077,10735],{"class":898},[555,11079,11080],{"class":592}," spark-env.sh.template",[555,11082,11083],{"class":592}," spark-env.sh\n",[546,11085,11087],{"className":889,"code":11086,"language":891,"meta":291,"style":291},"export SCALA_HOME=\u002Fapp\u002Fscala\u002Fscala-2.12.1\nexport JAVA_HOME=\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\nexport SPARK_MASTER_IP=nn.hadoop.data.easydebug.net\nexport SPARK_WORKER_MEMORY=1g\nexport HADOOP_CONF_DIR=\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\u002Fetc\u002Fhadoop\n",[552,11088,11089,11101,11112,11124,11136],{"__ignoreMap":291},[555,11090,11091,11093,11096,11098],{"class":557,"line":558},[555,11092,1803],{"class":561},[555,11094,11095],{"class":569}," SCALA_HOME",[555,11097,661],{"class":565},[555,11099,11100],{"class":569},"\u002Fapp\u002Fscala\u002Fscala-2.12.1\n",[555,11102,11103,11105,11107,11109],{"class":557,"line":295},[555,11104,1803],{"class":561},[555,11106,10567],{"class":569},[555,11108,661],{"class":565},[555,11110,11111],{"class":569},"\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\n",[555,11113,11114,11116,11119,11121],{"class":557,"line":292},[555,11115,1803],{"class":561},[555,11117,11118],{"class":569}," SPARK_MASTER_IP",[555,11120,661],{"class":565},[555,11122,11123],{"class":569},"nn.hadoop.data.easydebug.net\n",[555,11125,11126,11128,11131,11133],{"class":557,"line":923},[555,11127,1803],{"class":561},[555,11129,11130],{"class":569}," SPARK_WORKER_MEMORY",[555,11132,661],{"class":565},[555,11134,11135],{"class":569},"1g\n",[555,11137,11138,11140,11143,11145],{"class":557,"line":1786},[555,11139,1803],{"class":561},[555,11141,11142],{"class":569}," HADOOP_CONF_DIR",[555,11144,661],{"class":565},[555,11146,11147],{"class":569},"\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\u002Fetc\u002Fhadoop\n",[10,11149,11150],{},[181,11151,10525],{},[546,11153,11154],{"className":889,"code":10528,"language":891,"meta":291,"style":291},[552,11155,11156,11160],{"__ignoreMap":291},[555,11157,11158],{"class":557,"line":558},[555,11159,10535],{"class":898},[555,11161,11162],{"class":557,"line":295},[555,11163,10540],{"class":898},[1287,11165,11167],{"id":11166},"_4复制文件到其他机器","4.复制文件到其他机器",[10,11169,11170],{},"将 \u002Fapp\u002Fspark 复制到其他机器。",[1287,11172,11174],{"id":11173},"_5启动-spark","5.启动 Spark",[546,11176,11178],{"className":889,"code":11177,"language":891,"meta":291,"style":291},"\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\u002Fsbin\u002Fstart-all.sh\n",[552,11179,11180],{"__ignoreMap":291},[555,11181,11182],{"class":557,"line":558},[555,11183,11177],{"class":898},[32,11185,11187],{"id":11186},"安装完成-_","安装完成 ^_^",[4309,11189],{},[1287,11191,11193],{"id":11192},"因为系统变量改了几次最后贴一下完整的","因为系统变量改了几次，最后贴一下完整的",[10,11195,11196],{},"其实可以在配置前直接贴进去。",[546,11198,11200],{"className":889,"code":11199,"language":891,"meta":291,"style":291},"export JAVA_HOME=\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\nexport SCALA_HOME=\u002Fapp\u002Fscala\u002Fscala-2.12.1\nexport PATH=$JAVA_HOME\u002Fbin:$SCALA_HOME\u002Fbin:$PATH\nexport CLASSPATH=.:$JAVA_HOME\u002Flib\u002Fdt.jar:$JAVA_HOME\u002Flib\u002Ftools.jar\n\nHADOOP_HOME=\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\nexport HADOOP_INSTALL=$HADOOP_HOME\nexport HADOOP_MAPRED_HOME=$HADOOP_HOME\nexport HADOOP_COMMON_HOME=$HADOOP_HOME\nexport HADOOP_HDFS_HOME=$HADOOP_HOME\nexport YARN_HOME=$HADOOP_HOME\nexport HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME\u002Flib\u002Fnative\nexport PATH=$PATH:$HADOOP_HOME\u002Fsbin:$HADOOP_HOME\u002Fbin\n\nexport SPARK_HOME=\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\nexport PATH=\"$SPARK_HOME\u002Fbin:$PATH\"\n",[552,11201,11202,11212,11222,11233,11245,11249,11257,11267,11277,11287,11297,11307,11317,11327,11331,11341],{"__ignoreMap":291},[555,11203,11204,11206,11208,11210],{"class":557,"line":558},[555,11205,1803],{"class":561},[555,11207,10567],{"class":569},[555,11209,661],{"class":565},[555,11211,11111],{"class":569},[555,11213,11214,11216,11218,11220],{"class":557,"line":295},[555,11215,1803],{"class":561},[555,11217,11095],{"class":569},[555,11219,661],{"class":565},[555,11221,11100],{"class":569},[555,11223,11224,11226,11228,11230],{"class":557,"line":292},[555,11225,1803],{"class":561},[555,11227,10515],{"class":569},[555,11229,661],{"class":565},[555,11231,11232],{"class":569},"$JAVA_HOME\u002Fbin:$SCALA_HOME\u002Fbin:$PATH\n",[555,11234,11235,11237,11240,11242],{"class":557,"line":923},[555,11236,1803],{"class":561},[555,11238,11239],{"class":569}," CLASSPATH",[555,11241,661],{"class":565},[555,11243,11244],{"class":569},".:$JAVA_HOME\u002Flib\u002Fdt.jar:$JAVA_HOME\u002Flib\u002Ftools.jar\n",[555,11246,11247],{"class":557,"line":1786},[555,11248,3685],{"emptyLinePlaceholder":305},[555,11250,11251,11253,11255],{"class":557,"line":2519},[555,11252,10435],{"class":569},[555,11254,661],{"class":565},[555,11256,10440],{"class":592},[555,11258,11259,11261,11263,11265],{"class":557,"line":2530},[555,11260,1803],{"class":561},[555,11262,10447],{"class":569},[555,11264,661],{"class":565},[555,11266,10452],{"class":569},[555,11268,11269,11271,11273,11275],{"class":557,"line":2536},[555,11270,1803],{"class":561},[555,11272,10459],{"class":569},[555,11274,661],{"class":565},[555,11276,10452],{"class":569},[555,11278,11279,11281,11283,11285],{"class":557,"line":2550},[555,11280,1803],{"class":561},[555,11282,10470],{"class":569},[555,11284,661],{"class":565},[555,11286,10452],{"class":569},[555,11288,11289,11291,11293,11295],{"class":557,"line":2564},[555,11290,1803],{"class":561},[555,11292,10481],{"class":569},[555,11294,661],{"class":565},[555,11296,10452],{"class":569},[555,11298,11299,11301,11303,11305],{"class":557,"line":2578},[555,11300,1803],{"class":561},[555,11302,10492],{"class":569},[555,11304,661],{"class":565},[555,11306,10452],{"class":569},[555,11308,11309,11311,11313,11315],{"class":557,"line":3374},[555,11310,1803],{"class":561},[555,11312,10503],{"class":569},[555,11314,661],{"class":565},[555,11316,10508],{"class":569},[555,11318,11319,11321,11323,11325],{"class":557,"line":3447},[555,11320,1803],{"class":561},[555,11322,10515],{"class":569},[555,11324,661],{"class":565},[555,11326,10520],{"class":569},[555,11328,11329],{"class":557,"line":3453},[555,11330,3685],{"emptyLinePlaceholder":305},[555,11332,11333,11335,11337,11339],{"class":557,"line":3459},[555,11334,1803],{"class":561},[555,11336,11037],{"class":569},[555,11338,661],{"class":565},[555,11340,11042],{"class":569},[555,11342,11343,11345,11347,11349,11351,11353,11355,11357],{"class":557,"line":3465},[555,11344,1803],{"class":561},[555,11346,10515],{"class":569},[555,11348,661],{"class":565},[555,11350,664],{"class":565},[555,11352,11055],{"class":569},[555,11354,11058],{"class":592},[555,11356,11061],{"class":569},[555,11358,3103],{"class":565},[4309,11360],{},[8533,11362,11363,11366,11374,11382],{},[10,11364,11365],{},"参考文章",[10,11367,11368,11369],{},"CentOS 6.5 hadoop 2.7.3 集群环境搭建\n",[94,11370,11373],{"href":11371,"rel":11372},"http:\u002F\u002Fblog.csdn.net\u002Fmxxlevel\u002Farticle\u002Fdetails\u002F52653086",[98],"blog.csdn.net\u002Fmxxlevel\u002Farticle\u002Fdetails\u002F52653086",[10,11375,11376,11377],{},"Spark修炼之道（进阶篇）——Spark 入门到精通：第一节 Spark 1.5.0 集群搭建\n",[94,11378,11381],{"href":11379,"rel":11380},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F60309?spm=5176.8251999.569296.66.0H8Bal",[98],"yq.aliyun.com\u002Farticles\u002F60309",[10,11383,11384,11385],{},"Hadoop2.7.3+Spark2.1.0 完全分布式环境 搭建全过程\n",[94,11386,11389],{"href":11387,"rel":11388},"http:\u002F\u002Fwww.cnblogs.com\u002Fpurstar\u002Fp\u002F6293605.html",[98],"cnblogs.com\u002Fpurstar\u002Fp\u002F6293605.html",[1981,11391,11392],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":291,"searchDepth":292,"depth":292,"links":11394},[11395,11400,11406,11407,11408,11419,11427],{"id":4301,"depth":295,"text":4301,"children":11396},[11397,11398,11399],{"id":9937,"depth":292,"text":9938},{"id":2311,"depth":292,"text":2311},{"id":10020,"depth":292,"text":10021},{"id":10102,"depth":295,"text":10103,"children":11401},[11402,11403,11404,11405],{"id":10106,"depth":292,"text":10107},{"id":10130,"depth":292,"text":10131},{"id":10174,"depth":292,"text":10175},{"id":10221,"depth":292,"text":10222},{"id":10256,"depth":295,"text":10257},{"id":10283,"depth":295,"text":10284},{"id":10343,"depth":295,"text":10344,"children":11409},[11410,11411,11412,11413,11414,11415,11416,11417,11418],{"id":10347,"depth":292,"text":10107},{"id":10376,"depth":292,"text":10377},{"id":10392,"depth":292,"text":10175},{"id":10419,"depth":292,"text":10420},{"id":10888,"depth":292,"text":10889},{"id":10908,"depth":292,"text":10909},{"id":10915,"depth":292,"text":10916},{"id":10928,"depth":292,"text":10929},{"id":10941,"depth":292,"text":10942},{"id":10960,"depth":295,"text":10961,"children":11420},[11421,11422,11423,11424,11425,11426],{"id":10964,"depth":292,"text":10107},{"id":10979,"depth":292,"text":10980},{"id":10995,"depth":292,"text":10175},{"id":11021,"depth":292,"text":10420},{"id":11166,"depth":292,"text":11167},{"id":11173,"depth":292,"text":11174},{"id":11186,"depth":295,"text":11187,"children":11428},[11429],{"id":11192,"depth":292,"text":11193},"2017-04-03","1 个 NameNode + 2 个 DataNode 的最小集群从零搭起：装 JDK\u002FScala、配 SSH 免密、改 Hadoop 和 Spark 的各个配置文件，到启动 dfs \u002F yarn \u002F jobhistory \u002F spark。",{},"\u002Fposts\u002Fcentos7-hadoop-spark-cluster-setup",{"title":9881,"description":11431},"posts\u002Fcentos7-hadoop-spark-cluster-setup",[11437,11438,11439,2671],"Hadoop","Spark","大数据","Ff8cmlBkw4INxrPXQKkg9_Y5j43JB-fhk5e15w3XDvI",{"id":11442,"title":11443,"body":11444,"date":11573,"description":11574,"extension":303,"meta":11575,"navigation":305,"path":11576,"seo":11577,"stem":11578,"tags":11579,"__hash__":11583},"posts\u002Fposts\u002Fwindows-install-facebook-prophet.md","Facebook 开源预测工具 Prophet 安装方法",{"type":7,"value":11445,"toc":11565},[11446,11455,11457,11459,11469,11473,11483,11487,11495,11499,11529,11532,11534,11537,11542,11547,11552,11563],[10,11447,11448,11449,11454],{},"最近研究看到一篇介绍 ",[94,11450,11453],{"href":11451,"rel":11452},"http:\u002F\u002Fmp.weixin.qq.com\u002Fs\u002F6yZRKbH0GYvqHwCDmpL-Uw",[98],"facebook 开源的大规模预测框架 Prophet"," 的文章，折腾了两个晚上终于在 Win 环境下安装成功，把结果分享下。",[4309,11456],{},[32,11458,4301],{"id":4301},[8533,11460,11461],{},[174,11462,11463,11466],{},[177,11464,11465],{},"Windows 7 x64",[177,11467,11468],{},"Anaconda3-4.2.0-Windows-x86 (Python 3.5)",[32,11470,11472],{"id":11471},"安装-visual-c-2015-build-tools","安装 Visual C++ 2015 Build Tools",[8533,11474,11475],{},[10,11476,11477,11478],{},"Prophet 需要 C 语言编译，在 Win 环境下需要先安装 ",[94,11479,11482],{"href":11480,"rel":11481},"http:\u002F\u002Flandinghub.visualstudio.com\u002Fvisual-cpp-build-tools",[98],"Visual C++ 2015 Build Tools",[32,11484,11486],{"id":11485},"安装-pystan","安装 pystan",[8533,11488,11489],{},[10,11490,11491,11492],{},"Prophet 依赖 pystan 包所以也需要安装 pystan\n",[552,11493,11494],{},"pip install pystan",[32,11496,11498],{"id":11497},"安装-prophet","安装 Prophet",[8533,11500,11501,11509,11520],{},[10,11502,11503],{},[181,11504,11505,11506],{},"不要用 ",[552,11507,11508],{},"pip install fbprophet",[10,11510,11511,11516,11517],{},[94,11512,11515],{"href":11513,"rel":11514},"https:\u002F\u002Fgithub.com\u002Ffacebookincubator\u002Fprophet",[98],"下载"," prophet 最新源码。\n或者 ",[552,11518,11519],{},"git clone https:\u002F\u002Fgithub.com\u002Ffacebookincubator\u002Fprophet",[10,11521,11522,11523,11526],{},"解压到指定目录，例如 D:\\Anaconda3\\prophet\nCMD:\n",[552,11524,11525],{},"cd D:\\Anaconda3\\prophet\\python",[552,11527,11528],{},"pip install -e .",[32,11530,11531],{"id":11531},"安装完成",[4309,11533],{},[32,11535,11536],{"id":11536},"踩过的坑",[10,11538,11539],{},[181,11540,11541],{},"1. error: command ‘C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\cl.exe’ failed with exit status 2",[8533,11543,11544],{},[10,11545,11546],{},"先确认 Visual C++ 2015 Build Tools 是否安装；如果已经安装，请放弃使用 pip 安装 Prophet。下载源码安装 Prophet 无问题。",[10,11548,11549],{},[181,11550,11551],{},"2. No such file or directory: u’fbprophet\u002Fstan_models\u002Flinear_growth.pkl’",[8533,11553,11554],{},[10,11555,11556,11557,11562],{},"目测是一个",[94,11558,11561],{"href":11559,"rel":11560},"https:\u002F\u002Fgithub.com\u002Ffacebookincubator\u002Fprophet\u002Fpull\u002F100",[98],"已知 BUG","，已经修复。但好像 Release 的 v0.1 源码未更新。直接下载最新源码编译无报错。",[4309,11564],{},{"title":291,"searchDepth":292,"depth":292,"links":11566},[11567,11568,11569,11570,11571,11572],{"id":4301,"depth":295,"text":4301},{"id":11471,"depth":295,"text":11472},{"id":11485,"depth":295,"text":11486},{"id":11497,"depth":295,"text":11498},{"id":11531,"depth":295,"text":11531},{"id":11536,"depth":295,"text":11536},"2017-03-16","Win 下装 Prophet 不能用 pip install fbprophet，得先装 VC++ 2015 Build Tools 和 pystan，再从源码装。附两个踩坑记录。",{},"\u002Fposts\u002Fwindows-install-facebook-prophet",{"title":11443,"description":11574},"posts\u002Fwindows-install-facebook-prophet",[11580,11581,11582],"Python","Prophet","环境配置","VK4ZuyOpB9fUYEAljxs5oK8VsKbdxBhURT9c3OLmCAM",1792585396971]