Heim Java javaLernprogramm Aufbau widerstandsfähiger APIs: Fehler, die ich gemacht habe und wie ich sie überwunden habe

Aufbau widerstandsfähiger APIs: Fehler, die ich gemacht habe und wie ich sie überwunden habe

Jan 04, 2025 pm 03:48 PM

Building Resilient APIs: Mistakes I Made and How I Overcame Them

APIs sind das Rückgrat moderner Anwendungen. Als ich anfing, APIs mit Spring Boot zu erstellen, war ich so auf die Bereitstellung von Funktionen konzentriert, dass ich einen entscheidenden Aspekt übersehen habe: die Ausfallsicherheit. Ich habe auf die harte Tour gelernt, dass die Fähigkeit einer API, Fehler reibungslos zu verarbeiten und sich an unterschiedliche Bedingungen anzupassen, sie wirklich zuverlässig macht. Lassen Sie mich Ihnen einige Fehler erklären, die ich dabei gemacht habe, und wie ich sie behoben habe. Hoffentlich können Sie diese Fallstricke auf Ihrer eigenen Reise vermeiden.

Fehler 1: Timeout-Konfigurationen ignorieren

Was geschah: In einem meiner frühen Projekte habe ich eine API erstellt, die externe Aufrufe an Dienste von Drittanbietern durchführte. Ich ging davon aus, dass diese Dienste immer schnell reagieren würden und machte mir nicht die Mühe, Zeitüberschreitungen festzulegen. Alles schien in Ordnung zu sein, bis der Verkehr zunahm und die Dienste von Drittanbietern langsamer wurden. Meine API blieb einfach auf unbestimmte Zeit hängen und wartete auf eine Antwort.

Auswirkungen: Die Reaktionsfähigkeit der API nahm einen Sturzflug hin. Abhängige Dienste begannen auszufallen, und die Benutzer mussten mit langen Verzögerungen rechnen – einige bekamen sogar den gefürchteten 500 Internal Server Error.

Wie ich das Problem behoben habe: Da wurde mir klar, wie wichtig Timeout-Konfigurationen sind. So habe ich es mit Spring Boot behoben:

@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;
        }
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Diese Konfiguration legt nicht nur geeignete Zeitüberschreitungen fest, sondern umfasst auch die Protokollierung, um die Leistung externer Dienste zu überwachen.

Fehler 2: Keine Leistungsschalter implementieren

Was geschah: Es gab eine Zeit, in der ein interner Dienst, auf den wir angewiesen waren, für mehrere Stunden ausfiel. Meine API hat die Situation nicht ordnungsgemäß gemeistert. Stattdessen wurden diese fehlgeschlagenen Anfragen immer wieder wiederholt, was die Belastung des ohnehin schon überlasteten Systems zusätzlich erhöhte.

Kaskadierende Ausfälle sind eines der größten Probleme in verteilten Systemen. Wenn ein Dienst ausfällt, kann es zu einem Dominoeffekt kommen, der das gesamte System zum Absturz bringt.

Auswirkungen: Die wiederholten Wiederholungsversuche überlasteten das System, verlangsamten andere Teile der Anwendung und beeinträchtigten alle Benutzer.

Wie ich das Problem behoben habe: Da habe ich das Schutzschaltermuster entdeckt. Mit Spring Cloud Resilience4j konnte ich den Teufelskreis durchbrechen.

@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();
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Diese einfache Ergänzung verhinderte, dass meine API sich selbst, den internen Dienst oder den Drittanbieterdienst überlastete, und sorgte so für Systemstabilität.

Fehler 3: Schwache Fehlerbehandlung

Was geschah: Anfangs habe ich mir nicht viele Gedanken über die Fehlerbehandlung gemacht. Meine API hat entweder generische Fehler ausgegeben (wie HTTP 500 für alles) oder vertrauliche interne Details in Stack-Traces offengelegt.

Auswirkungen: Benutzer waren verwirrt darüber, was schief gelaufen ist, und die Offenlegung interner Details führte zu potenziellen Sicherheitsrisiken.

Wie ich das Problem behoben habe: Ich habe beschlossen, die Fehlerbehandlung mithilfe der @ControllerAdvice-Annotation von Spring zu zentralisieren. Folgendes habe ich getan:

@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;
        }
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Dadurch wurden Fehlermeldungen klar und sicher, was sowohl Benutzern als auch Entwicklern hilft.

Fehler 4: Ratenbegrenzung vernachlässigen

Was geschah: Eines schönen Tages starteten wir eine Werbekampagne und der Verkehr zu unserer API schoss in die Höhe. Obwohl dies eine großartige Nachricht für das Unternehmen war, begannen einige Benutzer, die API mit Anfragen zu überschwemmen, wodurch anderen Ressourcen entzogen wurden.

Auswirkungen: Die Leistung aller ging zurück und wir erhielten eine Flut von Beschwerden.

Wie ich das Problem behoben habe: Um dieses Problem zu lösen, habe ich eine Ratenbegrenzung mithilfe von Bucket4j mit Redis implementiert. Hier ist ein Beispiel:

