API 是現代應用程式的支柱。當我第一次開始使用 Spring Boot 建立 API 時,我過於專注於提供功能,而忽略了一個關鍵方面:彈性。我經歷了慘痛的教訓才明白,API 能夠優雅地處理故障並適應不同條件的能力才是它真正可靠的原因。讓我向您介紹我一路上犯的一些錯誤以及我是如何糾正這些錯誤的。希望您能夠在自己的旅程中避免這些陷阱。
錯誤一:忽略逾時配置
發生了什麼事:在我的一個早期專案中,我建立了一個 API,可以對第三方服務進行外部呼叫。我認為這些服務總是會快速回應,並且不會費心設定超時。一切看起來都很好,直到流量增加,第三方服務開始變慢。我的 API 將無限期掛起,等待回應。
影響: API 的反應能力急遽下降。相關服務開始出現故障,用戶面臨長時間的延遲,有些甚至遇到了可怕的 500 內部伺服器錯誤。
我是如何修復它的:那時我意識到超時配置的重要性。以下是我使用 Spring Boot 修復這個問題的方法:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(5)) .setReadTimeout(Duration.ofSeconds(5)) .additionalInterceptors(new RestTemplateLoggingInterceptor()) .build(); } // Custom interceptor to log request/response details @RequiredArgsConstructor public class RestTemplateLoggingInterceptor implements ClientHttpRequestInterceptor { private static final Logger log = LoggerFactory.getLogger(RestTemplateLoggingInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { long startTime = System.currentTimeMillis(); log.info("Making request to: {}", request.getURI()); ClientHttpResponse response = execution.execute(request, body); long duration = System.currentTimeMillis() - startTime; log.info("Request completed in {}ms with status: {}", duration, response.getStatusCode()); return response; } } }
此配置不僅設定適當的逾時,還包括日誌記錄以幫助監控外部服務效能。
錯誤2:沒有實施斷路器
發生了什麼事:曾經有一段時間,我們所依賴的內部服務宕機了幾個小時。我的 API 沒有很好地處理這種情況。相反,它不斷重試那些失敗的請求,為已經緊張的系統增加了更多的負載。
級聯故障是分散式系統中最具挑戰性的問題之一。當一項服務出現故障時,可能會產生骨牌效應,導致整個系統癱瘓。
影響:重複的重試使系統不堪重負,減慢了應用程式的其他部分並影響了所有用戶。
我是如何修復它的:就在那時我發現了斷路器模式。使用 Spring Cloud Resilience4j,我能夠打破這個循環。
@Configuration public class Resilience4jConfig { @Bean public CircuitBreakerConfig circuitBreakerConfig() { return CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(60)) .permittedNumberOfCallsInHalfOpenState(2) .slidingWindowSize(2) .build(); } @Bean public RetryConfig retryConfig() { return RetryConfig.custom() .maxAttempts(3) .waitDuration(Duration.ofSeconds(2)) .build(); } } @Service @Slf4j public class ResilientService { private final CircuitBreaker circuitBreaker; private final RestTemplate restTemplate; public ResilientService(CircuitBreakerRegistry registry, RestTemplate restTemplate) { this.circuitBreaker = registry.circuitBreaker("internalService"); this.restTemplate = restTemplate; } @CircuitBreaker(name = "internalService", fallbackMethod = "fallbackResponse") @Retry(name = "internalService") public String callInternalService() { return restTemplate.getForObject("https://internal-service.com/data", String.class); } public String fallbackResponse(Exception ex) { log.warn("Circuit breaker activated, returning fallback response", ex); return new FallbackResponse("Service temporarily unavailable", getBackupData()).toJson(); } private Object getBackupData() { // Implement cache or default data strategy return new CachedDataService().getLatestValidData(); } }
這個簡單的添加可以防止我的 API 壓垮自身、內部服務或第三方服務,確保系統穩定性。
錯誤 3:錯誤處理能力弱
發生了什麼事: 早期,我沒有對錯誤處理投入太多考慮。我的 API 要么拋出一般錯誤(例如所有內容的 HTTP 500),要么在堆疊追蹤中暴露敏感的內部詳細資訊。
影響:使用者對出了什麼問題感到困惑,內部細節的暴露造成了潛在的安全風險。
我是如何修復它的:我決定使用 Spring 的 @ControllerAdvice 註解來集中處理錯誤。這就是我所做的:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(5)) .setReadTimeout(Duration.ofSeconds(5)) .additionalInterceptors(new RestTemplateLoggingInterceptor()) .build(); } // Custom interceptor to log request/response details @RequiredArgsConstructor public class RestTemplateLoggingInterceptor implements ClientHttpRequestInterceptor { private static final Logger log = LoggerFactory.getLogger(RestTemplateLoggingInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { long startTime = System.currentTimeMillis(); log.info("Making request to: {}", request.getURI()); ClientHttpResponse response = execution.execute(request, body); long duration = System.currentTimeMillis() - startTime; log.info("Request completed in {}ms with status: {}", duration, response.getStatusCode()); return response; } } }
這使得錯誤訊息清晰且安全,為使用者和開發人員提供協助。
錯誤四:忽視速率限制
發生了什麼事:在一個晴朗的日子,我們發起了一項促銷活動,我們的 API 流量猛增。雖然這對企業來說是個好消息,但一些用戶開始向 API 發送垃圾郵件請求,導致其他人資源匱乏。
影響:每個人的表現都下降了,我們收到了大量投訴。
我如何修復它:為了解決這個問題,我使用 Bucket4j 和 Redis 實現了速率限制。這是一個例子:
@Configuration public class Resilience4jConfig { @Bean public CircuitBreakerConfig circuitBreakerConfig() { return CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(60)) .permittedNumberOfCallsInHalfOpenState(2) .slidingWindowSize(2) .build(); } @Bean public RetryConfig retryConfig() { return RetryConfig.custom() .maxAttempts(3) .waitDuration(Duration.ofSeconds(2)) .build(); } } @Service @Slf4j public class ResilientService { private final CircuitBreaker circuitBreaker; private final RestTemplate restTemplate; public ResilientService(CircuitBreakerRegistry registry, RestTemplate restTemplate) { this.circuitBreaker = registry.circuitBreaker("internalService"); this.restTemplate = restTemplate; } @CircuitBreaker(name = "internalService", fallbackMethod = "fallbackResponse") @Retry(name = "internalService") public String callInternalService() { return restTemplate.getForObject("https://internal-service.com/data", String.class); } public String fallbackResponse(Exception ex) { log.warn("Circuit breaker activated, returning fallback response", ex); return new FallbackResponse("Service temporarily unavailable", getBackupData()).toJson(); } private Object getBackupData() { // Implement cache or default data strategy return new CachedDataService().getLatestValidData(); } }
這確保了公平使用並保護 API 免受濫用。
錯誤5:忽視可觀察性
發生了什麼事:每當生產中出現問題時,就像大海撈針一樣。我沒有適當的日誌記錄或指標,因此診斷問題花費的時間比應有的時間要長。
影響:故障排除變成了一場噩夢,延遲了問題解決並使用戶感到沮喪。
我是如何解決這個問題的:我添加了 Spring Boot Actuator 來進行健康檢查,並將 Prometheus 與 Grafana 整合起來以實現指標視覺化:
@RestControllerAdvice @Slf4j public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(HttpClientErrorException.class) public ResponseEntity<errorresponse> handleHttpClientError(HttpClientErrorException ex, WebRequest request) { log.error("Client error occurred", ex); ErrorResponse error = ErrorResponse.builder() .timestamp(LocalDateTime.now()) .status(ex.getStatusCode().value()) .message(sanitizeErrorMessage(ex.getMessage())) .path(((ServletWebRequest) request).getRequest().getRequestURI()) .build(); return ResponseEntity.status(ex.getStatusCode()).body(error); } @ExceptionHandler(Exception.class) public ResponseEntity<errorresponse> handleGeneralException(Exception ex, WebRequest request) { log.error("Unexpected error occurred", ex); ErrorResponse error = ErrorResponse.builder() .timestamp(LocalDateTime.now()) .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) .message("An unexpected error occurred. Please try again later.") .path(((ServletWebRequest) request).getRequest().getRequestURI()) .build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } private String sanitizeErrorMessage(String message) { // Remove sensitive information from error messages return message.replaceAll("(password|secret|key)=\[.*?\]", "=[REDACTED]"); } } </errorresponse></errorresponse>
我還使用 ELK Stack(Elasticsearch、Logstash、Kibana)實作了結構化日誌記錄。這使得日誌更具可操作性。
重點
建立彈性 API 是一個旅程,錯誤是這個過程的一部分。以下是我學到的主要經驗教訓:
- 始終為外部呼叫配置逾時。
- 使用斷路器來防止級聯故障。
- 集中錯誤處理,使其清晰且安全。
- 實作速率限制以管理流量峰值。
這些變化改變了我進行 API 開發的方式。如果您遇到過類似的挑戰或有其他建議,我很想聽聽您的故事!
尾註:請記住,彈性不是您添加的功能,而是您從頭開始構建到系統中的特性。這些元件中的每一個在創建 API 方面都發揮著至關重要的作用,這些 API 不僅可以工作,而且可以在壓力下繼續可靠地工作。
以上是建立彈性 API:我犯的錯誤以及我如何克服這些錯誤的詳細內容。更多資訊請關注PHP中文網其他相關文章!

類加載器通過統一的類文件格式、動態加載、雙親委派模型和平台無關的字節碼,確保Java程序在不同平台上的一致性和兼容性,實現平台獨立性。

Java編譯器生成的代碼是平台無關的,但最終執行的代碼是平台特定的。 1.Java源代碼編譯成平台無關的字節碼。 2.JVM將字節碼轉換為特定平台的機器碼,確保跨平台運行但性能可能不同。

多線程在現代編程中重要,因為它能提高程序的響應性和資源利用率,並處理複雜的並發任務。 JVM通過線程映射、調度機制和同步鎖機制,在不同操作系統上確保多線程的一致性和高效性。

Java的平台獨立性是指編寫的代碼可以在任何安裝了JVM的平台上運行,無需修改。 1)Java源代碼編譯成字節碼,2)字節碼由JVM解釋執行,3)JVM提供內存管理和垃圾回收功能,確保程序在不同操作系統上運行。

Javaapplicationscanindeedencounterplatform-specificissuesdespitetheJVM'sabstraction.Reasonsinclude:1)Nativecodeandlibraries,2)Operatingsystemdifferences,3)JVMimplementationvariations,and4)Hardwaredependencies.Tomitigatethese,developersshould:1)Conduc

云计算显著提升了Java的平台独立性。1)Java代码编译为字节码,由JVM在不同操作系统上执行,确保跨平台运行。2)使用Docker和Kubernetes部署Java应用,提高可移植性和可扩展性。

Java'splatformindependenceallowsdeveloperstowritecodeonceandrunitonanydeviceorOSwithaJVM.Thisisachievedthroughcompilingtobytecode,whichtheJVMinterpretsorcompilesatruntime.ThisfeaturehassignificantlyboostedJava'sadoptionduetocross-platformdeployment,s

容器化技術如Docker增強而非替代Java的平台獨立性。 1)確保跨環境的一致性,2)管理依賴性,包括特定JVM版本,3)簡化部署過程,使Java應用更具適應性和易管理性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Atom編輯器mac版下載
最受歡迎的的開源編輯器

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。