SpringBoot默認配置的具體使用
引言
彼時 SpringBoot 初興,萬象更新,號稱“開箱即用”“約定優(yōu)于配置”,一時間風靡四方。
開發(fā)者趨之若鶩,紛紛稱快,仿佛自此架構之重可卸、配置之繁可省,一行 main() 即可氣定神閑、縱橫沙場。
然則時光久遠,方知此言非虛,卻也未盡其真。所謂默認,不過是你未曾開口,框架自作主張。表面無礙,實則步步殺機,線上事故十有八九,皆因“未曾配置”的“默認”。
回首往昔,實堪自嘲。曾自詡熟稔底層、精通原理,然于這些藏于陰影處的默認設定,竟茫然不覺。故障一起,倉皇失措,耗時良久,方才發(fā)現(xiàn),不過是框架做了一個并不適合的決定。
是以今日提筆,將過往種種記錄于此,只盼后來者少走彎路。
正文
Tomcat連接池
SpringBoot默認使用Tomcat作為Web容器,但默認的連接池配置在高并發(fā)場景下會成為瓶頸。
默認配置下,Tomcat的最大連接數(shù)只有200,最大線程數(shù)也只有200。這意味著當并發(fā)請求超過200時,后續(xù)請求就會排隊等待。在生產(chǎn)環(huán)境中,這個配置明顯不夠用。
server:
tomcat:
max-connections: 10000 # 最大連接數(shù)
threads:
max: 800 # 最大工作線程數(shù)
min-spare: 100 # 最小空閑線程數(shù)
accept-count: 100 # 等待隊列長度
connection-timeout: 20000
更坑的是,SpringBoot的默認超時時間是無限長。這會導致連接一直占用,直到客戶端主動斷開。
在網(wǎng)絡不穩(wěn)定的環(huán)境下,大量連接會一直掛著不釋放,最終耗盡服務器資源。
數(shù)據(jù)庫連接池
SpringBoot默認使用HikariCP作為數(shù)據(jù)庫連接池,但默認的連接池配置在生產(chǎn)環(huán)境下會成為瓶頸。默認最大連接數(shù)只有10個,對于稍微復雜一點的應用來說根本不夠用。
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000
特別要注意leak-detection-threshold這個配置。默認情況下這個檢測是關閉的,如果代碼中存在連接泄漏問題,根本發(fā)現(xiàn)不了。
開啟后,HikariCP會監(jiān)控連接的使用時間,超過閾值就會打印警告日志。
JPA懶加載
SpringBoot集成JPA時,默認開啟了懶加載。這個設計初衷是好的,但在實際使用中經(jīng)常會導致N+1查詢問題。
@Entity
public class User {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY) // 默認就是LAZY
private List<Order> orders;
}
當查詢用戶列表時,每訪問一次orders屬性,就會觸發(fā)一次數(shù)據(jù)庫查詢。
如果有100個用戶,就會執(zhí)行101次SQL。
這種情況下,要么使用@EntityGraph指定加載策略,要么在Repository中使用JOIN FETCH。
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();
Jackson時區(qū)序列化
SpringBoot默認使用Jackson處理JSON序列化,但時區(qū)處理經(jīng)常出問題。
默認情況下,Jackson會使用系統(tǒng)時區(qū),這在分布式部署時會導致不一致的問題。
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
serialization:
write-dates-as-timestamps: false
更要命的是,如果你的應用部署在不同時區(qū)的服務器上,同樣的時間可能會被序列化成不同的值。
這個問題在國際化應用中特別突出。
日志配置
SpringBoot默認使用Logback,但默認配置下沒有對日志文件進行滾動和清理。
長時間運行的應用會產(chǎn)生巨大的日志文件,最終占滿磁盤空間。
logging:
file:
name: app.log
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
total-size-cap: 3GB
另外,默認的日志級別是INFO,在生產(chǎn)環(huán)境中會產(chǎn)生大量不必要的日志。
合理設置日志級別可以顯著提升性能。
緩存配置
SpringBoot的@Cacheable注解默認使用ConcurrentHashMap作為緩存實現(xiàn),但這個實現(xiàn)沒有過期機制,也沒有大小限制。在高并發(fā)場景下,緩存會無限增長,最終導致內(nèi)存溢出。
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=10000,expireAfterWrite=600s
可以考慮使用Caffeine替代默認實現(xiàn),可以提供更好的性能和內(nèi)存管理能力。
監(jiān)控端點
SpringBoot Actuator默認暴露了很多監(jiān)控端點,包括健康檢查、配置信息、環(huán)境變量等。
這些信息在開發(fā)環(huán)境中很有用,但在生產(chǎn)環(huán)境中可能泄漏敏感信息。
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: when-authorized
只暴露必要的端點,并且配置適當?shù)陌踩呗?,避免信息泄漏?/p>
文件上傳大小限制
SpringBoot默認的文件上傳限制非常小,單個文件只能上傳1MB,整個請求大小限制10MB。
在實際業(yè)務中,這個限制經(jīng)常不夠用,用戶上傳稍大一點的文件就會報錯。
這個屬于是比較常見的問題,因為開發(fā)環(huán)境測試時通常用小文件,一切正常。等到用戶上傳幾MB的PDF文檔或者高清圖片時,系統(tǒng)就開始報 MaxUploadSizeExceededException 異常。
這個異常往往還發(fā)生在文件傳輸完成之后,用戶等了半天上傳完畢,結果被告知文件過大,體驗極差。
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
file-size-threshold: 2KB
location: /tmp
resolve-lazily: false
file-size-threshold 這個參數(shù)也很重要,它決定了多大的文件會直接寫入內(nèi)存。如果設置過大,大量并發(fā)上傳會占用過多內(nèi)存;設置過小,小文件也要寫磁盤,影響性能。一般設置為幾KB比較合適。
異步線程池配置
使用@Async注解時,SpringBoot默認使用SimpleAsyncTaskExecutor,這個執(zhí)行器每次都會創(chuàng)建新線程,沒有線程池復用機制。高并發(fā)情況下會創(chuàng)建大量線程,最終導致系統(tǒng)資源耗盡。
這個問題在開發(fā)階段很難發(fā)現(xiàn),因為異步任務通常不多。但在生產(chǎn)環(huán)境,如果有大量異步任務執(zhí)行,比如發(fā)送短信、推送、記錄日志等,系統(tǒng)會不斷創(chuàng)建新線程。每個線程默認占用1MB的??臻g,創(chuàng)建幾千個線程就是幾GB內(nèi)存。
更嚴重的是線程切換的開銷,CPU大部分時間都在做上下文切換,真正的業(yè)務邏輯反而執(zhí)行很慢。
spring:
task:
execution:
pool:
core-size: 8
max-size: 16
queue-capacity: 100
keep-alive: 60s
thread-name-prefix: async-task-
scheduling:
pool:
size: 4
thread-name-prefix: scheduling-
線程池大小的設置也有講究。
如果是CPU密集型任務,線程數(shù)設置為CPU核心數(shù)就夠了;如果是IO密集型任務,可以設置為CPU核心數(shù)的2-3倍。
queue-capacity設置了任務隊列長度,當線程池滿了之后,新任務會放到隊列里等待執(zhí)行。
靜態(tài)資源緩存策略
SpringBoot默認不為靜態(tài)資源設置HTTP緩存頭,這意味著瀏覽器每次都會重新請求CSS、JS、圖片等靜態(tài)文件,嚴重影響頁面加載性能。
用戶每次訪問頁面,瀏覽器都要重新下載所有靜態(tài)資源,即使這些文件根本沒有變化。對于資源較多的單頁應用來說,這個問題特別明顯。用戶看到的就是頁面加載慢,特別是網(wǎng)絡條件不好的時候,體驗很差。
spring:
web:
resources:
cache:
cachecontrol:
max-age: 365d
cache-public: true
chain:
strategy:
content:
enabled: true
paths: /**
cache: true
static-locations: classpath:/static/
開啟內(nèi)容版本化策略后,SpringBoot會根據(jù)文件內(nèi)容生成MD5哈希值作為版本號,文件名變成style-abc123.css這樣的格式。當文件內(nèi)容發(fā)生變化時,哈希值也會變化,瀏覽器會認為這是新文件重新下載;如果文件沒變化,瀏覽器就直接使用緩存,有效提升頁面加載速度。
數(shù)據(jù)庫事務超時
@Transactional注解默認沒有設置超時時間,長時間運行的事務會一直持有數(shù)據(jù)庫鎖,影響其他操作的執(zhí)行。特別是在批量數(shù)據(jù)處理時,很容易出現(xiàn)鎖表問題。
這個問題在并發(fā)量不高的時候不明顯,但隨著業(yè)務增長,長事務的危害就暴露出來了。
比如一個數(shù)據(jù)導入任務需要處理幾萬條記錄,如果放在一個事務里,可能要運行幾分鐘甚至更長時間。在這期間,相關的表都被鎖住,其他用戶的操作只能等待,系統(tǒng)響應變得很慢。
@Transactional(timeout = 30, rollbackFor = Exception.class)
public void batchProcess(List<Data> dataList) {
// 分批處理,避免長事務
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
List<Data> batch = dataList.subList(i,
Math.min(i + batchSize, dataList.size()));
processBatch(batch);
}
}
對于大批量數(shù)據(jù)處理,建議分成多個小事務,每個事務處理少量數(shù)據(jù)。這樣即使某個小事務失敗,也不會影響整體進度,而且可以及時釋放數(shù)據(jù)庫鎖,提高系統(tǒng)并發(fā)性能。
同時再加上rollbackFor = Exception.class確保所有異常都會觸發(fā)回滾,避免數(shù)據(jù)不一致。
寫在最后
Spring Boot 的“約定優(yōu)于配置”確實省心,但省的是開發(fā)者的心,不是系統(tǒng)的責任。每一項默認配置背后,其實都藏著設計者的假設和權衡,而這些假設,在我們的業(yè)務場景中也許未必成立。
這些坑我?guī)缀醵疾冗^,有些甚至反復踩了好幾次。
愿你讀到這里,能少走幾步彎路,可不能拿生產(chǎn)事故去交學費。
提前優(yōu)化配置,是對系統(tǒng)負責,也是對自己負責。
到此這篇關于SpringBoot默認配置的具體使用(遲早踩坑)的文章就介紹到這了,更多相關SpringBoot默認配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用Java實現(xiàn)動態(tài)生成MySQL數(shù)據(jù)庫
這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)動態(tài)生成MySQL數(shù)據(jù)庫,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-02-02
IntelliJ?IDEA軟件內(nèi)如何實現(xiàn)更新到最新版本
文章介紹了如何在IntelliJIDEA中更新到最新版本以及如何回到之前忽略的版本,解決辦法是通過選擇"IgnoreThisUpdate"來跳過舊版本,重復操作即可更新到最新版本2024-12-12
Java中的包(Package)與導入(Import)示例詳解
這篇文章主要詳細介紹了Java中的包(Package)和導入(Import)概念,包括包的定義、作用、JDK中主要的包、導入的目的與用法、特殊情況的導入、靜態(tài)導入、包的訪問權限和命名規(guī)范,文章通過豐富的解釋和代碼示例,幫助讀者深入理解這些概念的實際應用,需要的朋友可以參考下2024-11-11
springboot如何使用assembly打包項目和啟動腳本
這篇文章主要介紹了springboot如何使用assembly打包項目和啟動腳本問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
Java 實戰(zhàn)項目之疫情防控管理系統(tǒng)詳解
讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java實現(xiàn)一個疫情防控管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11
SpringMVC 參數(shù)綁定之視圖傳參到控制器的實現(xiàn)代碼
這篇文章主要介紹了SpringMVC 參數(shù)綁定之視圖傳參到控制器的相關知識,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
SpringBoot集成E-mail發(fā)送各種類型郵件
這篇文章主要為大家詳細介紹了SpringBoot集成E-mail發(fā)送各種類型郵件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04

