問(wèn)題排查指南)
簡(jiǎn)介本資源是一份面向Java全棧開(kāi)發(fā)者與運(yùn)維人員的若依RuoYi前后端分離項(xiàng)目實(shí)戰(zhàn)部署指南聚焦LinuxNginx與WindowsTomcat雙環(huán)境落地難點(diǎn)系統(tǒng)解決部署后404頁(yè)面、跨域調(diào)用第三方API如百度地圖等高頻問(wèn)題。文檔以實(shí)操為導(dǎo)向完整覆蓋后臺(tái)jar/war包構(gòu)建、前端dist目錄部署、Redis緩存配置、Nginx反向代理設(shè)置、Tomcat Context路徑定制及web.xml容錯(cuò)配置等關(guān)鍵環(huán)節(jié)并附Vue路由路徑修正、CORS策略配置等排錯(cuò)細(xì)節(jié)。資源為1個(gè)223KB的Word文檔.docx結(jié)構(gòu)清晰、圖文結(jié)合含命令示例、配置代碼片段與典型錯(cuò)誤歸因分析便于快速查閱與現(xiàn)場(chǎng)實(shí)施。目前已有7766人學(xué)習(xí)下載適合剛接觸若依框架、需獨(dú)立完成生產(chǎn)環(huán)境部署的中初級(jí)開(kāi)發(fā)者高效上手。1. “菜單打不開(kāi)”“登錄后空白頁(yè)”“404 刷不出來(lái)”——若依前后端分離部署里最玄學(xué)的三連擊90% 的人卡在這三步就放棄重裝你剛把若依RuoYi前后端分離版打包好scp 傳到服務(wù)器、nohup 啟動(dòng) jar、nginx reload 完畢瀏覽器輸入http://xxx.xxx.xxx.xxx—— 登錄頁(yè)出來(lái)了輸入賬號(hào)密碼一按回車(chē)頁(yè)面白了。F5 刷新404。點(diǎn)左側(cè)菜單沒(méi)反應(yīng)控制臺(tái)報(bào)Failed to load resource: the server responded with a status of 404 (Not Found)。再查 nginx 日志全是*1 open() /usr/share/nginx/html/profile/xxxx failed (2: No such file or directory)。你翻遍文檔、搜遍論壇有人說(shuō)改vue.config.js有人說(shuō)加web.xml有人說(shuō)刪掉publicPath前面的點(diǎn)有人說(shuō)要配history模式 fallback……越試越亂最后默默刪庫(kù)重來(lái)。這不是你技術(shù)不行是若依這套部署鏈路里埋了三個(gè)「默認(rèn)不報(bào)錯(cuò)但必然失效」的隱性斷點(diǎn)前端路由模式與服務(wù)端靜態(tài)資源路徑不匹配、Nginx 未正確處理 history 跳轉(zhuǎn)、Tomcat 對(duì)單頁(yè)應(yīng)用SPA的 404 沒(méi)兜底。這篇筆記不講“怎么安裝 Redis”只聚焦這三類(lèi)高頻翻車(chē)現(xiàn)場(chǎng)——它們不是配置錯(cuò)誤而是若依作為一套真實(shí)生產(chǎn)級(jí)框架在脫離開(kāi)發(fā)環(huán)境npm run serve后對(duì)部署側(cè)提出的明確契約要求。如果你正被“菜單加載失敗”“空白頁(yè)”“跨域 404”反復(fù)暴擊這篇就是你的后悔藥。2. 登錄成功但菜單空白/跳轉(zhuǎn) 404Vue Router history 模式 Nginx 靜態(tài)服務(wù)的致命錯(cuò)配若依前端基于 Vue Router默認(rèn)啟用history模式非hash模式這意味著 URL 是/system/user而非/#/system/user。好處是美觀(guān)、SEO 友好壞處是——所有非根路徑的請(qǐng)求都必須由前端接管否則服務(wù)端會(huì)當(dāng)成真實(shí)文件路徑去查找找不到就 404。而 Nginx 默認(rèn)行為正是收到/system/user請(qǐng)求就去root /usr/share/nginx/html;目錄下找system/user/index.html這個(gè)文件。它當(dāng)然不存在于是返回 404。此時(shí) Vue Router 根本沒(méi)機(jī)會(huì)啟動(dòng)更別說(shuō)渲染菜單了。這不是前端代碼 bug是服務(wù)端沒(méi)履行“把所有非 API 請(qǐng)求都 fallback 到index.html”的約定。2.1 根因深挖為什么location /塊里不加try_files就必然白屏看若依文檔里那句輕描淡寫(xiě)的“修改 nginx.conf”很多人只改了proxy_pass代理后端 API卻漏掉了最關(guān)鍵的location /塊配置。標(biāo)準(zhǔn) Nginx 配置中l(wèi)ocation /控制所有以/開(kāi)頭的請(qǐng)求。若此處僅寫(xiě)location / { root /usr/share/nginx/html; index index.html; }Nginx 會(huì)嚴(yán)格按字面路徑查找文件/system/user→ 查/usr/share/nginx/html/system/user→ 無(wú)此目錄 → 404。而 Vue Router 的history模式要求只要請(qǐng)求不是.js/.css/.png等靜態(tài)資源且不是/prod-api/這類(lèi) API 前綴就一律返回index.html讓前端 JS 自己解析路由。這個(gè)邏輯必須由try_files指令實(shí)現(xiàn)。2.2 正確解法Nginx 必須啟用try_filesfallback機(jī)制在nginx.conf的server塊內(nèi)找到或新增location /配置徹底替換為以下內(nèi)容注意這是唯一生效方案別再抄網(wǎng)上殘缺片段location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; }邏輯說(shuō)明try_files指令按順序嘗試匹配$uri如/system/user、$uri/如/system/user/若都失敗則強(qiáng)制返回/index.html即前端入口文件。這樣無(wú)論用戶(hù)直接訪(fǎng)問(wèn)/、/system/user還是刷新頁(yè)面Nginx 都返回index.htmlVue Router 才能初始化并根據(jù) URL 渲染對(duì)應(yīng)組件。參數(shù)說(shuō)明$uri是原始請(qǐng)求 URI不含 query string$uri/是加斜杠的目錄形式/index.html是 fallback 路徑必須以/開(kāi)頭表示相對(duì)于root的絕對(duì)路徑。若root設(shè)為/data/www則實(shí)際返回/data/www/index.html。2.3 驗(yàn)證是否生效三步快速自檢檢查 Nginx 配置語(yǔ)法執(zhí)行sudo nginx -t輸出syntax is ok且test is successful才算通過(guò)確認(rèn)靜態(tài)文件位置ls -l /usr/share/nginx/html/必須能看到index.html、favicon.ico、js/、css/等文件即你復(fù)制的dist目錄內(nèi)容抓包驗(yàn)證響應(yīng)體用瀏覽器開(kāi)發(fā)者工具 Network 面板訪(fǎng)問(wèn)/system/user查看 Response Headers 中Content-Type是否為text/htmlResponse Body 是否包含div idapp—— 若是說(shuō)明 Nginx 成功 fallback 到index.html若仍是 404 或返回空說(shuō)明location /配置未生效或路徑錯(cuò)誤。2.4 避坑常見(jiàn)問(wèn)題與血淚經(jīng)驗(yàn)現(xiàn)象改完nginx.confnginx -s reload后依然 404原因nginx.conf中存在多個(gè)location /塊比如location / { ... }和location /prod-api { ... }并存Nginx 優(yōu)先匹配最長(zhǎng)前綴若/prod-api塊寫(xiě)在/塊前面且未加^(guān)~修飾符可能導(dǎo)致/system/user被錯(cuò)誤匹配到 API 塊。解決確保location /是最外層、最寬泛的塊且放在所有具體路徑如/prod-api、/static之后或給 API 塊加^(guān)~強(qiáng)制前綴匹配location ^~ /prod-api { ... }?,F(xiàn)象Nginx 返回index.html但控制臺(tái)報(bào)GET http://ip/js/app.xxx.js net::ERR_ABORTED 404原因前端構(gòu)建時(shí)publicPath配置錯(cuò)誤導(dǎo)致生成的index.html中 JS/CSS 路徑指向/js/app.xxx.js但 Nginx 的root指向/usr/share/nginx/html實(shí)際文件在/usr/share/nginx/html/dist/js/下。解決部署前必須修改vue.config.js中的publicPath// vue.config.js module.exports { publicPath: ./, // 關(guān)鍵改為相對(duì)路徑而非 / 或 /prod/ }注意./表示相對(duì)于index.html當(dāng)前目錄這樣生成的index.html里script src./js/app.xxx.js才能正確加載。若設(shè)為/路徑變成絕對(duì)根路徑/js/...Nginx 會(huì)在root根目錄下找而非dist子目錄。現(xiàn)象try_files配置正確但訪(fǎng)問(wèn)/profile/avatar若依頭像上傳路徑仍 404原因若依后端默認(rèn)將用戶(hù)頭像存于profile/目錄如profile/avatar/xxx.png該路徑屬于靜態(tài)資源需單獨(dú)配置location讓 Nginx 直接提供而非 fallback 到index.html。解決在server塊內(nèi)添加獨(dú)立locationlocation /profile/ { alias /home/ruoyi/ruoyi-admin/profile/; # 替換為你的實(shí)際 profile 目錄絕對(duì)路徑 expires 1d; add_header Cache-Control public, immutable; }alias與root區(qū)別alias會(huì)完全替換匹配的路徑前綴/profile/avatar.png→ 映射到/home/ruoyi/.../avatar.pngroot是拼接/profile/avatar.png→/home/.../profile/avatar.png。此處必須用alias否則多一層profile。3. Windows Tomcat 部署后空白頁(yè)Tomcat 對(duì) SPA 的 404 處理缺失當(dāng)選擇 Windows Tomcat 方案時(shí)若依文檔指導(dǎo)你把dist目錄整個(gè)丟進(jìn)webapps再改server.xml加Context。但很多人忽略了一個(gè)殘酷事實(shí)Tomcat 本質(zhì)是 Servlet 容器不是靜態(tài)文件服務(wù)器它對(duì)index.html以外的路徑如/system/user默認(rèn)不識(shí)別直接返回 404且不會(huì)像 Nginx 那樣提供try_files機(jī)制。你看到的空白頁(yè)正是 Tomcat 在webapps/dist/下找不到/system/user這個(gè)“Servlet”或“JSP”時(shí)拋出的標(biāo)準(zhǔn) 404 頁(yè)面或空白。而若依前端需要的是所有 404 都重定向到index.html交由 Vue Router 處理。3.1 根因深挖為什么web.xml是 Tomcat 下 SPA 的唯一救命稻草Tomcat 的web.xml是 Web 應(yīng)用的部署描述符其中error-page元素允許你定義特定 HTTP 狀態(tài)碼的自定義響應(yīng)。若依文檔讓你新建WEB-INF/web.xml并配置error-page其核心邏輯是當(dāng) Tomcat 內(nèi)部找不到請(qǐng)求路徑時(shí)不返回默認(rèn) 404 頁(yè)面而是轉(zhuǎn)發(fā)forward到/index.html。這相當(dāng)于在 Tomcat 層面實(shí)現(xiàn)了 Nginx 的try_files功能。沒(méi)有它任何非根路徑的直接訪(fǎng)問(wèn)或刷新都會(huì)觸發(fā) Tomcat 的 404 流程前端 JS 根本沒(méi)機(jī)會(huì)運(yùn)行。3.2 正確解法web.xml必須精準(zhǔn)配置 error-page forward在dist目錄下創(chuàng)建WEB-INF文件夾注意大小寫(xiě)并在其中新建web.xml內(nèi)容必須嚴(yán)格如下XML 聲明和命名空間不可省略?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 metadata-completetrue display-nameVue SPA Fallback/display-name error-page error-code404/error-code location/index.html/location /error-page /web-app邏輯說(shuō)明error-page告訴 Tomcat當(dāng)發(fā)生 HTTP 404 錯(cuò)誤時(shí)不要返回默認(rèn)錯(cuò)誤頁(yè)而是內(nèi)部 forward 到/index.html注意是/開(kāi)頭的路徑表示相對(duì)于當(dāng)前 Web 應(yīng)用根目錄。Tomcat 會(huì)讀取dist/index.html并返回Vue Router 得以啟動(dòng)。參數(shù)說(shuō)明version4.0對(duì)應(yīng) Servlet 4.0 規(guī)范Tomcat 9若用 Tomcat 8請(qǐng)改為3.1并調(diào)整xsi:schemaLocation中的 URLmetadata-completetrue告訴容器無(wú)需掃描注解提升啟動(dòng)速度。3.3 驗(yàn)證是否生效兩步定位 Tomcat 是否接管 404檢查web.xml位置與權(quán)限確保文件位于dist/WEB-INF/web.xml不是dist/WEB-INF/外層且 Tomcat 啟動(dòng)用戶(hù)對(duì)該文件有讀取權(quán)限Windows 下通常無(wú)問(wèn)題觀(guān)察 Tomcat 日志啟動(dòng) Tomcat 后訪(fǎng)問(wèn)一個(gè)不存在的路徑如/fake/path查看logs/catalina.out或logs/localhost.date.log若看到類(lèi)似Forwarding to error page from request [...]的日志說(shuō)明web.xml生效若只有Processing ErrorPage[errorCode404, location/index.html]但無(wú)后續(xù)可能是location路徑錯(cuò)誤。3.4 避坑常見(jiàn)問(wèn)題與血淚經(jīng)驗(yàn)現(xiàn)象web.xml已添加但訪(fǎng)問(wèn)/system/user仍返回 Tomcat 默認(rèn) 404 頁(yè)面帶 Apache Tomcat 字樣原因location值寫(xiě)成了index.html缺少/前綴導(dǎo)致 Tomcat 嘗試在WEB-INF目錄下找index.html自然失敗。解決location必須以/開(kāi)頭如/index.html表示應(yīng)用根目錄下的文件。現(xiàn)象web.xml生效但index.html加載后控制臺(tái)報(bào)Uncaught SyntaxError: Unexpected token 原因index.html中引用的 JS/CSS 路徑錯(cuò)誤。Tomcat 將dist目錄作為 Web 應(yīng)用根所以publicPath必須設(shè)為./相對(duì)路徑而非/絕對(duì)根路徑。若設(shè)為/index.html會(huì)請(qǐng)求http://ip:8080/js/app.xxx.js但 Tomcat 實(shí)際將dist/js/映射到http://ip:8080/js/路徑不匹配。解決再次確認(rèn)vue.config.js中publicPath: ./重新npm run build:prod覆蓋dist目錄?,F(xiàn)象web.xml配置正確但登錄后點(diǎn)擊菜單仍跳轉(zhuǎn)失敗Network 面板顯示GET http://ip:8080/system/user 404原因Tomcat 的Context配置未生效或dist目錄未正確部署到webapps。若dist目錄名為myapp則 Tomcat 會(huì)將其部署為http://ip:8080/myapp/此時(shí)所有請(qǐng)求路徑應(yīng)為/myapp/system/user但前端router默認(rèn) base 是/導(dǎo)致請(qǐng)求發(fā)往根路徑。解決在vue.config.js中顯式設(shè)置baseUrlVue CLI 2或publicPathVue CLI 3為應(yīng)用上下文路徑// vue.config.js (Vue CLI 3) module.exports { publicPath: /myapp/, // 與 Tomcat 部署的 Context Path 一致 }注意publicPath改為/myapp/后index.html中所有資源路徑會(huì)自動(dòng)加上/myapp/前綴如script src/myapp/js/app.xxx.js確保 Tomcat 能正確路由。4. 跨域問(wèn)題百度地圖等第三方 API 調(diào)用失敗的三種真實(shí)場(chǎng)景與解法若依項(xiàng)目常需集成百度地圖、高德地圖、微信 JS-SDK 等第三方服務(wù)這些 API 通常要求調(diào)用方域名在白名單內(nèi)。而開(kāi)發(fā)時(shí)npm run serve啟動(dòng)的http://localhost:80與生產(chǎn)環(huán)境http://your-server-ip域名不同瀏覽器會(huì)攔截跨域請(qǐng)求控制臺(tái)報(bào)CORS header Access-Control-Allow-Origin missing或No Access-Control-Allow-Origin header is present。這不是若依的鍋是瀏覽器同源策略的剛性約束。但解決方案不能只靠“讓后端加 CORS”因?yàn)榈谌?API 的響應(yīng)頭由對(duì)方服務(wù)器控制你無(wú)法修改。4.1 根因深挖為什么“后端加 CrossOrigin”對(duì)第三方 API 無(wú)效CrossOrigin注解Spring Boot或CorsConfiguration是后端服務(wù)自己響應(yīng)時(shí)添加Access-Control-Allow-Origin頭用于解決前端 → 若依后端的跨域。但當(dāng)你在前端 JS 中直接fetch(https://api.map.baidu.com/...)請(qǐng)求是前端 → 百度服務(wù)器百度服務(wù)器的響應(yīng)頭里沒(méi)有Access-Control-Allow-Origin: *或你的域名瀏覽器就會(huì)拒絕。此時(shí)若依后端加再多CrossOrigin也無(wú)濟(jì)于事因?yàn)檎?qǐng)求根本沒(méi)經(jīng)過(guò)若依后端。4.2 正確解法必須走“后端代理”繞過(guò)瀏覽器同源限制唯一可靠方案是前端請(qǐng)求若依后端的一個(gè)代理接口如/proxy/baidu若依后端收到請(qǐng)求后用自己的服務(wù)器身份去調(diào)用百度 API再把結(jié)果原樣返回給前端。這樣前端只與同源的若依后端通信無(wú)跨域若依后端與百度通信服務(wù)器間無(wú)同源限制。這需要兩步改造第一步前端配置代理開(kāi)發(fā)期與統(tǒng)一接口生產(chǎn)期開(kāi)發(fā)期npm run serve利用 Vue CLI 的devServer.proxy將/proxy/baidu請(qǐng)求代理到若依后端// vue.config.js devServer: { proxy: { /proxy/baidu: { target: http://localhost:8080, // 若依后端地址 changeOrigin: true, pathRewrite: { ^/proxy/baidu: /baidu // 將 /proxy/baidu/xxx 重寫(xiě)為 /baidu/xxx } } } }生產(chǎn)期部署后前端代碼中所有調(diào)用百度 API 的地方必須改為請(qǐng)求若依后端的代理接口例如// 原始錯(cuò)誤寫(xiě)法跨域 // fetch(https://api.map.baidu.com/geocoding/v3/?address北京outputjsonakxxx) // 正確寫(xiě)法走若依代理 fetch(/prod-api/baidu/geocoding/v3/?address北京outputjsonakxxx)第二步后端實(shí)現(xiàn)代理 Controller若依 Spring Boot在若依后端項(xiàng)目中新建一個(gè) Controller負(fù)責(zé)轉(zhuǎn)發(fā)請(qǐng)求// com.ruoyi.common.controller.ProxyController.java RestController RequestMapping(/baidu) public class ProxyController { private static final String BAIDU_API_BASE https://api.map.baidu.com; GetMapping(/{path:.}) public ResponseEntity? proxyBaidu( PathVariable String path, RequestParam MapString, String params, HttpServletRequest request) throws IOException { // 構(gòu)建完整百度 API URL String url BAIDU_API_BASE / path ? buildQueryString(params); // 使用 RestTemplate 發(fā)起 GET 請(qǐng)求生產(chǎn)環(huán)境建議用 WebClient 或 OkHttp RestTemplate restTemplate new RestTemplate(); HttpHeaders headers new HttpHeaders(); headers.set(User-Agent, Mozilla/5.0); HttpEntityString entity new HttpEntity(headers); try { ResponseEntityString response restTemplate.exchange( url, HttpMethod.GET, entity, String.class); return ResponseEntity.status(response.getStatusCode()) .headers(response.getHeaders()) .body(response.getBody()); } catch (Exception e) { return ResponseEntity.status(500).body(Proxy failed: e.getMessage()); } } private String buildQueryString(MapString, String params) { return params.entrySet().stream() .map(e - e.getKey() URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8)) .collect(Collectors.joining()); } }邏輯說(shuō)明該 Controller 攔截所有/baidu/**請(qǐng)求提取路徑和查詢(xún)參數(shù)拼成百度 API 完整 URL再用RestTemplate以服務(wù)器身份發(fā)起請(qǐng)求并將百度的響應(yīng)狀態(tài)碼、Header、Body 原樣透?jìng)鹘o前端。參數(shù)說(shuō)明PathVariable String path匹配任意子路徑如geocoding/v3RequestParam MapString, String自動(dòng)收集所有 query 參數(shù)buildQueryString確保參數(shù)值 URL 編碼避免特殊字符如中文導(dǎo)致百度 API 解析失敗。4.3 避坑常見(jiàn)問(wèn)題與血淚經(jīng)驗(yàn)現(xiàn)象代理接口返回 400 或 500百度 API 響應(yīng){status:3,message:Invalid request}原因百度 API 的ak密鑰參數(shù)未正確傳遞或ak值在若依后端被 URL 編碼兩次前端已編碼后端又編碼一次。解決在buildQueryString中只對(duì)e.getValue()編碼且確保前端傳參時(shí)ak是原始字符串不手動(dòng)編碼或改用UriComponentsBuilder更安全UriComponentsBuilder builder UriComponentsBuilder.fromHttpUrl(url); params.forEach(builder::queryParam); String fullUrl builder.toUriString(); // 自動(dòng)處理編碼現(xiàn)象代理請(qǐng)求成功但返回的 JSON 中content-type是text/plain前端fetch().json()報(bào)錯(cuò)原因百度 API 響應(yīng)頭Content-Type可能是text/plain;charsetutf-8而RestTemplate默認(rèn)不識(shí)別返回String類(lèi)型。解決在RestTemplate請(qǐng)求中顯式指定StringHttpMessageConverter支持text/plainRestTemplate restTemplate new RestTemplate(); ListHttpMessageConverter? converters new ArrayList(); converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); converters.add(new MappingJackson2HttpMessageConverter()); restTemplate.setMessageConverters(converters);現(xiàn)象本地開(kāi)發(fā)代理正常但部署到 Linux 后/prod-api/baidu/...返回 404原因Nginx 配置中/prod-api代理規(guī)則未覆蓋/prod-api/baidu路徑或location /prod-api塊內(nèi)缺少proxy_pass指向后端。解決檢查nginx.conf中l(wèi)ocation /prod-api塊確保proxy_pass指向正確的后端地址如http://127.0.0.1:8080且無(wú)^~修飾符導(dǎo)致路徑截?cái)唷?. 啟動(dòng)失敗/后臺(tái)進(jìn)程消失nohup java -jar的隱形陷阱與健壯守護(hù)方案若依文檔推薦用nohup java -jar ruoyi-admin.jar msg.log 21 啟動(dòng)后端看似簡(jiǎn)單實(shí)則暗藏三大隱患日志文件無(wú)限增長(zhǎng)撐爆磁盤(pán)、Java 進(jìn)程意外退出后無(wú)人拉起、nohup無(wú)法管理進(jìn)程生命周期無(wú)法優(yōu)雅停止。某次服務(wù)器重啟后你發(fā)現(xiàn)ps aux | grep ruoyi沒(méi)有進(jìn)程msg.log里最后一行是OutOfMemoryError但沒(méi)人知道它何時(shí)掛的。這不是運(yùn)維疏忽是nohup作為 shell 工具根本不具備服務(wù)化管理能力。5.1 根因深挖為什么nohup不是生產(chǎn)環(huán)境的正確答案nohup的設(shè)計(jì)初衷是讓命令在終端關(guān)閉后繼續(xù)運(yùn)行它只是簡(jiǎn)單地將stdout和stderr重定向到nohup.out或你指定的文件然后fork一個(gè)子進(jìn)程執(zhí)行命令。它不監(jiān)控進(jìn)程健康狀態(tài)、不處理內(nèi)存溢出、不記錄啟動(dòng)/停止時(shí)間、不提供服務(wù)啟停腳本。在生產(chǎn)環(huán)境中若依后端可能因 GC 壓力、數(shù)據(jù)庫(kù)連接池耗盡、Redis 連接超時(shí)等原因崩潰nohup不會(huì)感知更不會(huì)重啟。你只能靠定時(shí)巡檢日志效率極低。5.2 正確解法用systemd替代nohup實(shí)現(xiàn)真正的服務(wù)化Linux 系統(tǒng)推薦使用systemd現(xiàn)代發(fā)行版標(biāo)配來(lái)管理 Java 服務(wù)。它提供進(jìn)程守護(hù)、自動(dòng)重啟、日志聚合、依賴(lài)管理等企業(yè)級(jí)能力。步驟如下第一步創(chuàng)建 systemd 服務(wù)文件sudo vim /etc/systemd/system/ruoyi-admin.service填入以下內(nèi)容請(qǐng)根據(jù)你的實(shí)際路徑修改[Unit] DescriptionRuoyi Admin Service Afternetwork.target redis.service Wantsredis.service [Service] Typesimple Userruoyi WorkingDirectory/home/ruoyi/ruoyi-admin ExecStart/usr/bin/java -Xms512m -Xmx1024m -Dfile.encodingUTF-8 -jar /home/ruoyi/ruoyi-admin/ruoyi-admin.jar Restartalways RestartSec10 SuccessExitStatus143 StandardOutputjournal StandardErrorjournal SyslogIdentifierruoyi-admin [Install] WantedBymulti-user.target邏輯說(shuō)明[Unit]定義服務(wù)依賴(lài)啟動(dòng)前確保網(wǎng)絡(luò)和 Redis 就緒[Service]是核心Typesimple表示主進(jìn)程即 Java 進(jìn)程Userruoyi指定非 root 用戶(hù)運(yùn)行安全最佳實(shí)踐ExecStart是啟動(dòng)命令-Xms/-Xmx設(shè)置 JVM 內(nèi)存Restartalways確保進(jìn)程崩潰后自動(dòng)重啟StandardOutputjournal將日志接入systemd日志系統(tǒng)無(wú)需手動(dòng)管理msg.log。參數(shù)說(shuō)明RestartSec10表示重啟間隔 10 秒SuccessExitStatus143將SIGTERM優(yōu)雅停止的退出碼視為成功避免systemctl stop后被誤判為失敗重啟。第二步啟用并啟動(dòng)服務(wù)# 重載 systemd 配置 sudo systemctl daemon-reload # 啟用開(kāi)機(jī)自啟 sudo systemctl enable ruoyi-admin.service # 啟動(dòng)服務(wù) sudo systemctl start ruoyi-admin.service # 查看狀態(tài)關(guān)鍵 sudo systemctl status ruoyi-admin.service # 查看實(shí)時(shí)日志替代 cat msg.log sudo journalctl -u ruoyi-admin.service -f5.3 驗(yàn)證是否生效三招確認(rèn) systemd 守護(hù)到位檢查進(jìn)程歸屬ps aux | grep ruoyi-adminUSER列應(yīng)為ruoyi非rootCMD列應(yīng)為完整java -Xms...命令模擬崩潰測(cè)試sudo kill -9 $(pgrep -f ruoyi-admin.jar)等待 10 秒執(zhí)行sudo systemctl status ruoyi-admin.service應(yīng)顯示active (running)且Main PID變化日志檢索能力sudo journalctl -u ruoyi-admin.service --since 2 hours ago | grep Started能精準(zhǔn)查到啟動(dòng)記錄證明日志已接入systemd。5.4 避坑常見(jiàn)問(wèn)題與血淚經(jīng)驗(yàn)現(xiàn)象systemctl start后狀態(tài)為failedjournalctl顯示Permission denied原因ruoyi用戶(hù)對(duì)ruoyi-admin.jar或application.yml文件無(wú)讀取權(quán)限或WorkingDirectory目錄不可訪(fǎng)問(wèn)。解決sudo chown -R ruoyi:ruoyi /home/ruoyi/ruoyi-adminsudo chmod 755 /home/ruoyi/ruoyi-admin確保所有文件屬主為ruoyi。現(xiàn)象服務(wù)啟動(dòng)成功但http://ip:8080無(wú)法訪(fǎng)問(wèn)netstat -tuln | grep 8080無(wú)監(jiān)聽(tīng)原因若依后端application.yml中server.port被注釋或設(shè)為0隨機(jī)端口或spring.profiles.active指向的配置文件未正確加載。解決檢查ruoyi-admin.jar內(nèi)BOOT-INF/classes/application.yml確認(rèn)server.port: 8080未被注釋若使用外部配置確保--spring.config.locationfile:/home/ruoyi/ruoyi-admin/config/參數(shù)已加入ExecStart。現(xiàn)象systemctl stop后進(jìn)程仍在ps aux還能看到 Java 進(jìn)程原因Java 進(jìn)程未響應(yīng)SIGTERM可能因線(xiàn)程阻塞或未注冊(cè) Shutdown Hook。解決在若依后端Application.java中添加優(yōu)雅關(guān)閉邏輯SpringBootApplication public class RuoyiAdminApplication { public static void main(String[] args) { ConfigurableApplicationContext context SpringApplication.run(RuoyiAdminApplication.class, args); // 注冊(cè) JVM 關(guān)閉鉤子 Runtime.getRuntime().addShutdownHook(new Thread(() - { System.out.println(Shutting down Ruoyi Admin gracefully...); // 此處可關(guān)閉線(xiàn)程池、釋放資源 })); } }6. 如何 5 分鐘內(nèi)定位部署故障一份可立即執(zhí)行的排查清單與決策樹(shù)從第一次部署若依到現(xiàn)在我給自己立下鐵律任何部署問(wèn)題必須在 5 分鐘內(nèi)鎖定根因否則立刻執(zhí)行標(biāo)準(zhǔn)化排查流程。這套流程不是憑空而來(lái)是踩過(guò)“Nginx 配置漏掉try_files”、“Tomcatweb.xml位置放錯(cuò)”、“publicPath用/而非./”等上百個(gè)坑后提煉出的最小可行路徑。它不依賴(lài)經(jīng)驗(yàn)直覺(jué)只依賴(lài)可驗(yàn)證的事實(shí)。現(xiàn)在我把這份清單交給你照著做5 分鐘出答案。6.1 第一步確定故障類(lèi)型30 秒打開(kāi)瀏覽器開(kāi)發(fā)者工具F12切到 Network 面板刷新頁(yè)面觀(guān)察第一個(gè)請(qǐng)求通常是index.html的狀態(tài)碼如果是 200前端資源加載成功問(wèn)題在 JS 執(zhí)行或后端 API跳到第 3 步如果是 404Nginx/Tomcat 未正確提供index.html問(wèn)題在服務(wù)端靜態(tài)資源配置跳到第 2 步如果是 502/503Nginx 無(wú)法連接后端如proxy_pass地址錯(cuò)誤、后端未啟動(dòng)問(wèn)題在反向代理跳到第 4 步。提示不要看控制臺(tái) Console 的 JS 錯(cuò)誤那是結(jié)果不是原因。先看 Network 的 HTTP 狀態(tài)碼它是真相的源頭。6.2 第二步驗(yàn)證靜態(tài)資源服務(wù)90 秒針對(duì) 404 故障執(zhí)行以下命令Linux# 1. 檢查 Nginx 配置中 root 路徑是否指向 dist 目錄 sudo nginx -T 2/dev/null | grep -A 5 location / # 2. 確認(rèn) dist 目錄存在且有 index.html ls -l /usr/share/nginx/html/index.html # Nginx 路徑 # 或 ls -l /home/ruoyi/tomcat/webapps/dist/index.html # Tomcat 路徑 # 3. 檢查 Nginx 是否監(jiān)聽(tīng) 80 端口 sudo ss -tuln | grep :80 # 4. 如果是 Tomcat確認(rèn) web.xml 是否在正確位置 ls -l /home/ruoyi/tomcat/webapps/dist/WEB-INF/web.xml若root路徑錯(cuò)誤 → 修改nginx.conf若index.html不存在 → 重新npm run build:prod并復(fù)制dist若ss無(wú)輸出 →sudo systemctl start nginx若web.xml不存在 → 按第 3 章方法重建。6.3 第三步驗(yàn)證前端路由與資源路徑120 秒若index.html返回 200但頁(yè)面白屏或菜單不加載# 1. 查看 index.html 源碼確認(rèn) script 標(biāo)簽 src 路徑 curl -s http://localhost/index.html | grep src\ # 2. 檢查該 JS 文件是否能直接訪(fǎng)問(wèn)替換為實(shí)際路徑 curl -I http://localhost/js/app.xxx.js # 應(yīng)返回 200 # 3. 檢查 Vue Router base 是否匹配 curl -s http://localhost/index.html | grep BASE_URL\|publicPath若src是/js/...但返回 404 →vue.config.js中publicPath必須為./若src是./js/...但返回 404 → Nginxroot路徑未指向dist目錄或 Tomcatdist未本文還有配套的精品資源點(diǎn)擊獲取