[{"data":1,"prerenderedAt":11094},["ShallowReactive",2],{"posts:all":3},[4,1517,1793,2183,2804,3029,3086,3802,5112,6386,6803,7549,8038,8981,9389,10951],{"id":5,"title":6,"body":7,"date":1504,"description":1505,"extension":1506,"meta":1507,"navigation":1508,"path":1509,"seo":1510,"stem":1511,"tags":1512,"__hash__":1516},"posts\u002Fposts\u002Fnuxt-dynamic-import-prefetch.md","懒加载没懒成？Nuxt 动态 import 自动 prefetch 的坑",{"type":8,"value":9,"toc":1489},"minimark",[10,28,31,86,89,96,100,103,118,125,132,207,212,217,226,237,271,274,277,383,443,451,454,457,462,468,474,477,482,558,561,564,570,586,592,595,601,619,622,627,630,636,674,689,692,698,711,714,720,745,751,761,764,767,773,779,782,785,790,840,846,849,859,863,912,918,921,934,940,944,1001,1004,1016,1021,1024,1169,1172,1183,1189,1196,1201,1204,1207,1211,1214,1217,1220,1292,1359,1362,1368,1371,1374,1380,1383,1421,1424,1427,1430,1433,1464,1467,1470,1473,1485],[11,12,13,14,21,22,27],"p",{},"最近我一直在做站点的性能优化。毕竟",[15,16,20],"a",{"href":17,"rel":18},"https:\u002F\u002Fwww.easydebug.net",[19],"nofollow","在线工具","和",[15,23,26],{"href":24,"rel":25},"https:\u002F\u002Fblog.easydebug.net",[19],"博客","都跑在阿里云的一台3M小水管小机器上，换算下来理论峰值 384 KB\u002Fs，实际也就是 300 KB\u002Fs 的水平。",[11,29,30],{},"最大的工作是给首屏减负，把不需要执行的 JS 从首屏上拿掉。毕竟首屏多下 200 KB，用户就得多等大半秒。现在用不上，那就等真用到时再下：",[32,33,38],"pre",{"className":34,"code":35,"language":36,"meta":37,"style":37},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const { renderLua } = await import('~\u002Flib\u002Fformat-lua')\n","ts","",[39,40,41],"code",{"__ignoreMap":37},[42,43,46,50,54,58,61,64,68,71,74,77,81,83],"span",{"class":44,"line":45},"line",1,[42,47,49],{"class":48},"spNyl","const",[42,51,53],{"class":52},"sMK4o"," {",[42,55,57],{"class":56},"sTEyZ"," renderLua ",[42,59,60],{"class":52},"}",[42,62,63],{"class":52}," =",[42,65,67],{"class":66},"s7zQu"," await",[42,69,70],{"class":52}," import",[42,72,73],{"class":56},"(",[42,75,76],{"class":52},"'",[42,78,80],{"class":79},"sfazB","~\u002Flib\u002Fformat-lua",[42,82,76],{"class":52},[42,84,85],{"class":56},")\n",[11,87,88],{},"改完后 F12 一看。不对劲。",[11,90,91,92,95],{},"我什么都没点，那个触发才下载的 JS 还是被下载了，只是 Priority = ",[39,93,94],{},"Lowest","。",[97,98,99],"h2",{"id":99},"背景",[11,101,102],{},"这里先说明一下。",[11,104,105,106,110,111,114,115],{},"目前使用版本 Node ",[107,108,109],"strong",{},"v24.18.0","、Nuxt ",[107,112,113],{},"4.5.2"," 、pnpm ",[107,116,117],{},"11.15.1",[11,119,120,121,124],{},"Nuxt 项目打包之后，代码会被切成很多个 ",[39,122,123],{},".js"," 文件，也就是常说的 chunk。一个页面不会用到全部 chunk，所以打包器要在 HTML 里留几个标记，告诉浏览器「这个文件待会儿要用，先去拿来」。",[11,126,127,128,131],{},"负责留标记的是一类 ",[39,129,130],{},"\u003Clink>"," 标签，常用的有两种：",[32,133,137],{"className":134,"code":135,"language":136,"meta":37,"style":37},"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",[39,138,139,177],{"__ignoreMap":37},[42,140,141,144,148,151,154,157,160,162,165,167,169,172,174],{"class":44,"line":45},[42,142,143],{"class":52},"\u003C",[42,145,147],{"class":146},"swJcz","link",[42,149,150],{"class":48}," rel",[42,152,153],{"class":52},"=",[42,155,156],{"class":52},"\"",[42,158,159],{"class":79},"modulepreload",[42,161,156],{"class":52},[42,163,164],{"class":48}," href",[42,166,153],{"class":52},[42,168,156],{"class":52},[42,170,171],{"class":79},"\u002F_nuxt\u002Fxxx.js",[42,173,156],{"class":52},[42,175,176],{"class":52},">\n",[42,178,180,182,184,186,188,190,193,195,197,199,201,203,205],{"class":44,"line":179},2,[42,181,143],{"class":52},[42,183,147],{"class":146},[42,185,150],{"class":48},[42,187,153],{"class":52},[42,189,156],{"class":52},[42,191,192],{"class":79},"prefetch",[42,194,156],{"class":52},[42,196,164],{"class":48},[42,198,153],{"class":52},[42,200,156],{"class":52},[42,202,171],{"class":79},[42,204,156],{"class":52},[42,206,176],{"class":52},[11,208,209,211],{},[39,210,159],{}," 的意思是「这个文件我现在就要用」。浏览器解析到这一行，立刻用高优先级去下载，跟首屏的 CSS、字体抢带宽。",[11,213,214,216],{},[39,215,192],{}," 的意思是「这个文件以后可能会用」。浏览器先把首屏的事干完，等网络闲下来，再用低优先级慢慢下。",[11,218,219,220,222,223,225],{},"两个标签的区别不在「下不下」，而在「什么时候下、抢不抢带宽」。两者最终都会把文件拿到本地，只是 ",[39,221,159],{}," 要跟首屏抢，",[39,224,192],{}," 排在队尾。",[11,227,228,229,232,233,236],{},"而我写的是 ",[39,230,231],{},"await import()","，既没有静态导入，也不该在首屏用到。却在 ",[39,234,235],{},"generate"," 时被转换成了 prefetch：",[32,238,240],{"className":134,"code":239,"language":136,"meta":37,"style":37},"\u003Clink rel=\"prefetch\" href=\"\u002F_nuxt\u002FDxxxxxxx.js\">\n",[39,241,242],{"__ignoreMap":37},[42,243,244,246,248,250,252,254,256,258,260,262,264,267,269],{"class":44,"line":45},[42,245,143],{"class":52},[42,247,147],{"class":146},[42,249,150],{"class":48},[42,251,153],{"class":52},[42,253,156],{"class":52},[42,255,192],{"class":79},[42,257,156],{"class":52},[42,259,164],{"class":48},[42,261,153],{"class":52},[42,263,156],{"class":52},[42,265,266],{"class":79},"\u002F_nuxt\u002FDxxxxxxx.js",[42,268,156],{"class":52},[42,270,176],{"class":52},[97,272,273],{"id":273},"复现",[11,275,276],{},"我新建了一个 Nuxt 项目，分别把动态导入的几种写法拆成几个独立页面，每个页面都配了一个体积相同的200 KB载荷。项目代码放在文章末尾了。",[278,279,280,293],"table",{},[281,282,283],"thead",{},[284,285,286,290],"tr",{},[287,288,289],"th",{},"页面",[287,291,292],{},"写法",[294,295,296,307,320,332,345,355,368],"tbody",{},[284,297,298,304],{},[299,300,301],"td",{},[39,302,303],{},"\u002Fclean\u002F",[299,305,306],{},"什么都不导入，基线",[284,308,309,314],{},[299,310,311],{},[39,312,313],{},"\u002Flink-modulepreload\u002F",[299,315,316,317],{},"顶层静态 ",[39,318,319],{},"import",[284,321,322,327],{},[299,323,324],{},[39,325,326],{},"\u002Flink-prefetch\u002F",[299,328,329],{},[39,330,331],{},"\u003Clink rel=\"prefetch\">",[284,333,334,339],{},[299,335,336],{},[39,337,338],{},"\u002Fawait-import-prefetch\u002F",[299,340,341,342,344],{},"函数内 ",[39,343,231],{},"（一级）",[284,346,347,352],{},[299,348,349],{},[39,350,351],{},"\u002Fawait-import-nested\u002F",[299,353,354],{},"页面 → 一级 mid → 二级载荷",[284,356,357,362],{},[299,358,359],{},[39,360,361],{},"\u002Fasync-component\u002F",[299,363,364,367],{},[39,365,366],{},"defineAsyncComponent","，无条件渲染",[284,369,370,375],{},[299,371,372],{},[39,373,374],{},"\u002Fasync-component-conditional\u002F",[299,376,377,379,380],{},[39,378,366],{}," + ",[39,381,382],{},"v-if",[32,384,388],{"className":385,"code":386,"language":387,"meta":37,"style":37},"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",[39,389,390,403,410,418],{"__ignoreMap":37},[42,391,392,396,399],{"class":44,"line":45},[42,393,395],{"class":394},"sBMFI","pnpm",[42,397,398],{"class":79}," gen",[42,400,402],{"class":401},"sHwdD","        # 生成载荷，必须先跑\n",[42,404,405,407],{"class":44,"line":179},[42,406,395],{"class":394},[42,408,409],{"class":79}," generate\n",[42,411,413,415],{"class":44,"line":412},3,[42,414,395],{"class":394},[42,416,417],{"class":79}," analyze\n",[42,419,421,424,427,430,433,436,440],{"class":44,"line":420},4,[42,422,423],{"class":394},"npx",[42,425,426],{"class":79}," --yes",[42,428,429],{"class":79}," http-server",[42,431,432],{"class":79}," .output\u002Fpublic",[42,434,435],{"class":79}," -p",[42,437,439],{"class":438},"sbssI"," 6521",[42,441,442],{"class":79}," -c-1\n",[11,444,445],{},[446,447],"img",{"alt":448,"src":449,"width":450},"服务器启动","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F0-npx.png",900,[97,452,453],{"id":453},"实测",[11,455,456],{},"先看基线。",[11,458,459,461],{},[39,460,303],{}," 什么都没导入，理论上应该最干净。实测下来它仍然下了两个文件：",[11,463,464],{},[446,465],{"alt":466,"src":467,"width":450},"clean 基线","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F1-clean.png",[11,469,470,471,473],{},"4.0 KB 和 3.7 KB，Priority 都是 ",[39,472,94],{},"。这是 Nuxt 内置的 error-404 和 error-500，被 prefetch 到了每一个页面上，七个页面一个不落。它们是 Nuxt 的固定开销，后面所有页面的数字里都包含这 7.7 KB。",[11,475,476],{},"然后看关键的那一页。",[11,478,479,481],{},[39,480,338],{}," 里，那个 200 KB 的载荷是这样导入的：",[32,483,485],{"className":34,"code":484,"language":36,"meta":37,"style":37},"async function load() {\n  const mod = await import('~\u002Flib\u002Fpayload-dynamic')\n  output.value = mod.payloadRender()\n}\n",[39,486,487,505,530,553],{"__ignoreMap":37},[42,488,489,492,495,499,502],{"class":44,"line":45},[42,490,491],{"class":48},"async",[42,493,494],{"class":48}," function",[42,496,498],{"class":497},"s2Zo4"," load",[42,500,501],{"class":52},"()",[42,503,504],{"class":52}," {\n",[42,506,507,510,513,515,517,519,521,523,526,528],{"class":44,"line":179},[42,508,509],{"class":48},"  const",[42,511,512],{"class":56}," mod",[42,514,63],{"class":52},[42,516,67],{"class":66},[42,518,70],{"class":52},[42,520,73],{"class":146},[42,522,76],{"class":52},[42,524,525],{"class":79},"~\u002Flib\u002Fpayload-dynamic",[42,527,76],{"class":52},[42,529,85],{"class":146},[42,531,532,535,538,541,543,545,547,550],{"class":44,"line":412},[42,533,534],{"class":56},"  output",[42,536,537],{"class":52},".",[42,539,540],{"class":56},"value",[42,542,63],{"class":52},[42,544,512],{"class":56},[42,546,537],{"class":52},[42,548,549],{"class":497},"payloadRender",[42,551,552],{"class":146},"()\n",[42,554,555],{"class":44,"line":420},[42,556,557],{"class":52},"}\n",[11,559,560],{},"它挂在一个按钮上。用户不点，这段代码就不会执行。",[11,562,563],{},"打开页面，什么都不点，等三秒：",[11,565,566],{},[446,567],{"alt":568,"src":569,"width":450},"零交互，178 KB 已被下走","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F4-1-await-import-prefetch.png",[11,571,572,573,576,577,580,581,576,584,95],{},"那个 178 KB 的 chunk 已经在列表里了，",[39,574,575],{},"Type"," 是 ",[39,578,579],{},"javascript","，",[39,582,583],{},"Priority",[39,585,94],{},[11,587,588,589,591],{},"我用 ",[39,590,231],{}," 的本意是「用到才下」。但页面刚一空闲，浏览器就把它下完了。",[11,593,594],{},"点击按钮，同一个文件会以另一种身份再出现一次：",[11,596,597],{},[446,598],{"alt":599,"src":600,"width":450},"点击后，同一个文件变成 High","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F4-2-await-import-prefetch.png",[11,602,603,604,607,608,576,610,580,613,615,616,95],{},"同一个文件 ",[39,605,606],{},"Dxxxxxxx.js","，这次的 ",[39,609,575],{},[39,611,612],{},"script",[39,614,583],{}," 变成 ",[39,617,618],{},"High",[11,620,621],{},"这说明 prefetch 并未替代这次加载，它只是把文件提前放进了缓存。用户真正触发时，浏览器仍会走完整的加载流程。",[11,623,624],{},[107,625,626],{},"省下的是等待，没省下的是流量。",[97,628,629],{"id":629},"异步组件也绕不开",[11,631,632,633,635],{},"那不用 ",[39,634,231],{},"，改用异步组件呢？",[32,637,639],{"className":34,"code":638,"language":36,"meta":37,"style":37},"const PayloadAsync = defineAsyncComponent(() => import('~\u002Fcomponents\u002FPayloadAsync.vue'))\n",[39,640,641],{"__ignoreMap":37},[42,642,643,645,648,650,653,655,657,660,662,664,666,669,671],{"class":44,"line":45},[42,644,49],{"class":48},[42,646,647],{"class":56}," PayloadAsync ",[42,649,153],{"class":52},[42,651,652],{"class":497}," defineAsyncComponent",[42,654,73],{"class":56},[42,656,501],{"class":52},[42,658,659],{"class":48}," =>",[42,661,70],{"class":52},[42,663,73],{"class":56},[42,665,76],{"class":52},[42,667,668],{"class":79},"~\u002Fcomponents\u002FPayloadAsync.vue",[42,670,76],{"class":52},[42,672,673],{"class":56},"))\n",[32,675,677],{"className":134,"code":676,"language":136,"meta":37,"style":37},"\u003CPayloadAsync \u002F>\n",[39,678,679],{"__ignoreMap":37},[42,680,681,683,686],{"class":44,"line":45},[42,682,143],{"class":52},[42,684,685],{"class":146},"PayloadAsync",[42,687,688],{"class":52}," \u002F>\n",[11,690,691],{},"实测：",[11,693,694],{},[446,695],{"alt":696,"src":697,"width":450},"异步组件：直接 modulepreload","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F6-async-component.png",[11,699,700,701,576,703,580,705,576,707,95],{},"178 KB，",[39,702,575],{},[39,704,612],{},[39,706,583],{},[107,708,709],{},[39,710,618],{},[11,712,713],{},"比 prefetch 更狠。它不是等网络空闲，而是和首屏的 CSS、字体一起抢带宽。我的理解是：这个组件在服务端渲染时确实被渲染了，Nuxt 判断它「首屏就要用」，于是用最高优先级预加载，省掉客户端水合时再等一次。",[11,715,716,717,719],{},"那给它加个 ",[39,718,382],{},"，让它别在首屏渲染会怎样？",[32,721,723],{"className":134,"code":722,"language":136,"meta":37,"style":37},"\u003CPayloadAsync v-if=\"show\" \u002F>\n",[39,724,725],{"__ignoreMap":37},[42,726,727,729,731,734,736,738,741,743],{"class":44,"line":45},[42,728,143],{"class":52},[42,730,685],{"class":146},[42,732,733],{"class":48}," v-if",[42,735,153],{"class":52},[42,737,156],{"class":52},[42,739,740],{"class":79},"show",[42,742,156],{"class":52},[42,744,688],{"class":52},[11,746,747],{},[446,748],{"alt":749,"src":750,"width":450},"v-if：降级为 prefetch，但流量没省","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F7-1-async-component-conditional.png",[11,752,753,755,756,758,759,95],{},[39,754,583],{}," 从 ",[39,757,618],{}," 降到了 ",[39,760,94],{},[11,762,763],{},"但页面总传输量还是 373 KB，和上一个页面一模一样。我没点按钮，那 178 KB 已经下完了。",[11,765,766],{},"点一下按钮，它才真正被用上：",[11,768,769],{},[446,770],{"alt":771,"src":772,"width":450},"点击后才渲染","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F7-2-async-component-conditional.png",[11,774,775,776,778],{},"所以 ",[39,777,382],{}," 只解决了「抢首屏带宽」，没解决「白下」。异步组件这条路，绕不开。",[97,780,781],{"id":781},"三组对照",[11,783,784],{},"七个页面里，有三组可以两两对比。对比出来的结论比单看一页更有意思。",[786,787,789],"h3",{"id":788},"一静态导入-vs-异步组件","一、静态导入 vs 异步组件",[278,791,792,805],{},[281,793,794],{},[284,795,796,798,801,803],{},[287,797,289],{},[287,799,800],{},"载荷",[287,802,575],{},[287,804,583],{},[294,806,807,824],{},[284,808,809,813,816,820],{},[299,810,811],{},[39,812,313],{},[299,814,815],{},"178 KB",[299,817,818],{},[39,819,612],{},[299,821,822],{},[39,823,618],{},[284,825,826,830,832,836],{},[299,827,828],{},[39,829,361],{},[299,831,815],{},[299,833,834],{},[39,835,612],{},[299,837,838],{},[39,839,618],{},[11,841,842],{},[446,843],{"alt":844,"src":845,"width":450},"静态导入：modulepreload","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F2-link-modulepreload.png",[11,847,848],{},"两者情况一样。",[11,850,851,852,854,855,858],{},"静态导入走 ",[39,853,159],{}," 理所当然。问题在于后面那一行：",[107,856,857],{},"一个叫「异步」的组件，只要它在首屏渲染了，Nuxt 就把它当首屏资源处理","，「异步」这两个字等于白叫。",[786,860,862],{"id":861},"二手写的-prefetch-vs-框架注入的-prefetch","二、手写的 prefetch vs 框架注入的 prefetch",[278,864,865,877],{},[281,866,867],{},[284,868,869,871,873,875],{},[287,870,289],{},[287,872,800],{},[287,874,575],{},[287,876,583],{},[294,878,879,896],{},[284,880,881,885,888,892],{},[299,882,883],{},[39,884,326],{},[299,886,887],{},"208 KB",[299,889,890],{},[39,891,579],{},[299,893,894],{},[39,895,94],{},[284,897,898,902,904,908],{},[299,899,900],{},[39,901,338],{},[299,903,815],{},[299,905,906],{},[39,907,579],{},[299,909,910],{},[39,911,94],{},[11,913,914],{},[446,915],{"alt":916,"src":917,"width":450},"手写 prefetch","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F3-link-prefetch.png",[11,919,920],{},"这一组验证一件事：Nuxt 注入的 prefetch 标签，和手写的有没有区别。",[11,922,923,924,926,927,580,929,926,931,933],{},"结论是没有。两者的 ",[39,925,575],{}," 都是 ",[39,928,579],{},[39,930,583],{},[39,932,94],{},"，加载时机也一致。",[11,935,936,937,939],{},"也就是说，Nuxt 注入的这一行，与手写的 ",[39,938,331],{}," 等价，区别只在于前者由构建过程自动完成，后者由开发者显式声明。",[786,941,943],{"id":942},"三一级动态-import-vs-条件渲染的异步组件","三、一级动态 import vs 条件渲染的异步组件",[278,945,946,961],{},[281,947,948],{},[284,949,950,952,954,956,958],{},[287,951,289],{},[287,953,800],{},[287,955,575],{},[287,957,583],{},[287,959,960],{},"页面总传输",[294,962,963,982],{},[284,964,965,969,971,975,979],{},[299,966,967],{},[39,968,338],{},[299,970,815],{},[299,972,973],{},[39,974,579],{},[299,976,977],{},[39,978,94],{},[299,980,981],{},"372 KB",[284,983,984,988,990,994,998],{},[299,985,986],{},[39,987,374],{},[299,989,815],{},[299,991,992],{},[39,993,579],{},[299,995,996],{},[39,997,94],{},[299,999,1000],{},"373 KB",[11,1002,1003],{},"两条路的结果一致。",[11,1005,1006,1008,1009,379,1011,1013,1014,95],{},[39,1007,231],{}," 和 ",[39,1010,366],{},[39,1012,382],{}," 的区别只在语法层面。只要被导入的模块挂在「一级」，结果都是零交互下先下载 178 KB，仅优先级为 ",[39,1015,94],{},[11,1017,1018],{},[107,1019,1020],{},"更换写法并不能规避。",[97,1022,1023],{"id":1023},"七页放在一起看",[278,1025,1026,1040],{},[281,1027,1028],{},[284,1029,1030,1032,1034,1036,1038],{},[287,1031,289],{},[287,1033,800],{},[287,1035,575],{},[287,1037,583],{},[287,1039,960],{},[294,1041,1042,1058,1076,1095,1113,1133,1151],{},[284,1043,1044,1048,1051,1053,1055],{},[299,1045,1046],{},[39,1047,303],{},[299,1049,1050],{},"—",[299,1052,1050],{},[299,1054,1050],{},[299,1056,1057],{},"194 KB",[284,1059,1060,1064,1066,1070,1074],{},[299,1061,1062],{},[39,1063,313],{},[299,1065,815],{},[299,1067,1068],{},[39,1069,612],{},[299,1071,1072],{},[39,1073,618],{},[299,1075,981],{},[284,1077,1078,1082,1084,1088,1092],{},[299,1079,1080],{},[39,1081,326],{},[299,1083,887],{},[299,1085,1086],{},[39,1087,579],{},[299,1089,1090],{},[39,1091,94],{},[299,1093,1094],{},"402 KB",[284,1096,1097,1101,1103,1107,1111],{},[299,1098,1099],{},[39,1100,338],{},[299,1102,815],{},[299,1104,1105],{},[39,1106,579],{},[299,1108,1109],{},[39,1110,94],{},[299,1112,981],{},[284,1114,1115,1119,1122,1126,1130],{},[299,1116,1117],{},[39,1118,351],{},[299,1120,1121],{},"0.6 KB",[299,1123,1124],{},[39,1125,579],{},[299,1127,1128],{},[39,1129,94],{},[299,1131,1132],{},"195 KB",[284,1134,1135,1139,1141,1145,1149],{},[299,1136,1137],{},[39,1138,361],{},[299,1140,815],{},[299,1142,1143],{},[39,1144,612],{},[299,1146,1147],{},[39,1148,618],{},[299,1150,1000],{},[284,1152,1153,1157,1159,1163,1167],{},[299,1154,1155],{},[39,1156,374],{},[299,1158,815],{},[299,1160,1161],{},[39,1162,579],{},[299,1164,1165],{},[39,1166,94],{},[299,1168,1000],{},[11,1170,1171],{},"几点结论：",[11,1173,1174,1180,1181,95],{},[107,1175,1176,1177,1179],{},"一级 ",[39,1178,231],{}," 的懒加载不成立。"," 零交互下 178 KB 即被下载，仅优先级为 ",[39,1182,94],{},[11,1184,1185,1188],{},[107,1186,1187],{},"二级动态导入成立。"," 页面总传输 195 KB，与基线基本持平。",[11,1190,1191],{},[107,1192,1193,1195],{},[39,1194,382],{}," 只改变优先级，不改变流量。",[11,1197,1198],{},[107,1199,1200],{},"异步组件无条件渲染时，行为等同于静态导入。",[11,1202,1203],{},"**另有固定开销：**error-404 与 error-500 两个 chunk 被 prefetch 到全部七个页面，每页 7.7 KB。",[11,1205,1206],{},"对照引言中那台 3M 带宽的机器：178 KB 约合半秒以上的等待，7.7 KB 则是每个页面都要重复支付的一次成本。",[97,1208,1210],{"id":1209},"规避往下藏一层","规避：往下藏一层",[11,1212,1213],{},"现象陈述完毕，下面说规避方式。",[11,1215,1216],{},"一级会被补 prefetch，那二级呢？",[11,1218,1219],{},"我把载荷往深处挪了一层。页面不再直接导入那个 200 KB 的模块，而是导入一个很小的中间模块，中间模块内部再去导入载荷：",[32,1221,1223],{"className":34,"code":1222,"language":36,"meta":37,"style":37},"\u002F\u002F 页面\nasync function load() {\n  const mid = await import('~\u002Flib\u002Fnested-mid')\n  output.value = await mid.loadLeaf()\n}\n",[39,1224,1225,1230,1242,1266,1287],{"__ignoreMap":37},[42,1226,1227],{"class":44,"line":45},[42,1228,1229],{"class":401},"\u002F\u002F 页面\n",[42,1231,1232,1234,1236,1238,1240],{"class":44,"line":179},[42,1233,491],{"class":48},[42,1235,494],{"class":48},[42,1237,498],{"class":497},[42,1239,501],{"class":52},[42,1241,504],{"class":52},[42,1243,1244,1246,1249,1251,1253,1255,1257,1259,1262,1264],{"class":44,"line":412},[42,1245,509],{"class":48},[42,1247,1248],{"class":56}," mid",[42,1250,63],{"class":52},[42,1252,67],{"class":66},[42,1254,70],{"class":52},[42,1256,73],{"class":146},[42,1258,76],{"class":52},[42,1260,1261],{"class":79},"~\u002Flib\u002Fnested-mid",[42,1263,76],{"class":52},[42,1265,85],{"class":146},[42,1267,1268,1270,1272,1274,1276,1278,1280,1282,1285],{"class":44,"line":420},[42,1269,534],{"class":56},[42,1271,537],{"class":52},[42,1273,540],{"class":56},[42,1275,63],{"class":52},[42,1277,67],{"class":66},[42,1279,1248],{"class":56},[42,1281,537],{"class":52},[42,1283,1284],{"class":497},"loadLeaf",[42,1286,552],{"class":146},[42,1288,1290],{"class":44,"line":1289},5,[42,1291,557],{"class":52},[32,1293,1295],{"className":34,"code":1294,"language":36,"meta":37,"style":37},"\u002F\u002F nested-mid.ts\nexport async function loadLeaf() {\n  const mod = await import('.\u002Fpayload-nested')\n  return mod.payloadRender()\n}\n",[39,1296,1297,1302,1319,1342,1355],{"__ignoreMap":37},[42,1298,1299],{"class":44,"line":45},[42,1300,1301],{"class":401},"\u002F\u002F nested-mid.ts\n",[42,1303,1304,1307,1310,1312,1315,1317],{"class":44,"line":179},[42,1305,1306],{"class":66},"export",[42,1308,1309],{"class":48}," async",[42,1311,494],{"class":48},[42,1313,1314],{"class":497}," loadLeaf",[42,1316,501],{"class":52},[42,1318,504],{"class":52},[42,1320,1321,1323,1325,1327,1329,1331,1333,1335,1338,1340],{"class":44,"line":412},[42,1322,509],{"class":48},[42,1324,512],{"class":56},[42,1326,63],{"class":52},[42,1328,67],{"class":66},[42,1330,70],{"class":52},[42,1332,73],{"class":146},[42,1334,76],{"class":52},[42,1336,1337],{"class":79},".\u002Fpayload-nested",[42,1339,76],{"class":52},[42,1341,85],{"class":146},[42,1343,1344,1347,1349,1351,1353],{"class":44,"line":420},[42,1345,1346],{"class":66},"  return",[42,1348,512],{"class":56},[42,1350,537],{"class":52},[42,1352,549],{"class":497},[42,1354,552],{"class":146},[42,1356,1357],{"class":44,"line":1289},[42,1358,557],{"class":52},[11,1360,1361],{},"同样不做任何操作，等待三秒：",[11,1363,1364],{},[446,1365],{"alt":1366,"src":1367,"width":450},"二级：那个 178 KB 不出现","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F5-1-await-import-nested.png",[11,1369,1370],{},"一级那个中间模块（0.6 KB）仍被 prefetch，但二级的 178 KB 连请求都没有发出。",[11,1372,1373],{},"点击按钮后才会加载：",[11,1375,1376],{},[446,1377],{"alt":1378,"src":1379,"width":450},"点击后才加载","\u002Fimages\u002Fnuxt-dynamic-import-prefetch\u002F5-2-await-import-nested.png",[11,1381,1382],{},"再对比页面总传输量：",[278,1384,1385,1394],{},[281,1386,1387],{},[284,1388,1389,1391],{},[287,1390,289],{},[287,1392,1393],{},"总传输",[294,1395,1396,1405,1413],{},[284,1397,1398,1403],{},[299,1399,1400,1402],{},[39,1401,303],{},"（基线）",[299,1404,1057],{},[284,1406,1407,1411],{},[299,1408,1409],{},[39,1410,351],{},[299,1412,1132],{},[284,1414,1415,1419],{},[299,1416,1417],{},[39,1418,338],{},[299,1420,981],{},[11,1422,1423],{},"二级嵌套的页面只比基线多出 1 KB，就是那个中转模块的体积。而一级动态导入的页面，多出 178 KB。",[11,1425,1426],{},"我目前的处理方式是把体积大的模块下移到二级。中间那层转发模块很小，即使被 prefetch 影响也有限。",[97,1428,1429],{"id":1429},"关于这个问题的记录",[11,1431,1432],{},"最后我查了一下 GitHub，发现这不是新问题：",[1434,1435,1436,1448,1456],"ul",{},[1437,1438,1439,1444,1445,1447],"li",{},[15,1440,1443],{"href":1441,"rel":1442},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fissues\u002F18376",[19],"#18376"," Disable ",[39,1446,192],{}," for dynamic imports（2023-01-20 提出，2026-05-05 关闭）",[1437,1449,1450,1455],{},[15,1451,1454],{"href":1452,"rel":1453},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fissues\u002F14584",[19],"#14584"," optimisations for prefetching chunks（2022-08-15 提出，2026-09-06 关闭）",[1437,1457,1458,1463],{},[15,1459,1462],{"href":1460,"rel":1461},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F36391",[19],"#36391"," unified client prefetch scheduler（2026-09-23 合并，目标版本 5.x）",[11,1465,1466],{},"#18376 描述的场景和本文遇到的一模一样。上游近期把客户端预取的调度统一到了一套队列（#36391），但它调整的是预取的秩序，不是「要不要预取动态 import」，是否影响本文描述的现象尚不确定，需要持续观察。",[11,1468,1469],{},"在此之前，可行的做法仍是自行处理：把大体积模块放到第二层动态 import，第一层只保留一个转发用的空壳。",[97,1471,1472],{"id":1472},"相关代码",[11,1474,1475,1476,1480,1481,1484],{},"相关代码在 ",[15,1477,1478],{"href":1478,"rel":1479},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002Fblog-code-pool",[19]," 的 ",[39,1482,1483],{},"nuxt-prefetch-lazy-load\u002F"," 目录下。",[1486,1487,1488],"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":37,"searchDepth":179,"depth":179,"links":1490},[1491,1492,1493,1494,1495,1500,1501,1502,1503],{"id":99,"depth":179,"text":99},{"id":273,"depth":179,"text":273},{"id":453,"depth":179,"text":453},{"id":629,"depth":179,"text":629},{"id":781,"depth":179,"text":781,"children":1496},[1497,1498,1499],{"id":788,"depth":412,"text":789},{"id":861,"depth":412,"text":862},{"id":942,"depth":412,"text":943},{"id":1023,"depth":179,"text":1023},{"id":1209,"depth":179,"text":1210},{"id":1429,"depth":179,"text":1429},{"id":1472,"depth":179,"text":1472},"2026-10-03","我给首屏减负时发现，`await import()` 的懒加载是假的。Nuxt 会在构建时给一级动态 import 注入 `\u003Clink rel=\"prefetch\">`，用户零交互，整个 chunk 就已经下走了。这篇用七个页面的实测把这个机制拆开，并给出一个真能省下流量的写法。","md",{},true,"\u002Fposts\u002Fnuxt-dynamic-import-prefetch",{"title":6,"description":1505},"posts\u002Fnuxt-dynamic-import-prefetch",[1513,1514,1515],"Nuxt","性能优化","前端","4-_CamwM9KkIMPqAv9uo0GyICR2vXpsqz_bnzq5uz_0",{"id":1518,"title":1519,"body":1520,"date":1781,"description":1782,"extension":1506,"meta":1783,"navigation":1508,"path":1784,"seo":1785,"stem":1786,"tags":1787,"__hash__":1792},"posts\u002Fposts\u002Feasyauth-2fa-webdav-s3-backup-client.md","换完手机，GitHub 把我挡在了门外",{"type":8,"value":1521,"toc":1769},[1522,1525,1528,1531,1534,1538,1541,1544,1570,1573,1576,1579,1611,1614,1617,1621,1624,1627,1634,1641,1648,1651,1654,1657,1661,1664,1678,1685,1688,1691,1694,1697,1704,1710,1713,1720,1723,1726,1729,1766],[11,1523,1524],{},"换完手机那天，GitHub 把我挡在了门外。",[11,1526,1527],{},"那天我坐在电脑前，屏幕上弹出来的还是那行熟悉的提示——请输入两步验证代码。我掏出手机打开验证器，列表是空的，一条都没有。换机之前我以为它会跟着微软账号一起同步回来，结果并没有。中间到底卡在哪一步断的，我到现在也说不清。",[11,1529,1530],{},"先翻恢复码，没存；翻邮箱，没有；翻笔记，还是没有。最后只能走申诉：填表、等回复、再补材料，一个账号折腾了好几天才算有个说法。",[11,1532,1533],{},"那几天我一直在想一件事：我的密码一个都没丢，锁是我自己加的，可钥匙不知道被我放哪儿去了。",[97,1535,1537],{"id":1536},"那些验证器本来也没打算让你把数据拿走","那些验证器，本来也没打算让你把数据拿走",[11,1539,1540],{},"先说清楚，把验证码放在手机上没有任何问题。Microsoft Authenticator 我用了很多年，平时很顺手，它也确实尽职。",[11,1542,1543],{},"问题是，一直到出事那天我才发现，我从来没搞清楚过几件事：我的数据存在哪里、上一次同步是什么时候成功的、以及万一要用的时候，我能不能把它拿出来。",[1434,1545,1546,1552,1558,1564],{},[1437,1547,1548,1551],{},[107,1549,1550],{},"Google Authenticator","：同步要连 Google 的服务。在国内能不能连上、什么时候能连上，基本看运气。它可以把账号扫码迁到另一个 App，但不会给你一份能自己保管的备份文件。",[1437,1553,1554,1557],{},[107,1555,1556],{},"Microsoft Authenticator","：备份锁在微软账号里，跨系统恢复限制不少——在安卓上备份的，往 iPhone 上恢复往往就不行。我这次就是栽在这儿：换完机打开，一条都没有。",[1437,1559,1560,1563],{},[107,1561,1562],{},"Authy","：注册要手机号，数据在它自己的云上。想手动导出一份，它不给这个选项。",[1437,1565,1566,1569],{},[107,1567,1568],{},"把 2FA 顺手塞进密码管理器","（1Password、Bitwarden 之类）：确实方便，但等于把密码和验证码放进同一个篮子。篮子漏了一次，二次验证也就白开了——它的全部意义本来就在于\"再多一道锁\"。",[11,1571,1572],{},"绕一圈你会发现，这些方案都在回答同一个问题：\"我的验证码存在谁那儿？\" 而答案基本都是\"存我这儿就行，你不用管\"。",[97,1574,1575],{"id":1575},"我想要的东西其实很朴素",[11,1577,1578],{},"列出来就五条：",[1580,1581,1582,1588,1594,1600,1606],"ol",{},[1437,1583,1584,1587],{},[107,1585,1586],{},"数据存在本地","，最好能用上系统的加密存储；",[1437,1589,1590,1593],{},[107,1591,1592],{},"能备份到我本来就有的地方","——坚果云、自己的 NAS、对象存储，这些我早就在用了，不想再为它单独开个账号；",[1437,1595,1596,1599],{},[107,1597,1598],{},"备份必须是加密的","，网盘服务商拿到也看不懂；",[1437,1601,1602,1605],{},[107,1603,1604],{},"兼容标准","，能从别家导进来，也能导出去，别再添一个孤岛；",[1437,1607,1608,95],{},[107,1609,1610],{},"换手机的时候，别再让我把几十个账号一个个重新扫一遍",[11,1612,1613],{},"按这几条筛，能同时满足的其实不多：有些方案得自己搭服务、平时开着浏览器用，而我想要的只是在手机上点一下。",[11,1615,1616],{},"后来我用的是 EasyAuth。",[97,1618,1620],{"id":1619},"easyauth-是什么","EasyAuth 是什么",[11,1622,1623],{},"一句话：一个开源的 2FA 验证器 App，本地存储、离线可用，并且支持把数据加密备份到你自己的 WebDAV 或 S3 存储。",[11,1625,1626],{},"它是 Flutter 写的，Apache 2.0 协议完全开源，代码公开可审计，没有账号体系、没有遥测、没有广告，也不需要 Google 服务框架。",[11,1628,1629,1630,1633],{},"目前能直接下载安装的是 ",[107,1631,1632],{},"Android 版 APK","（GitHub \u002F Gitee 上都有）。iOS、Windows、Linux 还没有预编译版本，源码是公开的，可以自己编译。",[11,1635,1636,1637,1640],{},"算法上是 TOTP（RFC 6238）和 HOTP（RFC 4226）两种标准，兼容 Google Authenticator 那套 ",[39,1638,1639],{},"otpauth:\u002F\u002F"," 协议和迁移二维码——意味着它能跟别家互相搬家，不是又一个只能进不能出的孤岛。",[11,1642,1643],{},[446,1644],{"alt":1645,"src":1646,"width":1647},"EasyAuth 主界面：口令按发行方分卡片，右侧是倒计时环","\u002Fimages\u002Feasyauth-2fa-webdav-s3-backup-client\u002F01-home-list.jpg",360,[11,1649,1650],{},"界面很朴素：顶上一行搜索框，下面是口令卡片，每张卡片三行——口令名、发行方、当前六位数字，右边一圈倒计时。没有花活，也没有让我盯着看的动画。",[97,1652,1653],{"id":1653},"它把备份这件事做对了",[11,1655,1656],{},"如果只让我说一个推荐理由，就是备份。",[786,1658,1660],{"id":1659},"备份存哪儿你说了算","备份存哪儿，你说了算",[11,1662,1663],{},"支持两类：",[1434,1665,1666,1672],{},[1437,1667,1668,1671],{},[107,1669,1670],{},"WebDAV","：坚果云、Nextcloud，以及群晖、威联通这些自建 NAS 基本都自带；",[1437,1673,1674,1677],{},[107,1675,1676],{},"S3 兼容","：AWS S3、阿里云 OSS、腾讯云 COS、七牛云、MinIO、Cloudflare R2 这类对象存储。",[11,1679,1680],{},[446,1681],{"alt":1682,"src":1683,"width":1684},"备份参数配置","\u002Fimages\u002Feasyauth-2fa-webdav-s3-backup-client\u002F02-03-backup.jpg",750,[11,1686,1687],{},"关键在于\"你自己的\"这四个字：填的是你自己服务器的地址、你自己的密钥，备份文件进的是你自己的空间。哪天不想用了，文件也还在你自己的空间里，不会因为某个服务关停就跟着消失。",[786,1689,1690],{"id":1690},"备份本身是加密的",[11,1692,1693],{},"备份文件用 AES-256-GCM 加密，密钥由你设置的备份密码经 PBKDF2-HMAC-SHA256 迭代 60 万次派生。换句话说，你的网盘账号就算被别人拿到了，他下载走那个 zip 也只是一堆乱码。",[786,1695,1696],{"id":1696},"多版本可回退",[11,1698,1699,1700,1703],{},"备份文件名带时间戳，形如 ",[39,1701,1702],{},"backup_20260527_120413.zip","，默认在远端保留最近 10 份，超出的自动清理。也可以在 App 里直接看到远端有哪些备份、手动删掉不要的，不用专门登录服务器去收拾。",[11,1705,1706],{},[446,1707],{"alt":1708,"src":1709,"width":1684},"管理远端备份：直接在 App 里查看和删除","\u002Fimages\u002Feasyauth-2fa-webdav-s3-backup-client\u002F04-05-backup.jpg",[786,1711,1712],{"id":1712},"恢复不会手滑",[11,1714,1715,1716,1719],{},"恢复是覆盖式的，所以它一定会先弹一次确认。密码错了、或者备份文件损坏，它会直接报错，",[107,1717,1718],{},"不会","动你已经存在的数据。这个细节挺重要——恢复本来就是出事的时候才会做的事，那个时刻最不该再出岔子。",[97,1721,1722],{"id":1722},"说在后面",[11,1724,1725],{},"2FA 的意义本来就是把钥匙多放一把在自己手里。既然如此，这把钥匙最好真的在自己手里。",[11,1727,1728],{},"对了，还得交代一句：EasyAuth 是我自己写的，起因就是开头那件事。它完全开源，用得上的话自取：",[1434,1730,1731,1738,1745,1752,1759],{},[1437,1732,1733,1734],{},"官网：",[15,1735,1736],{"href":1736,"rel":1737},"https:\u002F\u002Feasyauth.easydebug.net\u002F",[19],[1437,1739,1740,1741],{},"GitHub 仓库：",[15,1742,1743],{"href":1743,"rel":1744},"https:\u002F\u002Fgithub.com\u002FEasyDebug-NET\u002FEasyAuth",[19],[1437,1746,1747,1748],{},"GitHub 下载：",[15,1749,1750],{"href":1750,"rel":1751},"https:\u002F\u002Fgithub.com\u002FEasyDebug-NET\u002FEasyAuth\u002Freleases\u002Flatest",[19],[1437,1753,1754,1755],{},"Gitee 仓库：",[15,1756,1757],{"href":1757,"rel":1758},"https:\u002F\u002Fgitee.com\u002FEasyDebug-NET\u002FEasyAuth",[19],[1437,1760,1761,1762],{},"Gitee 下载：",[15,1763,1764],{"href":1764,"rel":1765},"https:\u002F\u002Fgitee.com\u002FEasyDebug-NET\u002FEasyAuth\u002Freleases\u002Flatest",[19],[11,1767,1768],{},"有问题欢迎在仓库里提 Issue。",{"title":37,"searchDepth":179,"depth":179,"links":1770},[1771,1772,1773,1774,1780],{"id":1536,"depth":179,"text":1537},{"id":1575,"depth":179,"text":1575},{"id":1619,"depth":179,"text":1620},{"id":1653,"depth":179,"text":1653,"children":1775},[1776,1777,1778,1779],{"id":1659,"depth":412,"text":1660},{"id":1690,"depth":412,"text":1690},{"id":1696,"depth":412,"text":1696},{"id":1712,"depth":412,"text":1712},{"id":1722,"depth":179,"text":1722},"2026-09-16","换完手机，验证器的备份没把数据带回来，验证码一条没剩，我被自己的 GitHub 挡在门外好几天。这篇讲清 2FA 的备份困境，以及我最后换上的开源方案 EasyAuth。",{},"\u002Fposts\u002Feasyauth-2fa-webdav-s3-backup-client",{"title":1519,"description":1782},"posts\u002Feasyauth-2fa-webdav-s3-backup-client",[1788,1789,1790,1791],"2FA","安全","开源","工具","ErycfAcXQtinKViH5aJAQ5tjU_pW4zNL68_NWXVM7ro",{"id":1794,"title":1795,"body":1796,"date":2172,"description":2173,"extension":1506,"meta":2174,"navigation":1508,"path":2175,"seo":2176,"stem":2177,"tags":2178,"__hash__":2182},"posts\u002Fposts\u002Fcentos7-firewalld-common-commands.md","CentOS7 Firewalld 防火墙常用命令",{"type":8,"value":1797,"toc":2159},[1798,1801,1817,1820,1834,1837,1851,1854,1868,1871,1885,1889,1922,1925,1961,1964,2098,2102,2124,2127,2141,2144,2156],[97,1799,1800],{"id":1800},"开启防火墙",[32,1802,1804],{"className":385,"code":1803,"language":387,"meta":37,"style":37},"systemctl start firewalld.service\n",[39,1805,1806],{"__ignoreMap":37},[42,1807,1808,1811,1814],{"class":44,"line":45},[42,1809,1810],{"class":394},"systemctl",[42,1812,1813],{"class":79}," start",[42,1815,1816],{"class":79}," firewalld.service\n",[97,1818,1819],{"id":1819},"关闭防火墙",[32,1821,1823],{"className":385,"code":1822,"language":387,"meta":37,"style":37},"systemctl stop firewalld.service\n",[39,1824,1825],{"__ignoreMap":37},[42,1826,1827,1829,1832],{"class":44,"line":45},[42,1828,1810],{"class":394},[42,1830,1831],{"class":79}," stop",[42,1833,1816],{"class":79},[97,1835,1836],{"id":1836},"查看防火墙状态",[32,1838,1840],{"className":385,"code":1839,"language":387,"meta":37,"style":37},"systemctl status firewalld.service\n",[39,1841,1842],{"__ignoreMap":37},[42,1843,1844,1846,1849],{"class":44,"line":45},[42,1845,1810],{"class":394},[42,1847,1848],{"class":79}," status",[42,1850,1816],{"class":79},[97,1852,1853],{"id":1853},"开启开机启动防火墙",[32,1855,1857],{"className":385,"code":1856,"language":387,"meta":37,"style":37},"systemctl enable firewalld.service\n",[39,1858,1859],{"__ignoreMap":37},[42,1860,1861,1863,1866],{"class":44,"line":45},[42,1862,1810],{"class":394},[42,1864,1865],{"class":79}," enable",[42,1867,1816],{"class":79},[97,1869,1870],{"id":1870},"关闭开机启动防火墙",[32,1872,1874],{"className":385,"code":1873,"language":387,"meta":37,"style":37},"systemctl disable firewalld.service\n",[39,1875,1876],{"__ignoreMap":37},[42,1877,1878,1880,1883],{"class":44,"line":45},[42,1879,1810],{"class":394},[42,1881,1882],{"class":79}," disable",[42,1884,1816],{"class":79},[97,1886,1888],{"id":1887},"开启某个端口80","开启某个端口（80）",[32,1890,1892],{"className":385,"code":1891,"language":387,"meta":37,"style":37},"firewall-cmd --zone=public --add-port=80\u002Ftcp --permanent   \u002F\u002F永久\nfirewall-cmd --zone=public --add-port=80\u002Ftcp               \u002F\u002F临时\n",[39,1893,1894,1911],{"__ignoreMap":37},[42,1895,1896,1899,1902,1905,1908],{"class":44,"line":45},[42,1897,1898],{"class":394},"firewall-cmd",[42,1900,1901],{"class":79}," --zone=public",[42,1903,1904],{"class":79}," --add-port=80\u002Ftcp",[42,1906,1907],{"class":79}," --permanent",[42,1909,1910],{"class":79},"   \u002F\u002F永久\n",[42,1912,1913,1915,1917,1919],{"class":44,"line":179},[42,1914,1898],{"class":394},[42,1916,1901],{"class":79},[42,1918,1904],{"class":79},[42,1920,1921],{"class":79},"               \u002F\u002F临时\n",[97,1923,1924],{"id":1924},"端口转发",[32,1926,1928],{"className":385,"code":1927,"language":387,"meta":37,"style":37},"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",[39,1929,1930,1944,1949],{"__ignoreMap":37},[42,1931,1932,1934,1936,1939,1941],{"class":44,"line":45},[42,1933,1898],{"class":394},[42,1935,1901],{"class":79},[42,1937,1938],{"class":79}," --add-masquerade",[42,1940,1907],{"class":79},[42,1942,1943],{"class":79},"    \u002F\u002F打开IP地址伪装\n",[42,1945,1946],{"class":44,"line":179},[42,1947,1948],{"class":394},"\u002F\u002F将8080转发到80\n",[42,1950,1951,1953,1955,1958],{"class":44,"line":412},[42,1952,1898],{"class":394},[42,1954,1901],{"class":79},[42,1956,1957],{"class":79}," --add-forward-port=port=80:proto=tcp:toport=8080",[42,1959,1960],{"class":79}," --permanent\n",[97,1962,1963],{"id":1963},"创建黑名单",[32,1965,1967],{"className":385,"code":1966,"language":387,"meta":37,"style":37},"\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",[39,1968,1969,1977,1991,1999,2018,2025,2036,2042,2056,2070,2084],{"__ignoreMap":37},[42,1970,1971,1974],{"class":44,"line":45},[42,1972,1973],{"class":394},"\u002F\u002F创建blacklist",[42,1975,1976],{"class":79}," ipset\n",[42,1978,1979,1981,1983,1985,1988],{"class":44,"line":179},[42,1980,1898],{"class":394},[42,1982,1907],{"class":79},[42,1984,1901],{"class":79},[42,1986,1987],{"class":79}," --new-ipset=blacklist",[42,1989,1990],{"class":79}," --type=hash:ip\n",[42,1992,1993,1996],{"class":44,"line":412},[42,1994,1995],{"class":394},"\u002F\u002F封禁",[42,1997,1998],{"class":79}," blacklist\n",[42,2000,2001,2003,2005,2007,2010,2012,2015],{"class":44,"line":420},[42,2002,1898],{"class":394},[42,2004,1907],{"class":79},[42,2006,1901],{"class":79},[42,2008,2009],{"class":79}," --add-rich-rule=",[42,2011,76],{"class":52},[42,2013,2014],{"class":79},"rule source ipset=blacklist drop",[42,2016,2017],{"class":52},"'\n",[42,2019,2020,2023],{"class":44,"line":1289},[42,2021,2022],{"class":394},"\u002F\u002F查看",[42,2024,1998],{"class":79},[42,2026,2028,2030,2033],{"class":44,"line":2027},6,[42,2029,1898],{"class":394},[42,2031,2032],{"class":79}," --ipset=blacklist",[42,2034,2035],{"class":79}," --get-entries\n",[42,2037,2039],{"class":44,"line":2038},7,[42,2040,2041],{"class":394},"\u002F\u002F添加IP到黑名单\n",[42,2043,2045,2047,2049,2051,2053],{"class":44,"line":2044},8,[42,2046,1898],{"class":394},[42,2048,1907],{"class":79},[42,2050,1901],{"class":79},[42,2052,2032],{"class":79},[42,2054,2055],{"class":79}," --add-entry=212.237.51.36\n",[42,2057,2059,2061,2063,2065,2067],{"class":44,"line":2058},9,[42,2060,1898],{"class":394},[42,2062,1907],{"class":79},[42,2064,1901],{"class":79},[42,2066,2032],{"class":79},[42,2068,2069],{"class":79}," --add-entry=188.226.191.66\n",[42,2071,2073,2075,2077,2079,2081],{"class":44,"line":2072},10,[42,2074,1898],{"class":394},[42,2076,1907],{"class":79},[42,2078,1901],{"class":79},[42,2080,2032],{"class":79},[42,2082,2083],{"class":79}," --add-entry=80.211.137.182\n",[42,2085,2087,2089,2091,2093,2095],{"class":44,"line":2086},11,[42,2088,1898],{"class":394},[42,2090,1907],{"class":79},[42,2092,1901],{"class":79},[42,2094,2032],{"class":79},[42,2096,2097],{"class":79}," --add-entry=60.191.66.226\n",[97,2099,2101],{"id":2100},"禁止被-ping丢弃-icmp-包","禁止被 PING（丢弃 ICMP 包）",[32,2103,2105],{"className":385,"code":2104,"language":387,"meta":37,"style":37},"firewall-cmd --permanent --zone=public --add-rich-rule='rule protocol value=icmp drop'  \n",[39,2106,2107],{"__ignoreMap":37},[42,2108,2109,2111,2113,2115,2117,2119,2122],{"class":44,"line":45},[42,2110,1898],{"class":394},[42,2112,1907],{"class":79},[42,2114,1901],{"class":79},[42,2116,2009],{"class":79},[42,2118,76],{"class":52},[42,2120,2121],{"class":79},"rule protocol value=icmp drop",[42,2123,2017],{"class":52},[97,2125,2126],{"id":2126},"查看所有规则",[32,2128,2130],{"className":385,"code":2129,"language":387,"meta":37,"style":37},"firewall-cmd --zone=public --list-all\n",[39,2131,2132],{"__ignoreMap":37},[42,2133,2134,2136,2138],{"class":44,"line":45},[42,2135,1898],{"class":394},[42,2137,1901],{"class":79},[42,2139,2140],{"class":79}," --list-all\n",[97,2142,2143],{"id":2143},"重载防火墙配置",[32,2145,2147],{"className":385,"code":2146,"language":387,"meta":37,"style":37},"firewall-cmd --reload\n",[39,2148,2149],{"__ignoreMap":37},[42,2150,2151,2153],{"class":44,"line":45},[42,2152,1898],{"class":394},[42,2154,2155],{"class":79}," --reload\n",[1486,2157,2158],{},"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":37,"searchDepth":179,"depth":179,"links":2160},[2161,2162,2163,2164,2165,2166,2167,2168,2169,2170,2171],{"id":1800,"depth":179,"text":1800},{"id":1819,"depth":179,"text":1819},{"id":1836,"depth":179,"text":1836},{"id":1853,"depth":179,"text":1853},{"id":1870,"depth":179,"text":1870},{"id":1887,"depth":179,"text":1888},{"id":1924,"depth":179,"text":1924},{"id":1963,"depth":179,"text":1963},{"id":2100,"depth":179,"text":2101},{"id":2126,"depth":179,"text":2126},{"id":2143,"depth":179,"text":2143},"2025-01-24","firewalld 的日常操作速查：开关服务、放行端口、端口转发、ipset 黑名单、丢弃 ICMP、重载配置。",{},"\u002Fposts\u002Fcentos7-firewalld-common-commands",{"title":1795,"description":2173},"posts\u002Fcentos7-firewalld-common-commands",[2179,2180,2181],"CentOS","运维","防火墙","UCJp32SdIZih_LOwZd24fMb5Be7CTSvhNDdHcTNgHRU",{"id":2184,"title":2185,"body":2186,"date":2792,"description":2793,"extension":1506,"meta":2794,"navigation":1508,"path":2795,"seo":2796,"stem":2797,"tags":2798,"__hash__":2803},"posts\u002Fposts\u002Fdelayq-redis-delay-queue.md","DelayQ：用 Redis 和数据库做一个延时队列",{"type":8,"value":2187,"toc":2778},[2188,2191,2194,2197,2200,2203,2214,2217,2220,2223,2226,2232,2235,2241,2244,2270,2273,2277,2280,2286,2304,2307,2311,2326,2330,2336,2339,2381,2384,2387,2393,2396,2399,2402,2408,2470,2480,2485,2494,2497,2516,2526,2529,2536,2544,2547,2670,2673,2714,2720,2727,2731,2734,2754,2757,2760,2769,2772,2775],[11,2189,2190],{},"业务里总有一类需求，叫“过一会儿再做”。",[11,2192,2193],{},"订单创建后 30 分钟没付款就自动关单；下单 15 分钟后推一条提醒；接口调失败了，等 5 分钟再重试一次；会员到期前三天发一封续费通知。",[11,2195,2196],{},"这类需求拆开看，目标都很朴素：到点执行、失败了要能重试、最后还得看得见状态。难点从来不在“延”，而在“时”—— 谁来记着这件事，以及进程重启之后它还记得吗。",[11,2198,2199],{},"DelayQ 就是我为这件事写的一个中间件。",[97,2201,2202],{"id":2202},"先说为什么不用现成的",[11,2204,2205,2206,2209,2210,2213],{},"最省事的做法是在业务代码里 ",[39,2207,2208],{},"Thread.sleep()","，或者丢给 ",[39,2211,2212],{},"ScheduledExecutorService","。单机跑个小脚本没问题，可它撑不住两件事：进程一重启，内存里的任务就全没了；多起一个实例，两边各算各的。",[11,2215,2216],{},"换成定时任务扫库呢？那得先回答两个问题：多久扫一次 —— 扫得越勤精度越高、数据库压力也越大；以及怎么保证几个实例不会同时抢到同一个任务。这两个问题不难，但每次都要重新想一遍。",[11,2218,2219],{},"那上 MQ 呢？RabbitMQ 用 TTL + 死信队列能做延时，RocketMQ 有自带的延时消息。都是成熟方案，但它们的延时级别往往是固定的几档，而且通常还附带一整套运维成本。我需要的是“任意秒数 + 到点调一个 HTTP 接口”，为这一件事养一个 MQ，不太划算。",[11,2221,2222],{},"DelayQ 的设计参考了有赞延迟队列的思路：把“任务的元信息”和“待触发的时间线”拆开存，中间用一条扫描线连接。",[97,2224,2225],{"id":2225},"它是什么",[11,2227,2228,2229],{},"一句话：",[107,2230,2231],{},"基于 Redis 和数据库的延时队列中间件，Java 开发，到点之后去调用一个你指定的 RESTful 接口。",[11,2233,2234],{},"跑起来之后是这样的：",[11,2236,2237],{},[446,2238],{"alt":2239,"src":2240},"DelayQ 架构：Job Pool 与 Delay Bucket 在 Redis，Timer 和 Runner 是两个 Java 线程池","\u002Fimages\u002Fdelayq-redis-delay-queue\u002Farchitecture.png",[11,2242,2243],{},"四块职责：",[1580,2245,2246,2252,2258,2264],{},[1437,2247,2248,2251],{},[107,2249,2250],{},"Job Pool"," —— 存放所有 Job 的元信息，落在 Redis Hash 和数据库里；",[1437,2253,2254,2257],{},[107,2255,2256],{},"Delay Bucket"," —— 十组以时间为维度的有序队列（Redis ZSet），按 Job Id 判定存进哪个 Bucket，里面只放 Job Id；",[1437,2259,2260,2263],{},[107,2261,2262],{},"Timer"," —— 实时扫描各个 Bucket，把 delay 时间已经到期的 Job 丢进对应的 Ready Queue；",[1437,2265,2266,2269],{},[107,2267,2268],{},"Ready Queue"," —— 按任务类型（Topic）执行，执行完把任务状态回写进 Job Pool。它对应架构图右侧那个 Runner。",[11,2271,2272],{},"这里有个刻意的分工：用 Redis ZSet 存时间线，“找下一个该执行的任务”就是一次有序查询，不用把整个队列翻一遍；而真正的任务体（要调的 URL、header、body）放在 Job Pool 里，Bucket 里只留一个 Id。高频变动的那个结构因此始终很轻。",[97,2274,2276],{"id":2275},"它不假装自己是通用-mq","它不假装自己是通用 MQ",[11,2278,2279],{},"先把边界说清楚，省得你跑起来才发现不对路。",[11,2281,2282,2283],{},"DelayQ 只做一件事：",[107,2284,2285],{},"到点了，替你去调一个 HTTP 接口。",[1434,2287,2288,2295,2298,2301],{},[1437,2289,2290,2291,2294],{},"目前",[107,2292,2293],{},"只支持 Post 请求","，其他请求方式还在计划里；",[1437,2296,2297],{},"数据库是用 PostgreSQL 开发的，理论上兼容 Oracle、MySQL、MariaDB，但这个“理论上”需要你自己验一遍；",[1437,2299,2300],{},"它依赖 Redis 和一个数据库，不是一个零依赖的独立进程；",[1437,2302,2303],{},"它不做消息的广播、分区、顺序保证，那些是 MQ 的活儿。",[97,2305,2306],{"id":2306},"三个真正用得上的细节",[786,2308,2310],{"id":2309},"delay-和-ttr","delay 和 ttr",[11,2312,2313,2314,2317,2318,2321,2322,2325],{},"创建任务时有三个参数：",[39,2315,2316],{},"topic"," 是任务类型，",[39,2319,2320],{},"delay"," 是从现在起等多少秒再执行，",[39,2323,2324],{},"ttr"," 是超时时间，单位都是秒。",[786,2327,2329],{"id":2328},"passrule怎么判断成功了","passrule：怎么判断“成功了”",[11,2331,2332,2333,95],{},"调远端接口最麻烦的是“它到底成没成”。HTTP 200 不代表业务成功，返回体里可能明明白白写着 ",[39,2334,2335],{},"\"code\": 0",[11,2337,2338],{},"DelayQ 的做法是让你给一条通过规则：",[32,2340,2344],{"className":2341,"code":2342,"language":2343,"meta":37,"style":37},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\"passrule\": { \"code\": \"1\" }\n","json",[39,2345,2346],{"__ignoreMap":37},[42,2347,2348,2350,2353,2355,2358,2361,2364,2366,2368,2371,2373,2376,2378],{"class":44,"line":45},[42,2349,156],{"class":52},[42,2351,2352],{"class":79},"passrule",[42,2354,156],{"class":52},[42,2356,2357],{"class":56},": ",[42,2359,2360],{"class":52},"{",[42,2362,2363],{"class":52}," \"",[42,2365,39],{"class":48},[42,2367,156],{"class":52},[42,2369,2370],{"class":52},":",[42,2372,2363],{"class":52},[42,2374,2375],{"class":79},"1",[42,2377,156],{"class":52},[42,2379,2380],{"class":52}," }\n",[11,2382,2383],{},"它拿远端服务返回的报文去比这条规则，对上了才算任务成功。比只看 HTTP 状态码靠谱得多 —— 任务状态是 success 还是 error，得由业务结果说了算。",[786,2385,2386],{"id":2386},"任务是看得见的",[11,2388,2389],{},[446,2390],{"alt":2391,"src":2392},"任务列表：ID、Job 类型、延迟时间、超时时间、状态、预计执行时间、创建时间","\u002Fimages\u002Fdelayq-redis-delay-queue\u002Ftask-list.png",[11,2394,2395],{},"列表页把每个 Job 的类型、delay、ttr、状态（success \u002F error）、预计执行时间和创建时间都摆了出来，顶部可以按 ID、Job 类型、状态筛选。排查的时候不至于去翻日志猜 —— 这一点在我自己的使用里比想象中重要。",[97,2397,2398],{"id":2398},"跑起来",[11,2400,2401],{},"环境只要三样：Java 1.8+、Redis、一个数据库。",[11,2403,2404,2407],{},[107,2405,2406],{},"1. 建表。"," 仓库里给了 PostgreSQL 和 Oracle 两份 DDL，PostgreSQL 这份是：",[32,2409,2413],{"className":2410,"code":2411,"language":2412,"meta":37,"style":37},"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",[39,2414,2415,2420,2425,2430,2435,2440,2445,2450,2455,2460,2465],{"__ignoreMap":37},[42,2416,2417],{"class":44,"line":45},[42,2418,2419],{},"CREATE TABLE tb_delayq_job (\n",[42,2421,2422],{"class":44,"line":179},[42,2423,2424],{},"    id int8 NOT NULL,\n",[42,2426,2427],{"class":44,"line":412},[42,2428,2429],{},"    topic varchar(50) NULL,\n",[42,2431,2432],{"class":44,"line":420},[42,2433,2434],{},"    delay int4 NULL,\n",[42,2436,2437],{"class":44,"line":1289},[42,2438,2439],{},"    ttr int4 NULL,\n",[42,2441,2442],{"class":44,"line":2027},[42,2443,2444],{},"    body varchar(1000) NULL,\n",[42,2446,2447],{"class":44,"line":2038},[42,2448,2449],{},"    status varchar(10) NULL,\n",[42,2451,2452],{"class":44,"line":2044},[42,2453,2454],{},"    execution_time int8 NULL,\n",[42,2456,2457],{"class":44,"line":2058},[42,2458,2459],{},"    create_time int8 NULL,\n",[42,2461,2462],{"class":44,"line":2072},[42,2463,2464],{},"    CONSTRAINT pk_tb_delayq_job_id PRIMARY KEY (id)\n",[42,2466,2467],{"class":44,"line":2086},[42,2468,2469],{},");\n",[11,2471,2472,2475,2476,2479],{},[107,2473,2474],{},"2. 改配置。"," 编辑 ",[39,2477,2478],{},"application-dev.yml","，填上 Redis 和数据库的连接信息。",[11,2481,2482],{},[107,2483,2484],{},"3. 启动。",[32,2486,2488],{"className":385,"code":2487,"language":387,"meta":37,"style":37},".\u002Fstartup.sh\n",[39,2489,2490],{"__ignoreMap":37},[42,2491,2492],{"class":44,"line":45},[42,2493,2487],{"class":394},[11,2495,2496],{},"或者直接跑 jar：",[32,2498,2500],{"className":385,"code":2499,"language":387,"meta":37,"style":37},"java -jar DelayQ.jar --spring.profiles.active=dev\n",[39,2501,2502],{"__ignoreMap":37},[42,2503,2504,2507,2510,2513],{"class":44,"line":45},[42,2505,2506],{"class":394},"java",[42,2508,2509],{"class":79}," -jar",[42,2511,2512],{"class":79}," DelayQ.jar",[42,2514,2515],{"class":79}," --spring.profiles.active=dev\n",[11,2517,2518,2521,2522,2525],{},[107,2519,2520],{},"4. 打开界面。"," 访问 ",[39,2523,2524],{},"http:\u002F\u002Flocalhost:16996\u002Fdelay-q\u002Findex","，就是上面那个任务列表。",[97,2527,2528],{"id":2528},"加一个任务",[11,2530,2531,2532,2535],{},"向 ",[39,2533,2534],{},"add"," 接口发一个 Post，查询参数就是前面说的三个：",[32,2537,2542],{"className":2538,"code":2540,"language":2541},[2539],"language-text","POST http:\u002F\u002Flocalhost:16996\u002Fdelay-q\u002Fadd?topic=HttpPostTask&delay=60&ttr=10\n","text",[39,2543,2540],{"__ignoreMap":37},[11,2545,2546],{},"请求体里写清楚“到点之后要干什么”：",[32,2548,2550],{"className":2341,"code":2549,"language":2343,"meta":37,"style":37},"{\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",[39,2551,2552,2557,2579,2592,2612,2617,2633,2645,2661,2666],{"__ignoreMap":37},[42,2553,2554],{"class":44,"line":45},[42,2555,2556],{"class":52},"{\n",[42,2558,2559,2562,2565,2567,2569,2571,2574,2576],{"class":44,"line":179},[42,2560,2561],{"class":52},"  \"",[42,2563,2564],{"class":48},"url",[42,2566,156],{"class":52},[42,2568,2370],{"class":52},[42,2570,2363],{"class":52},[42,2572,2573],{"class":79},"http:\u002F\u002F172.16.6.202:16996\u002Fdelay-q\u002Fadd?topic=PrintTask&delay=10&ttr=10",[42,2575,156],{"class":52},[42,2577,2578],{"class":52},",\n",[42,2580,2581,2583,2586,2588,2590],{"class":44,"line":412},[42,2582,2561],{"class":52},[42,2584,2585],{"class":48},"headers",[42,2587,156],{"class":52},[42,2589,2370],{"class":52},[42,2591,504],{"class":52},[42,2593,2594,2597,2600,2602,2604,2606,2609],{"class":44,"line":420},[42,2595,2596],{"class":52},"    \"",[42,2598,2599],{"class":394},"Content-Type",[42,2601,156],{"class":52},[42,2603,2370],{"class":52},[42,2605,2363],{"class":52},[42,2607,2608],{"class":79},"application\u002Fjson",[42,2610,2611],{"class":52},"\"\n",[42,2613,2614],{"class":44,"line":1289},[42,2615,2616],{"class":52},"  },\n",[42,2618,2619,2621,2624,2626,2628,2631],{"class":44,"line":2027},[42,2620,2561],{"class":52},[42,2622,2623],{"class":48},"body",[42,2625,156],{"class":52},[42,2627,2370],{"class":52},[42,2629,2630],{"class":52}," \"\"",[42,2632,2578],{"class":52},[42,2634,2635,2637,2639,2641,2643],{"class":44,"line":2038},[42,2636,2561],{"class":52},[42,2638,2352],{"class":48},[42,2640,156],{"class":52},[42,2642,2370],{"class":52},[42,2644,504],{"class":52},[42,2646,2647,2649,2651,2653,2655,2657,2659],{"class":44,"line":2044},[42,2648,2596],{"class":52},[42,2650,39],{"class":394},[42,2652,156],{"class":52},[42,2654,2370],{"class":52},[42,2656,2363],{"class":52},[42,2658,2375],{"class":79},[42,2660,2611],{"class":52},[42,2662,2663],{"class":44,"line":2058},[42,2664,2665],{"class":52},"  }\n",[42,2667,2668],{"class":44,"line":2072},[42,2669,557],{"class":52},[11,2671,2672],{},"参数逐个说：",[1434,2674,2675,2684,2689,2694,2699,2704,2709],{},[1437,2676,2677,2679,2680,2683],{},[39,2678,2316],{},"（必填）：任务类型，",[39,2681,2682],{},"HttpPostTask"," 是内置的那一种；",[1437,2685,2686,2688],{},[39,2687,2320],{},"（必填）：等待秒数；",[1437,2690,2691,2693],{},[39,2692,2324],{},"（必填）：超时秒数；",[1437,2695,2696,2698],{},[39,2697,2564],{},"（必填）：远端服务地址；",[1437,2700,2701,2703],{},[39,2702,2585],{},"（可选）：请求头；",[1437,2705,2706,2708],{},[39,2707,2623],{},"（可选）：POST 的报文；",[1437,2710,2711,2713],{},[39,2712,2352],{},"（可选）：上面那条通过规则。",[11,2715,2716],{},[446,2717],{"alt":2718,"src":2719},"用 Postman 发一个 add 请求，响应里的 data 就是新 Job 的 ID","\u002Fimages\u002Fdelayq-redis-delay-queue\u002Fpostman-add-task.png",[11,2721,2722,2723,2726],{},"返回体里的 ",[39,2724,2725],{},"data"," 就是新建 Job 的 ID，拿它去列表页就能查到这条任务后面执行成什么样了。",[97,2728,2730],{"id":2729},"现在的样子和下一步","现在的样子，和下一步",[11,2732,2733],{},"它还算年轻，有几件事我自己清楚没做好，一并写在这里：",[1580,2735,2736,2742,2748],{},[1437,2737,2738,2741],{},[107,2739,2740],{},"多实例部署时，多个 Timer 会同时扫描。"," 同一批 Bucket 被重复扫，白白消耗资源 —— 这是最该先解决的问题；",[1437,2743,2744,2747],{},[107,2745,2746],{},"只支持 Post。"," 后续想加上更多请求方式；",[1437,2749,2750,2753],{},[107,2751,2752],{},"界面功能还薄。"," 列表、筛选、删除够用，但离完善还有距离。",[97,2755,2756],{"id":2756},"最后",[11,2758,2759],{},"仓库在这里，Apache License 2.0，代码随便看、随便用：",[1434,2761,2762],{},[1437,2763,2764,2765],{},"Gitee：",[15,2766,2767],{"href":2767,"rel":2768},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002FDelayQ",[19],[11,2770,2771],{},"如果你也在纠结“这几万个到点要执行的任务该放哪儿”，可以先 clone 下来跑一遍 —— 建一张表、连上 Redis，就能看到任务在列表里一条条变成 success。",[11,2773,2774],{},"用着有问题或者有想法，欢迎直接提 Issue。",[1486,2776,2777],{},"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":37,"searchDepth":179,"depth":179,"links":2779},[2780,2781,2782,2783,2788,2789,2790,2791],{"id":2202,"depth":179,"text":2202},{"id":2225,"depth":179,"text":2225},{"id":2275,"depth":179,"text":2276},{"id":2306,"depth":179,"text":2306,"children":2784},[2785,2786,2787],{"id":2309,"depth":412,"text":2310},{"id":2328,"depth":412,"text":2329},{"id":2386,"depth":412,"text":2386},{"id":2398,"depth":179,"text":2398},{"id":2528,"depth":179,"text":2528},{"id":2729,"depth":179,"text":2730},{"id":2756,"depth":179,"text":2756},"2022-02-17","一个基于 Redis 和数据库的延时队列中间件，Java 开发，到点之后替你调用一个 RESTful 接口。这篇讲清它的四块架构、设计取舍、已知短板，以及怎么跑起来。",{},"\u002Fposts\u002Fdelayq-redis-delay-queue",{"title":2185,"description":2793},"posts\u002Fdelayq-redis-delay-queue",[2799,2800,2801,1790,2802],"Java","Redis","消息队列","中间件","qFydD73LTlomELfwW8Ptc7IUnd1O_iwTqM-VEb0PDj4",{"id":2805,"title":2806,"body":2807,"date":3019,"description":3020,"extension":1506,"meta":3021,"navigation":1508,"path":3022,"seo":3023,"stem":3024,"tags":3025,"__hash__":3028},"posts\u002Fposts\u002Fmysql-batch-rename-table-column.md","MySQL 批量修改表名列名大小写",{"type":8,"value":2808,"toc":3015},[2809,2816,2820,2886,2890,3005,3012],[11,2810,2811,2812,2815],{},"接手老库时经常会碰到表名列名大小写混乱的情况。手写 rename 语句太累，不如查 ",[39,2813,2814],{},"information_schema"," 让数据库自己生成一批 SQL，复制出来跑就行。",[97,2817,2819],{"id":2818},"批量表名大写改小写","批量表名：大写改小写",[32,2821,2823],{"className":2410,"code":2822,"language":2412,"meta":37,"style":37},"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",[39,2824,2825,2830,2835,2840,2845,2850,2855,2860,2865,2870,2875,2880],{"__ignoreMap":37},[42,2826,2827],{"class":44,"line":45},[42,2828,2829],{},"SELECT\n",[42,2831,2832],{"class":44,"line":179},[42,2833,2834],{}," concat(\n",[42,2836,2837],{"class":44,"line":412},[42,2838,2839],{},"     'rename table  '\n",[42,2841,2842],{"class":44,"line":420},[42,2843,2844],{},"     , TABLE_NAME\n",[42,2846,2847],{"class":44,"line":1289},[42,2848,2849],{},"     , ' to '\n",[42,2851,2852],{"class":44,"line":2027},[42,2853,2854],{},"     , LOWER(TABLE_NAME)\n",[42,2856,2857],{"class":44,"line":2038},[42,2858,2859],{},"     ,' ;'\n",[42,2861,2862],{"class":44,"line":2044},[42,2863,2864],{}," ) AS 'SQL'\n",[42,2866,2867],{"class":44,"line":2058},[42,2868,2869],{},"FROM\n",[42,2871,2872],{"class":44,"line":2072},[42,2873,2874],{}," information_schema.TABLES t \n",[42,2876,2877],{"class":44,"line":2086},[42,2878,2879],{},"WHERE\n",[42,2881,2883],{"class":44,"line":2882},12,[42,2884,2885],{}," TABLE_SCHEMA = 'xxxxxx';\n",[97,2887,2889],{"id":2888},"批量列名大写改小写","批量列名：大写改小写",[32,2891,2893],{"className":2410,"code":2892,"language":2412,"meta":37,"style":37},"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",[39,2894,2895,2899,2904,2909,2914,2919,2924,2929,2934,2938,2943,2948,2953,2959,2965,2971,2977,2983,2988,2994,2999],{"__ignoreMap":37},[42,2896,2897],{"class":44,"line":45},[42,2898,2829],{},[42,2900,2901],{"class":44,"line":179},[42,2902,2903],{},"concat(\n",[42,2905,2906],{"class":44,"line":412},[42,2907,2908],{},"    'alter table '\n",[42,2910,2911],{"class":44,"line":420},[42,2912,2913],{},"    , TABLE_NAME\n",[42,2915,2916],{"class":44,"line":1289},[42,2917,2918],{},"    , ' change column '\n",[42,2920,2921],{"class":44,"line":2027},[42,2922,2923],{},"    , COLUMN_NAME\n",[42,2925,2926],{"class":44,"line":2038},[42,2927,2928],{},"    , ' '\n",[42,2930,2931],{"class":44,"line":2044},[42,2932,2933],{},"    , LOWER(COLUMN_NAME)\n",[42,2935,2936],{"class":44,"line":2058},[42,2937,2928],{},[42,2939,2940],{"class":44,"line":2072},[42,2941,2942],{},"    , COLUMN_TYPE\n",[42,2944,2945],{"class":44,"line":2086},[42,2946,2947],{},"    , ' comment \\''\n",[42,2949,2950],{"class":44,"line":2882},[42,2951,2952],{},"    , TRIM(REPLACE(REPLACE(REPLACE(REPLACE(COLUMN_COMMENT,',',':'),'\"',''),CHAR(10),''),CHAR(13),''))\n",[42,2954,2956],{"class":44,"line":2955},13,[42,2957,2958],{},"     , '\\''\n",[42,2960,2962],{"class":44,"line":2961},14,[42,2963,2964],{},"     , ' '\n",[42,2966,2968],{"class":44,"line":2967},15,[42,2969,2970],{},"     , IF(COLUMN_DEFAULT IS NULL, '', concat(' default \\'',TRIM(COLUMN_DEFAULT),'\\'')) \n",[42,2972,2974],{"class":44,"line":2973},16,[42,2975,2976],{},"     , ';' \n",[42,2978,2980],{"class":44,"line":2979},17,[42,2981,2982],{},") AS 'SQL'\n",[42,2984,2986],{"class":44,"line":2985},18,[42,2987,2869],{},[42,2989,2991],{"class":44,"line":2990},19,[42,2992,2993],{},"    information_schema.COLUMNS t\n",[42,2995,2997],{"class":44,"line":2996},20,[42,2998,2879],{},[42,3000,3002],{"class":44,"line":3001},21,[42,3003,3004],{},"    TABLE_SCHEMA = 'xxxxxx';\n",[11,3006,3007,3008,3011],{},"把 ",[39,3009,3010],{},"xxxxxx"," 换成你的库名，执行一遍拿到生成的 SQL，再贴到客户端里跑。",[1486,3013,3014],{},"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":37,"searchDepth":179,"depth":179,"links":3016},[3017,3018],{"id":2818,"depth":179,"text":2819},{"id":2888,"depth":179,"text":2889},"2021-10-24","用 information_schema 生成批量 rename table \u002F change column 语句，把大写的表名列名一次性改成小写。",{},"\u002Fposts\u002Fmysql-batch-rename-table-column",{"title":2806,"description":3020},"posts\u002Fmysql-batch-rename-table-column",[3026,3027],"MySQL","数据库","kQ0Q8KVGIqox_MqjbX0RkuflyO9Psmo12rnvlY0jrzA",{"id":3030,"title":3031,"body":3032,"date":3077,"description":3078,"extension":1506,"meta":3079,"navigation":1508,"path":3080,"seo":3081,"stem":3082,"tags":3083,"__hash__":3085},"posts\u002Fposts\u002Fwin7-windows-update-error-80072ee2.md","新装 Win7 后 Windows Update 报 80072EE2 \u002F 80072EFE",{"type":8,"value":3033,"toc":3073},[3034,3037,3040,3046,3049,3052,3068],[97,3035,3036],{"id":3036},"故障场景",[11,3038,3039],{},"最近有台老电脑重装 Win7 系统，装完以后无法安装补丁，要么就报错 80072EE2，要么就报 80072EFE。",[11,3041,3042],{},[446,3043],{"alt":3044,"src":3045},"Windows Update 报错 80072EE2","\u002Fimages\u002Fwin7-windows-update\u002Ferror-80072ee2.png",[97,3047,3048],{"id":3048},"解决方法",[11,3050,3051],{},"安装补丁：",[1580,3053,3054,3061],{},[1437,3055,3056],{},[15,3057,3060],{"href":3058,"rel":3059},"https:\u002F\u002Fwww.catalog.update.microsoft.com\u002FSearch.aspx?q=KB3020369",[19],"Windows6.1-KB3020369",[1437,3062,3063],{},[15,3064,3067],{"href":3065,"rel":3066},"https:\u002F\u002Fwww.catalog.update.microsoft.com\u002FSearch.aspx?q=KB3125574",[19],"Windows6.1-KB3125574",[11,3069,3070],{},[107,3071,3072],{},"一定要注意顺序：先安装 KB3020369，后安装 KB3125574。",{"title":37,"searchDepth":179,"depth":179,"links":3074},[3075,3076],{"id":3036,"depth":179,"text":3036},{"id":3048,"depth":179,"text":3048},"2021-07-31","老电脑重装 Win7 后装不上补丁，报这两个错误码。按顺序打两个补丁就能救回来。",{},"\u002Fposts\u002Fwin7-windows-update-error-80072ee2",{"title":3031,"description":3078},"posts\u002Fwin7-windows-update-error-80072ee2",[3084,2180],"Windows","O1JcuhOCG-hIb4YmRAwmWs2mXMhR0gsCrjgHbP5fjRE",{"id":3087,"title":3088,"body":3089,"date":3791,"description":3792,"extension":1506,"meta":3793,"navigation":1508,"path":3794,"seo":3795,"stem":3796,"tags":3797,"__hash__":3801},"posts\u002Fposts\u002Fnacos-snowflake-worker-id.md","用 Nacos 分配 Snowflake 的 Worker ID",{"type":8,"value":3090,"toc":3785},[3091,3093,3096,3099,3107,3110,3116,3123,3768,3771,3783],[97,3092,99],{"id":99},[11,3094,3095],{},"Snowflake 唯一 ID 算法大家都很熟悉，他是 Twitter 公司提出来的算法。但是对于 workerId 和 datacenterId 的维护往往需要各项目自行设计。\n基本都是基于 zookeeper or 数据库 or Redis 的。\n本例就是用 Nacos 分配 worker id。",[97,3097,3098],{"id":3098},"源码",[11,3100,3101,3102],{},"仓库地址：",[15,3103,3106],{"href":3104,"rel":3105},"https:\u002F\u002Fgitee.com\u002FJiaXiaohei\u002Fsnowflake-nacos",[19],"gitee.com\u002FJiaXiaohei\u002Fsnowflake-nacos",[97,3108,3109],{"id":3109},"原理",[11,3111,3112,3113],{},"核心类：",[39,3114,3115],{},"SnowflakeIdGenerator.class",[11,3117,3118,3119,3122],{},"基本原理：基于 Nacos 的监听服务 ",[39,3120,3121],{},"namingService.subscribe","，监听服务下的实例列表变化。\n注册一个服务到 Nacos，如果服务列表发生变化，获取服务所有实例的 Ip 地址和端口，用本机 Ip 地址和端口判断本机在实例列表的 index。",[32,3124,3127],{"className":3125,"code":3126,"language":2506,"meta":37,"style":37},"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",[39,3128,3129,3134,3139,3144,3149,3154,3159,3164,3169,3174,3179,3184,3189,3194,3199,3204,3209,3214,3219,3223,3228,3233,3239,3245,3251,3256,3262,3267,3273,3279,3284,3289,3295,3300,3306,3311,3317,3322,3328,3334,3340,3346,3351,3357,3363,3369,3375,3381,3387,3393,3398,3403,3408,3414,3419,3424,3429,3435,3441,3446,3451,3456,3462,3467,3473,3478,3484,3490,3496,3502,3508,3514,3520,3526,3532,3538,3544,3549,3555,3561,3567,3573,3579,3585,3590,3595,3600,3606,3611,3617,3622,3627,3633,3639,3645,3651,3657,3663,3668,3673,3678,3684,3689,3695,3701,3706,3711,3717,3723,3729,3735,3741,3747,3753,3758,3763],{"__ignoreMap":37},[42,3130,3131],{"class":44,"line":45},[42,3132,3133],{},"import com.alibaba.cloud.nacos.NacosDiscoveryProperties;\n",[42,3135,3136],{"class":44,"line":179},[42,3137,3138],{},"import com.alibaba.cloud.nacos.NacosServiceManager;\n",[42,3140,3141],{"class":44,"line":412},[42,3142,3143],{},"import com.alibaba.nacos.api.exception.NacosException;\n",[42,3145,3146],{"class":44,"line":420},[42,3147,3148],{},"import com.alibaba.nacos.api.naming.NamingService;\n",[42,3150,3151],{"class":44,"line":1289},[42,3152,3153],{},"import com.alibaba.nacos.api.naming.listener.AbstractEventListener;\n",[42,3155,3156],{"class":44,"line":2027},[42,3157,3158],{},"import com.alibaba.nacos.api.naming.listener.Event;\n",[42,3160,3161],{"class":44,"line":2038},[42,3162,3163],{},"import com.alibaba.nacos.api.naming.listener.NamingEvent;\n",[42,3165,3166],{"class":44,"line":2044},[42,3167,3168],{},"import com.alibaba.nacos.api.naming.pojo.Instance;\n",[42,3170,3171],{"class":44,"line":2058},[42,3172,3173],{},"import org.slf4j.Logger;\n",[42,3175,3176],{"class":44,"line":2072},[42,3177,3178],{},"import org.slf4j.LoggerFactory;\n",[42,3180,3181],{"class":44,"line":2086},[42,3182,3183],{},"import org.springframework.beans.factory.annotation.Autowired;\n",[42,3185,3186],{"class":44,"line":2882},[42,3187,3188],{},"import org.springframework.stereotype.Component;\n",[42,3190,3191],{"class":44,"line":2955},[42,3192,3193],{"emptyLinePlaceholder":1508},"\n",[42,3195,3196],{"class":44,"line":2961},[42,3197,3198],{},"import javax.annotation.PostConstruct;\n",[42,3200,3201],{"class":44,"line":2967},[42,3202,3203],{},"import java.text.DecimalFormat;\n",[42,3205,3206],{"class":44,"line":2973},[42,3207,3208],{},"import java.util.Comparator;\n",[42,3210,3211],{"class":44,"line":2979},[42,3212,3213],{},"import java.util.List;\n",[42,3215,3216],{"class":44,"line":2985},[42,3217,3218],{},"import java.util.stream.Collectors;\n",[42,3220,3221],{"class":44,"line":2990},[42,3222,3193],{"emptyLinePlaceholder":1508},[42,3224,3225],{"class":44,"line":2996},[42,3226,3227],{},"\u002F**\n",[42,3229,3230],{"class":44,"line":3001},[42,3231,3232],{}," * SnowflakeId + Nacos\n",[42,3234,3236],{"class":44,"line":3235},22,[42,3237,3238],{}," *\u002F\n",[42,3240,3242],{"class":44,"line":3241},23,[42,3243,3244],{},"@Component\n",[42,3246,3248],{"class":44,"line":3247},24,[42,3249,3250],{},"public class SnowflakeIdGenerator {\n",[42,3252,3254],{"class":44,"line":3253},25,[42,3255,3193],{"emptyLinePlaceholder":1508},[42,3257,3259],{"class":44,"line":3258},26,[42,3260,3261],{},"    protected final Logger logger = LoggerFactory.getLogger(this.getClass());\n",[42,3263,3265],{"class":44,"line":3264},27,[42,3266,3193],{"emptyLinePlaceholder":1508},[42,3268,3270],{"class":44,"line":3269},28,[42,3271,3272],{},"    @Autowired\n",[42,3274,3276],{"class":44,"line":3275},29,[42,3277,3278],{},"    private NacosServiceManager nacosServiceManager;\n",[42,3280,3282],{"class":44,"line":3281},30,[42,3283,3193],{"emptyLinePlaceholder":1508},[42,3285,3287],{"class":44,"line":3286},31,[42,3288,3272],{},[42,3290,3292],{"class":44,"line":3291},32,[42,3293,3294],{},"    private NacosDiscoveryProperties nacosDiscoveryProperties;\n",[42,3296,3298],{"class":44,"line":3297},33,[42,3299,3193],{"emptyLinePlaceholder":1508},[42,3301,3303],{"class":44,"line":3302},34,[42,3304,3305],{},"    private static SnowflakeIdWorker snowflakeIdWorker;\n",[42,3307,3309],{"class":44,"line":3308},35,[42,3310,3193],{"emptyLinePlaceholder":1508},[42,3312,3314],{"class":44,"line":3313},36,[42,3315,3316],{},"    private static int nodeId;\n",[42,3318,3320],{"class":44,"line":3319},37,[42,3321,3193],{"emptyLinePlaceholder":1508},[42,3323,3325],{"class":44,"line":3324},38,[42,3326,3327],{},"    @PostConstruct\n",[42,3329,3331],{"class":44,"line":3330},39,[42,3332,3333],{},"    public void run() throws Exception {\n",[42,3335,3337],{"class":44,"line":3336},40,[42,3338,3339],{},"        init();\n",[42,3341,3343],{"class":44,"line":3342},41,[42,3344,3345],{},"    }\n",[42,3347,3349],{"class":44,"line":3348},42,[42,3350,3193],{"emptyLinePlaceholder":1508},[42,3352,3354],{"class":44,"line":3353},43,[42,3355,3356],{},"    \u002F**\n",[42,3358,3360],{"class":44,"line":3359},44,[42,3361,3362],{},"     * 获取雪花Id\n",[42,3364,3366],{"class":44,"line":3365},45,[42,3367,3368],{},"     *\n",[42,3370,3372],{"class":44,"line":3371},46,[42,3373,3374],{},"     * @return\n",[42,3376,3378],{"class":44,"line":3377},47,[42,3379,3380],{},"     *\u002F\n",[42,3382,3384],{"class":44,"line":3383},48,[42,3385,3386],{},"    public static long nextId() {\n",[42,3388,3390],{"class":44,"line":3389},49,[42,3391,3392],{},"        return snowflakeIdWorker.nextId();\n",[42,3394,3396],{"class":44,"line":3395},50,[42,3397,3345],{},[42,3399,3401],{"class":44,"line":3400},51,[42,3402,3193],{"emptyLinePlaceholder":1508},[42,3404,3406],{"class":44,"line":3405},52,[42,3407,3356],{},[42,3409,3411],{"class":44,"line":3410},53,[42,3412,3413],{},"     * 获取当前节点Id\n",[42,3415,3417],{"class":44,"line":3416},54,[42,3418,3368],{},[42,3420,3422],{"class":44,"line":3421},55,[42,3423,3374],{},[42,3425,3427],{"class":44,"line":3426},56,[42,3428,3380],{},[42,3430,3432],{"class":44,"line":3431},57,[42,3433,3434],{},"    public static int nodeId() {\n",[42,3436,3438],{"class":44,"line":3437},58,[42,3439,3440],{},"        return nodeId;\n",[42,3442,3444],{"class":44,"line":3443},59,[42,3445,3345],{},[42,3447,3449],{"class":44,"line":3448},60,[42,3450,3193],{"emptyLinePlaceholder":1508},[42,3452,3454],{"class":44,"line":3453},61,[42,3455,3356],{},[42,3457,3459],{"class":44,"line":3458},62,[42,3460,3461],{},"     * 获取当前服务所有节点 + 增加服务监听\n",[42,3463,3465],{"class":44,"line":3464},63,[42,3466,3368],{},[42,3468,3470],{"class":44,"line":3469},64,[42,3471,3472],{},"     * @throws NacosException\n",[42,3474,3476],{"class":44,"line":3475},65,[42,3477,3380],{},[42,3479,3481],{"class":44,"line":3480},66,[42,3482,3483],{},"    private void init() throws NacosException {\n",[42,3485,3487],{"class":44,"line":3486},67,[42,3488,3489],{},"        NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());\n",[42,3491,3493],{"class":44,"line":3492},68,[42,3494,3495],{},"        namingService.subscribe(nacosDiscoveryProperties.getService(), new AbstractEventListener() {\n",[42,3497,3499],{"class":44,"line":3498},69,[42,3500,3501],{},"            @Override\n",[42,3503,3505],{"class":44,"line":3504},70,[42,3506,3507],{},"            public void onEvent(Event event) {\n",[42,3509,3511],{"class":44,"line":3510},71,[42,3512,3513],{},"                if (-1 == nacosDiscoveryProperties.getPort()) {\n",[42,3515,3517],{"class":44,"line":3516},72,[42,3518,3519],{},"                    return;\n",[42,3521,3523],{"class":44,"line":3522},73,[42,3524,3525],{},"                }\n",[42,3527,3529],{"class":44,"line":3528},74,[42,3530,3531],{},"                nodeId = calcNodeId(((NamingEvent) event).getInstances());\n",[42,3533,3535],{"class":44,"line":3534},75,[42,3536,3537],{},"                if (nodeId > 1024) {\n",[42,3539,3541],{"class":44,"line":3540},76,[42,3542,3543],{},"                    throw new IllegalArgumentException(\"Worker & Datacenter Id calc results exceed 1024\");\n",[42,3545,3547],{"class":44,"line":3546},77,[42,3548,3525],{},[42,3550,3552],{"class":44,"line":3551},78,[42,3553,3554],{},"                long workerId = nodeId % 31;\n",[42,3556,3558],{"class":44,"line":3557},79,[42,3559,3560],{},"                long datacenterId = (long) Math.floor((float) nodeId \u002F 31);\n",[42,3562,3564],{"class":44,"line":3563},80,[42,3565,3566],{},"                logger.info(\"nodeId:\" + nodeId + \" workerId:\" + workerId + \" datacenterId:\" + datacenterId);\n",[42,3568,3570],{"class":44,"line":3569},81,[42,3571,3572],{},"                snowflakeIdWorker = new SnowflakeIdWorker(workerId, datacenterId);\n",[42,3574,3576],{"class":44,"line":3575},82,[42,3577,3578],{},"            }\n",[42,3580,3582],{"class":44,"line":3581},83,[42,3583,3584],{},"        });\n",[42,3586,3588],{"class":44,"line":3587},84,[42,3589,3345],{},[42,3591,3593],{"class":44,"line":3592},85,[42,3594,3193],{"emptyLinePlaceholder":1508},[42,3596,3598],{"class":44,"line":3597},86,[42,3599,3356],{},[42,3601,3603],{"class":44,"line":3602},87,[42,3604,3605],{},"     * 用ip+port计算服务列表的索引\n",[42,3607,3609],{"class":44,"line":3608},88,[42,3610,3368],{},[42,3612,3614],{"class":44,"line":3613},89,[42,3615,3616],{},"     * @param instanceList\n",[42,3618,3620],{"class":44,"line":3619},90,[42,3621,3374],{},[42,3623,3625],{"class":44,"line":3624},91,[42,3626,3380],{},[42,3628,3630],{"class":44,"line":3629},92,[42,3631,3632],{},"    private int calcNodeId(List\u003CInstance> instanceList) {\n",[42,3634,3636],{"class":44,"line":3635},93,[42,3637,3638],{},"        List\u003CLong> ipPosrList = instanceList.stream()\n",[42,3640,3642],{"class":44,"line":3641},94,[42,3643,3644],{},"                .map(x -> dealIpPort(x.getIp(), x.getPort()))\n",[42,3646,3648],{"class":44,"line":3647},95,[42,3649,3650],{},"                .sorted(Comparator.naturalOrder())\n",[42,3652,3654],{"class":44,"line":3653},96,[42,3655,3656],{},"                .collect(Collectors.toList());\n",[42,3658,3660],{"class":44,"line":3659},97,[42,3661,3662],{},"        return ipPosrList.indexOf(dealIpPort(nacosDiscoveryProperties.getIp(), nacosDiscoveryProperties.getPort()));\n",[42,3664,3666],{"class":44,"line":3665},98,[42,3667,3345],{},[42,3669,3671],{"class":44,"line":3670},99,[42,3672,3193],{"emptyLinePlaceholder":1508},[42,3674,3676],{"class":44,"line":3675},100,[42,3677,3356],{},[42,3679,3681],{"class":44,"line":3680},101,[42,3682,3683],{},"     * ip补0 + 端口号\n",[42,3685,3687],{"class":44,"line":3686},102,[42,3688,3368],{},[42,3690,3692],{"class":44,"line":3691},103,[42,3693,3694],{},"     * @param ip\n",[42,3696,3698],{"class":44,"line":3697},104,[42,3699,3700],{},"     * @param port\n",[42,3702,3704],{"class":44,"line":3703},105,[42,3705,3374],{},[42,3707,3709],{"class":44,"line":3708},106,[42,3710,3380],{},[42,3712,3714],{"class":44,"line":3713},107,[42,3715,3716],{},"    private static Long dealIpPort(String ip, int port) {\n",[42,3718,3720],{"class":44,"line":3719},108,[42,3721,3722],{},"        String[] ips = ip.split(\"\\\\.\");\n",[42,3724,3726],{"class":44,"line":3725},109,[42,3727,3728],{},"        StringBuilder sbr = new StringBuilder();\n",[42,3730,3732],{"class":44,"line":3731},110,[42,3733,3734],{},"        for (int i = 0; i \u003C ips.length; i++) {\n",[42,3736,3738],{"class":44,"line":3737},111,[42,3739,3740],{},"            sbr.append(new DecimalFormat(\"000\").format(Integer.parseInt(ips[i])));\n",[42,3742,3744],{"class":44,"line":3743},112,[42,3745,3746],{},"        }\n",[42,3748,3750],{"class":44,"line":3749},113,[42,3751,3752],{},"        return Long.parseLong(sbr.toString() + port);\n",[42,3754,3756],{"class":44,"line":3755},114,[42,3757,3345],{},[42,3759,3761],{"class":44,"line":3760},115,[42,3762,3193],{"emptyLinePlaceholder":1508},[42,3764,3766],{"class":44,"line":3765},116,[42,3767,557],{},[97,3769,3770],{"id":3770},"参考",[11,3772,3773,3774,3777,3778],{},"雪花生成类 ",[39,3775,3776],{},"SnowflakeIdWorker.class"," 参考：\n",[15,3779,3782],{"href":3780,"rel":3781},"https:\u002F\u002Fblog.csdn.net\u002Flq18050010830\u002Farticle\u002Fdetails\u002F89845790",[19],"blog.csdn.net\u002Flq18050010830\u002Farticle\u002Fdetails\u002F89845790",[1486,3784,3014],{},{"title":37,"searchDepth":179,"depth":179,"links":3786},[3787,3788,3789,3790],{"id":99,"depth":179,"text":99},{"id":3098,"depth":179,"text":3098},{"id":3109,"depth":179,"text":3109},{"id":3770,"depth":179,"text":3770},"2021-07-27","借 Nacos 的服务实例列表动态算出 workerId 和 datacenterId，不用自己维护 zookeeper 或数据库。",{},"\u002Fposts\u002Fnacos-snowflake-worker-id",{"title":3088,"description":3792},"posts\u002Fnacos-snowflake-worker-id",[3798,3799,3800,2799],"Nacos","Snowflake","分布式","1n0hBraNNaP-3M4VTWnepV6xrtPntl_nyzhKqdkPPyc",{"id":3803,"title":3804,"body":3805,"date":3899,"description":5104,"extension":1506,"meta":5105,"navigation":1508,"path":5106,"seo":5107,"stem":5108,"tags":5109,"__hash__":5111},"posts\u002Fposts\u002Fgitlab-ce-upgrade-11-to-13.md","记一次 GitLab-CE 跨版本从 11 升级到 13 踩的坑",{"type":8,"value":3806,"toc":5098},[3807,3810,3816,3819,3823,3832,3838,3840,3844,3847,4010,4013,4104,4106,4110,4113,4460,4463,4587,4590,4878,4881,5077,5080,5095],[786,3808,3809],{"id":3809},"环境",[32,3811,3814],{"className":3812,"code":3813,"language":2541},[2539],"OS:CentOS 8\nHostname:HyperV-GitLab\n",[39,3815,3813],{"__ignoreMap":37},[3817,3818],"hr",{},[786,3820,3822],{"id":3821},"_1不可跨大版本升级","1.不可跨大版本升级",[11,3824,3825,3826,3831],{},"必须先升级到当前大版本的最新版 在升级到下一个大版本\n参考",[15,3827,3830],{"href":3828,"rel":3829},"https:\u002F\u002Fdocs.gitlab.com\u002Fee\u002Fpolicy\u002Fmaintenance.html#upgrade-recommendations",[19],"官方网站","对于版本升级的说明",[11,3833,3834],{},[446,3835],{"alt":3836,"src":3837},"GitLab 版本升级路径","\u002Fimages\u002Fgitlab-ce-upgrade\u002Fupgrade-path.png",[3817,3839],{},[786,3841,3843],{"id":3842},"_2pg_dump-aborting-because-of-server-version-mismatch","2.pg_dump: aborting because of server version mismatch",[11,3845,3846],{},"因为使用了外部数据源（PG），而且是 12 的高版本，GitLab 自带 pg_dump 版本只到 10 或者 11。在升级的备份部分，就备份失败，报错如下：",[32,3848,3850],{"className":385,"code":3849,"language":387,"meta":37,"style":37},"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",[39,3851,3852,3895,3920,3958,3980,3991,3999],{"__ignoreMap":37},[42,3853,3854,3857,3860,3863,3866,3869,3872,3875,3878,3881,3884,3887,3890,3893],{"class":44,"line":45},[42,3855,3856],{"class":394},"gitlab",[42,3858,3859],{"class":79}," preinstall:",[42,3861,3862],{"class":79}," Automatically",[42,3864,3865],{"class":79}," backing",[42,3867,3868],{"class":79}," up",[42,3870,3871],{"class":79}," only",[42,3873,3874],{"class":79}," the",[42,3876,3877],{"class":79}," GitLab",[42,3879,3880],{"class":79}," SQL",[42,3882,3883],{"class":79}," database",[42,3885,3886],{"class":56}," (excluding ",[42,3888,3889],{"class":79},"everything",[42,3891,3892],{"class":79}," else!",[42,3894,85],{"class":56},[42,3896,3897,3900,3903,3906,3909,3912,3914,3917],{"class":44,"line":179},[42,3898,3899],{"class":394},"2020-10-01",[42,3901,3902],{"class":79}," 21:47:39",[42,3904,3905],{"class":79}," +0800",[42,3907,3908],{"class":79}," --",[42,3910,3911],{"class":79}," Dumping",[42,3913,3883],{"class":79},[42,3915,3916],{"class":79}," ...",[42,3918,3919],{"class":56}," \n",[42,3921,3922,3925,3928,3930,3933,3935,3938,3941,3944,3947,3950,3953,3955],{"class":44,"line":412},[42,3923,3924],{"class":394},"Dumping",[42,3926,3927],{"class":79}," PostgreSQL",[42,3929,3883],{"class":79},[42,3931,3932],{"class":79}," db_gitlab",[42,3934,3916],{"class":79},[42,3936,3937],{"class":79}," pg_dump:",[42,3939,3940],{"class":79}," server",[42,3942,3943],{"class":79}," version:",[42,3945,3946],{"class":438}," 12.4",[42,3948,3949],{"class":52},";",[42,3951,3952],{"class":394}," pg_dump",[42,3954,3943],{"class":79},[42,3956,3957],{"class":438}," 11.7\n",[42,3959,3960,3963,3966,3969,3972,3974,3977],{"class":44,"line":420},[42,3961,3962],{"class":394},"pg_dump:",[42,3964,3965],{"class":79}," aborting",[42,3967,3968],{"class":79}," because",[42,3970,3971],{"class":79}," of",[42,3973,3940],{"class":79},[42,3975,3976],{"class":79}," version",[42,3978,3979],{"class":79}," mismatch\n",[42,3981,3982,3985,3988],{"class":44,"line":1289},[42,3983,3984],{"class":52},"[",[42,3986,3987],{"class":56},"FAILED",[42,3989,3990],{"class":52},"]\n",[42,3992,3993,3996],{"class":44,"line":2027},[42,3994,3995],{"class":394},"rake",[42,3997,3998],{"class":79}," aborted!\n",[42,4000,4001,4004,4007],{"class":44,"line":2038},[42,4002,4003],{"class":394},"Backup::Error:",[42,4005,4006],{"class":79}," Backup",[42,4008,4009],{"class":79}," failed\n",[11,4011,4012],{},"解决方法：",[32,4014,4016],{"className":385,"code":4015,"language":387,"meta":37,"style":37},"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",[39,4017,4018,4023,4037,4052,4063,4067,4072,4080,4090],{"__ignoreMap":37},[42,4019,4020],{"class":44,"line":45},[42,4021,4022],{"class":394},"1.安装postgresql12的客户端\n",[42,4024,4025,4028,4031,4034],{"class":44,"line":179},[42,4026,4027],{"class":394},"dnf",[42,4029,4030],{"class":79}," install",[42,4032,4033],{"class":79}," -y",[42,4035,4036],{"class":79}," https:\u002F\u002Fdownload.postgresql.org\u002Fpub\u002Frepos\u002Fyum\u002Freporpms\u002FEL-8-x86_64\u002Fpgdg-redhat-repo-latest.noarch.rpm\n",[42,4038,4039,4041,4044,4047,4049],{"class":44,"line":412},[42,4040,4027],{"class":394},[42,4042,4043],{"class":79}," -qy",[42,4045,4046],{"class":79}," module",[42,4048,1882],{"class":79},[42,4050,4051],{"class":79}," postgresql\n",[42,4053,4054,4056,4058,4060],{"class":44,"line":420},[42,4055,4027],{"class":394},[42,4057,4030],{"class":79},[42,4059,4033],{"class":79},[42,4061,4062],{"class":79}," postgresql12\n",[42,4064,4065],{"class":44,"line":1289},[42,4066,3193],{"emptyLinePlaceholder":1508},[42,4068,4069],{"class":44,"line":2027},[42,4070,4071],{"class":394},"2.重新建立pg_dump的软连接\n",[42,4073,4074,4077],{"class":44,"line":2038},[42,4075,4076],{"class":497},"cd",[42,4078,4079],{"class":79}," \u002Fopt\u002Fgitlab\u002Fembedded\u002Fbin\n",[42,4081,4082,4085,4087],{"class":44,"line":2044},[42,4083,4084],{"class":394},"mv",[42,4086,3952],{"class":79},[42,4088,4089],{"class":79}," pg_dump.bak\n",[42,4091,4092,4095,4098,4101],{"class":44,"line":2058},[42,4093,4094],{"class":394},"ln",[42,4096,4097],{"class":79}," -s",[42,4099,4100],{"class":79}," \u002Fusr\u002Fbin\u002Fpg_dump",[42,4102,4103],{"class":79}," \u002Fopt\u002Fgitlab\u002Fembedded\u002Fbin\u002Fpg_dump\n",[3817,4105],{},[786,4107,4109],{"id":4108},"_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",[11,4111,4112],{},"GitLab 一开始就用 Yum\u002Fdnf 安装，后面升级自然也一直用 Yum 命令升级；只到从 13.1.4 升级到 13.1.11 版本时，一直提示",[32,4114,4116],{"className":385,"code":4115,"language":387,"meta":37,"style":37},"[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",[39,4117,4118,4131,4148,4153,4158,4175,4179,4184,4204,4208,4213,4217,4228,4232,4239,4247,4252,4278,4283,4304,4309,4314,4319,4324,4329,4342,4355,4363,4367,4392,4406,4420,4424,4429,4440,4444,4449],{"__ignoreMap":37},[42,4119,4120,4122,4125,4128],{"class":44,"line":45},[42,4121,3984],{"class":52},[42,4123,4124],{"class":56},"root@HyperV-GitLab opt",[42,4126,4127],{"class":52},"]",[42,4129,4130],{"class":56},"# yum update gitlab-ce-13.1.11\n",[42,4132,4133,4136,4139,4142,4145],{"class":44,"line":179},[42,4134,4135],{"class":394},"上次元数据过期检查：6:53:15",[42,4137,4138],{"class":79}," 前，执行于",[42,4140,4141],{"class":79}," 2020年10月01日",[42,4143,4144],{"class":79}," 星期四",[42,4146,4147],{"class":79}," 15时40分48秒。\n",[42,4149,4150],{"class":44,"line":412},[42,4151,4152],{"class":394},"依赖关系解决。\n",[42,4154,4155],{"class":44,"line":420},[42,4156,4157],{"class":79},"==================================================================================================================================\n",[42,4159,4160,4163,4166,4169,4172],{"class":44,"line":1289},[42,4161,4162],{"class":394}," 软件包",[42,4164,4165],{"class":79},"                       架构",[42,4167,4168],{"class":79},"                      版本",[42,4170,4171],{"class":79},"                                   仓库",[42,4173,4174],{"class":79},"                          大小\n",[42,4176,4177],{"class":44,"line":2027},[42,4178,4157],{"class":79},[42,4180,4181],{"class":44,"line":2038},[42,4182,4183],{"class":394},"升级:\n",[42,4185,4186,4189,4192,4195,4198,4201],{"class":44,"line":2044},[42,4187,4188],{"class":394}," gitlab-ce",[42,4190,4191],{"class":79},"                    x86_64",[42,4193,4194],{"class":79},"                    13.1.11-ce.0.el8",[42,4196,4197],{"class":79},"                       gitlab-ce",[42,4199,4200],{"class":438},"                    716",[42,4202,4203],{"class":79}," M\n",[42,4205,4206],{"class":44,"line":2058},[42,4207,3193],{"emptyLinePlaceholder":1508},[42,4209,4210],{"class":44,"line":2072},[42,4211,4212],{"class":394},"事务概要\n",[42,4214,4215],{"class":44,"line":2086},[42,4216,4157],{"class":79},[42,4218,4219,4222,4225],{"class":44,"line":2882},[42,4220,4221],{"class":394},"升级",[42,4223,4224],{"class":438},"  1",[42,4226,4227],{"class":79}," 软件包\n",[42,4229,4230],{"class":44,"line":2955},[42,4231,3193],{"emptyLinePlaceholder":1508},[42,4233,4234,4237],{"class":44,"line":2961},[42,4235,4236],{"class":394},"总下载：716",[42,4238,4203],{"class":79},[42,4240,4241,4244],{"class":44,"line":2967},[42,4242,4243],{"class":394},"确定吗？[y\u002FN]：",[42,4245,4246],{"class":79}," y\n",[42,4248,4249],{"class":44,"line":2973},[42,4250,4251],{"class":394},"下载软件包：\n",[42,4253,4254,4257,4260,4263,4266,4269,4272,4275],{"class":44,"line":2979},[42,4255,4256],{"class":394},"gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm",[42,4258,4259],{"class":438},"                                                             3.7",[42,4261,4262],{"class":79}," MB\u002Fs",[42,4264,4265],{"class":52}," |",[42,4267,4268],{"class":394}," 716",[42,4270,4271],{"class":79}," MB",[42,4273,4274],{"class":79},"     03:13",[42,4276,4277],{"class":56},"    \n",[42,4279,4280],{"class":44,"line":2985},[42,4281,4282],{"class":394},"----------------------------------------------------------------------------------------------------------------------------------\n",[42,4284,4285,4288,4291,4293,4295,4297,4299,4301],{"class":44,"line":2990},[42,4286,4287],{"class":394},"总计",[42,4289,4290],{"class":438},"                                                                                              3.7",[42,4292,4262],{"class":79},[42,4294,4265],{"class":52},[42,4296,4268],{"class":394},[42,4298,4271],{"class":79},[42,4300,4274],{"class":79},[42,4302,4303],{"class":56},"     \n",[42,4305,4306],{"class":44,"line":2996},[42,4307,4308],{"class":394},"运行事务检查\n",[42,4310,4311],{"class":44,"line":3001},[42,4312,4313],{"class":394},"事务检查成功。\n",[42,4315,4316],{"class":44,"line":3235},[42,4317,4318],{"class":394},"运行事务测试\n",[42,4320,4321],{"class":44,"line":3241},[42,4322,4323],{"class":394},"事务测试成功。\n",[42,4325,4326],{"class":44,"line":3247},[42,4327,4328],{"class":394},"运行事务\n",[42,4330,4331,4334,4337,4340],{"class":44,"line":3253},[42,4332,4333],{"class":394},"  准备中",[42,4335,4336],{"class":79},"  :",[42,4338,4339],{"class":79},"                                                                                                                   1\u002F1",[42,4341,3919],{"class":56},[42,4343,4344,4347,4350,4353],{"class":44,"line":3258},[42,4345,4346],{"class":394},"  运行脚本:",[42,4348,4349],{"class":79}," gitlab-ce-13.1.11-ce.0.el8.x86_64",[42,4351,4352],{"class":79},"                                                                                 1\u002F2",[42,4354,3919],{"class":56},[42,4356,4357,4360],{"class":44,"line":3264},[42,4358,4359],{"class":394},"错误：%prein(gitlab-ce-13.1.11-ce.0.el8.x86_64",[42,4361,4362],{"class":56},") 脚本执行失败，退出状态码为 1\n",[42,4364,4365],{"class":44,"line":3269},[42,4366,3193],{"emptyLinePlaceholder":1508},[42,4368,4369,4372,4375,4378,4381,4383,4386,4389],{"class":44,"line":3275},[42,4370,4371],{"class":394},"Error",[42,4373,4374],{"class":79}," in",[42,4376,4377],{"class":79}," PREIN",[42,4379,4380],{"class":79}," scriptlet",[42,4382,4374],{"class":79},[42,4384,4385],{"class":79}," rpm",[42,4387,4388],{"class":79}," package",[42,4390,4391],{"class":79}," gitlab-ce\n",[42,4393,4394,4397,4400,4402,4404],{"class":44,"line":3281},[42,4395,4396],{"class":394},"  验证",[42,4398,4399],{"class":79},"    :",[42,4401,4349],{"class":79},[42,4403,4352],{"class":79},[42,4405,3919],{"class":56},[42,4407,4408,4410,4412,4415,4418],{"class":44,"line":3286},[42,4409,4396],{"class":394},[42,4411,4399],{"class":79},[42,4413,4414],{"class":79}," gitlab-ce-13.1.4-ce.0.el8.x86_64",[42,4416,4417],{"class":79},"                                                                                  2\u002F2",[42,4419,3919],{"class":56},[42,4421,4422],{"class":44,"line":3291},[42,4423,3193],{"emptyLinePlaceholder":1508},[42,4425,4426],{"class":44,"line":3297},[42,4427,4428],{"class":394},"失败:\n",[42,4430,4431,4434,4437],{"class":44,"line":3302},[42,4432,4433],{"class":394},"  gitlab-ce-13.1.4-ce.0.el8.x86_64",[42,4435,4436],{"class":79},"                                gitlab-ce-13.1.11-ce.0.el8.x86_64",[42,4438,4439],{"class":56},"                               \n",[42,4441,4442],{"class":44,"line":3308},[42,4443,3193],{"emptyLinePlaceholder":1508},[42,4445,4446],{"class":44,"line":3313},[42,4447,4448],{"class":394},"错误：事务失败\n",[42,4450,4451,4453,4455,4457],{"class":44,"line":3319},[42,4452,3984],{"class":52},[42,4454,4124],{"class":56},[42,4456,4127],{"class":52},[42,4458,4459],{"class":56},"#\n",[11,4461,4462],{},"手动下载 rpm 包安装，也有相同的报错信息：",[32,4464,4466],{"className":385,"code":4465,"language":387,"meta":37,"style":37},"[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",[39,4467,4468,4479,4505,4513,4520,4526,4537,4548,4552,4557],{"__ignoreMap":37},[42,4469,4470,4472,4474,4476],{"class":44,"line":45},[42,4471,3984],{"class":52},[42,4473,4124],{"class":56},[42,4475,4127],{"class":52},[42,4477,4478],{"class":56},"# rpm -Uvh gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm \n",[42,4480,4481,4484,4487,4490,4493,4496,4499,4502],{"class":44,"line":179},[42,4482,4483],{"class":394},"警告：gitlab-ce-13.1.11-ce.0.el8.x86_64.rpm:",[42,4485,4486],{"class":79}," 头V4",[42,4488,4489],{"class":79}," RSA\u002FSHA256",[42,4491,4492],{"class":79}," Signature,",[42,4494,4495],{"class":79}," 密钥",[42,4497,4498],{"class":79}," ID",[42,4500,4501],{"class":79}," f27eab47:",[42,4503,4504],{"class":79}," NOKEY\n",[42,4506,4507,4510],{"class":44,"line":412},[42,4508,4509],{"class":394},"Verifying...",[42,4511,4512],{"class":401},"                          ################################# [100%]\n",[42,4514,4515,4518],{"class":44,"line":420},[42,4516,4517],{"class":394},"准备中...",[42,4519,4512],{"class":401},[42,4521,4522,4524],{"class":44,"line":1289},[42,4523,4359],{"class":394},[42,4525,4362],{"class":56},[42,4527,4528,4531,4534],{"class":44,"line":2027},[42,4529,4530],{"class":394},"错误：gitlab-ce-13.1.11-ce.0.el8.x86_64:",[42,4532,4533],{"class":79}," 安裝",[42,4535,4536],{"class":79}," 已失败\n",[42,4538,4539,4542,4545],{"class":44,"line":2038},[42,4540,4541],{"class":394},"错误：gitlab-ce-13.1.4-ce.0.el8.x86_64:",[42,4543,4544],{"class":79}," 删除",[42,4546,4547],{"class":79}," 已跳过\n",[42,4549,4550],{"class":44,"line":2044},[42,4551,3193],{"emptyLinePlaceholder":1508},[42,4553,4554],{"class":44,"line":2058},[42,4555,4556],{"class":394},"OR\n",[42,4558,4559,4562,4565,4567,4570,4573,4576,4579,4582,4584],{"class":44,"line":2072},[42,4560,4561],{"class":394},"error:",[42,4563,4564],{"class":79}," prein",[42,4566,73],{"class":52},[42,4568,4569],{"class":394},"gitlab-ce-13.1.11-ce.0.el8.x86_64",[42,4571,4572],{"class":52},")",[42,4574,4575],{"class":79},"scriptlet",[42,4577,4578],{"class":79}," failed,",[42,4580,4581],{"class":79}," exit",[42,4583,1848],{"class":79},[42,4585,4586],{"class":438}," 1\n",[11,4588,4589],{},"用 rpm -Uvvh 查看安装详情，发现错误如下：",[32,4591,4593],{"className":385,"code":4592,"language":387,"meta":37,"style":37},"+ 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",[39,4594,4595,4603,4620,4646,4658,4673,4678,4683,4687,4692,4696,4701,4706,4713,4727,4772,4780,4817,4839,4867],{"__ignoreMap":37},[42,4596,4597,4600],{"class":44,"line":45},[42,4598,4599],{"class":394},"+",[42,4601,4602],{"class":79}," upgrade_check\n",[42,4604,4605,4608,4611,4614,4617],{"class":44,"line":179},[42,4606,4607],{"class":394},"++",[42,4609,4610],{"class":79}," gitlab-ctl",[42,4612,4613],{"class":79}," upgrade-check",[42,4615,4616],{"class":438}," 13.1.4",[42,4618,4619],{"class":438}," 13.13.1\n",[42,4621,4622,4625,4628,4631,4634,4637,4640,4643],{"class":44,"line":412},[42,4623,4624],{"class":394},"\u002Fbin\u002Fbash:",[42,4626,4627],{"class":79}," warning:",[42,4629,4630],{"class":79}," setlocale:",[42,4632,4633],{"class":79}," LC_ALL:",[42,4635,4636],{"class":79}," cannot",[42,4638,4639],{"class":79}," change",[42,4641,4642],{"class":79}," locale",[42,4644,4645],{"class":56}," (en_US.UTF-8)\n",[42,4647,4648,4650,4653,4655],{"class":44,"line":420},[42,4649,4599],{"class":394},[42,4651,4652],{"class":79}," ctl_results=",[42,4654,76],{"class":52},[42,4656,4657],{"class":79},"[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",[42,4659,4660,4663,4665,4668,4670],{"class":44,"line":1289},[42,4661,4662],{"class":79},"I don",[42,4664,76],{"class":52},[42,4666,4667],{"class":56},"\\'",[42,4669,76],{"class":52},[42,4671,4672],{"class":79},"t know that command.\n",[42,4674,4675],{"class":44,"line":2027},[42,4676,4677],{"class":79},"omnibus-ctl: command (subcommand)\n",[42,4679,4680],{"class":44,"line":2038},[42,4681,4682],{"class":79},"check-config\n",[42,4684,4685],{"class":44,"line":2044},[42,4686,3193],{"emptyLinePlaceholder":1508},[42,4688,4689],{"class":44,"line":2058},[42,4690,4691],{"class":79},".........\n",[42,4693,4694],{"class":44,"line":2072},[42,4695,3193],{"emptyLinePlaceholder":1508},[42,4697,4698],{"class":44,"line":2086},[42,4699,4700],{"class":79},"Container Registry Commands:\n",[42,4702,4703],{"class":44,"line":2882},[42,4704,4705],{"class":79},"  registry-garbage-collect\n",[42,4707,4708,4711],{"class":44,"line":2955},[42,4709,4710],{"class":79},"    Run Container Registry garbage collection.",[42,4712,2017],{"class":52},[42,4714,4715,4717,4720,4722,4725],{"class":44,"line":2961},[42,4716,4599],{"class":394},[42,4718,4719],{"class":79}," found=",[42,4721,76],{"class":52},[42,4723,4724],{"class":79},"[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.",[42,4726,2017],{"class":52},[42,4728,4729,4731,4734,4736,4738,4740,4742,4744,4746,4749,4751,4753,4755,4757,4759,4761,4764,4766,4768,4770],{"class":44,"line":2967},[42,4730,4599],{"class":394},[42,4732,4733],{"class":52}," '",[42,4735,3984],{"class":79},[42,4737,76],{"class":52},[42,4739,4733],{"class":52},[42,4741,4724],{"class":79},[42,4743,76],{"class":52},[42,4745,4733],{"class":52},[42,4747,4748],{"class":79},"!=",[42,4750,76],{"class":52},[42,4752,4733],{"class":52},[42,4754,4662],{"class":79},[42,4756,76],{"class":52},[42,4758,4667],{"class":56},[42,4760,76],{"class":52},[42,4762,4763],{"class":79},"t know that command.",[42,4765,76],{"class":52},[42,4767,4733],{"class":52},[42,4769,4127],{"class":79},[42,4771,2017],{"class":52},[42,4773,4774,4776,4778],{"class":44,"line":2973},[42,4775,4599],{"class":394},[42,4777,4581],{"class":79},[42,4779,4586],{"class":438},[42,4781,4782,4785,4788,4790,4792,4794,4796,4799,4801,4804,4806,4809,4812,4814],{"class":44,"line":2979},[42,4783,4784],{"class":394},"D:",[42,4786,4787],{"class":79}," %prein",[42,4789,73],{"class":52},[42,4791,4569],{"class":394},[42,4793,4572],{"class":52},[42,4795,2370],{"class":79},[42,4797,4798],{"class":79}," waitpid",[42,4800,73],{"class":52},[42,4802,4803],{"class":394},"35960",[42,4805,4572],{"class":52},[42,4807,4808],{"class":79}," rc",[42,4810,4811],{"class":438}," 35960",[42,4813,1848],{"class":79},[42,4815,4816],{"class":438}," 100\n",[42,4818,4819,4821,4823,4825,4827,4829,4831,4833,4835,4837],{"class":44,"line":2985},[42,4820,4561],{"class":394},[42,4822,4787],{"class":79},[42,4824,73],{"class":52},[42,4826,4569],{"class":394},[42,4828,4572],{"class":52},[42,4830,4380],{"class":79},[42,4832,4578],{"class":79},[42,4834,4581],{"class":79},[42,4836,1848],{"class":79},[42,4838,4586],{"class":438},[42,4840,4841,4844,4847,4850,4853,4856,4859,4861,4864],{"class":44,"line":2990},[42,4842,4843],{"class":394},"ufdio:",[42,4845,4846],{"class":438},"       6",[42,4848,4849],{"class":79}," reads,",[42,4851,4852],{"class":438},"  8219028",[42,4854,4855],{"class":79}," total",[42,4857,4858],{"class":79}," bytes",[42,4860,4374],{"class":79},[42,4862,4863],{"class":438}," 0.001505",[42,4865,4866],{"class":79}," secs\n",[42,4868,4869,4871,4874,4876],{"class":44,"line":2996},[42,4870,4561],{"class":394},[42,4872,4873],{"class":79}," gitlab-ce-13.1.11-ce.0.el8.x86_64:",[42,4875,4030],{"class":79},[42,4877,4009],{"class":79},[11,4879,4880],{},"可能是因为我安装系统的时候选择了中文，系统里竟然没有英文语言包？！\nlocale -a 如下：",[32,4882,4884],{"className":385,"code":4883,"language":387,"meta":37,"style":37},"[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",[39,4885,4886,4899,4910,4948,4975,5002,5007,5012,5017,5022,5027,5032,5037,5042,5047,5052,5057,5062,5067,5072],{"__ignoreMap":37},[42,4887,4888,4890,4893,4896],{"class":44,"line":45},[42,4889,3984],{"class":52},[42,4891,4892],{"class":56},"root@HyperV-GitLab ",[42,4894,4895],{"class":52},"~]",[42,4897,4898],{"class":56},"# vi \u002Fetc\u002Fenvironment\n",[42,4900,4901,4903,4905,4907],{"class":44,"line":179},[42,4902,3984],{"class":52},[42,4904,4892],{"class":56},[42,4906,4895],{"class":52},[42,4908,4909],{"class":56},"# locale -a\n",[42,4911,4912,4915,4918,4921,4924,4927,4930,4933,4936,4939,4942,4945],{"class":44,"line":412},[42,4913,4914],{"class":394},"locale:",[42,4916,4917],{"class":79}," Cannot",[42,4919,4920],{"class":79}," set",[42,4922,4923],{"class":79}," LC_CTYPE",[42,4925,4926],{"class":79}," to",[42,4928,4929],{"class":79}," default",[42,4931,4932],{"class":79}," locale:",[42,4934,4935],{"class":79}," No",[42,4937,4938],{"class":79}," such",[42,4940,4941],{"class":79}," file",[42,4943,4944],{"class":79}," or",[42,4946,4947],{"class":79}," directory\n",[42,4949,4950,4952,4954,4956,4959,4961,4963,4965,4967,4969,4971,4973],{"class":44,"line":420},[42,4951,4914],{"class":394},[42,4953,4917],{"class":79},[42,4955,4920],{"class":79},[42,4957,4958],{"class":79}," LC_MESSAGES",[42,4960,4926],{"class":79},[42,4962,4929],{"class":79},[42,4964,4932],{"class":79},[42,4966,4935],{"class":79},[42,4968,4938],{"class":79},[42,4970,4941],{"class":79},[42,4972,4944],{"class":79},[42,4974,4947],{"class":79},[42,4976,4977,4979,4981,4983,4986,4988,4990,4992,4994,4996,4998,5000],{"class":44,"line":1289},[42,4978,4914],{"class":394},[42,4980,4917],{"class":79},[42,4982,4920],{"class":79},[42,4984,4985],{"class":79}," LC_COLLATE",[42,4987,4926],{"class":79},[42,4989,4929],{"class":79},[42,4991,4932],{"class":79},[42,4993,4935],{"class":79},[42,4995,4938],{"class":79},[42,4997,4941],{"class":79},[42,4999,4944],{"class":79},[42,5001,4947],{"class":79},[42,5003,5004],{"class":44,"line":2027},[42,5005,5006],{"class":394},"C\n",[42,5008,5009],{"class":44,"line":2038},[42,5010,5011],{"class":394},"C.utf8\n",[42,5013,5014],{"class":44,"line":2044},[42,5015,5016],{"class":394},"POSIX\n",[42,5018,5019],{"class":44,"line":2058},[42,5020,5021],{"class":394},"zh_CN\n",[42,5023,5024],{"class":44,"line":2072},[42,5025,5026],{"class":394},"zh_CN.gb18030\n",[42,5028,5029],{"class":44,"line":2086},[42,5030,5031],{"class":394},"zh_CN.gbk\n",[42,5033,5034],{"class":44,"line":2882},[42,5035,5036],{"class":394},"zh_CN.utf8\n",[42,5038,5039],{"class":44,"line":2955},[42,5040,5041],{"class":394},"zh_HK\n",[42,5043,5044],{"class":44,"line":2961},[42,5045,5046],{"class":394},"zh_HK.utf8\n",[42,5048,5049],{"class":44,"line":2967},[42,5050,5051],{"class":394},"zh_SG\n",[42,5053,5054],{"class":44,"line":2973},[42,5055,5056],{"class":394},"zh_SG.gbk\n",[42,5058,5059],{"class":44,"line":2979},[42,5060,5061],{"class":394},"zh_SG.utf8\n",[42,5063,5064],{"class":44,"line":2985},[42,5065,5066],{"class":394},"zh_TW\n",[42,5068,5069],{"class":44,"line":2990},[42,5070,5071],{"class":394},"zh_TW.euctw\n",[42,5073,5074],{"class":44,"line":2996},[42,5075,5076],{"class":394},"zh_TW.utf8\n",[11,5078,5079],{},"解决方法：\n安装英文语言包",[32,5081,5083],{"className":385,"code":5082,"language":387,"meta":37,"style":37},"yum install langpacks-en.noarch\n",[39,5084,5085],{"__ignoreMap":37},[42,5086,5087,5090,5092],{"class":44,"line":45},[42,5088,5089],{"class":394},"yum",[42,5091,4030],{"class":79},[42,5093,5094],{"class":79}," langpacks-en.noarch\n",[1486,5096,5097],{},"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":37,"searchDepth":179,"depth":179,"links":5099},[5100,5101,5102,5103],{"id":3809,"depth":412,"text":3809},{"id":3821,"depth":412,"text":3822},{"id":3842,"depth":412,"text":3843},{"id":4108,"depth":412,"text":4109},"从 11 升到 13 的路上踩了三个坑：不能跨大版本、pg_dump 版本不匹配，以及系统没装英文语言包导致 rpm 安装脚本失败。",{},"\u002Fposts\u002Fgitlab-ce-upgrade-11-to-13",{"title":3804,"description":5104},"posts\u002Fgitlab-ce-upgrade-11-to-13",[5110,2180,2179],"GitLab","nnFc8mJVonUKcYjTZFb618eeRy0IZQpUDEcefyCsPhU",{"id":5113,"title":5114,"body":5115,"date":6377,"description":6378,"extension":1506,"meta":6379,"navigation":1508,"path":6380,"seo":6381,"stem":6382,"tags":6383,"__hash__":6385},"posts\u002Fposts\u002Fjar2pom-maven-dependency.md","基于 Maven 中心库，通过 Jar 包生成 POM 的 dependency",{"type":8,"value":5116,"toc":6369},[5117,5119,5138,5141,5144,5150,5169,5172,5991,5995,6002,6011,6014,6182,6189,6209,6237,6243,6246,6249,6336,6339,6356,6367],[97,5118,99],{"id":99},[11,5120,5121,5122,5127,5128,5133,5134,95],{},"之前写过一篇",[15,5123,5126],{"href":5124,"rel":5125},"https:\u002F\u002Fblog.csdn.net\u002FJustinJia91\u002Farticle\u002Fdetails\u002F79605682",[19],"通过 Jar 包生成 Maven POM.xml 的 dependency","，方便将非 Maven 项目转换为 Maven 项目。\n原项目对 Jar 的搜索基于 Nexus2 的相关接口开发，随着很多仓库从 Nexus2 升级到 Nexus3 或者其他服务，原项目渐渐不可用了。\n新代码将 Nexus2 接口改为对接 ",[15,5129,5132],{"href":5130,"rel":5131},"https:\u002F\u002Fsearch.maven.org\u002F",[19],"Maven 中心库","。站内另留了一份 ",[15,5135,5137],{"href":5136},"\u002Fposts\u002Fjar2pom-nexus2-dependency","Nexus2 版的代码",[97,5139,5140],{"id":5140},"项目结构",[11,5142,5143],{},"整个项目只有 6 个 Java 文件 —— 一个主类，加上五个用来接中心库返回值的实体类：",[32,5145,5148],{"className":5146,"code":5147,"language":2541},[2539],"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",[39,5149,5147],{"__ignoreMap":37},[11,5151,5152,5153,5156,5157,5160,5161,5164,5165,5168],{},"Maven 坐标是 ",[39,5154,5155],{},"cn.jxh.tool:jar2pom:2.0-SNAPSHOT","，依赖只有三个：",[39,5158,5159],{},"httpclient"," 4.5.5 负责发请求，",[39,5162,5163],{},"fastjson"," 1.2.68 负责解 JSON，",[39,5166,5167],{},"dom4j"," 负责拼 XML。README 里记的开发环境是 JDK 1.7 + Maven 3.5 + IDEA 社区版。",[97,5170,5171],{"id":5171},"核心代码",[32,5173,5175],{"className":3125,"code":5174,"language":2506,"meta":37,"style":37},"    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",[39,5176,5177,5182,5187,5192,5197,5201,5206,5210,5215,5220,5225,5230,5235,5239,5243,5248,5252,5257,5261,5265,5269,5273,5278,5282,5287,5291,5295,5299,5303,5307,5312,5316,5321,5325,5329,5334,5339,5343,5348,5353,5358,5362,5367,5372,5376,5381,5385,5389,5393,5397,5402,5406,5410,5414,5418,5423,5427,5431,5436,5441,5446,5451,5456,5461,5465,5470,5475,5480,5485,5490,5495,5500,5504,5509,5514,5518,5523,5528,5533,5538,5543,5548,5552,5557,5562,5566,5571,5576,5580,5585,5590,5594,5599,5604,5608,5612,5616,5620,5624,5629,5633,5638,5642,5646,5651,5656,5661,5666,5671,5676,5680,5685,5689,5693,5697,5702,5706,5712,5717,5722,5728,5734,5740,5746,5751,5757,5763,5768,5773,5779,5785,5791,5797,5803,5808,5813,5819,5824,5829,5834,5840,5845,5850,5856,5861,5866,5872,5878,5884,5889,5895,5901,5907,5913,5918,5924,5930,5936,5942,5947,5953,5959,5964,5969,5974,5980,5986],{"__ignoreMap":37},[42,5178,5179],{"class":44,"line":45},[42,5180,5181],{},"    public static void main(String[] args) {\n",[42,5183,5184],{"class":44,"line":179},[42,5185,5186],{},"        \u002F\u002F先通过Jar的SHA1查询 如果不存在则解析Manifest查询\n",[42,5188,5189],{"class":44,"line":412},[42,5190,5191],{},"        File libs = new File(\"D:\\\\lib\");\n",[42,5193,5194],{"class":44,"line":420},[42,5195,5196],{},"        for (File jar : libs.listFiles()) {\n",[42,5198,5199],{"class":44,"line":1289},[42,5200,3193],{"emptyLinePlaceholder":1508},[42,5202,5203],{"class":44,"line":2027},[42,5204,5205],{},"            System.out.println(\"\u003C!--  \" + jar.getName() + \" -->\");\n",[42,5207,5208],{"class":44,"line":2038},[42,5209,3193],{"emptyLinePlaceholder":1508},[42,5211,5212],{"class":44,"line":2044},[42,5213,5214],{},"            Docs doc = getPomByChecksum(jar);\n",[42,5216,5217],{"class":44,"line":2058},[42,5218,5219],{},"            if (null != doc) {\n",[42,5221,5222],{"class":44,"line":2072},[42,5223,5224],{},"                System.out.println(\"\u003C!--  Search by Checksum -->\");\n",[42,5226,5227],{"class":44,"line":2086},[42,5228,5229],{},"                System.out.println(assemblePomElement(doc).asXML());\n",[42,5231,5232],{"class":44,"line":2882},[42,5233,5234],{},"                continue;\n",[42,5236,5237],{"class":44,"line":2955},[42,5238,3578],{},[42,5240,5241],{"class":44,"line":2961},[42,5242,3193],{"emptyLinePlaceholder":1508},[42,5244,5245],{"class":44,"line":2967},[42,5246,5247],{},"            doc = getPomByManifest(jar);\n",[42,5249,5250],{"class":44,"line":2973},[42,5251,5219],{},[42,5253,5254],{"class":44,"line":2979},[42,5255,5256],{},"                System.out.println(\"\u003C!--  Search by Manifest -->\");\n",[42,5258,5259],{"class":44,"line":2985},[42,5260,5229],{},[42,5262,5263],{"class":44,"line":2990},[42,5264,5234],{},[42,5266,5267],{"class":44,"line":2996},[42,5268,3578],{},[42,5270,5271],{"class":44,"line":3001},[42,5272,3193],{"emptyLinePlaceholder":1508},[42,5274,5275],{"class":44,"line":3235},[42,5276,5277],{},"            System.out.println(\"\u003C!--  No data was found -->\");\n",[42,5279,5280],{"class":44,"line":3241},[42,5281,3193],{"emptyLinePlaceholder":1508},[42,5283,5284],{"class":44,"line":3247},[42,5285,5286],{},"            System.out.println();\n",[42,5288,5289],{"class":44,"line":3253},[42,5290,3746],{},[42,5292,5293],{"class":44,"line":3258},[42,5294,3193],{"emptyLinePlaceholder":1508},[42,5296,5297],{"class":44,"line":3264},[42,5298,3345],{},[42,5300,5301],{"class":44,"line":3269},[42,5302,3193],{"emptyLinePlaceholder":1508},[42,5304,5305],{"class":44,"line":3275},[42,5306,3356],{},[42,5308,5309],{"class":44,"line":3281},[42,5310,5311],{},"     * 通过Jar SHA1返回Pom dependency\n",[42,5313,5314],{"class":44,"line":3286},[42,5315,3368],{},[42,5317,5318],{"class":44,"line":3291},[42,5319,5320],{},"     * @param file\n",[42,5322,5323],{"class":44,"line":3297},[42,5324,3374],{},[42,5326,5327],{"class":44,"line":3302},[42,5328,3380],{},[42,5330,5331],{"class":44,"line":3308},[42,5332,5333],{},"    public static Docs getPomByChecksum(File file) {\n",[42,5335,5336],{"class":44,"line":3313},[42,5337,5338],{},"        Docs docs = null;\n",[42,5340,5341],{"class":44,"line":3319},[42,5342,3193],{"emptyLinePlaceholder":1508},[42,5344,5345],{"class":44,"line":3324},[42,5346,5347],{},"        String checkSum = getCheckSum(file, \"SHA1\");\n",[42,5349,5350],{"class":44,"line":3330},[42,5351,5352],{},"        String jsonString = doGet(nexusUrl + \"&q=1:\" + checkSum);\n",[42,5354,5355],{"class":44,"line":3336},[42,5356,5357],{},"        SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n",[42,5359,5360],{"class":44,"line":3342},[42,5361,3193],{"emptyLinePlaceholder":1508},[42,5363,5364],{"class":44,"line":3348},[42,5365,5366],{},"        if (solrSearch.getResponseHeader().getStatus() == 0 && solrSearch.getResponse().getDocs().size() > 0) {\n",[42,5368,5369],{"class":44,"line":3353},[42,5370,5371],{},"            docs = solrSearch.getResponse().getDocs().get(0);\n",[42,5373,5374],{"class":44,"line":3359},[42,5375,3746],{},[42,5377,5378],{"class":44,"line":3365},[42,5379,5380],{},"        return docs;\n",[42,5382,5383],{"class":44,"line":3371},[42,5384,3345],{},[42,5386,5387],{"class":44,"line":3377},[42,5388,3193],{"emptyLinePlaceholder":1508},[42,5390,5391],{"class":44,"line":3383},[42,5392,3193],{"emptyLinePlaceholder":1508},[42,5394,5395],{"class":44,"line":3389},[42,5396,3356],{},[42,5398,5399],{"class":44,"line":3395},[42,5400,5401],{},"     * 通过Jar Manifest返回Pom dependency\n",[42,5403,5404],{"class":44,"line":3400},[42,5405,3368],{},[42,5407,5408],{"class":44,"line":3405},[42,5409,5320],{},[42,5411,5412],{"class":44,"line":3410},[42,5413,3374],{},[42,5415,5416],{"class":44,"line":3416},[42,5417,3380],{},[42,5419,5420],{"class":44,"line":3421},[42,5421,5422],{},"    public static Docs getPomByManifest(File file) {\n",[42,5424,5425],{"class":44,"line":3426},[42,5426,5338],{},[42,5428,5429],{"class":44,"line":3431},[42,5430,3193],{"emptyLinePlaceholder":1508},[42,5432,5433],{"class":44,"line":3437},[42,5434,5435],{},"        try {\n",[42,5437,5438],{"class":44,"line":3443},[42,5439,5440],{},"            JarFile jarfile = new JarFile(file);\n",[42,5442,5443],{"class":44,"line":3448},[42,5444,5445],{},"            Manifest mainmanifest = jarfile.getManifest();\n",[42,5447,5448],{"class":44,"line":3453},[42,5449,5450],{},"            jarfile.close();\n",[42,5452,5453],{"class":44,"line":3458},[42,5454,5455],{},"            if (null == mainmanifest) {\n",[42,5457,5458],{"class":44,"line":3464},[42,5459,5460],{},"                return null;\n",[42,5462,5463],{"class":44,"line":3469},[42,5464,3578],{},[42,5466,5467],{"class":44,"line":3475},[42,5468,5469],{},"            String a = null, v = null;\n",[42,5471,5472],{"class":44,"line":3480},[42,5473,5474],{},"            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Extension-Name\"))) {\n",[42,5476,5477],{"class":44,"line":3486},[42,5478,5479],{},"                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Extension-Name\"));\n",[42,5481,5482],{"class":44,"line":3492},[42,5483,5484],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Title\"))) {\n",[42,5486,5487],{"class":44,"line":3498},[42,5488,5489],{},"                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Title\"));\n",[42,5491,5492],{"class":44,"line":3504},[42,5493,5494],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Title\"))) {\n",[42,5496,5497],{"class":44,"line":3510},[42,5498,5499],{},"                a = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Title\"));\n",[42,5501,5502],{"class":44,"line":3516},[42,5503,3578],{},[42,5505,5506],{"class":44,"line":3522},[42,5507,5508],{},"            if (a != null && a.length() != 0) {\n",[42,5510,5511],{"class":44,"line":3528},[42,5512,5513],{},"                a = a.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n",[42,5515,5516],{"class":44,"line":3534},[42,5517,3578],{},[42,5519,5520],{"class":44,"line":3540},[42,5521,5522],{},"            if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Bundle-Version\"))) {\n",[42,5524,5525],{"class":44,"line":3546},[42,5526,5527],{},"                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Bundle-Version\"));\n",[42,5529,5530],{"class":44,"line":3551},[42,5531,5532],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Implementation-Version\"))) {\n",[42,5534,5535],{"class":44,"line":3557},[42,5536,5537],{},"                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Implementation-Version\"));\n",[42,5539,5540],{"class":44,"line":3563},[42,5541,5542],{},"            } else if (mainmanifest.getMainAttributes().containsKey(new Attributes.Name(\"Specification-Version\"))) {\n",[42,5544,5545],{"class":44,"line":3569},[42,5546,5547],{},"                v = mainmanifest.getMainAttributes().getValue(new Attributes.Name(\"Specification-Version\"));\n",[42,5549,5550],{"class":44,"line":3575},[42,5551,3578],{},[42,5553,5554],{"class":44,"line":3581},[42,5555,5556],{},"            if (v != null && v.length() != 0) {\n",[42,5558,5559],{"class":44,"line":3587},[42,5560,5561],{},"                v = v.replace(\"\\\"\", \"\").replace(\" \", \"-\");\n",[42,5563,5564],{"class":44,"line":3592},[42,5565,3578],{},[42,5567,5568],{"class":44,"line":3597},[42,5569,5570],{},"            String jsonString = doGet(nexusUrl + \"&q=a:\" + a + \"%20v:\" + v);\n",[42,5572,5573],{"class":44,"line":3602},[42,5574,5575],{},"            SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n",[42,5577,5578],{"class":44,"line":3608},[42,5579,3193],{"emptyLinePlaceholder":1508},[42,5581,5582],{"class":44,"line":3613},[42,5583,5584],{},"            if (solrSearch.getResponseHeader().getStatus() == 0) {\n",[42,5586,5587],{"class":44,"line":3619},[42,5588,5589],{},"                docs = solrSearch.getResponse().getDocs().get(0);\n",[42,5591,5592],{"class":44,"line":3624},[42,5593,3578],{},[42,5595,5596],{"class":44,"line":3629},[42,5597,5598],{},"        } catch (IOException e) {\n",[42,5600,5601],{"class":44,"line":3635},[42,5602,5603],{},"            e.printStackTrace();\n",[42,5605,5606],{"class":44,"line":3641},[42,5607,3746],{},[42,5609,5610],{"class":44,"line":3647},[42,5611,5380],{},[42,5613,5614],{"class":44,"line":3653},[42,5615,3345],{},[42,5617,5618],{"class":44,"line":3659},[42,5619,3193],{"emptyLinePlaceholder":1508},[42,5621,5622],{"class":44,"line":3665},[42,5623,3356],{},[42,5625,5626],{"class":44,"line":3670},[42,5627,5628],{},"     * 组装 dependency\n",[42,5630,5631],{"class":44,"line":3675},[42,5632,3368],{},[42,5634,5635],{"class":44,"line":3680},[42,5636,5637],{},"     * @param docs\n",[42,5639,5640],{"class":44,"line":3686},[42,5641,3374],{},[42,5643,5644],{"class":44,"line":3691},[42,5645,3380],{},[42,5647,5648],{"class":44,"line":3697},[42,5649,5650],{},"    public static Element assemblePomElement(Docs docs) {\n",[42,5652,5653],{"class":44,"line":3703},[42,5654,5655],{},"        Element dependency = new DOMElement(\"dependency\");\n",[42,5657,5658],{"class":44,"line":3708},[42,5659,5660],{},"        if (docs != null) {\n",[42,5662,5663],{"class":44,"line":3713},[42,5664,5665],{},"            dependency.addAttribute(\"groupId\", docs.getG());\n",[42,5667,5668],{"class":44,"line":3719},[42,5669,5670],{},"            dependency.addAttribute(\"artifactId\", docs.getA());\n",[42,5672,5673],{"class":44,"line":3725},[42,5674,5675],{},"            dependency.addAttribute(\"version\", docs.getV());\n",[42,5677,5678],{"class":44,"line":3731},[42,5679,3746],{},[42,5681,5682],{"class":44,"line":3737},[42,5683,5684],{},"        return dependency;\n",[42,5686,5687],{"class":44,"line":3743},[42,5688,3345],{},[42,5690,5691],{"class":44,"line":3749},[42,5692,3193],{"emptyLinePlaceholder":1508},[42,5694,5695],{"class":44,"line":3755},[42,5696,3356],{},[42,5698,5699],{"class":44,"line":3760},[42,5700,5701],{},"     * 发起Get请求\n",[42,5703,5704],{"class":44,"line":3765},[42,5705,3368],{},[42,5707,5709],{"class":44,"line":5708},117,[42,5710,5711],{},"     * @param url\n",[42,5713,5715],{"class":44,"line":5714},118,[42,5716,3374],{},[42,5718,5720],{"class":44,"line":5719},119,[42,5721,3380],{},[42,5723,5725],{"class":44,"line":5724},120,[42,5726,5727],{},"    public static String doGet(String url) {\n",[42,5729,5731],{"class":44,"line":5730},121,[42,5732,5733],{},"        String srtResult = null;\n",[42,5735,5737],{"class":44,"line":5736},122,[42,5738,5739],{},"        CloseableHttpClient httpCilent = HttpClients.createDefault();\n",[42,5741,5743],{"class":44,"line":5742},123,[42,5744,5745],{},"        HttpGet httpGet = new HttpGet(url);\n",[42,5747,5749],{"class":44,"line":5748},124,[42,5750,5435],{},[42,5752,5754],{"class":44,"line":5753},125,[42,5755,5756],{},"            HttpResponse httpResponse = httpCilent.execute(httpGet);\n",[42,5758,5760],{"class":44,"line":5759},126,[42,5761,5762],{},"            srtResult = EntityUtils.toString(httpResponse.getEntity());\n",[42,5764,5766],{"class":44,"line":5765},127,[42,5767,5598],{},[42,5769,5771],{"class":44,"line":5770},128,[42,5772,5603],{},[42,5774,5776],{"class":44,"line":5775},129,[42,5777,5778],{},"        } finally {\n",[42,5780,5782],{"class":44,"line":5781},130,[42,5783,5784],{},"            try {\n",[42,5786,5788],{"class":44,"line":5787},131,[42,5789,5790],{},"                httpCilent.close();\n",[42,5792,5794],{"class":44,"line":5793},132,[42,5795,5796],{},"            } catch (IOException e) {\n",[42,5798,5800],{"class":44,"line":5799},133,[42,5801,5802],{},"                e.printStackTrace();\n",[42,5804,5806],{"class":44,"line":5805},134,[42,5807,3578],{},[42,5809,5811],{"class":44,"line":5810},135,[42,5812,3746],{},[42,5814,5816],{"class":44,"line":5815},136,[42,5817,5818],{},"        return srtResult;\n",[42,5820,5822],{"class":44,"line":5821},137,[42,5823,3345],{},[42,5825,5827],{"class":44,"line":5826},138,[42,5828,3193],{"emptyLinePlaceholder":1508},[42,5830,5832],{"class":44,"line":5831},139,[42,5833,3356],{},[42,5835,5837],{"class":44,"line":5836},140,[42,5838,5839],{},"     * 计算CheckSum\n",[42,5841,5843],{"class":44,"line":5842},141,[42,5844,3368],{},[42,5846,5848],{"class":44,"line":5847},142,[42,5849,5320],{},[42,5851,5853],{"class":44,"line":5852},143,[42,5854,5855],{},"     * @param algorithm SHA1 or MD5\n",[42,5857,5859],{"class":44,"line":5858},144,[42,5860,3374],{},[42,5862,5864],{"class":44,"line":5863},145,[42,5865,3380],{},[42,5867,5869],{"class":44,"line":5868},146,[42,5870,5871],{},"    public static String getCheckSum(File file, String algorithm) {\n",[42,5873,5875],{"class":44,"line":5874},147,[42,5876,5877],{},"        if (!file.isFile()) {\n",[42,5879,5881],{"class":44,"line":5880},148,[42,5882,5883],{},"            return null;\n",[42,5885,5887],{"class":44,"line":5886},149,[42,5888,3746],{},[42,5890,5892],{"class":44,"line":5891},150,[42,5893,5894],{},"        MessageDigest digest = null;\n",[42,5896,5898],{"class":44,"line":5897},151,[42,5899,5900],{},"        FileInputStream in = null;\n",[42,5902,5904],{"class":44,"line":5903},152,[42,5905,5906],{},"        byte buffer[] = new byte[1024];\n",[42,5908,5910],{"class":44,"line":5909},153,[42,5911,5912],{},"        int len;\n",[42,5914,5916],{"class":44,"line":5915},154,[42,5917,5435],{},[42,5919,5921],{"class":44,"line":5920},155,[42,5922,5923],{},"            digest = MessageDigest.getInstance(algorithm);\n",[42,5925,5927],{"class":44,"line":5926},156,[42,5928,5929],{},"            in = new FileInputStream(file);\n",[42,5931,5933],{"class":44,"line":5932},157,[42,5934,5935],{},"            while ((len = in.read(buffer, 0, 1024)) != -1) {\n",[42,5937,5939],{"class":44,"line":5938},158,[42,5940,5941],{},"                digest.update(buffer, 0, len);\n",[42,5943,5945],{"class":44,"line":5944},159,[42,5946,3578],{},[42,5948,5950],{"class":44,"line":5949},160,[42,5951,5952],{},"            in.close();\n",[42,5954,5956],{"class":44,"line":5955},161,[42,5957,5958],{},"        } catch (Exception e) {\n",[42,5960,5962],{"class":44,"line":5961},162,[42,5963,5603],{},[42,5965,5967],{"class":44,"line":5966},163,[42,5968,5883],{},[42,5970,5972],{"class":44,"line":5971},164,[42,5973,3746],{},[42,5975,5977],{"class":44,"line":5976},165,[42,5978,5979],{},"        BigInteger bigInt = new BigInteger(1, digest.digest());\n",[42,5981,5983],{"class":44,"line":5982},166,[42,5984,5985],{},"        return bigInt.toString(16);\n",[42,5987,5989],{"class":44,"line":5988},167,[42,5990,3345],{},[97,5992,5994],{"id":5993},"solr-的响应是怎么变成对象的","Solr 的响应是怎么变成对象的",[11,5996,5997,5998,6001],{},"中心库这套接口底下是 Solr，回给你的是一坨嵌套 JSON。代码没有一层层 ",[39,5999,6000],{},"getJSONObject()"," 去抠字段，而是让 fastjson 直接把整段响应收进对象：",[32,6003,6005],{"className":3125,"code":6004,"language":2506,"meta":37,"style":37},"SolrSearch solrSearch = JSONObject.parseObject(jsonString, SolrSearch.class);\n",[39,6006,6007],{"__ignoreMap":37},[42,6008,6009],{"class":44,"line":45},[42,6010,6004],{},[11,6012,6013],{},"映射规则就是「字段名对上即可」，所以五个实体类都很薄：",[278,6015,6016,6032],{},[281,6017,6018],{},[284,6019,6020,6023,6026,6029],{},[287,6021,6022],{},"类",[287,6024,6025],{},"对应节点",[287,6027,6028],{},"字段",[287,6030,6031],{},"实际用到",[294,6033,6034,6059,6084,6110,6163],{},[284,6035,6036,6041,6044,6056],{},[299,6037,6038],{},[39,6039,6040],{},"SolrSearch",[299,6042,6043],{},"根节点",[299,6045,6046,6049,6050,6049,6053],{},[39,6047,6048],{},"responseHeader"," \u002F ",[39,6051,6052],{},"response",[39,6054,6055],{},"spellcheck",[299,6057,6058],{},"三个容器",[284,6060,6061,6066,6070,6078],{},[299,6062,6063],{},[39,6064,6065],{},"ResponseHeader",[299,6067,6068],{},[39,6069,6048],{},[299,6071,6072,6049,6075],{},[39,6073,6074],{},"status",[39,6076,6077],{},"QTime",[299,6079,6080,6083],{},[39,6081,6082],{},"status == 0"," 即查询成功",[284,6085,6086,6091,6095,6106],{},[299,6087,6088],{},[39,6089,6090],{},"Response",[299,6092,6093],{},[39,6094,6052],{},[299,6096,6097,6049,6100,6049,6103],{},[39,6098,6099],{},"numFound",[39,6101,6102],{},"start",[39,6104,6105],{},"docs",[299,6107,6108],{},[39,6109,6105],{},[284,6111,6112,6117,6123,6155],{},[299,6113,6114],{},[39,6115,6116],{},"Docs",[299,6118,6119,6122],{},[39,6120,6121],{},"docs[]"," 的每一项",[299,6124,6125,6049,6128,6049,6131,6049,6133,6049,6136,6049,6138,6049,6141,6049,6144,6049,6147,6049,6149,6049,6152],{},[39,6126,6127],{},"id",[39,6129,6130],{},"g",[39,6132,15],{},[39,6134,6135],{},"v",[39,6137,11],{},[39,6139,6140],{},"latestVersion",[39,6142,6143],{},"versionCount",[39,6145,6146],{},"timestamp",[39,6148,2541],{},[39,6150,6151],{},"ec",[39,6153,6154],{},"repositoryId",[299,6156,6157,6049,6159,6049,6161],{},[39,6158,6130],{},[39,6160,15],{},[39,6162,6135],{},[284,6164,6165,6170,6174,6179],{},[299,6166,6167],{},[39,6168,6169],{},"Spellcheck",[299,6171,6172],{},[39,6173,6055],{},[299,6175,6176],{},[39,6177,6178],{},"suggestions",[299,6180,6181],{},"没用上",[11,6183,6184,6185,6188],{},"能落进 ",[39,6186,6187],{},"dependency"," 的只有三个字段，正好是 Solr 那份命名里的单字母缩写：",[32,6190,6192],{"className":3125,"code":6191,"language":2506,"meta":37,"style":37},"dependency.addAttribute(\"groupId\", docs.getG());\ndependency.addAttribute(\"artifactId\", docs.getA());\ndependency.addAttribute(\"version\", docs.getV());\n",[39,6193,6194,6199,6204],{"__ignoreMap":37},[42,6195,6196],{"class":44,"line":45},[42,6197,6198],{},"dependency.addAttribute(\"groupId\", docs.getG());\n",[42,6200,6201],{"class":44,"line":179},[42,6202,6203],{},"dependency.addAttribute(\"artifactId\", docs.getA());\n",[42,6205,6206],{"class":44,"line":412},[42,6207,6208],{},"dependency.addAttribute(\"version\", docs.getV());\n",[11,6210,6211,6049,6213,6049,6215,6217,6218,6220,6221,6223,6224,6226,6227,6229,6230,6232,6233,6236],{},[39,6212,6130],{},[39,6214,15],{},[39,6216,6135],{}," 之外的那些 —— ",[39,6219,6140],{},"（最新版本）、",[39,6222,11],{},"（打包类型）、",[39,6225,6151],{},"（该版本下有哪些文件）、",[39,6228,6143],{},"（版本总数）、",[39,6231,6154],{},"（命中哪个仓库）、",[39,6234,6235],{},"Spellcheck.suggestions","（拼写建议）—— 都解析了但没用。类的骨架留在那里，将来想加「取最新版」或者「提示拼写」，字段是现成的。",[11,6238,6239,6240,95],{},"用实体类而不是手写 JSON 路径，好处很直白：解析一行就完成，后面取值都是 getter，字段名写错了编译器直接报，而不是运行时静默接到一个 ",[39,6241,6242],{},"null",[97,6244,6245],{"id":6245},"它什么时候查不出结果",[11,6247,6248],{},"这个工具解决的是「一批本地 jar，不知道 Maven 坐标」。但它依赖两个前提：jar 公开发布过，且 Manifest 没被改过。下面这几种情况它就交不出来，用之前心里有个数：",[1580,6250,6251,6261,6271,6302,6316],{},[1437,6252,6253,6256,6257,6260],{},[107,6254,6255],{},"jar 压根不在 Maven 中心库。"," 中心库只收公开发布过的构件。国内厂商私服的包、公司内部工具包，以及因为授权原因不能上中心库的（典型是 Oracle 的 JDBC 驱动），SHA1 查不到，Manifest 也拼不出可用坐标，最后只打印一行 ",[39,6258,6259],{},"No data was found","，得自己手写。",[1437,6262,6263,6266,6267,6270],{},[107,6264,6265],{},"只查中心库一个源。"," ",[39,6268,6269],{},"nexusUrl"," 是写死的中心库地址，查不了公司私服（Nexus3、Artifactory）。要换成自家仓库，改这个常量和对应的查询参数即可 —— 2018 那版就是从 Nexus2 这么换过来的，前提是私服也提供可搜索接口。",[1437,6272,6273,6276,6277,6280,6281,6280,6284,6287,6288,6280,6291,6280,6294,6297,6298,6301],{},[107,6274,6275],{},"Manifest 里的名字不等于 Maven 坐标。"," 回退这条路，artifactId 是拿 ",[39,6278,6279],{},"Extension-Name"," → ",[39,6282,6283],{},"Implementation-Title",[39,6285,6286],{},"Specification-Title"," 依次取的，version 取 ",[39,6289,6290],{},"Bundle-Version",[39,6292,6293],{},"Implementation-Version",[39,6295,6296],{},"Specification-Version","。很多 jar 这几个字段是空的，或者写的是「XXX SDK」这种给人看的名字，搜出来的结果就不保证对；被改过包名、重新打过的 jar，SHA1 也会落空，最后都只能走到这条「猜」的路上。所以输出里标着 ",[39,6299,6300],{},"Search by Manifest"," 的那几条，建议开 Maven 仓库页面核一眼。",[1437,6303,6304,6307,6308,6311,6312,6315],{},[107,6305,6306],{},"命中多条时只取第一条。"," 无论走哪条路，最后都是 ",[39,6309,6310],{},"getDocs().get(0)","，没有按版本排序再挑这类逻辑 —— 查询串里本来就带了 ",[39,6313,6314],{},"rows=1","，只打算要一条。",[1437,6317,6318,6266,6321,6324,6325,580,6328,6331,6332,6335],{},[107,6319,6320],{},"用法上的硬编码。",[39,6322,6323],{},"main()"," 里待处理目录写死成 ",[39,6326,6327],{},"D:\\lib",[39,6329,6330],{},"libs.listFiles()"," 只扫一层、也不筛后缀，换成别的路径、或者 ",[39,6333,6334],{},"lib"," 下面还有子目录，都得先改代码；结果是打在标准输出里的，得自己从控制台复制出来。",[97,6337,6338],{"id":6338},"下载地址",[1434,6340,6341,6348],{},[1437,6342,2764,6343],{},[15,6344,6347],{"href":6345,"rel":6346},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002Fjar2pom",[19],"gitee.com\u002FMegawatt\u002Fjar2pom",[1437,6349,6350,6351],{},"CSDN：",[15,6352,6355],{"href":6353,"rel":6354},"https:\u002F\u002Fdownload.csdn.net\u002Fdownload\u002FJustinJia91\u002F12427956",[19],"download.csdn.net\u002Fdownload\u002FJustinJia91\u002F12427956",[11,6357,6358,6359,6362,6363,6366],{},"早年的仓库链接挂在 ",[39,6360,6361],{},"JiaXiaohei"," 这个账号名下，后来改成了 ",[39,6364,6365],{},"Megawatt","，老链接会自动跳过去，外部引用不用改。",[1486,6368,3014],{},{"title":37,"searchDepth":179,"depth":179,"links":6370},[6371,6372,6373,6374,6375,6376],{"id":99,"depth":179,"text":99},{"id":5140,"depth":179,"text":5140},{"id":5171,"depth":179,"text":5171},{"id":5993,"depth":179,"text":5994},{"id":6245,"depth":179,"text":6245},{"id":6338,"depth":179,"text":6338},"2020-05-16","把非 Maven 项目转成 Maven 时，得给一堆本地 jar 补 dependency。这个工具先用 jar 的 SHA1 去 Maven 中心库反查坐标，查不到再退回解析 Manifest。这篇带上项目结构、响应映射，以及它查不出结果的几种情况。",{},"\u002Fposts\u002Fjar2pom-maven-dependency",{"title":5114,"description":6378},"posts\u002Fjar2pom-maven-dependency",[6384,2799,1791],"Maven","7qQvEHaeu_Zv1Jdgu5cgjaj0f_murSgSjVcFWhE2zPE",{"id":6387,"title":6388,"body":6389,"date":6794,"description":6795,"extension":1506,"meta":6796,"navigation":1508,"path":6797,"seo":6798,"stem":6799,"tags":6800,"__hash__":6802},"posts\u002Fposts\u002Fflutter-android-sdkmanager-not-found.md","解决 Flutter 的 Android sdkmanager tool not found",{"type":8,"value":6390,"toc":6788},[6391,6393,6399,6402,6405,6411,6732,6734,6745,6750,6756,6761,6767,6772,6775,6778,6785],[97,6392,3809],{"id":3809},[32,6394,6397],{"className":6395,"code":6396,"language":2541},[2539],"Win10\nFlutter 1.12.13+hotfix.8\nAndroid Studio 3.6.1\n",[39,6398,6396],{"__ignoreMap":37},[97,6400,6401],{"id":6401},"问题描述",[11,6403,6404],{},"最近因为显卡 Boom 了，索性重装了系统。\n装完 Flutter 以后 doctor 通不过，一直找不到 sdkmanager。",[11,6406,6407],{},[446,6408],{"alt":6409,"src":6410},"flutter doctor 输出","\u002Fimages\u002Fflutter-android-sdkmanager\u002F01-flutter-doctor.png",[32,6412,6414],{"className":385,"code":6413,"language":387,"meta":37,"style":37},"> 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",[39,6415,6416,6424,6459,6498,6505,6534,6550,6573,6589,6597,6616,6634,6638,6660,6664,6671,6689,6707,6724,6728],{"__ignoreMap":37},[42,6417,6418,6421],{"class":44,"line":45},[42,6419,6420],{"class":52},">",[42,6422,6423],{"class":56}," flutter doctor\n",[42,6425,6426,6429,6432,6435,6438,6441,6444,6447,6450,6453,6456],{"class":44,"line":179},[42,6427,6428],{"class":394},"Doctor",[42,6430,6431],{"class":79}," summary",[42,6433,6434],{"class":56}," (to ",[42,6436,6437],{"class":79},"see",[42,6439,6440],{"class":79}," all",[42,6442,6443],{"class":79}," details,",[42,6445,6446],{"class":79}," run",[42,6448,6449],{"class":79}," flutter",[42,6451,6452],{"class":79}," doctor",[42,6454,6455],{"class":79}," -v",[42,6457,6458],{"class":56},"):\n",[42,6460,6461,6463,6466,6468,6471,6474,6477,6480,6483,6486,6489,6492,6495],{"class":44,"line":412},[42,6462,3984],{"class":52},[42,6464,6465],{"class":56},"√",[42,6467,4127],{"class":52},[42,6469,6470],{"class":56}," Flutter (",[42,6472,6473],{"class":394},"Channel",[42,6475,6476],{"class":79}," stable,",[42,6478,6479],{"class":79}," v1.12.13+hotfix.8,",[42,6481,6482],{"class":79}," on",[42,6484,6485],{"class":79}," Microsoft",[42,6487,6488],{"class":79}," Windows",[42,6490,6491],{"class":56}," [Version ",[42,6493,6494],{"class":79},"10.0.18363.720],",[42,6496,6497],{"class":79}," locale\n",[42,6499,6500,6503],{"class":44,"line":420},[42,6501,6502],{"class":394},"    zh-CN",[42,6504,85],{"class":56},[42,6506,6507,6510,6513,6516,6519,6521,6524,6527,6529,6532],{"class":44,"line":1289},[42,6508,6509],{"class":52},"[!]",[42,6511,6512],{"class":56}," Android toolchain - develop ",[42,6514,6515],{"class":66},"for",[42,6517,6518],{"class":56}," Android devices ",[42,6520,73],{"class":52},[42,6522,6523],{"class":394},"Android",[42,6525,6526],{"class":79}," SDK",[42,6528,3976],{"class":79},[42,6530,6531],{"class":438}," 29.0.3",[42,6533,85],{"class":52},[42,6535,6536,6539,6542,6545,6547],{"class":44,"line":2027},[42,6537,6538],{"class":394},"    X",[42,6540,6541],{"class":79}," Android",[42,6543,6544],{"class":79}," license",[42,6546,1848],{"class":79},[42,6548,6549],{"class":79}," unknown.\n",[42,6551,6552,6555,6558,6560,6563,6566,6568,6570],{"class":44,"line":2038},[42,6553,6554],{"class":394},"      Try",[42,6556,6557],{"class":79}," re-installing",[42,6559,4944],{"class":79},[42,6561,6562],{"class":79}," updating",[42,6564,6565],{"class":79}," your",[42,6567,6541],{"class":79},[42,6569,6526],{"class":79},[42,6571,6572],{"class":79}," Manager.\n",[42,6574,6575,6578,6581,6583,6586],{"class":44,"line":2044},[42,6576,6577],{"class":394},"      See",[42,6579,6580],{"class":79}," https:\u002F\u002Fdeveloper.android.com\u002Fstudio\u002F#downloads",[42,6582,4944],{"class":79},[42,6584,6585],{"class":79}," visit",[42,6587,6588],{"class":79}," https:\u002F\u002Fflutter.dev\u002Fsetup\u002F#android-setup\n",[42,6590,6591,6594],{"class":44,"line":2058},[42,6592,6593],{"class":66},"      for",[42,6595,6596],{"class":56}," detailed instructions.\n",[42,6598,6599,6601,6603,6605,6608,6611,6614],{"class":44,"line":2072},[42,6600,3984],{"class":52},[42,6602,6465],{"class":56},[42,6604,4127],{"class":52},[42,6606,6607],{"class":56}," Android Studio (",[42,6609,6610],{"class":394},"version",[42,6612,6613],{"class":438}," 3.6",[42,6615,85],{"class":56},[42,6617,6618,6620,6622,6624,6627,6629,6632],{"class":44,"line":2086},[42,6619,3984],{"class":52},[42,6621,6465],{"class":56},[42,6623,4127],{"class":52},[42,6625,6626],{"class":56}," Connected device (",[42,6628,2375],{"class":394},[42,6630,6631],{"class":79}," available",[42,6633,85],{"class":56},[42,6635,6636],{"class":44,"line":2882},[42,6637,3193],{"emptyLinePlaceholder":1508},[42,6639,6640,6643,6646,6649,6652,6654,6657],{"class":44,"line":2955},[42,6641,6642],{"class":52},"!",[42,6644,6645],{"class":394}," Doctor",[42,6647,6648],{"class":79}," found",[42,6650,6651],{"class":79}," issues",[42,6653,4374],{"class":79},[42,6655,6656],{"class":438}," 1",[42,6658,6659],{"class":79}," category.\n",[42,6661,6662],{"class":44,"line":2961},[42,6663,3193],{"emptyLinePlaceholder":1508},[42,6665,6666,6668],{"class":44,"line":2967},[42,6667,6420],{"class":52},[42,6669,6670],{"class":56}," flutter doctor --android-licenses\n",[42,6672,6673,6675,6678,6681,6684,6686],{"class":44,"line":2973},[42,6674,6523],{"class":394},[42,6676,6677],{"class":79}," sdkmanager",[42,6679,6680],{"class":79}," tool",[42,6682,6683],{"class":79}," not",[42,6685,6648],{"class":79},[42,6687,6688],{"class":56}," (D:\\Android\\Sdk\\tools\\bin\\sdkmanager).\n",[42,6690,6691,6694,6696,6698,6700,6702,6704],{"class":44,"line":2979},[42,6692,6693],{"class":394},"Try",[42,6695,6557],{"class":79},[42,6697,4944],{"class":79},[42,6699,6562],{"class":79},[42,6701,6565],{"class":79},[42,6703,6541],{"class":79},[42,6705,6706],{"class":79}," SDK,\n",[42,6708,6709,6712,6715,6718,6721],{"class":44,"line":2985},[42,6710,6711],{"class":394},"visit",[42,6713,6714],{"class":79}," https:\u002F\u002Fflutter.dev\u002Fsetup\u002F#android-setup",[42,6716,6717],{"class":79}," for",[42,6719,6720],{"class":79}," detailed",[42,6722,6723],{"class":79}," instructions.\n",[42,6725,6726],{"class":44,"line":2990},[42,6727,3193],{"emptyLinePlaceholder":1508},[42,6729,6730],{"class":44,"line":2996},[42,6731,176],{"class":52},[97,6733,3048],{"id":3048},[11,6735,6736,6737,6740,6741,6744],{},"在 Android Studio 中启动 ",[107,6738,6739],{},"SDK Manager","，去掉选项 ",[107,6742,6743],{},"Hide Obsolete Packages","：",[11,6746,6747],{},[446,6748],{"alt":6739,"src":6749},"\u002Fimages\u002Fflutter-android-sdkmanager\u002F02-sdk-manager.png",[11,6751,6752,6753,6744],{},"选择安装 ",[107,6754,6755],{},"Android SDK Tools (Obsolete)",[11,6757,6758],{},[446,6759],{"alt":6755,"src":6760},"\u002Fimages\u002Fflutter-android-sdkmanager\u002F03-sdk-tools-obsolete.png",[11,6762,6763,6764,6744],{},"点击 ",[107,6765,6766],{},"Apply",[11,6768,6769],{},[446,6770],{"alt":6766,"src":6771},"\u002Fimages\u002Fflutter-android-sdkmanager\u002F04-apply.png",[11,6773,6774],{},"安装完成。",[97,6776,6777],{"id":6777},"原因",[11,6779,6780,6781,6784],{},"不知道为啥 ",[107,6782,6783],{},"Android SDK Tools"," 被废弃了，但是好像 Flutter 还需要其提供支持。",[1486,6786,6787],{},"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":37,"searchDepth":179,"depth":179,"links":6789},[6790,6791,6792,6793],{"id":3809,"depth":179,"text":3809},{"id":6401,"depth":179,"text":6401},{"id":3048,"depth":179,"text":3048},{"id":6777,"depth":179,"text":6777},"2020-03-30","重装系统后 flutter doctor 一直报 Android license status unknown，找不到 sdkmanager。根因是被废弃的 Android SDK Tools 没装。",{},"\u002Fposts\u002Fflutter-android-sdkmanager-not-found",{"title":6388,"description":6795},"posts\u002Fflutter-android-sdkmanager-not-found",[6801,6523],"Flutter","GiT5gHSSUrSXafs-nkG3w45OUYqiQiNCK5LT3EeUBBg",{"id":6804,"title":6805,"body":6806,"date":7540,"description":7541,"extension":1506,"meta":7542,"navigation":1508,"path":7543,"seo":7544,"stem":7545,"tags":7546,"__hash__":7548},"posts\u002Fposts\u002Fcentos-yum-pgadmin4-python3-missing.md","解决 CentOS yum 安装 pgadmin4 报 nothing provides python3-*** 的问题",{"type":8,"value":6807,"toc":7534},[6808,6810,6816,6818,6821,6827,7501,7503,7506,7508,7514,7531],[97,6809,3809],{"id":3809},[32,6811,6814],{"className":6812,"code":6813,"language":2541},[2539],"CentOS8 (最小安装)\nPython3 (yum\u002Fdnf安装)\n",[39,6815,6813],{"__ignoreMap":37},[97,6817,6401],{"id":6401},[11,6819,6820],{},"安装时提示缺少 python3-alembic 等包，\n但是安装了 python3 仍然提示找不到包。",[11,6822,6823],{},[446,6824],{"alt":6825,"src":6826},"yum 安装报错","\u002Fimages\u002Fcentos-yum-pgadmin4\u002Ferror.png",[32,6828,6830],{"className":385,"code":6829,"language":387,"meta":37,"style":37},"[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",[39,6831,6832,6844,6860,6865,6903,6927,6949,6966,6991,7015,7039,7056,7073,7095,7117,7134,7150,7172,7194,7211,7227,7244,7261,7283,7305,7322,7338,7354,7370,7392,7414,7431,7447,7469],{"__ignoreMap":37},[42,6833,6834,6836,6839,6841],{"class":44,"line":45},[42,6835,3984],{"class":52},[42,6837,6838],{"class":56},"root@HyperV-PSQL ",[42,6840,4895],{"class":52},[42,6842,6843],{"class":56},"# yum -y install pgadmin4\n",[42,6845,6846,6849,6851,6854,6857],{"class":44,"line":179},[42,6847,6848],{"class":394},"上次元数据过期检查：0:11:32",[42,6850,4138],{"class":79},[42,6852,6853],{"class":79}," 2020年03月10日",[42,6855,6856],{"class":79}," 星期二",[42,6858,6859],{"class":79}," 11时35分24秒。\n",[42,6861,6862],{"class":44,"line":412},[42,6863,6864],{"class":394},"错误：\n",[42,6866,6867,6870,6872,6875,6878,6881,6884,6887,6889,6891,6894,6897,6900],{"class":44,"line":420},[42,6868,6869],{"class":394}," 问题:",[42,6871,4388],{"class":79},[42,6873,6874],{"class":79}," pgadmin4-4.19-1.rhel8.x86_64",[42,6876,6877],{"class":79}," requires",[42,6879,6880],{"class":79}," pgadmin4-web,",[42,6882,6883],{"class":79}," but",[42,6885,6886],{"class":79}," none",[42,6888,3971],{"class":79},[42,6890,3874],{"class":79},[42,6892,6893],{"class":79}," providers",[42,6895,6896],{"class":79}," can",[42,6898,6899],{"class":79}," be",[42,6901,6902],{"class":79}," installed\n",[42,6904,6905,6908,6910,6912,6914,6917,6920,6922,6924],{"class":44,"line":1289},[42,6906,6907],{"class":394},"  -",[42,6909,4636],{"class":79},[42,6911,4030],{"class":79},[42,6913,3874],{"class":79},[42,6915,6916],{"class":79}," best",[42,6918,6919],{"class":79}," candidate",[42,6921,6717],{"class":79},[42,6923,3874],{"class":79},[42,6925,6926],{"class":79}," job\n",[42,6928,6929,6931,6934,6937,6940,6943,6946],{"class":44,"line":2027},[42,6930,6907],{"class":394},[42,6932,6933],{"class":79}," nothing",[42,6935,6936],{"class":79}," provides",[42,6938,6939],{"class":79}," python3-alembic",[42,6941,6942],{"class":79}," needed",[42,6944,6945],{"class":79}," by",[42,6947,6948],{"class":79}," pgadmin4-web-4.16-1.rhel8.noarch\n",[42,6950,6951,6953,6955,6957,6960,6962,6964],{"class":44,"line":2038},[42,6952,6907],{"class":394},[42,6954,6933],{"class":79},[42,6956,6936],{"class":79},[42,6958,6959],{"class":79}," pgadmin4-python3-unittest2",[42,6961,6942],{"class":79},[42,6963,6945],{"class":79},[42,6965,6948],{"class":79},[42,6967,6968,6970,6972,6974,6977,6980,6982,6985,6987,6989],{"class":44,"line":2044},[42,6969,6907],{"class":394},[42,6971,6933],{"class":79},[42,6973,6936],{"class":79},[42,6975,6976],{"class":79}," python3-blinker",[42,6978,6979],{"class":52}," >",[42,6981,153],{"class":79},[42,6983,6984],{"class":438}," 1.4",[42,6986,6942],{"class":79},[42,6988,6945],{"class":79},[42,6990,6948],{"class":79},[42,6992,6993,6995,6997,6999,7002,7004,7006,7009,7011,7013],{"class":44,"line":2058},[42,6994,6907],{"class":394},[42,6996,6933],{"class":79},[42,6998,6936],{"class":79},[42,7000,7001],{"class":79}," python3-beautifulsoup4",[42,7003,6979],{"class":52},[42,7005,153],{"class":79},[42,7007,7008],{"class":438}," 4.4.1",[42,7010,6942],{"class":79},[42,7012,6945],{"class":79},[42,7014,6948],{"class":79},[42,7016,7017,7019,7021,7023,7026,7028,7030,7033,7035,7037],{"class":44,"line":2072},[42,7018,6907],{"class":394},[42,7020,6933],{"class":79},[42,7022,6936],{"class":79},[42,7024,7025],{"class":79}," python3-flask-sqlalchemy",[42,7027,6979],{"class":52},[42,7029,153],{"class":79},[42,7031,7032],{"class":438}," 2.3.2",[42,7034,6942],{"class":79},[42,7036,6945],{"class":79},[42,7038,6948],{"class":79},[42,7040,7041,7043,7045,7047,7049,7051,7053],{"class":44,"line":2086},[42,7042,6907],{"class":394},[42,7044,6933],{"class":79},[42,7046,6936],{"class":79},[42,7048,6939],{"class":79},[42,7050,6942],{"class":79},[42,7052,6945],{"class":79},[42,7054,7055],{"class":79}," pgadmin4-web-4.16-2.rhel8.noarch\n",[42,7057,7058,7060,7062,7064,7067,7069,7071],{"class":44,"line":2882},[42,7059,6907],{"class":394},[42,7061,6933],{"class":79},[42,7063,6936],{"class":79},[42,7065,7066],{"class":79}," python3-unittest2",[42,7068,6942],{"class":79},[42,7070,6945],{"class":79},[42,7072,7055],{"class":79},[42,7074,7075,7077,7079,7081,7083,7085,7087,7089,7091,7093],{"class":44,"line":2955},[42,7076,6907],{"class":394},[42,7078,6933],{"class":79},[42,7080,6936],{"class":79},[42,7082,6976],{"class":79},[42,7084,6979],{"class":52},[42,7086,153],{"class":79},[42,7088,6984],{"class":438},[42,7090,6942],{"class":79},[42,7092,6945],{"class":79},[42,7094,7055],{"class":79},[42,7096,7097,7099,7101,7103,7105,7107,7109,7111,7113,7115],{"class":44,"line":2961},[42,7098,6907],{"class":394},[42,7100,6933],{"class":79},[42,7102,6936],{"class":79},[42,7104,7001],{"class":79},[42,7106,6979],{"class":52},[42,7108,153],{"class":79},[42,7110,7008],{"class":438},[42,7112,6942],{"class":79},[42,7114,6945],{"class":79},[42,7116,7055],{"class":79},[42,7118,7119,7121,7123,7125,7127,7129,7131],{"class":44,"line":2967},[42,7120,6907],{"class":394},[42,7122,6933],{"class":79},[42,7124,6936],{"class":79},[42,7126,6939],{"class":79},[42,7128,6942],{"class":79},[42,7130,6945],{"class":79},[42,7132,7133],{"class":79}," pgadmin4-web-4.17-1.rhel8.noarch\n",[42,7135,7136,7138,7140,7142,7144,7146,7148],{"class":44,"line":2973},[42,7137,6907],{"class":394},[42,7139,6933],{"class":79},[42,7141,6936],{"class":79},[42,7143,7066],{"class":79},[42,7145,6942],{"class":79},[42,7147,6945],{"class":79},[42,7149,7133],{"class":79},[42,7151,7152,7154,7156,7158,7160,7162,7164,7166,7168,7170],{"class":44,"line":2979},[42,7153,6907],{"class":394},[42,7155,6933],{"class":79},[42,7157,6936],{"class":79},[42,7159,6976],{"class":79},[42,7161,6979],{"class":52},[42,7163,153],{"class":79},[42,7165,6984],{"class":438},[42,7167,6942],{"class":79},[42,7169,6945],{"class":79},[42,7171,7133],{"class":79},[42,7173,7174,7176,7178,7180,7182,7184,7186,7188,7190,7192],{"class":44,"line":2985},[42,7175,6907],{"class":394},[42,7177,6933],{"class":79},[42,7179,6936],{"class":79},[42,7181,7001],{"class":79},[42,7183,6979],{"class":52},[42,7185,153],{"class":79},[42,7187,7008],{"class":438},[42,7189,6942],{"class":79},[42,7191,6945],{"class":79},[42,7193,7133],{"class":79},[42,7195,7196,7198,7200,7202,7204,7206,7208],{"class":44,"line":2990},[42,7197,6907],{"class":394},[42,7199,6933],{"class":79},[42,7201,6936],{"class":79},[42,7203,6939],{"class":79},[42,7205,6942],{"class":79},[42,7207,6945],{"class":79},[42,7209,7210],{"class":79}," pgadmin4-web-4.17-2.rhel8.noarch\n",[42,7212,7213,7215,7217,7219,7221,7223,7225],{"class":44,"line":2996},[42,7214,6907],{"class":394},[42,7216,6933],{"class":79},[42,7218,6936],{"class":79},[42,7220,7066],{"class":79},[42,7222,6942],{"class":79},[42,7224,6945],{"class":79},[42,7226,7210],{"class":79},[42,7228,7229,7231,7233,7235,7238,7240,7242],{"class":44,"line":3001},[42,7230,6907],{"class":394},[42,7232,6933],{"class":79},[42,7234,6936],{"class":79},[42,7236,7237],{"class":79}," python3-bcrypt",[42,7239,6942],{"class":79},[42,7241,6945],{"class":79},[42,7243,7210],{"class":79},[42,7245,7246,7248,7250,7252,7255,7257,7259],{"class":44,"line":3235},[42,7247,6907],{"class":394},[42,7249,6933],{"class":79},[42,7251,6936],{"class":79},[42,7253,7254],{"class":79}," python3-pynacl",[42,7256,6942],{"class":79},[42,7258,6945],{"class":79},[42,7260,7210],{"class":79},[42,7262,7263,7265,7267,7269,7271,7273,7275,7277,7279,7281],{"class":44,"line":3241},[42,7264,6907],{"class":394},[42,7266,6933],{"class":79},[42,7268,6936],{"class":79},[42,7270,6976],{"class":79},[42,7272,6979],{"class":52},[42,7274,153],{"class":79},[42,7276,6984],{"class":438},[42,7278,6942],{"class":79},[42,7280,6945],{"class":79},[42,7282,7210],{"class":79},[42,7284,7285,7287,7289,7291,7293,7295,7297,7299,7301,7303],{"class":44,"line":3247},[42,7286,6907],{"class":394},[42,7288,6933],{"class":79},[42,7290,6936],{"class":79},[42,7292,7001],{"class":79},[42,7294,6979],{"class":52},[42,7296,153],{"class":79},[42,7298,7008],{"class":438},[42,7300,6942],{"class":79},[42,7302,6945],{"class":79},[42,7304,7210],{"class":79},[42,7306,7307,7309,7311,7313,7315,7317,7319],{"class":44,"line":3253},[42,7308,6907],{"class":394},[42,7310,6933],{"class":79},[42,7312,6936],{"class":79},[42,7314,6939],{"class":79},[42,7316,6942],{"class":79},[42,7318,6945],{"class":79},[42,7320,7321],{"class":79}," pgadmin4-web-4.18-1.rhel8.noarch\n",[42,7323,7324,7326,7328,7330,7332,7334,7336],{"class":44,"line":3258},[42,7325,6907],{"class":394},[42,7327,6933],{"class":79},[42,7329,6936],{"class":79},[42,7331,7066],{"class":79},[42,7333,6942],{"class":79},[42,7335,6945],{"class":79},[42,7337,7321],{"class":79},[42,7339,7340,7342,7344,7346,7348,7350,7352],{"class":44,"line":3264},[42,7341,6907],{"class":394},[42,7343,6933],{"class":79},[42,7345,6936],{"class":79},[42,7347,7237],{"class":79},[42,7349,6942],{"class":79},[42,7351,6945],{"class":79},[42,7353,7321],{"class":79},[42,7355,7356,7358,7360,7362,7364,7366,7368],{"class":44,"line":3269},[42,7357,6907],{"class":394},[42,7359,6933],{"class":79},[42,7361,6936],{"class":79},[42,7363,7254],{"class":79},[42,7365,6942],{"class":79},[42,7367,6945],{"class":79},[42,7369,7321],{"class":79},[42,7371,7372,7374,7376,7378,7380,7382,7384,7386,7388,7390],{"class":44,"line":3275},[42,7373,6907],{"class":394},[42,7375,6933],{"class":79},[42,7377,6936],{"class":79},[42,7379,6976],{"class":79},[42,7381,6979],{"class":52},[42,7383,153],{"class":79},[42,7385,6984],{"class":438},[42,7387,6942],{"class":79},[42,7389,6945],{"class":79},[42,7391,7321],{"class":79},[42,7393,7394,7396,7398,7400,7402,7404,7406,7408,7410,7412],{"class":44,"line":3281},[42,7395,6907],{"class":394},[42,7397,6933],{"class":79},[42,7399,6936],{"class":79},[42,7401,7001],{"class":79},[42,7403,6979],{"class":52},[42,7405,153],{"class":79},[42,7407,7008],{"class":438},[42,7409,6942],{"class":79},[42,7411,6945],{"class":79},[42,7413,7321],{"class":79},[42,7415,7416,7418,7420,7422,7424,7426,7428],{"class":44,"line":3286},[42,7417,6907],{"class":394},[42,7419,6933],{"class":79},[42,7421,6936],{"class":79},[42,7423,7237],{"class":79},[42,7425,6942],{"class":79},[42,7427,6945],{"class":79},[42,7429,7430],{"class":79}," pgadmin4-web-4.19-1.rhel8.noarch\n",[42,7432,7433,7435,7437,7439,7441,7443,7445],{"class":44,"line":3291},[42,7434,6907],{"class":394},[42,7436,6933],{"class":79},[42,7438,6936],{"class":79},[42,7440,7254],{"class":79},[42,7442,6942],{"class":79},[42,7444,6945],{"class":79},[42,7446,7430],{"class":79},[42,7448,7449,7451,7453,7455,7457,7459,7461,7463,7465,7467],{"class":44,"line":3297},[42,7450,6907],{"class":394},[42,7452,6933],{"class":79},[42,7454,6936],{"class":79},[42,7456,6976],{"class":79},[42,7458,6979],{"class":52},[42,7460,153],{"class":79},[42,7462,6984],{"class":438},[42,7464,6942],{"class":79},[42,7466,6945],{"class":79},[42,7468,7430],{"class":79},[42,7470,7471,7473,7476,7478,7481,7483,7486,7489,7491,7494,7496,7499],{"class":44,"line":3302},[42,7472,73],{"class":52},[42,7474,7475],{"class":394},"尝试添加",[42,7477,4733],{"class":52},[42,7479,7480],{"class":79},"--skip-broken",[42,7482,76],{"class":52},[42,7484,7485],{"class":79}," 来跳过无法安装的软件包",[42,7487,7488],{"class":79}," 或",[42,7490,4733],{"class":52},[42,7492,7493],{"class":79},"--nobest",[42,7495,76],{"class":52},[42,7497,7498],{"class":79}," 来不只使用最佳选择的软件包",[42,7500,85],{"class":52},[97,7502,6777],{"id":6777},[11,7504,7505],{},"官方的 rpm repository 没有提供对应的包。",[97,7507,3048],{"id":3048},[11,7509,7510,7511,6744],{},"安装 ",[107,7512,7513],{},"EPEL",[32,7515,7517],{"className":385,"code":7516,"language":387,"meta":37,"style":37},"yum install epel-release -y\n",[39,7518,7519],{"__ignoreMap":37},[42,7520,7521,7523,7525,7528],{"class":44,"line":45},[42,7522,5089],{"class":394},[42,7524,4030],{"class":79},[42,7526,7527],{"class":79}," epel-release",[42,7529,7530],{"class":79}," -y\n",[1486,7532,7533],{},"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":37,"searchDepth":179,"depth":179,"links":7535},[7536,7537,7538,7539],{"id":3809,"depth":179,"text":3809},{"id":6401,"depth":179,"text":6401},{"id":6777,"depth":179,"text":6777},{"id":3048,"depth":179,"text":3048},"2020-03-13","CentOS 8 上装 pgadmin4 一直缺 python3-alembic 之类的包，但 python3 明明装了。原因是官方 rpm 仓库没提供这些包，得先装 EPEL。",{},"\u002Fposts\u002Fcentos-yum-pgadmin4-python3-missing",{"title":6805,"description":7541},"posts\u002Fcentos-yum-pgadmin4-python3-missing",[2179,7547,2180],"PostgreSQL","m8HlbPTWcENXN-aO3c9aheDUKgp-zFDXfe9aG1lKEXI",{"id":7550,"title":7551,"body":7552,"date":8028,"description":8029,"extension":1506,"meta":8030,"navigation":1508,"path":8031,"seo":8032,"stem":8033,"tags":8034,"__hash__":8037},"posts\u002Fposts\u002Fmacpro-gpu-switch.md","MacPro GPU Switch：用 JavaFX 做一个 macOS 显卡切换工具",{"type":8,"value":7553,"toc":8013},[7554,7557,7560,7563,7619,7622,7625,7628,7634,7637,7640,7643,7646,7653,7673,7676,7679,7683,7689,7695,7705,7742,7745,7749,7764,7771,7775,7778,7792,7803,7817,7835,7838,7860,7864,7870,7895,7898,7904,7908,7940,7942,7945,7948,7959,7974,7977,7991,7994,7996,7999,8007,8010],[11,7555,7556],{},"MacBook Pro 15 寸是台双显卡的机器：一块 Intel 核显，一块 Radeon 独显。",[11,7558,7559],{},"用久了会撞上一个很具体的烦恼 —— 你没法决定现在用哪块。",[11,7561,7562],{},"macOS 的系统设置里，关于显卡只有「自动切换图形卡模式」这一个勾选框，它管的是“要不要自动切”，管不了“现在锁在哪一块”。真正能决定这件事的，是一条电源管理命令：",[32,7564,7566],{"className":385,"code":7565,"language":387,"meta":37,"style":37},"sudo pmset -a GPUSwitch 0   # 整合（只用核显）\nsudo pmset -a GPUSwitch 1   # 高性能（只用独显）\nsudo pmset -a GPUSwitch 2   # 自动\n",[39,7567,7568,7588,7603],{"__ignoreMap":37},[42,7569,7570,7573,7576,7579,7582,7585],{"class":44,"line":45},[42,7571,7572],{"class":394},"sudo",[42,7574,7575],{"class":79}," pmset",[42,7577,7578],{"class":79}," -a",[42,7580,7581],{"class":79}," GPUSwitch",[42,7583,7584],{"class":438}," 0",[42,7586,7587],{"class":401},"   # 整合（只用核显）\n",[42,7589,7590,7592,7594,7596,7598,7600],{"class":44,"line":179},[42,7591,7572],{"class":394},[42,7593,7575],{"class":79},[42,7595,7578],{"class":79},[42,7597,7581],{"class":79},[42,7599,6656],{"class":438},[42,7601,7602],{"class":401},"   # 高性能（只用独显）\n",[42,7604,7605,7607,7609,7611,7613,7616],{"class":44,"line":412},[42,7606,7572],{"class":394},[42,7608,7575],{"class":79},[42,7610,7578],{"class":79},[42,7612,7581],{"class":79},[42,7614,7615],{"class":438}," 2",[42,7617,7618],{"class":401},"   # 自动\n",[11,7620,7621],{},"能用，但每次都得开终端、敲一遍、输一次密码。为了两秒钟的切换走完这一套，太长了。",[11,7623,7624],{},"MacPro GPU Switch 就是把这条命令包进一个窗口：选核显就一直用核显，选独显就一直用独显，选自动就交回给系统。",[97,7626,7627],{"id":7627},"它长这样",[11,7629,7630],{},[446,7631],{"alt":7632,"src":7633},"MacPro GPU Switch 主界面：左侧大字显示当前模式，右侧填 Root 密码并选择模式","\u002Fimages\u002Fmacpro-gpu-switch\u002Fmain-window.png",[11,7635,7636],{},"界面很直白：左边大字写着当前处于哪个模式，颜色也跟着变（整合是青色，高性能是橙色，自动是蓝色）；右边填 Root 密码，下面三个单选框对应三种模式；底部是「关于」「应用」「关闭」。",[11,7638,7639],{},"勾上「记住密码」，下次打开就不用再输。",[11,7641,7642],{},"它做的事就四步：读当前状态 → 你选一个新模式 → 点「应用」→ 它替你跑那条 sudo 命令，再把新状态读回来显示。",[97,7644,7645],{"id":7645},"三种模式",[11,7647,7648,7649,7652],{},"对应 ",[39,7650,7651],{},"GPUSwitch"," 的三个值：",[1580,7654,7655,7661,7667],{},[1437,7656,7657,7660],{},[107,7658,7659],{},"整合","（0）—— 只用核显，最省电最凉快，重活干不了；",[1437,7662,7663,7666],{},[107,7664,7665],{},"高性能","（1）—— 只用独显，性能拉满，代价是发热和续航；",[1437,7668,7669,7672],{},[107,7670,7671],{},"自动","（2）—— 交给系统判断，也就是 macOS 默认的那套逻辑。",[97,7674,7675],{"id":7675},"实现上其实只有三件事",[11,7677,7678],{},"代码量很小，核心就是三次 Shell 调用。",[786,7680,7682],{"id":7681},"一读懂当前状态","一、读懂当前状态",[11,7684,7685,7688],{},[39,7686,7687],{},"pmset -g"," 会打印一大串电源管理配置，显卡那一行长这样：",[32,7690,7693],{"className":7691,"code":7692,"language":2541},[2539],"gpuswitch             0\n",[39,7694,7692],{"__ignoreMap":37},[11,7696,7697,7698,7700,7701,7704],{},"所以读状态的逻辑就是：跑一遍 ",[39,7699,7687],{},"，逐行找以 ",[39,7702,7703],{},"gpuswitch"," 开头的那行，把数字抠出来。",[32,7706,7708],{"className":3125,"code":7707,"language":2506,"meta":37,"style":37},"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",[39,7709,7710,7715,7719,7724,7729,7734,7738],{"__ignoreMap":37},[42,7711,7712],{"class":44,"line":45},[42,7713,7714],{},"String[] command = {\"\u002Fbin\u002Fsh\", \"-c\", \"pmset -g\"};\n",[42,7716,7717],{"class":44,"line":179},[42,7718,3193],{"emptyLinePlaceholder":1508},[42,7720,7721],{"class":44,"line":412},[42,7722,7723],{},"for (String process : processList) {\n",[42,7725,7726],{"class":44,"line":420},[42,7727,7728],{},"    if (process.startsWith(\"gpuswitch\")) {\n",[42,7730,7731],{"class":44,"line":1289},[42,7732,7733],{},"        i = Integer.parseInt(process.replaceAll(\"gpuswitch| \", \"\"));\n",[42,7735,7736],{"class":44,"line":2027},[42,7737,3345],{},[42,7739,7740],{"class":44,"line":2038},[42,7741,557],{},[11,7743,7744],{},"拿到 0 \u002F 1 \u002F 2，再去点亮界面上对应的那个单选框。",[786,7746,7748],{"id":7747},"二切换","二、切换",[32,7750,7752],{"className":3125,"code":7751,"language":2506,"meta":37,"style":37},"String[] command = {\"\u002Fbin\u002Fbash\", \"-c\",\n    \"echo \\\"\" + password + \"\\\" | sudo -S pmset -a GPUSwitch \" + status};\n",[39,7753,7754,7759],{"__ignoreMap":37},[42,7755,7756],{"class":44,"line":45},[42,7757,7758],{},"String[] command = {\"\u002Fbin\u002Fbash\", \"-c\",\n",[42,7760,7761],{"class":44,"line":179},[42,7762,7763],{},"    \"echo \\\"\" + password + \"\\\" | sudo -S pmset -a GPUSwitch \" + status};\n",[11,7765,7766,7767,7770],{},"关键在 ",[39,7768,7769],{},"sudo -S"," 这个参数。它让 sudo 从标准输入读密码 —— 不加它，sudo 会去找终端要密码，而一个图形界面程序没有终端，命令会直接卡在那里。把密码 echo 进去、管道喂给 sudo，就能在没有终端的环境里把这条命令跑完。",[786,7772,7774],{"id":7773},"三验证密码","三、验证密码",[11,7776,7777],{},"切显卡之前得先知道密码对不对，否则用户点「应用」只会撞上一个莫名其妙的失败。",[32,7779,7781],{"className":3125,"code":7780,"language":2506,"meta":37,"style":37},"String[] command = {\"\u002Fbin\u002Fbash\", \"-c\",\n    \"echo \\\"\" + password + \"\\\" | sudo -S whoami\"};\n",[39,7782,7783,7787],{"__ignoreMap":37},[42,7784,7785],{"class":44,"line":45},[42,7786,7758],{},[42,7788,7789],{"class":44,"line":179},[42,7790,7791],{},"    \"echo \\\"\" + password + \"\\\" | sudo -S whoami\"};\n",[11,7793,7794,7795,7798,7799,7802],{},"跑一次 ",[39,7796,7797],{},"whoami","，输出的第一行是 ",[39,7800,7801],{},"root","，就说明密码正确。",[11,7804,7805,7806,7808,7809,7812,7813,7816],{},"用 ",[39,7807,7797],{}," 而不是 ",[39,7810,7811],{},"sudo -v","，好处是它验证权限的方式和后面那条 ",[39,7814,7815],{},"pmset"," 完全一致，同时又没有任何副作用 —— 密码错了也不会误伤配置。",[11,7818,7819,7820,7823,7824,7827,7828,1008,7831,7834],{},"写正式代码之前，我先在 ",[39,7821,7822],{},"src\u002Ftest"," 下扔了两个带 ",[39,7825,7826],{},"main"," 的小类，把命令单独跑通再搬进工具类。目录里留下的 ",[39,7829,7830],{},"CheckPassword",[39,7832,7833],{},"RunPmset"," 就是当时的验证代码。",[786,7836,7837],{"id":7837},"界面部分",[11,7839,7840,7841,1480,7844,7847,7848,7851,7852,7855,7856,7859],{},"JavaFX + FXML，没什么花活。三个 ",[39,7842,7843],{},"RadioButton",[39,7845,7846],{},"userData"," 里分别挂着 0 \u002F 1 \u002F 2，用 ",[39,7849,7850],{},"ToggleGroup"," 保证互斥；窗口初始化时先读一次当前状态，用 ",[39,7853,7854],{},"fire()"," 把对应的那个点亮。为了避免用户还没选模式就点「应用」，",[39,7857,7858],{},"applyApp()"," 里也做了一次空判断。",[97,7861,7863],{"id":7862},"记住密码是怎么存的","「记住密码」是怎么存的",[11,7865,7866,7867,6744],{},"勾上「记住密码」之后，密码不会明文落盘，而是先加密再写进程序目录下的 ",[39,7868,7869],{},"conf.properties",[1580,7871,7872,7881],{},[1437,7873,7874,6744,7877,7880],{},[107,7875,7876],{},"算法",[39,7878,7879],{},"AES\u002FECB\u002FPKCS5Padding","，密文再转成 URL-safe 的 Base64 保存；",[1437,7882,7883,7886,7887,7890,7891,7894],{},[107,7884,7885],{},"密钥不是写死的常量","，而是每台机器独有的一个标识 —— 跑一遍 ",[39,7888,7889],{},"system_profiler SPHardwareDataType","，抓 ",[39,7892,7893],{},"Hardware UUID:"," 那一行，去掉空格和连字符，正好 32 位十六进制，直接拿来当 AES-256 的密钥。",[11,7896,7897],{},"配置文件里只存两样东西：一个「是否记住密码」的开关，和密文本身。",[11,7899,7900,7901,7903],{},"取 Hardware UUID 而不是写死一个字符串，好处是凭据跟机器绑在一起 —— 把 ",[39,7902,7869],{}," 拷到别的机器上，密文就解不开了。反过来，换机器或者重装系统之后，之前记住的密码会失效，重新输一次就行。",[97,7905,7907],{"id":7906},"支持范围和一句要紧的警告","支持范围，和一句要紧的警告",[1434,7909,7910,7917,7924,7931],{},[1437,7911,7912,7913,7916],{},"实际验证过的只有一台：",[107,7914,7915],{},"MacBook Pro（15-inch，2018）+ macOS Mojave 10.14.3","；",[1437,7918,7919,7920,7923],{},"理论上所有能用 ",[39,7921,7922],{},"sudo pmset -a GPUSwitch"," 切换显卡的型号都能用，欢迎大家补充自己的型号；",[1437,7925,7926,7927,7930],{},"⚠️ ",[107,7928,7929],{},"只有核显的机器切勿尝试。"," 这是 README 里唯一用感叹号写的一句，我原样搬过来 —— 没有独显的机器上执行这条命令没有意义，也没人验证过会发生什么；",[1437,7932,7933,7934,1008,7936,7939],{},"它只在 macOS 上有意义，依赖 ",[39,7935,7815],{},[39,7937,7938],{},"system_profiler"," 这两个系统命令。",[97,7941,2398],{"id":2398},[11,7943,7944],{},"开发环境是当年的那一套：Apache Maven 3.6、JDK 8、IntelliJ IDEA 2018.3。",[11,7946,7947],{},"当年 Oracle JDK 8 自带 JavaFX，不用另外下 SDK —— 这也是我选 JavaFX 而不是拉一堆第三方 UI 库的原因：对一个这么小的工具来说，零额外依赖比什么都重要。",[11,7949,7950,7951,7954,7955,7958],{},"跑起来很简单：clone 下来用 IDEA 打开，运行 ",[39,7952,7953],{},"cn.jxh.switcher.Main"," 就行。想打包成 macOS 的原生应用，pom 里已经配好了 JavaFX 的 Maven 插件（zenjava 8.8.3，应用名 ",[39,7956,7957],{},"MacProGPUSwitch","）。",[11,7960,7961,7962,7965,7966,7969,7970,7973],{},"版本号那处设计顺带提一句：",[39,7963,7964],{},"app.properties"," 里放的不是写死的数字，而是 ",[39,7967,7968],{},"${project.version}","，打包时由 Maven 的 resource filtering 替换成真实版本。改 pom 里的 ",[39,7971,7972],{},"\u003Cversion>"," 一处，界面「关于」里显示的版本号就跟着走。",[97,7975,7976],{"id":7976},"版本",[1434,7978,7979,7985],{},[1437,7980,7981,7984],{},[107,7982,7983],{},"1.0.0","（2019-02-10）：第一个版本，能读状态、能切换；",[1437,7986,7987,7990],{},[107,7988,7989],{},"1.0.1","（2019-02-12）：加上 Root 密码验证，以及「记住密码」。",[11,7992,7993],{},"写完这篇的时候，正好是 1.0.1。",[97,7995,2756],{"id":2756},[11,7997,7998],{},"仓库在这里，Apache License 2.0：",[1434,8000,8001],{},[1437,8002,2764,8003],{},[15,8004,8005],{"href":8005,"rel":8006},"https:\u002F\u002Fgitee.com\u002FMegawatt\u002FMacProGPUSwitch",[19],[11,8008,8009],{},"这是个很小的工具，解决的问题也很窄。但如果你也是 2018 款 MacBook Pro 的用户，被那块随时会醒过来的独显烦过，它可能正好是你要的那个开关。",[1486,8011,8012],{},"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":37,"searchDepth":179,"depth":179,"links":8014},[8015,8016,8017,8023,8024,8025,8026,8027],{"id":7627,"depth":179,"text":7627},{"id":7645,"depth":179,"text":7645},{"id":7675,"depth":179,"text":7675,"children":8018},[8019,8020,8021,8022],{"id":7681,"depth":412,"text":7682},{"id":7747,"depth":412,"text":7748},{"id":7773,"depth":412,"text":7774},{"id":7837,"depth":412,"text":7837},{"id":7862,"depth":179,"text":7863},{"id":7906,"depth":179,"text":7907},{"id":2398,"depth":179,"text":2398},{"id":7976,"depth":179,"text":7976},{"id":2756,"depth":179,"text":2756},"2019-02-12","一个 JavaFX 写的小工具，把 macOS 里只能靠 sudo pmset 切换的显卡模式搬进了图形界面。这篇讲清它解决什么问题、三行核心代码怎么调 pmset，以及「记住密码」是怎么存下来的。",{},"\u002Fposts\u002Fmacpro-gpu-switch",{"title":7551,"description":8029},"posts\u002Fmacpro-gpu-switch",[2799,8035,8036,1790,1791],"JavaFX","macOS","R7W1l20eNuApI0c5e887Q4zxAmcFjrquPAtbU78Vc_g",{"id":8039,"title":5126,"body":8040,"date":8974,"description":8975,"extension":1506,"meta":8976,"navigation":1508,"path":5136,"seo":8977,"stem":8978,"tags":8979,"__hash__":8980},"posts\u002Fposts\u002Fjar2pom-nexus2-dependency.md",{"type":8,"value":8041,"toc":8970},[8042,8051,8896,8900,8959,8962,8968],[8043,8044,8045],"blockquote",{},[11,8046,8047,8048,95],{},"此版本基于 Nexus OSS 2 相关接口。\n基于 Maven 中心库的版本请",[15,8049,8050],{"href":6380},"点这里",[32,8052,8054],{"className":3125,"code":8053,"language":2506,"meta":37,"style":37},"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",[39,8055,8056,8061,8065,8070,8075,8080,8085,8090,8095,8100,8105,8110,8115,8119,8124,8129,8134,8139,8144,8149,8153,8157,8162,8167,8172,8177,8181,8186,8190,8194,8199,8203,8208,8213,8217,8221,8225,8230,8234,8238,8242,8247,8251,8256,8261,8265,8270,8275,8280,8284,8288,8292,8296,8300,8304,8308,8312,8316,8320,8324,8328,8333,8337,8342,8347,8351,8355,8359,8363,8367,8371,8375,8379,8384,8388,8392,8396,8400,8404,8409,8413,8417,8421,8425,8429,8433,8437,8441,8445,8449,8453,8457,8461,8465,8469,8473,8477,8481,8485,8489,8493,8497,8502,8507,8511,8515,8519,8524,8528,8532,8536,8541,8545,8550,8554,8558,8563,8567,8571,8576,8580,8585,8590,8595,8600,8605,8610,8615,8619,8624,8628,8632,8636,8640,8644,8648,8652,8656,8660,8664,8668,8672,8676,8680,8684,8688,8692,8696,8700,8704,8708,8712,8716,8720,8724,8728,8732,8736,8740,8744,8748,8752,8756,8760,8764,8768,8772,8776,8781,8786,8791,8796,8801,8806,8811,8816,8821,8826,8831,8836,8841,8846,8851,8856,8861,8866,8871,8876,8881,8886,8891],{"__ignoreMap":37},[42,8057,8058],{"class":44,"line":45},[42,8059,8060],{},"package com.jxh.tool;\n",[42,8062,8063],{"class":44,"line":179},[42,8064,3193],{"emptyLinePlaceholder":1508},[42,8066,8067],{"class":44,"line":412},[42,8068,8069],{},"import org.apache.http.HttpResponse;\n",[42,8071,8072],{"class":44,"line":420},[42,8073,8074],{},"import org.apache.http.client.methods.HttpGet;\n",[42,8076,8077],{"class":44,"line":1289},[42,8078,8079],{},"import org.apache.http.impl.client.CloseableHttpClient;\n",[42,8081,8082],{"class":44,"line":2027},[42,8083,8084],{},"import org.apache.http.impl.client.HttpClients;\n",[42,8086,8087],{"class":44,"line":2038},[42,8088,8089],{},"import org.apache.http.util.EntityUtils;\n",[42,8091,8092],{"class":44,"line":2044},[42,8093,8094],{},"import org.dom4j.Document;\n",[42,8096,8097],{"class":44,"line":2058},[42,8098,8099],{},"import org.dom4j.DocumentException;\n",[42,8101,8102],{"class":44,"line":2072},[42,8103,8104],{},"import org.dom4j.DocumentHelper;\n",[42,8106,8107],{"class":44,"line":2086},[42,8108,8109],{},"import org.dom4j.Element;\n",[42,8111,8112],{"class":44,"line":2882},[42,8113,8114],{},"import org.dom4j.dom.DOMElement;\n",[42,8116,8117],{"class":44,"line":2955},[42,8118,3193],{"emptyLinePlaceholder":1508},[42,8120,8121],{"class":44,"line":2961},[42,8122,8123],{},"import java.io.File;\n",[42,8125,8126],{"class":44,"line":2967},[42,8127,8128],{},"import java.io.FileInputStream;\n",[42,8130,8131],{"class":44,"line":2973},[42,8132,8133],{},"import java.io.IOException;\n",[42,8135,8136],{"class":44,"line":2979},[42,8137,8138],{},"import java.math.BigInteger;\n",[42,8140,8141],{"class":44,"line":2985},[42,8142,8143],{},"import java.security.MessageDigest;\n",[42,8145,8146],{"class":44,"line":2990},[42,8147,8148],{},"import java.util.jar.*;\n",[42,8150,8151],{"class":44,"line":2996},[42,8152,3193],{"emptyLinePlaceholder":1508},[42,8154,8155],{"class":44,"line":3001},[42,8156,3227],{},[42,8158,8159],{"class":44,"line":3235},[42,8160,8161],{}," * 通过Jar包SHA1或MD5生成Pom文件\n",[42,8163,8164],{"class":44,"line":3241},[42,8165,8166],{}," *\n",[42,8168,8169],{"class":44,"line":3247},[42,8170,8171],{}," * @author JiaXiaohei\n",[42,8173,8174],{"class":44,"line":3253},[42,8175,8176],{}," * @date 2018-03-16\n",[42,8178,8179],{"class":44,"line":3258},[42,8180,3238],{},[42,8182,8183],{"class":44,"line":3264},[42,8184,8185],{},"public class Jar2pom {\n",[42,8187,8188],{"class":44,"line":3269},[42,8189,3193],{"emptyLinePlaceholder":1508},[42,8191,8192],{"class":44,"line":3275},[42,8193,3356],{},[42,8195,8196],{"class":44,"line":3281},[42,8197,8198],{},"     * Maven库\n",[42,8200,8201],{"class":44,"line":3286},[42,8202,3380],{},[42,8204,8205],{"class":44,"line":3291},[42,8206,8207],{},"    \u002F\u002Fpublic static final String nexusUrl = \"http:\u002F\u002Fmaven.aliyun.com\u002Fnexus\u002Fservice\u002Flocal\u002Flucene\u002Fsearch\";\n",[42,8209,8210],{"class":44,"line":3297},[42,8211,8212],{},"    public static final String nexusUrl = \"http:\u002F\u002F59.110.144.164\u002Fnexus\u002Fservice\u002Flocal\u002Flucene\u002Fsearch\";\n",[42,8214,8215],{"class":44,"line":3302},[42,8216,3193],{"emptyLinePlaceholder":1508},[42,8218,8219],{"class":44,"line":3308},[42,8220,3193],{"emptyLinePlaceholder":1508},[42,8222,8223],{"class":44,"line":3313},[42,8224,5181],{},[42,8226,8227],{"class":44,"line":3319},[42,8228,8229],{},"    \u002F\u002F先通过Jar的SHA1查询 如果不存在则解析Manifest查询\n",[42,8231,8232],{"class":44,"line":3324},[42,8233,5191],{},[42,8235,8236],{"class":44,"line":3330},[42,8237,5196],{},[42,8239,8240],{"class":44,"line":3336},[42,8241,5205],{},[42,8243,8244],{"class":44,"line":3342},[42,8245,8246],{},"            if (!getPomByChecksum(jar).isTextOnly()) {\n",[42,8248,8249],{"class":44,"line":3348},[42,8250,5224],{},[42,8252,8253],{"class":44,"line":3353},[42,8254,8255],{},"                System.out.println(getPomByChecksum(jar).asXML());\n",[42,8257,8258],{"class":44,"line":3359},[42,8259,8260],{},"            } else if (!getPomByManifest(jar).isTextOnly()) {\n",[42,8262,8263],{"class":44,"line":3365},[42,8264,5256],{},[42,8266,8267],{"class":44,"line":3371},[42,8268,8269],{},"                System.out.println(getPomByManifest(jar).asXML());\n",[42,8271,8272],{"class":44,"line":3377},[42,8273,8274],{},"            } else {\n",[42,8276,8277],{"class":44,"line":3383},[42,8278,8279],{},"                System.out.println(\"\u003C!--  No data was found -->\");\n",[42,8281,8282],{"class":44,"line":3389},[42,8283,3578],{},[42,8285,8286],{"class":44,"line":3395},[42,8287,5286],{},[42,8289,8290],{"class":44,"line":3400},[42,8291,3746],{},[42,8293,8294],{"class":44,"line":3405},[42,8295,3193],{"emptyLinePlaceholder":1508},[42,8297,8298],{"class":44,"line":3410},[42,8299,3345],{},[42,8301,8302],{"class":44,"line":3416},[42,8303,3193],{"emptyLinePlaceholder":1508},[42,8305,8306],{"class":44,"line":3421},[42,8307,3356],{},[42,8309,8310],{"class":44,"line":3426},[42,8311,5311],{},[42,8313,8314],{"class":44,"line":3431},[42,8315,3368],{},[42,8317,8318],{"class":44,"line":3437},[42,8319,5320],{},[42,8321,8322],{"class":44,"line":3443},[42,8323,3374],{},[42,8325,8326],{"class":44,"line":3448},[42,8327,3380],{},[42,8329,8330],{"class":44,"line":3453},[42,8331,8332],{},"    public static Element getPomByChecksum(File file) {\n",[42,8334,8335],{"class":44,"line":3458},[42,8336,5347],{},[42,8338,8339],{"class":44,"line":3464},[42,8340,8341],{},"        String xml = doGet(nexusUrl + \"?sha1=\" + checkSum);\n",[42,8343,8344],{"class":44,"line":3469},[42,8345,8346],{},"        return assemblePomElement(xml);\n",[42,8348,8349],{"class":44,"line":3475},[42,8350,3345],{},[42,8352,8353],{"class":44,"line":3480},[42,8354,3193],{"emptyLinePlaceholder":1508},[42,8356,8357],{"class":44,"line":3486},[42,8358,3356],{},[42,8360,8361],{"class":44,"line":3492},[42,8362,5401],{},[42,8364,8365],{"class":44,"line":3498},[42,8366,3368],{},[42,8368,8369],{"class":44,"line":3504},[42,8370,5320],{},[42,8372,8373],{"class":44,"line":3510},[42,8374,3374],{},[42,8376,8377],{"class":44,"line":3516},[42,8378,3380],{},[42,8380,8381],{"class":44,"line":3522},[42,8382,8383],{},"    public static Element getPomByManifest(File file) {\n",[42,8385,8386],{"class":44,"line":3528},[42,8387,5435],{},[42,8389,8390],{"class":44,"line":3534},[42,8391,5440],{},[42,8393,8394],{"class":44,"line":3540},[42,8395,5445],{},[42,8397,8398],{"class":44,"line":3546},[42,8399,5450],{},[42,8401,8402],{"class":44,"line":3551},[42,8403,5455],{},[42,8405,8406],{"class":44,"line":3557},[42,8407,8408],{},"                return new DOMElement(\"dependency\");\n",[42,8410,8411],{"class":44,"line":3563},[42,8412,3578],{},[42,8414,8415],{"class":44,"line":3569},[42,8416,5469],{},[42,8418,8419],{"class":44,"line":3575},[42,8420,5474],{},[42,8422,8423],{"class":44,"line":3581},[42,8424,5479],{},[42,8426,8427],{"class":44,"line":3587},[42,8428,5484],{},[42,8430,8431],{"class":44,"line":3592},[42,8432,5489],{},[42,8434,8435],{"class":44,"line":3597},[42,8436,5494],{},[42,8438,8439],{"class":44,"line":3602},[42,8440,5499],{},[42,8442,8443],{"class":44,"line":3608},[42,8444,3578],{},[42,8446,8447],{"class":44,"line":3613},[42,8448,5508],{},[42,8450,8451],{"class":44,"line":3619},[42,8452,5513],{},[42,8454,8455],{"class":44,"line":3624},[42,8456,3578],{},[42,8458,8459],{"class":44,"line":3629},[42,8460,5522],{},[42,8462,8463],{"class":44,"line":3635},[42,8464,5527],{},[42,8466,8467],{"class":44,"line":3641},[42,8468,5532],{},[42,8470,8471],{"class":44,"line":3647},[42,8472,5537],{},[42,8474,8475],{"class":44,"line":3653},[42,8476,5542],{},[42,8478,8479],{"class":44,"line":3659},[42,8480,5547],{},[42,8482,8483],{"class":44,"line":3665},[42,8484,3578],{},[42,8486,8487],{"class":44,"line":3670},[42,8488,5556],{},[42,8490,8491],{"class":44,"line":3675},[42,8492,5561],{},[42,8494,8495],{"class":44,"line":3680},[42,8496,3578],{},[42,8498,8499],{"class":44,"line":3686},[42,8500,8501],{},"            String xml = doGet(nexusUrl + \"?a=\" + a + \"&v=\" + v);\n",[42,8503,8504],{"class":44,"line":3691},[42,8505,8506],{},"            return assemblePomElement(xml);\n",[42,8508,8509],{"class":44,"line":3697},[42,8510,5598],{},[42,8512,8513],{"class":44,"line":3703},[42,8514,5603],{},[42,8516,8517],{"class":44,"line":3708},[42,8518,3746],{},[42,8520,8521],{"class":44,"line":3713},[42,8522,8523],{},"        return new DOMElement(\"dependency\");\n",[42,8525,8526],{"class":44,"line":3719},[42,8527,3345],{},[42,8529,8530],{"class":44,"line":3725},[42,8531,3193],{"emptyLinePlaceholder":1508},[42,8533,8534],{"class":44,"line":3731},[42,8535,3356],{},[42,8537,8538],{"class":44,"line":3737},[42,8539,8540],{},"     * 解析获取的XML 组装dependency\n",[42,8542,8543],{"class":44,"line":3743},[42,8544,3368],{},[42,8546,8547],{"class":44,"line":3749},[42,8548,8549],{},"     * @param xml\n",[42,8551,8552],{"class":44,"line":3755},[42,8553,3374],{},[42,8555,8556],{"class":44,"line":3760},[42,8557,3380],{},[42,8559,8560],{"class":44,"line":3765},[42,8561,8562],{},"    public static Element assemblePomElement(String xml) {\n",[42,8564,8565],{"class":44,"line":5708},[42,8566,5655],{},[42,8568,8569],{"class":44,"line":5714},[42,8570,3193],{"emptyLinePlaceholder":1508},[42,8572,8573],{"class":44,"line":5719},[42,8574,8575],{},"        if (xml != null && xml.length() != 0) {\n",[42,8577,8578],{"class":44,"line":5724},[42,8579,5784],{},[42,8581,8582],{"class":44,"line":5730},[42,8583,8584],{},"                Document document = DocumentHelper.parseText(xml);\n",[42,8586,8587],{"class":44,"line":5736},[42,8588,8589],{},"                Element dataElement = document.getRootElement().element(\"data\");\n",[42,8591,8592],{"class":44,"line":5742},[42,8593,8594],{},"                if (dataElement.getText() != null && dataElement.getText().length() != 0) {\n",[42,8596,8597],{"class":44,"line":5748},[42,8598,8599],{},"                    Element artifactElement = dataElement.element(\"artifact\");\n",[42,8601,8602],{"class":44,"line":5753},[42,8603,8604],{},"                    dependency.add((Element) artifactElement.element(\"groupId\").clone());\n",[42,8606,8607],{"class":44,"line":5759},[42,8608,8609],{},"                    dependency.add((Element) artifactElement.element(\"artifactId\").clone());\n",[42,8611,8612],{"class":44,"line":5765},[42,8613,8614],{},"                    dependency.add((Element) artifactElement.element(\"version\").clone());\n",[42,8616,8617],{"class":44,"line":5770},[42,8618,3525],{},[42,8620,8621],{"class":44,"line":5775},[42,8622,8623],{},"            } catch (DocumentException e) {\n",[42,8625,8626],{"class":44,"line":5781},[42,8627,5802],{},[42,8629,8630],{"class":44,"line":5787},[42,8631,3578],{},[42,8633,8634],{"class":44,"line":5793},[42,8635,3746],{},[42,8637,8638],{"class":44,"line":5799},[42,8639,5684],{},[42,8641,8642],{"class":44,"line":5805},[42,8643,3345],{},[42,8645,8646],{"class":44,"line":5810},[42,8647,3193],{"emptyLinePlaceholder":1508},[42,8649,8650],{"class":44,"line":5815},[42,8651,3356],{},[42,8653,8654],{"class":44,"line":5821},[42,8655,5701],{},[42,8657,8658],{"class":44,"line":5826},[42,8659,3368],{},[42,8661,8662],{"class":44,"line":5831},[42,8663,5711],{},[42,8665,8666],{"class":44,"line":5836},[42,8667,3374],{},[42,8669,8670],{"class":44,"line":5842},[42,8671,3380],{},[42,8673,8674],{"class":44,"line":5847},[42,8675,5727],{},[42,8677,8678],{"class":44,"line":5852},[42,8679,5733],{},[42,8681,8682],{"class":44,"line":5858},[42,8683,5739],{},[42,8685,8686],{"class":44,"line":5863},[42,8687,5745],{},[42,8689,8690],{"class":44,"line":5868},[42,8691,5435],{},[42,8693,8694],{"class":44,"line":5874},[42,8695,5756],{},[42,8697,8698],{"class":44,"line":5880},[42,8699,5762],{},[42,8701,8702],{"class":44,"line":5886},[42,8703,5598],{},[42,8705,8706],{"class":44,"line":5891},[42,8707,5603],{},[42,8709,8710],{"class":44,"line":5897},[42,8711,5778],{},[42,8713,8714],{"class":44,"line":5903},[42,8715,5784],{},[42,8717,8718],{"class":44,"line":5909},[42,8719,5790],{},[42,8721,8722],{"class":44,"line":5915},[42,8723,5796],{},[42,8725,8726],{"class":44,"line":5920},[42,8727,5802],{},[42,8729,8730],{"class":44,"line":5926},[42,8731,3578],{},[42,8733,8734],{"class":44,"line":5932},[42,8735,3746],{},[42,8737,8738],{"class":44,"line":5938},[42,8739,5818],{},[42,8741,8742],{"class":44,"line":5944},[42,8743,3345],{},[42,8745,8746],{"class":44,"line":5949},[42,8747,3193],{"emptyLinePlaceholder":1508},[42,8749,8750],{"class":44,"line":5955},[42,8751,3356],{},[42,8753,8754],{"class":44,"line":5961},[42,8755,5839],{},[42,8757,8758],{"class":44,"line":5966},[42,8759,3368],{},[42,8761,8762],{"class":44,"line":5971},[42,8763,5320],{},[42,8765,8766],{"class":44,"line":5976},[42,8767,5855],{},[42,8769,8770],{"class":44,"line":5982},[42,8771,3374],{},[42,8773,8774],{"class":44,"line":5988},[42,8775,3380],{},[42,8777,8779],{"class":44,"line":8778},168,[42,8780,5871],{},[42,8782,8784],{"class":44,"line":8783},169,[42,8785,5877],{},[42,8787,8789],{"class":44,"line":8788},170,[42,8790,5883],{},[42,8792,8794],{"class":44,"line":8793},171,[42,8795,3746],{},[42,8797,8799],{"class":44,"line":8798},172,[42,8800,5894],{},[42,8802,8804],{"class":44,"line":8803},173,[42,8805,5900],{},[42,8807,8809],{"class":44,"line":8808},174,[42,8810,5906],{},[42,8812,8814],{"class":44,"line":8813},175,[42,8815,5912],{},[42,8817,8819],{"class":44,"line":8818},176,[42,8820,5435],{},[42,8822,8824],{"class":44,"line":8823},177,[42,8825,5923],{},[42,8827,8829],{"class":44,"line":8828},178,[42,8830,5929],{},[42,8832,8834],{"class":44,"line":8833},179,[42,8835,5935],{},[42,8837,8839],{"class":44,"line":8838},180,[42,8840,5941],{},[42,8842,8844],{"class":44,"line":8843},181,[42,8845,3578],{},[42,8847,8849],{"class":44,"line":8848},182,[42,8850,5952],{},[42,8852,8854],{"class":44,"line":8853},183,[42,8855,5958],{},[42,8857,8859],{"class":44,"line":8858},184,[42,8860,5603],{},[42,8862,8864],{"class":44,"line":8863},185,[42,8865,5883],{},[42,8867,8869],{"class":44,"line":8868},186,[42,8870,3746],{},[42,8872,8874],{"class":44,"line":8873},187,[42,8875,5979],{},[42,8877,8879],{"class":44,"line":8878},188,[42,8880,5985],{},[42,8882,8884],{"class":44,"line":8883},189,[42,8885,3345],{},[42,8887,8889],{"class":44,"line":8888},190,[42,8890,3193],{"emptyLinePlaceholder":1508},[42,8892,8894],{"class":44,"line":8893},191,[42,8895,557],{},[97,8897,8899],{"id":8898},"依赖-jar","依赖 Jar",[32,8901,8905],{"className":8902,"code":8903,"language":8904,"meta":37,"style":37},"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",[39,8906,8907,8912,8917,8922,8927,8932,8936,8940,8945,8950,8955],{"__ignoreMap":37},[42,8908,8909],{"class":44,"line":45},[42,8910,8911],{},"        \u003Cdependency>\n",[42,8913,8914],{"class":44,"line":179},[42,8915,8916],{},"            \u003CgroupId>org.apache.httpcomponents\u003C\u002FgroupId>\n",[42,8918,8919],{"class":44,"line":412},[42,8920,8921],{},"            \u003CartifactId>httpclient\u003C\u002FartifactId>\n",[42,8923,8924],{"class":44,"line":420},[42,8925,8926],{},"            \u003Cversion>4.5.5\u003C\u002Fversion>\n",[42,8928,8929],{"class":44,"line":1289},[42,8930,8931],{},"        \u003C\u002Fdependency>\n",[42,8933,8934],{"class":44,"line":2027},[42,8935,3193],{"emptyLinePlaceholder":1508},[42,8937,8938],{"class":44,"line":2038},[42,8939,8911],{},[42,8941,8942],{"class":44,"line":2044},[42,8943,8944],{},"            \u003CgroupId>org.dom4j\u003C\u002FgroupId>\n",[42,8946,8947],{"class":44,"line":2058},[42,8948,8949],{},"            \u003CartifactId>dom4j\u003C\u002FartifactId>\n",[42,8951,8952],{"class":44,"line":2072},[42,8953,8954],{},"            \u003Cversion>[2.1.1,)\u003C\u002Fversion>\n",[42,8956,8957],{"class":44,"line":2086},[42,8958,8931],{},[97,8960,8961],{"id":8961},"开发环境",[32,8963,8966],{"className":8964,"code":8965,"language":2541},[2539],"jdk1.7.0_80\napache-maven-3.5.0\nIntelliJ IDEA Community Edition 2017.1.4 x64\n",[39,8967,8965],{"__ignoreMap":37},[1486,8969,3014],{},{"title":37,"searchDepth":179,"depth":179,"links":8971},[8972,8973],{"id":8898,"depth":179,"text":8899},{"id":8961,"depth":179,"text":8961},"2018-03-18","给非 Maven 项目的一堆本地 jar 生成 dependency。用 jar 的 SHA1 去 Nexus OSS 2 的 lucene 接口反查坐标，查不到再退回解析 Manifest。",{},{"title":5126,"description":8975},"posts\u002Fjar2pom-nexus2-dependency",[6384,2799,1791],"U-hZXreFP_8qOUmHlunA2VlS8As0p2eZxV55ZMOgpjc",{"id":8982,"title":8983,"body":8984,"date":9379,"description":9380,"extension":1506,"meta":9381,"navigation":1508,"path":9382,"seo":9383,"stem":9384,"tags":9385,"__hash__":9388},"posts\u002Fposts\u002Foracle-sort-positive-negative.md","ORACLE 正负数分开排序 SQL",{"type":8,"value":8985,"toc":9373},[8986,8989,9018,9021,9061,9131,9135,9155,9180,9257,9261,9279,9301,9371],[97,8987,8988],{"id":8988},"表结构",[32,8990,8992],{"className":2410,"code":8991,"language":2412,"meta":37,"style":37},"create table TEST_ORDER\n(\n  t_name  VARCHAR2(5),\n  t_value NUMBER(3)\n);\n",[39,8993,8994,8999,9004,9009,9014],{"__ignoreMap":37},[42,8995,8996],{"class":44,"line":45},[42,8997,8998],{},"create table TEST_ORDER\n",[42,9000,9001],{"class":44,"line":179},[42,9002,9003],{},"(\n",[42,9005,9006],{"class":44,"line":412},[42,9007,9008],{},"  t_name  VARCHAR2(5),\n",[42,9010,9011],{"class":44,"line":420},[42,9012,9013],{},"  t_value NUMBER(3)\n",[42,9015,9016],{"class":44,"line":1289},[42,9017,2469],{},[97,9019,9020],{"id":9020},"表数据",[32,9022,9024],{"className":2410,"code":9023,"language":2412,"meta":37,"style":37},"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",[39,9025,9026,9031,9036,9041,9046,9051,9056],{"__ignoreMap":37},[42,9027,9028],{"class":44,"line":45},[42,9029,9030],{},"insert into TEST_ORDER (t_name, t_value) values ('A', 10);\n",[42,9032,9033],{"class":44,"line":179},[42,9034,9035],{},"insert into TEST_ORDER (t_name, t_value) values ('B', 30);\n",[42,9037,9038],{"class":44,"line":412},[42,9039,9040],{},"insert into TEST_ORDER (t_name, t_value) values ('C', 20);\n",[42,9042,9043],{"class":44,"line":420},[42,9044,9045],{},"insert into TEST_ORDER (t_name, t_value) values ('D', 40);\n",[42,9047,9048],{"class":44,"line":1289},[42,9049,9050],{},"insert into TEST_ORDER (t_name, t_value) values ('E', -10);\n",[42,9052,9053],{"class":44,"line":2027},[42,9054,9055],{},"insert into TEST_ORDER (t_name, t_value) values ('F', -30);\n",[42,9057,9058],{"class":44,"line":2038},[42,9059,9060],{},"insert into TEST_ORDER (t_name, t_value) values ('G', -20);\n",[278,9062,9063,9073],{},[281,9064,9065],{},[284,9066,9067,9070],{},[287,9068,9069],{},"T_NAME",[287,9071,9072],{},"T_VALUE",[294,9074,9075,9083,9091,9099,9107,9115,9123],{},[284,9076,9077,9080],{},[299,9078,9079],{},"A",[299,9081,9082],{},"10",[284,9084,9085,9088],{},[299,9086,9087],{},"B",[299,9089,9090],{},"30",[284,9092,9093,9096],{},[299,9094,9095],{},"C",[299,9097,9098],{},"20",[284,9100,9101,9104],{},[299,9102,9103],{},"D",[299,9105,9106],{},"40",[284,9108,9109,9112],{},[299,9110,9111],{},"E",[299,9113,9114],{},"-10",[284,9116,9117,9120],{},[299,9118,9119],{},"F",[299,9121,9122],{},"-30",[284,9124,9125,9128],{},[299,9126,9127],{},"G",[299,9129,9130],{},"-20",[97,9132,9134],{"id":9133},"先正后负-正数升序-负数降序","先正后负 正数升序 负数降序",[32,9136,9138],{"className":2410,"code":9137,"language":2412,"meta":37,"style":37},"SELECT T_NAME, T_VALUE\n  FROM TEST_ORDER\n ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC;\n",[39,9139,9140,9145,9150],{"__ignoreMap":37},[42,9141,9142],{"class":44,"line":45},[42,9143,9144],{},"SELECT T_NAME, T_VALUE\n",[42,9146,9147],{"class":44,"line":179},[42,9148,9149],{},"  FROM TEST_ORDER\n",[42,9151,9152],{"class":44,"line":412},[42,9153,9154],{}," ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC;\n",[32,9156,9158],{"className":2410,"code":9157,"language":2412,"meta":37,"style":37},"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",[39,9159,9160,9165,9170,9175],{"__ignoreMap":37},[42,9161,9162],{"class":44,"line":45},[42,9163,9164],{},"SELECT T_NAME,\n",[42,9166,9167],{"class":44,"line":179},[42,9168,9169],{},"       T_VALUE,\n",[42,9171,9172],{"class":44,"line":412},[42,9173,9174],{},"       RANK() OVER(ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) ASC) AS RANKING\n",[42,9176,9177],{"class":44,"line":420},[42,9178,9179],{},"  FROM TEST_ORDER;\n",[278,9181,9182,9193],{},[281,9183,9184],{},[284,9185,9186,9188,9190],{},[287,9187,9069],{},[287,9189,9072],{},[287,9191,9192],{},"RANKING",[294,9194,9195,9203,9212,9221,9230,9239,9248],{},[284,9196,9197,9199,9201],{},[299,9198,9079],{},[299,9200,9082],{},[299,9202,2375],{},[284,9204,9205,9207,9209],{},[299,9206,9095],{},[299,9208,9098],{},[299,9210,9211],{},"2",[284,9213,9214,9216,9218],{},[299,9215,9087],{},[299,9217,9090],{},[299,9219,9220],{},"3",[284,9222,9223,9225,9227],{},[299,9224,9103],{},[299,9226,9106],{},[299,9228,9229],{},"4",[284,9231,9232,9234,9236],{},[299,9233,9111],{},[299,9235,9114],{},[299,9237,9238],{},"5",[284,9240,9241,9243,9245],{},[299,9242,9127],{},[299,9244,9130],{},[299,9246,9247],{},"6",[284,9249,9250,9252,9254],{},[299,9251,9119],{},[299,9253,9122],{},[299,9255,9256],{},"7",[97,9258,9260],{"id":9259},"先正后负-正数降序-负数升序","先正后负 正数降序 负数升序",[32,9262,9264],{"className":2410,"code":9263,"language":2412,"meta":37,"style":37},"SELECT T_NAME, T_VALUE\n  FROM TEST_ORDER\n ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC;\n",[39,9265,9266,9270,9274],{"__ignoreMap":37},[42,9267,9268],{"class":44,"line":45},[42,9269,9144],{},[42,9271,9272],{"class":44,"line":179},[42,9273,9149],{},[42,9275,9276],{"class":44,"line":412},[42,9277,9278],{}," ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC;\n",[32,9280,9282],{"className":2410,"code":9281,"language":2412,"meta":37,"style":37},"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",[39,9283,9284,9288,9292,9297],{"__ignoreMap":37},[42,9285,9286],{"class":44,"line":45},[42,9287,9164],{},[42,9289,9290],{"class":44,"line":179},[42,9291,9169],{},[42,9293,9294],{"class":44,"line":412},[42,9295,9296],{},"       RANK() OVER(ORDER BY SIGN(T_VALUE) DESC, ABS(T_VALUE) DESC) AS RANKING\n",[42,9298,9299],{"class":44,"line":420},[42,9300,9179],{},[278,9302,9303,9313],{},[281,9304,9305],{},[284,9306,9307,9309,9311],{},[287,9308,9069],{},[287,9310,9072],{},[287,9312,9192],{},[294,9314,9315,9323,9331,9339,9347,9355,9363],{},[284,9316,9317,9319,9321],{},[299,9318,9103],{},[299,9320,9106],{},[299,9322,2375],{},[284,9324,9325,9327,9329],{},[299,9326,9087],{},[299,9328,9090],{},[299,9330,9211],{},[284,9332,9333,9335,9337],{},[299,9334,9095],{},[299,9336,9098],{},[299,9338,9220],{},[284,9340,9341,9343,9345],{},[299,9342,9079],{},[299,9344,9082],{},[299,9346,9229],{},[284,9348,9349,9351,9353],{},[299,9350,9119],{},[299,9352,9122],{},[299,9354,9238],{},[284,9356,9357,9359,9361],{},[299,9358,9127],{},[299,9360,9130],{},[299,9362,9247],{},[284,9364,9365,9367,9369],{},[299,9366,9111],{},[299,9368,9114],{},[299,9370,9256],{},[1486,9372,3014],{},{"title":37,"searchDepth":179,"depth":179,"links":9374},[9375,9376,9377,9378],{"id":8988,"depth":179,"text":8988},{"id":9020,"depth":179,"text":9020},{"id":9133,"depth":179,"text":9134},{"id":9259,"depth":179,"text":9260},"2017-09-12","用 SIGN() 把正数和负数分成两组，再各自按绝对值升序或降序排。附 RANK() 版本。",{},"\u002Fposts\u002Foracle-sort-positive-negative",{"title":8983,"description":9380},"posts\u002Foracle-sort-positive-negative",[9386,9387],"Oracle","SQL","9V1MVwXuVwjJ4oNgm5p6skDOU5MF6ROWQUpk-Wwqlak",{"id":9390,"title":9391,"body":9392,"date":10940,"description":10941,"extension":1506,"meta":10942,"navigation":1508,"path":10943,"seo":10944,"stem":10945,"tags":10946,"__hash__":10950},"posts\u002Fposts\u002Fcentos7-hadoop-spark-cluster-setup.md","CentOS7 下 Hadoop 2.7.3 + Spark 2.1.0 集群环境搭建",{"type":8,"value":9393,"toc":10903},[9394,9396,9445,9449,9505,9507,9528,9532,9545,9610,9614,9618,9638,9642,9645,9658,9667,9670,9682,9686,9729,9733,9764,9768,9788,9791,9795,9798,9814,9817,9851,9855,9858,9884,9888,9900,9903,9927,9931,9936,10031,10036,10051,10056,10083,10088,10141,10146,10231,10236,10252,10319,10324,10396,10400,10416,10420,10423,10427,10436,10440,10449,10453,10468,10472,10475,10487,10491,10503,10506,10529,10532,10536,10574,10579,10594,10658,10662,10674,10678,10681,10685,10694,10698,10700,10704,10707,10869,10871,10900],[97,9395,3809],{"id":3809},[278,9397,9398,9411],{},[281,9399,9400],{},[284,9401,9402,9405,9408],{},[287,9403,9404],{},"主机名",[287,9406,9407],{},"ip",[287,9409,9410],{},"进程",[294,9412,9413,9424,9435],{},[284,9414,9415,9418,9421],{},[299,9416,9417],{},"nn.hadoop.data.example.net",[299,9419,9420],{},"172.16.156.220",[299,9422,9423],{},"NameNode、Master、ResourceManager、SecondaryNameNode、JobHistoryServer",[284,9425,9426,9429,9432],{},[299,9427,9428],{},"dn1.hadoop.data.example.net",[299,9430,9431],{},"172.16.156.221",[299,9433,9434],{},"NodeManager、DataNode、Worker",[284,9436,9437,9440,9443],{},[299,9438,9439],{},"dn2.hadoop.data.example.net",[299,9441,9442],{},"172.16.156.222",[299,9444,9434],{},[786,9446,9448],{"id":9447},"yum-安装如下包可能有部分包用不到","yum 安装如下包（可能有部分包用不到）",[32,9450,9452],{"className":385,"code":9451,"language":387,"meta":37,"style":37},"yum install pcre-devel openssl openssl-devel openssh-clients htop gcc zlib lrzsz zip unzip vim telnet-server ncurses wget net-tools\n",[39,9453,9454],{"__ignoreMap":37},[42,9455,9456,9458,9460,9463,9466,9469,9472,9475,9478,9481,9484,9487,9490,9493,9496,9499,9502],{"class":44,"line":45},[42,9457,5089],{"class":394},[42,9459,4030],{"class":79},[42,9461,9462],{"class":79}," pcre-devel",[42,9464,9465],{"class":79}," openssl",[42,9467,9468],{"class":79}," openssl-devel",[42,9470,9471],{"class":79}," openssh-clients",[42,9473,9474],{"class":79}," htop",[42,9476,9477],{"class":79}," gcc",[42,9479,9480],{"class":79}," zlib",[42,9482,9483],{"class":79}," lrzsz",[42,9485,9486],{"class":79}," zip",[42,9488,9489],{"class":79}," unzip",[42,9491,9492],{"class":79}," vim",[42,9494,9495],{"class":79}," telnet-server",[42,9497,9498],{"class":79}," ncurses",[42,9500,9501],{"class":79}," wget",[42,9503,9504],{"class":79}," net-tools\n",[786,9506,1819],{"id":1819},[32,9508,9510],{"className":385,"code":9509,"language":387,"meta":37,"style":37},"systemctl stop firewalld.service\nsystemctl disable firewalld.service\n",[39,9511,9512,9520],{"__ignoreMap":37},[42,9513,9514,9516,9518],{"class":44,"line":45},[42,9515,1810],{"class":394},[42,9517,1831],{"class":79},[42,9519,1816],{"class":79},[42,9521,9522,9524,9526],{"class":44,"line":179},[42,9523,1810],{"class":394},[42,9525,1882],{"class":79},[42,9527,1816],{"class":79},[786,9529,9531],{"id":9530},"配置-hosts-文件","配置 hosts 文件",[32,9533,9535],{"className":385,"code":9534,"language":387,"meta":37,"style":37},"vi \u002Fetc\u002Fhosts\n",[39,9536,9537],{"__ignoreMap":37},[42,9538,9539,9542],{"class":44,"line":45},[42,9540,9541],{"class":394},"vi",[42,9543,9544],{"class":79}," \u002Fetc\u002Fhosts\n",[32,9546,9548],{"className":385,"code":9547,"language":387,"meta":37,"style":37},"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",[39,9549,9550,9567,9585,9589,9596,9603],{"__ignoreMap":37},[42,9551,9552,9555,9558,9561,9564],{"class":44,"line":45},[42,9553,9554],{"class":394},"127.0.0.1",[42,9556,9557],{"class":79},"   localhost",[42,9559,9560],{"class":79}," localhost.localdomain",[42,9562,9563],{"class":79}," localhost4",[42,9565,9566],{"class":79}," localhost4.localdomain4\n",[42,9568,9569,9571,9574,9577,9579,9582],{"class":44,"line":179},[42,9570,2370],{"class":497},[42,9572,9573],{"class":79},":1",[42,9575,9576],{"class":79},"         localhost",[42,9578,9560],{"class":79},[42,9580,9581],{"class":79}," localhost6",[42,9583,9584],{"class":79}," localhost6.localdomain6\n",[42,9586,9587],{"class":44,"line":412},[42,9588,3193],{"emptyLinePlaceholder":1508},[42,9590,9591,9593],{"class":44,"line":420},[42,9592,9420],{"class":394},[42,9594,9595],{"class":79},"  nn.hadoop.data.example.net\n",[42,9597,9598,9600],{"class":44,"line":1289},[42,9599,9431],{"class":394},[42,9601,9602],{"class":79},"  dn1.hadoop.data.example.net\n",[42,9604,9605,9607],{"class":44,"line":2027},[42,9606,9442],{"class":394},[42,9608,9609],{"class":79},"  dn2.hadoop.data.example.net\n",[97,9611,9613],{"id":9612},"安装-jdk-和-scala","安装 JDK 和 Scala",[786,9615,9617],{"id":9616},"_0创建文件夹","0.创建文件夹",[32,9619,9621],{"className":385,"code":9620,"language":387,"meta":37,"style":37},"mkdir \u002Fapp\u002Fjava\nmkdir \u002Fapp\u002Fscala\n",[39,9622,9623,9631],{"__ignoreMap":37},[42,9624,9625,9628],{"class":44,"line":45},[42,9626,9627],{"class":394},"mkdir",[42,9629,9630],{"class":79}," \u002Fapp\u002Fjava\n",[42,9632,9633,9635],{"class":44,"line":179},[42,9634,9627],{"class":394},[42,9636,9637],{"class":79}," \u002Fapp\u002Fscala\n",[786,9639,9641],{"id":9640},"_1下载","1.下载",[11,9643,9644],{},"下载 JDK",[32,9646,9648],{"className":385,"code":9647,"language":387,"meta":37,"style":37},"wget http:\u002F\u002Fdownload.oracle.com\u002Fotn-pub\u002Fjava\u002Fjdk\u002F8u121-b13\u002Fe9e7ea248e2c4826b92b3f075a80e441\u002Fjdk-8u121-linux-x64.tar.gz\n",[39,9649,9650],{"__ignoreMap":37},[42,9651,9652,9655],{"class":44,"line":45},[42,9653,9654],{"class":394},"wget",[42,9656,9657],{"class":79}," http:\u002F\u002Fdownload.oracle.com\u002Fotn-pub\u002Fjava\u002Fjdk\u002F8u121-b13\u002Fe9e7ea248e2c4826b92b3f075a80e441\u002Fjdk-8u121-linux-x64.tar.gz\n",[11,9659,9660,9661,9666],{},"如果失效，",[15,9662,9665],{"href":9663,"rel":9664},"http:\u002F\u002Fwww.oracle.com\u002Ftechnetwork\u002Fjava\u002Fjavase\u002Fdownloads\u002Findex.html",[19],"点这里下载"," 并上传至服务器",[11,9668,9669],{},"下载 Scala",[32,9671,9673],{"className":385,"code":9672,"language":387,"meta":37,"style":37},"wget http:\u002F\u002Fdownloads.lightbend.com\u002Fscala\u002F2.12.1\u002Fscala-2.12.1.tgz\n",[39,9674,9675],{"__ignoreMap":37},[42,9676,9677,9679],{"class":44,"line":45},[42,9678,9654],{"class":394},[42,9680,9681],{"class":79}," http:\u002F\u002Fdownloads.lightbend.com\u002Fscala\u002F2.12.1\u002Fscala-2.12.1.tgz\n",[786,9683,9685],{"id":9684},"_2移动解压","2.移动&解压",[32,9687,9689],{"className":385,"code":9688,"language":387,"meta":37,"style":37},"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",[39,9690,9691,9700,9711,9720],{"__ignoreMap":37},[42,9692,9693,9695,9698],{"class":44,"line":45},[42,9694,4084],{"class":394},[42,9696,9697],{"class":79}," jdk-8u121-linux-x64.tar.gz",[42,9699,9630],{"class":79},[42,9701,9702,9705,9708],{"class":44,"line":179},[42,9703,9704],{"class":394},"tar",[42,9706,9707],{"class":79}," -zxvf",[42,9709,9710],{"class":79}," jdk-8u121-linux-x64.tar.gz\n",[42,9712,9713,9715,9718],{"class":44,"line":412},[42,9714,4084],{"class":394},[42,9716,9717],{"class":79}," scala-2.12.1.tgz",[42,9719,9637],{"class":79},[42,9721,9722,9724,9726],{"class":44,"line":420},[42,9723,9704],{"class":394},[42,9725,9707],{"class":79},[42,9727,9728],{"class":79}," scala-2.12.1.tgz\n",[786,9730,9732],{"id":9731},"_3授权","3.授权",[32,9734,9736],{"className":385,"code":9735,"language":387,"meta":37,"style":37},"chmod -R 775 \u002Fapp\u002F\nchown -R hadoop \u002Fapp\u002F\n",[39,9737,9738,9752],{"__ignoreMap":37},[42,9739,9740,9743,9746,9749],{"class":44,"line":45},[42,9741,9742],{"class":394},"chmod",[42,9744,9745],{"class":79}," -R",[42,9747,9748],{"class":438}," 775",[42,9750,9751],{"class":79}," \u002Fapp\u002F\n",[42,9753,9754,9757,9759,9762],{"class":44,"line":179},[42,9755,9756],{"class":394},"chown",[42,9758,9745],{"class":79},[42,9760,9761],{"class":79}," hadoop",[42,9763,9751],{"class":79},[97,9765,9767],{"id":9766},"创建-hadoop-用户","创建 hadoop 用户",[32,9769,9771],{"className":385,"code":9770,"language":387,"meta":37,"style":37},"useradd hadoop\npasswd hadoop\n",[39,9772,9773,9781],{"__ignoreMap":37},[42,9774,9775,9778],{"class":44,"line":45},[42,9776,9777],{"class":394},"useradd",[42,9779,9780],{"class":79}," hadoop\n",[42,9782,9783,9786],{"class":44,"line":179},[42,9784,9785],{"class":394},"passwd",[42,9787,9780],{"class":79},[11,9789,9790],{},"如无特殊说明 以后均为 hadoop 用户操作",[97,9792,9794],{"id":9793},"ssh-免密登录","SSH 免密登录",[11,9796,9797],{},"生成秘钥：~\u002F.ssh\u002Fid_rsa 和 ~\u002F.ssh\u002Fid_rsa.pub",[32,9799,9801],{"className":385,"code":9800,"language":387,"meta":37,"style":37},"ssh-keygen -t rsa \n",[39,9802,9803],{"__ignoreMap":37},[42,9804,9805,9808,9811],{"class":44,"line":45},[42,9806,9807],{"class":394},"ssh-keygen",[42,9809,9810],{"class":79}," -t",[42,9812,9813],{"class":79}," rsa\n",[11,9815,9816],{},"拷贝公钥到其他机器上",[32,9818,9820],{"className":385,"code":9819,"language":387,"meta":37,"style":37},"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",[39,9821,9822,9833,9842],{"__ignoreMap":37},[42,9823,9824,9827,9830],{"class":44,"line":45},[42,9825,9826],{"class":394},"ssh-copy-id",[42,9828,9829],{"class":79}," -i",[42,9831,9832],{"class":79}," nn.hadoop.data.example.net\n",[42,9834,9835,9837,9839],{"class":44,"line":179},[42,9836,9826],{"class":394},[42,9838,9829],{"class":79},[42,9840,9841],{"class":79}," dn1.hadoop.data.example.net\n",[42,9843,9844,9846,9848],{"class":44,"line":412},[42,9845,9826],{"class":394},[42,9847,9829],{"class":79},[42,9849,9850],{"class":79}," dn2.hadoop.data.example.net\n",[97,9852,9854],{"id":9853},"安装-hadoop","安装 Hadoop",[786,9856,9617],{"id":9857},"_0创建文件夹-1",[32,9859,9861],{"className":385,"code":9860,"language":387,"meta":37,"style":37},"mkdir \u002Fapp\u002Fhadoop\u002Fdata\nmkdir \u002Fapp\u002Fhadoop\u002Fname\nmkdir \u002Fapp\u002Fhadoop\u002Ftmp\n",[39,9862,9863,9870,9877],{"__ignoreMap":37},[42,9864,9865,9867],{"class":44,"line":45},[42,9866,9627],{"class":394},[42,9868,9869],{"class":79}," \u002Fapp\u002Fhadoop\u002Fdata\n",[42,9871,9872,9874],{"class":44,"line":179},[42,9873,9627],{"class":394},[42,9875,9876],{"class":79}," \u002Fapp\u002Fhadoop\u002Fname\n",[42,9878,9879,9881],{"class":44,"line":412},[42,9880,9627],{"class":394},[42,9882,9883],{"class":79}," \u002Fapp\u002Fhadoop\u002Ftmp\n",[786,9885,9887],{"id":9886},"_1下载-hadoop","1.下载 hadoop",[32,9889,9891],{"className":385,"code":9890,"language":387,"meta":37,"style":37},"wget http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.cgi\u002Fhadoop\u002Fcommon\u002Fhadoop-2.7.3\u002Fhadoop-2.7.3.tar.gz\n",[39,9892,9893],{"__ignoreMap":37},[42,9894,9895,9897],{"class":44,"line":45},[42,9896,9654],{"class":394},[42,9898,9899],{"class":79}," http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.cgi\u002Fhadoop\u002Fcommon\u002Fhadoop-2.7.3\u002Fhadoop-2.7.3.tar.gz\n",[786,9901,9685],{"id":9902},"_2移动解压-1",[32,9904,9906],{"className":385,"code":9905,"language":387,"meta":37,"style":37},"mv hadoop-2.7.3.tar.gz \u002Fapp\u002Fhadoop\ntar -zxvf hadoop-2.7.3.tar.gz\n",[39,9907,9908,9918],{"__ignoreMap":37},[42,9909,9910,9912,9915],{"class":44,"line":45},[42,9911,4084],{"class":394},[42,9913,9914],{"class":79}," hadoop-2.7.3.tar.gz",[42,9916,9917],{"class":79}," \u002Fapp\u002Fhadoop\n",[42,9919,9920,9922,9924],{"class":44,"line":179},[42,9921,9704],{"class":394},[42,9923,9707],{"class":79},[42,9925,9926],{"class":79}," hadoop-2.7.3.tar.gz\n",[786,9928,9930],{"id":9929},"_3修改配置文件","3.修改配置文件",[11,9932,9933],{},[107,9934,9935],{},"\u002Fetc\u002Fprofile（root 权限）",[32,9937,9939],{"className":385,"code":9938,"language":387,"meta":37,"style":37},"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",[39,9940,9941,9951,9963,9974,9985,9996,10007,10019],{"__ignoreMap":37},[42,9942,9943,9946,9948],{"class":44,"line":45},[42,9944,9945],{"class":56},"HADOOP_HOME",[42,9947,153],{"class":52},[42,9949,9950],{"class":79},"\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\n",[42,9952,9953,9955,9958,9960],{"class":44,"line":179},[42,9954,1306],{"class":48},[42,9956,9957],{"class":56}," HADOOP_INSTALL",[42,9959,153],{"class":52},[42,9961,9962],{"class":56},"$HADOOP_HOME\n",[42,9964,9965,9967,9970,9972],{"class":44,"line":412},[42,9966,1306],{"class":48},[42,9968,9969],{"class":56}," HADOOP_MAPRED_HOME",[42,9971,153],{"class":52},[42,9973,9962],{"class":56},[42,9975,9976,9978,9981,9983],{"class":44,"line":420},[42,9977,1306],{"class":48},[42,9979,9980],{"class":56}," HADOOP_COMMON_HOME",[42,9982,153],{"class":52},[42,9984,9962],{"class":56},[42,9986,9987,9989,9992,9994],{"class":44,"line":1289},[42,9988,1306],{"class":48},[42,9990,9991],{"class":56}," HADOOP_HDFS_HOME",[42,9993,153],{"class":52},[42,9995,9962],{"class":56},[42,9997,9998,10000,10003,10005],{"class":44,"line":2027},[42,9999,1306],{"class":48},[42,10001,10002],{"class":56}," YARN_HOME",[42,10004,153],{"class":52},[42,10006,9962],{"class":56},[42,10008,10009,10011,10014,10016],{"class":44,"line":2038},[42,10010,1306],{"class":48},[42,10012,10013],{"class":56}," HADOOP_COMMON_LIB_NATIVE_DIR",[42,10015,153],{"class":52},[42,10017,10018],{"class":56},"$HADOOP_HOME\u002Flib\u002Fnative\n",[42,10020,10021,10023,10026,10028],{"class":44,"line":2044},[42,10022,1306],{"class":48},[42,10024,10025],{"class":56}," PATH",[42,10027,153],{"class":52},[42,10029,10030],{"class":56},"$PATH:$HADOOP_HOME\u002Fsbin:$HADOOP_HOME\u002Fbin\n",[11,10032,10033],{},[107,10034,10035],{},"slaves",[32,10037,10039],{"className":385,"code":10038,"language":387,"meta":37,"style":37},"dn1.hadoop.data.example.net\ndn2.hadoop.data.example.net\n",[39,10040,10041,10046],{"__ignoreMap":37},[42,10042,10043],{"class":44,"line":45},[42,10044,10045],{"class":394},"dn1.hadoop.data.example.net\n",[42,10047,10048],{"class":44,"line":179},[42,10049,10050],{"class":394},"dn2.hadoop.data.example.net\n",[11,10052,10053],{},[107,10054,10055],{},"hadoop-env.sh",[32,10057,10059],{"className":385,"code":10058,"language":387,"meta":37,"style":37},"# export JAVA_HOME=${JAVA_HOME}\n改为\nexport JAVA_HOME=\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\u002F\n",[39,10060,10061,10066,10071],{"__ignoreMap":37},[42,10062,10063],{"class":44,"line":45},[42,10064,10065],{"class":401},"# export JAVA_HOME=${JAVA_HOME}\n",[42,10067,10068],{"class":44,"line":179},[42,10069,10070],{"class":394},"改为\n",[42,10072,10073,10075,10078,10080],{"class":44,"line":412},[42,10074,1306],{"class":48},[42,10076,10077],{"class":56}," JAVA_HOME",[42,10079,153],{"class":52},[42,10081,10082],{"class":56},"\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\u002F\n",[11,10084,10085],{},[107,10086,10087],{},"core-site.xml",[32,10089,10091],{"className":8902,"code":10090,"language":8904,"meta":37,"style":37},"\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",[39,10092,10093,10098,10103,10108,10113,10118,10122,10127,10132,10136],{"__ignoreMap":37},[42,10094,10095],{"class":44,"line":45},[42,10096,10097],{},"\u003Cconfiguration>\n",[42,10099,10100],{"class":44,"line":179},[42,10101,10102],{},"    \u003Cproperty>\n",[42,10104,10105],{"class":44,"line":412},[42,10106,10107],{},"    \u003Cname>fs.defaultFS\u003C\u002Fname>\n",[42,10109,10110],{"class":44,"line":420},[42,10111,10112],{},"    \u003Cvalue>hdfs:\u002F\u002Fnn.hadoop.data.example.net:9000\u003C\u002Fvalue>\n",[42,10114,10115],{"class":44,"line":1289},[42,10116,10117],{},"    \u003C\u002Fproperty>\n",[42,10119,10120],{"class":44,"line":2027},[42,10121,10102],{},[42,10123,10124],{"class":44,"line":2038},[42,10125,10126],{},"    \u003Cname>hadoop.tmp.dir\u003C\u002Fname>\n",[42,10128,10129],{"class":44,"line":2044},[42,10130,10131],{},"    \u003Cvalue>\u002Fapp\u002Fhadoop\u002Ftmp\u003C\u002Fvalue>\n",[42,10133,10134],{"class":44,"line":2058},[42,10135,10117],{},[42,10137,10138],{"class":44,"line":2072},[42,10139,10140],{},"\u003C\u002Fconfiguration>\n",[11,10142,10143],{},[107,10144,10145],{},"hdfs-site.xml",[32,10147,10149],{"className":8902,"code":10148,"language":8904,"meta":37,"style":37},"\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",[39,10150,10151,10155,10159,10164,10169,10173,10177,10182,10187,10191,10195,10200,10205,10209,10213,10218,10223,10227],{"__ignoreMap":37},[42,10152,10153],{"class":44,"line":45},[42,10154,10097],{},[42,10156,10157],{"class":44,"line":179},[42,10158,10102],{},[42,10160,10161],{"class":44,"line":412},[42,10162,10163],{},"        \u003Cname>dfs.namenode.secondary.http-address\u003C\u002Fname>\n",[42,10165,10166],{"class":44,"line":420},[42,10167,10168],{},"        \u003Cvalue>nn.hadoop.data.example.net:50090\u003C\u002Fvalue>\n",[42,10170,10171],{"class":44,"line":1289},[42,10172,10117],{},[42,10174,10175],{"class":44,"line":2027},[42,10176,10102],{},[42,10178,10179],{"class":44,"line":2038},[42,10180,10181],{},"        \u003Cname>dfs.namenode.name.dir\u003C\u002Fname>\n",[42,10183,10184],{"class":44,"line":2044},[42,10185,10186],{},"        \u003Cvalue>\u002Fapp\u002Fhadoop\u002Fname\u003C\u002Fvalue>\n",[42,10188,10189],{"class":44,"line":2058},[42,10190,10117],{},[42,10192,10193],{"class":44,"line":2072},[42,10194,10102],{},[42,10196,10197],{"class":44,"line":2086},[42,10198,10199],{},"        \u003Cname>dfs.replication\u003C\u002Fname>\n",[42,10201,10202],{"class":44,"line":2882},[42,10203,10204],{},"        \u003Cvalue>1\u003C\u002Fvalue>\n",[42,10206,10207],{"class":44,"line":2955},[42,10208,10117],{},[42,10210,10211],{"class":44,"line":2961},[42,10212,10102],{},[42,10214,10215],{"class":44,"line":2967},[42,10216,10217],{},"        \u003Cname>dfs.datanode.data.dir\u003C\u002Fname>\n",[42,10219,10220],{"class":44,"line":2973},[42,10221,10222],{},"        \u003Cvalue>\u002Fapp\u002Fhadoop\u002Fdata\u003C\u002Fvalue>\n",[42,10224,10225],{"class":44,"line":2979},[42,10226,10117],{},[42,10228,10229],{"class":44,"line":2985},[42,10230,10140],{},[11,10232,10233],{},[107,10234,10235],{},"mapred-site.xml",[32,10237,10239],{"className":385,"code":10238,"language":387,"meta":37,"style":37},"cp mapred-site.xml.template mapred-site.xml\n",[39,10240,10241],{"__ignoreMap":37},[42,10242,10243,10246,10249],{"class":44,"line":45},[42,10244,10245],{"class":394},"cp",[42,10247,10248],{"class":79}," mapred-site.xml.template",[42,10250,10251],{"class":79}," mapred-site.xml\n",[32,10253,10255],{"className":8902,"code":10254,"language":8904,"meta":37,"style":37},"\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",[39,10256,10257,10261,10265,10270,10275,10279,10283,10288,10293,10297,10301,10306,10311,10315],{"__ignoreMap":37},[42,10258,10259],{"class":44,"line":45},[42,10260,10097],{},[42,10262,10263],{"class":44,"line":179},[42,10264,10102],{},[42,10266,10267],{"class":44,"line":412},[42,10268,10269],{},"        \u003Cname>mapreduce.framework.name\u003C\u002Fname>\n",[42,10271,10272],{"class":44,"line":420},[42,10273,10274],{},"        \u003Cvalue>yarn\u003C\u002Fvalue>\n",[42,10276,10277],{"class":44,"line":1289},[42,10278,10117],{},[42,10280,10281],{"class":44,"line":2027},[42,10282,10102],{},[42,10284,10285],{"class":44,"line":2038},[42,10286,10287],{},"        \u003Cname>mapreduce.jobhistory.address\u003C\u002Fname>\n",[42,10289,10290],{"class":44,"line":2044},[42,10291,10292],{},"        \u003Cvalue>nn.hadoop.data.example.net:10020\u003C\u002Fvalue>\n",[42,10294,10295],{"class":44,"line":2058},[42,10296,10117],{},[42,10298,10299],{"class":44,"line":2072},[42,10300,10102],{},[42,10302,10303],{"class":44,"line":2086},[42,10304,10305],{},"        \u003Cname>mapreduce.jobhistory.webapp.address\u003C\u002Fname>\n",[42,10307,10308],{"class":44,"line":2882},[42,10309,10310],{},"        \u003Cvalue>nn.hadoop.data.example.net:19888\u003C\u002Fvalue>\n",[42,10312,10313],{"class":44,"line":2955},[42,10314,10117],{},[42,10316,10317],{"class":44,"line":2961},[42,10318,10140],{},[11,10320,10321],{},[107,10322,10323],{},"yarn-site.xml",[32,10325,10327],{"className":8902,"code":10326,"language":8904,"meta":37,"style":37},"\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",[39,10328,10329,10333,10338,10342,10347,10352,10356,10360,10365,10370,10374,10378,10383,10388,10392],{"__ignoreMap":37},[42,10330,10331],{"class":44,"line":45},[42,10332,10097],{},[42,10334,10335],{"class":44,"line":179},[42,10336,10337],{},"\u003C!-- Site specific YARN configuration properties -->\n",[42,10339,10340],{"class":44,"line":412},[42,10341,10102],{},[42,10343,10344],{"class":44,"line":420},[42,10345,10346],{},"        \u003Cname>yarn.resourcemanager.hostname\u003C\u002Fname>\n",[42,10348,10349],{"class":44,"line":1289},[42,10350,10351],{},"        \u003Cvalue>nn.hadoop.data.example.net\u003C\u002Fvalue>\n",[42,10353,10354],{"class":44,"line":2027},[42,10355,10117],{},[42,10357,10358],{"class":44,"line":2038},[42,10359,10102],{},[42,10361,10362],{"class":44,"line":2044},[42,10363,10364],{},"        \u003Cname>yarn.nodemanager.aux-services\u003C\u002Fname>\n",[42,10366,10367],{"class":44,"line":2058},[42,10368,10369],{},"        \u003Cvalue>mapreduce_shuffle\u003C\u002Fvalue>\n",[42,10371,10372],{"class":44,"line":2072},[42,10373,10117],{},[42,10375,10376],{"class":44,"line":2086},[42,10377,10102],{},[42,10379,10380],{"class":44,"line":2882},[42,10381,10382],{},"         \u003Cname>yarn.nodemanager.aux-services.mapreduce.shuffle.class\u003C\u002Fname>\n",[42,10384,10385],{"class":44,"line":2955},[42,10386,10387],{},"         \u003Cvalue>org.apache.hadoop.mapred.ShuffleHandler\u003C\u002Fvalue>\n",[42,10389,10390],{"class":44,"line":2961},[42,10391,10117],{},[42,10393,10394],{"class":44,"line":2967},[42,10395,10140],{},[786,10397,10399],{"id":10398},"_4格式化-namenode","4.格式化 namenode",[32,10401,10403],{"className":385,"code":10402,"language":387,"meta":37,"style":37},"hadoop namenode -format\n",[39,10404,10405],{"__ignoreMap":37},[42,10406,10407,10410,10413],{"class":44,"line":45},[42,10408,10409],{"class":394},"hadoop",[42,10411,10412],{"class":79}," namenode",[42,10414,10415],{"class":79}," -format\n",[786,10417,10419],{"id":10418},"_5复制文件到其他机器","5.复制文件到其他机器",[11,10421,10422],{},"将 \u002Fapp\u002Fhadoop（包括 data、name、tmp 和配置好的 hadoop）复制到其他机器。",[786,10424,10426],{"id":10425},"_6启动-dfs","6.启动 dfs",[32,10428,10430],{"className":385,"code":10429,"language":387,"meta":37,"style":37},"start-dfs.sh\n",[39,10431,10432],{"__ignoreMap":37},[42,10433,10434],{"class":44,"line":45},[42,10435,10429],{"class":394},[786,10437,10439],{"id":10438},"_7启动-yarn","7.启动 yarn",[32,10441,10443],{"className":385,"code":10442,"language":387,"meta":37,"style":37},"start-yarn.sh\n",[39,10444,10445],{"__ignoreMap":37},[42,10446,10447],{"class":44,"line":45},[42,10448,10442],{"class":394},[786,10450,10452],{"id":10451},"_8启动-jobhistory","8.启动 jobhistory",[32,10454,10456],{"className":385,"code":10455,"language":387,"meta":37,"style":37},"mr-jobhistory-daemon.sh start historyserver\n",[39,10457,10458],{"__ignoreMap":37},[42,10459,10460,10463,10465],{"class":44,"line":45},[42,10461,10462],{"class":394},"mr-jobhistory-daemon.sh",[42,10464,1813],{"class":79},[42,10466,10467],{"class":79}," historyserver\n",[97,10469,10471],{"id":10470},"安装-spark2","安装 Spark2",[786,10473,9617],{"id":10474},"_0创建文件夹-2",[32,10476,10478],{"className":385,"code":10477,"language":387,"meta":37,"style":37},"mkdir \u002Fapp\u002Fspark\n",[39,10479,10480],{"__ignoreMap":37},[42,10481,10482,10484],{"class":44,"line":45},[42,10483,9627],{"class":394},[42,10485,10486],{"class":79}," \u002Fapp\u002Fspark\n",[786,10488,10490],{"id":10489},"_1下载-spark2","1.下载 Spark2",[32,10492,10494],{"className":385,"code":10493,"language":387,"meta":37,"style":37},"wget http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.lua\u002Fspark\u002Fspark-2.1.0\u002Fspark-2.1.0-bin-hadoop2.7.tgz\n",[39,10495,10496],{"__ignoreMap":37},[42,10497,10498,10500],{"class":44,"line":45},[42,10499,9654],{"class":394},[42,10501,10502],{"class":79}," http:\u002F\u002Fwww.apache.org\u002Fdyn\u002Fcloser.lua\u002Fspark\u002Fspark-2.1.0\u002Fspark-2.1.0-bin-hadoop2.7.tgz\n",[786,10504,9685],{"id":10505},"_2移动解压-2",[32,10507,10509],{"className":385,"code":10508,"language":387,"meta":37,"style":37},"mv spark-2.1.0-bin-hadoop2.7.tgz \u002Fapp\u002Fspark\ntar -zxvf spark-2.1.0-bin-hadoop2.7.tgz\n",[39,10510,10511,10520],{"__ignoreMap":37},[42,10512,10513,10515,10518],{"class":44,"line":45},[42,10514,4084],{"class":394},[42,10516,10517],{"class":79}," spark-2.1.0-bin-hadoop2.7.tgz",[42,10519,10486],{"class":79},[42,10521,10522,10524,10526],{"class":44,"line":179},[42,10523,9704],{"class":394},[42,10525,9707],{"class":79},[42,10527,10528],{"class":79}," spark-2.1.0-bin-hadoop2.7.tgz\n",[786,10530,9930],{"id":10531},"_3修改配置文件-1",[11,10533,10534],{},[107,10535,9935],{},[32,10537,10539],{"className":385,"code":10538,"language":387,"meta":37,"style":37},"export SPARK_HOME=\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\nexport PATH=\"$SPARK_HOME\u002Fbin:$PATH\"\n",[39,10540,10541,10553],{"__ignoreMap":37},[42,10542,10543,10545,10548,10550],{"class":44,"line":45},[42,10544,1306],{"class":48},[42,10546,10547],{"class":56}," SPARK_HOME",[42,10549,153],{"class":52},[42,10551,10552],{"class":56},"\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\n",[42,10554,10555,10557,10559,10561,10563,10566,10569,10572],{"class":44,"line":179},[42,10556,1306],{"class":48},[42,10558,10025],{"class":56},[42,10560,153],{"class":52},[42,10562,156],{"class":52},[42,10564,10565],{"class":56},"$SPARK_HOME",[42,10567,10568],{"class":79},"\u002Fbin:",[42,10570,10571],{"class":56},"$PATH",[42,10573,2611],{"class":52},[11,10575,10576],{},[107,10577,10578],{},"spark-env.sh",[32,10580,10582],{"className":385,"code":10581,"language":387,"meta":37,"style":37},"cp spark-env.sh.template spark-env.sh\n",[39,10583,10584],{"__ignoreMap":37},[42,10585,10586,10588,10591],{"class":44,"line":45},[42,10587,10245],{"class":394},[42,10589,10590],{"class":79}," spark-env.sh.template",[42,10592,10593],{"class":79}," spark-env.sh\n",[32,10595,10597],{"className":385,"code":10596,"language":387,"meta":37,"style":37},"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",[39,10598,10599,10611,10622,10634,10646],{"__ignoreMap":37},[42,10600,10601,10603,10606,10608],{"class":44,"line":45},[42,10602,1306],{"class":48},[42,10604,10605],{"class":56}," SCALA_HOME",[42,10607,153],{"class":52},[42,10609,10610],{"class":56},"\u002Fapp\u002Fscala\u002Fscala-2.12.1\n",[42,10612,10613,10615,10617,10619],{"class":44,"line":179},[42,10614,1306],{"class":48},[42,10616,10077],{"class":56},[42,10618,153],{"class":52},[42,10620,10621],{"class":56},"\u002Fapp\u002Fjava\u002Fjdk1.8.0_121\n",[42,10623,10624,10626,10629,10631],{"class":44,"line":412},[42,10625,1306],{"class":48},[42,10627,10628],{"class":56}," SPARK_MASTER_IP",[42,10630,153],{"class":52},[42,10632,10633],{"class":56},"nn.hadoop.data.easydebug.net\n",[42,10635,10636,10638,10641,10643],{"class":44,"line":420},[42,10637,1306],{"class":48},[42,10639,10640],{"class":56}," SPARK_WORKER_MEMORY",[42,10642,153],{"class":52},[42,10644,10645],{"class":56},"1g\n",[42,10647,10648,10650,10653,10655],{"class":44,"line":1289},[42,10649,1306],{"class":48},[42,10651,10652],{"class":56}," HADOOP_CONF_DIR",[42,10654,153],{"class":52},[42,10656,10657],{"class":56},"\u002Fapp\u002Fhadoop\u002Fhadoop-2.7.3\u002Fetc\u002Fhadoop\n",[11,10659,10660],{},[107,10661,10035],{},[32,10663,10664],{"className":385,"code":10038,"language":387,"meta":37,"style":37},[39,10665,10666,10670],{"__ignoreMap":37},[42,10667,10668],{"class":44,"line":45},[42,10669,10045],{"class":394},[42,10671,10672],{"class":44,"line":179},[42,10673,10050],{"class":394},[786,10675,10677],{"id":10676},"_4复制文件到其他机器","4.复制文件到其他机器",[11,10679,10680],{},"将 \u002Fapp\u002Fspark 复制到其他机器。",[786,10682,10684],{"id":10683},"_5启动-spark","5.启动 Spark",[32,10686,10688],{"className":385,"code":10687,"language":387,"meta":37,"style":37},"\u002Fapp\u002Fspark\u002Fspark-2.1.0-bin-hadoop2.7\u002Fsbin\u002Fstart-all.sh\n",[39,10689,10690],{"__ignoreMap":37},[42,10691,10692],{"class":44,"line":45},[42,10693,10687],{"class":394},[97,10695,10697],{"id":10696},"安装完成-_","安装完成 ^_^",[3817,10699],{},[786,10701,10703],{"id":10702},"因为系统变量改了几次最后贴一下完整的","因为系统变量改了几次，最后贴一下完整的",[11,10705,10706],{},"其实可以在配置前直接贴进去。",[32,10708,10710],{"className":385,"code":10709,"language":387,"meta":37,"style":37},"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",[39,10711,10712,10722,10732,10743,10755,10759,10767,10777,10787,10797,10807,10817,10827,10837,10841,10851],{"__ignoreMap":37},[42,10713,10714,10716,10718,10720],{"class":44,"line":45},[42,10715,1306],{"class":48},[42,10717,10077],{"class":56},[42,10719,153],{"class":52},[42,10721,10621],{"class":56},[42,10723,10724,10726,10728,10730],{"class":44,"line":179},[42,10725,1306],{"class":48},[42,10727,10605],{"class":56},[42,10729,153],{"class":52},[42,10731,10610],{"class":56},[42,10733,10734,10736,10738,10740],{"class":44,"line":412},[42,10735,1306],{"class":48},[42,10737,10025],{"class":56},[42,10739,153],{"class":52},[42,10741,10742],{"class":56},"$JAVA_HOME\u002Fbin:$SCALA_HOME\u002Fbin:$PATH\n",[42,10744,10745,10747,10750,10752],{"class":44,"line":420},[42,10746,1306],{"class":48},[42,10748,10749],{"class":56}," CLASSPATH",[42,10751,153],{"class":52},[42,10753,10754],{"class":56},".:$JAVA_HOME\u002Flib\u002Fdt.jar:$JAVA_HOME\u002Flib\u002Ftools.jar\n",[42,10756,10757],{"class":44,"line":1289},[42,10758,3193],{"emptyLinePlaceholder":1508},[42,10760,10761,10763,10765],{"class":44,"line":2027},[42,10762,9945],{"class":56},[42,10764,153],{"class":52},[42,10766,9950],{"class":79},[42,10768,10769,10771,10773,10775],{"class":44,"line":2038},[42,10770,1306],{"class":48},[42,10772,9957],{"class":56},[42,10774,153],{"class":52},[42,10776,9962],{"class":56},[42,10778,10779,10781,10783,10785],{"class":44,"line":2044},[42,10780,1306],{"class":48},[42,10782,9969],{"class":56},[42,10784,153],{"class":52},[42,10786,9962],{"class":56},[42,10788,10789,10791,10793,10795],{"class":44,"line":2058},[42,10790,1306],{"class":48},[42,10792,9980],{"class":56},[42,10794,153],{"class":52},[42,10796,9962],{"class":56},[42,10798,10799,10801,10803,10805],{"class":44,"line":2072},[42,10800,1306],{"class":48},[42,10802,9991],{"class":56},[42,10804,153],{"class":52},[42,10806,9962],{"class":56},[42,10808,10809,10811,10813,10815],{"class":44,"line":2086},[42,10810,1306],{"class":48},[42,10812,10002],{"class":56},[42,10814,153],{"class":52},[42,10816,9962],{"class":56},[42,10818,10819,10821,10823,10825],{"class":44,"line":2882},[42,10820,1306],{"class":48},[42,10822,10013],{"class":56},[42,10824,153],{"class":52},[42,10826,10018],{"class":56},[42,10828,10829,10831,10833,10835],{"class":44,"line":2955},[42,10830,1306],{"class":48},[42,10832,10025],{"class":56},[42,10834,153],{"class":52},[42,10836,10030],{"class":56},[42,10838,10839],{"class":44,"line":2961},[42,10840,3193],{"emptyLinePlaceholder":1508},[42,10842,10843,10845,10847,10849],{"class":44,"line":2967},[42,10844,1306],{"class":48},[42,10846,10547],{"class":56},[42,10848,153],{"class":52},[42,10850,10552],{"class":56},[42,10852,10853,10855,10857,10859,10861,10863,10865,10867],{"class":44,"line":2973},[42,10854,1306],{"class":48},[42,10856,10025],{"class":56},[42,10858,153],{"class":52},[42,10860,156],{"class":52},[42,10862,10565],{"class":56},[42,10864,10568],{"class":79},[42,10866,10571],{"class":56},[42,10868,2611],{"class":52},[3817,10870],{},[8043,10872,10873,10876,10884,10892],{},[11,10874,10875],{},"参考文章",[11,10877,10878,10879],{},"CentOS 6.5 hadoop 2.7.3 集群环境搭建\n",[15,10880,10883],{"href":10881,"rel":10882},"http:\u002F\u002Fblog.csdn.net\u002Fmxxlevel\u002Farticle\u002Fdetails\u002F52653086",[19],"blog.csdn.net\u002Fmxxlevel\u002Farticle\u002Fdetails\u002F52653086",[11,10885,10886,10887],{},"Spark修炼之道（进阶篇）——Spark 入门到精通：第一节 Spark 1.5.0 集群搭建\n",[15,10888,10891],{"href":10889,"rel":10890},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F60309?spm=5176.8251999.569296.66.0H8Bal",[19],"yq.aliyun.com\u002Farticles\u002F60309",[11,10893,10894,10895],{},"Hadoop2.7.3+Spark2.1.0 完全分布式环境 搭建全过程\n",[15,10896,10899],{"href":10897,"rel":10898},"http:\u002F\u002Fwww.cnblogs.com\u002Fpurstar\u002Fp\u002F6293605.html",[19],"cnblogs.com\u002Fpurstar\u002Fp\u002F6293605.html",[1486,10901,10902],{},"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":37,"searchDepth":179,"depth":179,"links":10904},[10905,10910,10916,10917,10918,10929,10937],{"id":3809,"depth":179,"text":3809,"children":10906},[10907,10908,10909],{"id":9447,"depth":412,"text":9448},{"id":1819,"depth":412,"text":1819},{"id":9530,"depth":412,"text":9531},{"id":9612,"depth":179,"text":9613,"children":10911},[10912,10913,10914,10915],{"id":9616,"depth":412,"text":9617},{"id":9640,"depth":412,"text":9641},{"id":9684,"depth":412,"text":9685},{"id":9731,"depth":412,"text":9732},{"id":9766,"depth":179,"text":9767},{"id":9793,"depth":179,"text":9794},{"id":9853,"depth":179,"text":9854,"children":10919},[10920,10921,10922,10923,10924,10925,10926,10927,10928],{"id":9857,"depth":412,"text":9617},{"id":9886,"depth":412,"text":9887},{"id":9902,"depth":412,"text":9685},{"id":9929,"depth":412,"text":9930},{"id":10398,"depth":412,"text":10399},{"id":10418,"depth":412,"text":10419},{"id":10425,"depth":412,"text":10426},{"id":10438,"depth":412,"text":10439},{"id":10451,"depth":412,"text":10452},{"id":10470,"depth":179,"text":10471,"children":10930},[10931,10932,10933,10934,10935,10936],{"id":10474,"depth":412,"text":9617},{"id":10489,"depth":412,"text":10490},{"id":10505,"depth":412,"text":9685},{"id":10531,"depth":412,"text":9930},{"id":10676,"depth":412,"text":10677},{"id":10683,"depth":412,"text":10684},{"id":10696,"depth":179,"text":10697,"children":10938},[10939],{"id":10702,"depth":412,"text":10703},"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":9391,"description":10941},"posts\u002Fcentos7-hadoop-spark-cluster-setup",[10947,10948,10949,2179],"Hadoop","Spark","大数据","l1G-4Zt0wIq9pgZ5Nnet1mvOeQBaa2Cqo3BLnROXsFQ",{"id":10952,"title":10953,"body":10954,"date":11083,"description":11084,"extension":1506,"meta":11085,"navigation":1508,"path":11086,"seo":11087,"stem":11088,"tags":11089,"__hash__":11093},"posts\u002Fposts\u002Fwindows-install-facebook-prophet.md","Facebook 开源预测工具 Prophet 安装方法",{"type":8,"value":10955,"toc":11075},[10956,10965,10967,10969,10979,10983,10993,10997,11005,11009,11039,11042,11044,11047,11052,11057,11062,11073],[11,10957,10958,10959,10964],{},"最近研究看到一篇介绍 ",[15,10960,10963],{"href":10961,"rel":10962},"http:\u002F\u002Fmp.weixin.qq.com\u002Fs\u002F6yZRKbH0GYvqHwCDmpL-Uw",[19],"facebook 开源的大规模预测框架 Prophet"," 的文章，折腾了两个晚上终于在 Win 环境下安装成功，把结果分享下。",[3817,10966],{},[97,10968,3809],{"id":3809},[8043,10970,10971],{},[1434,10972,10973,10976],{},[1437,10974,10975],{},"Windows 7 x64",[1437,10977,10978],{},"Anaconda3-4.2.0-Windows-x86 (Python 3.5)",[97,10980,10982],{"id":10981},"安装-visual-c-2015-build-tools","安装 Visual C++ 2015 Build Tools",[8043,10984,10985],{},[11,10986,10987,10988],{},"Prophet 需要 C 语言编译，在 Win 环境下需要先安装 ",[15,10989,10992],{"href":10990,"rel":10991},"http:\u002F\u002Flandinghub.visualstudio.com\u002Fvisual-cpp-build-tools",[19],"Visual C++ 2015 Build Tools",[97,10994,10996],{"id":10995},"安装-pystan","安装 pystan",[8043,10998,10999],{},[11,11000,11001,11002],{},"Prophet 依赖 pystan 包所以也需要安装 pystan\n",[39,11003,11004],{},"pip install pystan",[97,11006,11008],{"id":11007},"安装-prophet","安装 Prophet",[8043,11010,11011,11019,11030],{},[11,11012,11013],{},[107,11014,11015,11016],{},"不要用 ",[39,11017,11018],{},"pip install fbprophet",[11,11020,11021,11026,11027],{},[15,11022,11025],{"href":11023,"rel":11024},"https:\u002F\u002Fgithub.com\u002Ffacebookincubator\u002Fprophet",[19],"下载"," prophet 最新源码。\n或者 ",[39,11028,11029],{},"git clone https:\u002F\u002Fgithub.com\u002Ffacebookincubator\u002Fprophet",[11,11031,11032,11033,11036],{},"解压到指定目录，例如 D:\\Anaconda3\\prophet\nCMD:\n",[39,11034,11035],{},"cd D:\\Anaconda3\\prophet\\python",[39,11037,11038],{},"pip install -e .",[97,11040,11041],{"id":11041},"安装完成",[3817,11043],{},[97,11045,11046],{"id":11046},"踩过的坑",[11,11048,11049],{},[107,11050,11051],{},"1. error: command ‘C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\BIN\\cl.exe’ failed with exit status 2",[8043,11053,11054],{},[11,11055,11056],{},"先确认 Visual C++ 2015 Build Tools 是否安装；如果已经安装，请放弃使用 pip 安装 Prophet。下载源码安装 Prophet 无问题。",[11,11058,11059],{},[107,11060,11061],{},"2. No such file or directory: u’fbprophet\u002Fstan_models\u002Flinear_growth.pkl’",[8043,11063,11064],{},[11,11065,11066,11067,11072],{},"目测是一个",[15,11068,11071],{"href":11069,"rel":11070},"https:\u002F\u002Fgithub.com\u002Ffacebookincubator\u002Fprophet\u002Fpull\u002F100",[19],"已知 BUG","，已经修复。但好像 Release 的 v0.1 源码未更新。直接下载最新源码编译无报错。",[3817,11074],{},{"title":37,"searchDepth":179,"depth":179,"links":11076},[11077,11078,11079,11080,11081,11082],{"id":3809,"depth":179,"text":3809},{"id":10981,"depth":179,"text":10982},{"id":10995,"depth":179,"text":10996},{"id":11007,"depth":179,"text":11008},{"id":11041,"depth":179,"text":11041},{"id":11046,"depth":179,"text":11046},"2017-03-16","Win 下装 Prophet 不能用 pip install fbprophet，得先装 VC++ 2015 Build Tools 和 pystan，再从源码装。附两个踩坑记录。",{},"\u002Fposts\u002Fwindows-install-facebook-prophet",{"title":10953,"description":11084},"posts\u002Fwindows-install-facebook-prophet",[11090,11091,11092],"Python","Prophet","环境配置","yXvlR_ZI35tb-kDjwjaABLwx4Qxkwe20Ek459jNtV6c",1790913748999]