@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();
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Dies gewährleistete eine faire Nutzung und schützte die API vor Missbrauch.

Fehler 5: Die Beobachtbarkeit außer Acht lassen

Was geschah: Immer wenn in der Produktion etwas schief ging, war es wie die Suche nach der Nadel im Heuhaufen. Ich verfügte weder über eine ordnungsgemäße Protokollierung noch über Metriken, sodass die Diagnose von Problemen viel länger dauerte, als es hätte sein sollen.

Auswirkungen: Die Fehlerbehebung wurde zu einem Albtraum, der die Problemlösung verzögerte und die Benutzer frustrierte.

Wie ich das Problem behoben habe: Ich habe Spring Boot Actuator für Zustandsprüfungen hinzugefügt und Prometheus mit Grafana für die Metrikvisualisierung integriert:

@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]");
    }
}
Nach dem Login kopieren

Ich habe auch eine strukturierte Protokollierung mithilfe des ELK-Stacks (Elasticsearch, Logstash, Kibana) implementiert. Dadurch wurden Protokolle weitaus umsetzbarer.

Imbissbuden

Der Aufbau widerstandsfähiger APIs ist eine Reise, und Fehler sind Teil des Prozesses. Hier sind die wichtigsten Lektionen, die ich gelernt habe:

  1. Konfigurieren Sie immer Timeouts für externe Anrufe.
  2. Verwenden Sie Leistungsschalter, um kaskadierende Ausfälle zu verhindern.
  3. Zentralisieren Sie die Fehlerbehandlung, um sie klar und sicher zu gestalten.
  4. Implementieren Sie eine Ratenbegrenzung, um Verkehrsspitzen zu bewältigen.

Diese Änderungen haben meine Herangehensweise an die API-Entwicklung verändert. Wenn Sie vor ähnlichen Herausforderungen standen oder andere Tipps haben, würde ich gerne Ihre Geschichten hören!

Endbemerkung: Denken Sie daran, dass Resilienz keine Funktion ist, die Sie hinzufügen – es ist eine Eigenschaft, die Sie von Grund auf in Ihr System einbauen. Jede dieser Komponenten spielt eine entscheidende Rolle bei der Erstellung von APIs, die nicht nur funktionieren, sondern auch unter Belastung zuverlässig funktionieren.

Das obige ist der detaillierte Inhalt vonAufbau widerstandsfähiger APIs: Fehler, die ich gemacht habe und wie ich sie überwunden habe. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Apr 19, 2025 pm 04:51 PM

Fehlerbehebung und Lösungen für die Sicherheitssoftware des Unternehmens, die dazu führt, dass einige Anwendungen nicht ordnungsgemäß funktionieren. Viele Unternehmen werden Sicherheitssoftware bereitstellen, um die interne Netzwerksicherheit zu gewährleisten. ...

Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Apr 19, 2025 pm 06:21 PM

Die Verarbeitung von Feldzuordnungen im Systemdocken stößt häufig auf ein schwieriges Problem bei der Durchführung von Systemdocken: So kartieren Sie die Schnittstellenfelder des Systems und ...

Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Apr 19, 2025 pm 11:42 PM

Bei Verwendung von MyBatis-Plus oder anderen ORM-Frameworks für Datenbankvorgänge müssen häufig Abfragebedingungen basierend auf dem Attributnamen der Entitätsklasse erstellt werden. Wenn Sie jedes Mal manuell ...

Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Apr 19, 2025 pm 11:30 PM

Lösungen zum Umwandeln von Namen in Zahlen zur Implementierung der Sortierung in vielen Anwendungsszenarien müssen Benutzer möglicherweise in Gruppen sortieren, insbesondere in einem ...

Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Apr 19, 2025 pm 11:45 PM

Beginnen Sie den Frühling mit der Intellijideaultimate -Version ...

Wie kann ich Java -Objekte sicher in Arrays umwandeln? Wie kann ich Java -Objekte sicher in Arrays umwandeln? Apr 19, 2025 pm 11:33 PM

Konvertierung von Java-Objekten und -Arrays: Eingehende Diskussion der Risiken und korrekten Methoden zur Konvertierung des Guss-Typs Viele Java-Anfänger werden auf die Umwandlung eines Objekts in ein Array stoßen ...

E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? Apr 19, 2025 pm 11:27 PM

Detaillierte Erläuterung des Designs von SKU- und SPU-Tabellen auf E-Commerce-Plattformen In diesem Artikel werden die Datenbankdesignprobleme von SKU und SPU in E-Commerce-Plattformen erörtert, insbesondere wie man mit benutzerdefinierten Verkäufen umgeht ...

Wie kann ich elegant den variablen Entitätsklassennamen erstellen, wenn Tkmybatis für Datenbankabfrage verwendet werden? Wie kann ich elegant den variablen Entitätsklassennamen erstellen, wenn Tkmybatis für Datenbankabfrage verwendet werden? Apr 19, 2025 pm 09:51 PM

Wenn Sie TKMybatis für Datenbankabfragen verwenden, ist das Aufbau von Abfragebedingungen ein häufiges Problem. Dieser Artikel wird ...

See all articles