optimierungen gateway-Modul

This commit is contained in:
stefan
2025-09-04 13:54:30 +02:00
parent 3b40cb9c45
commit 6675e2de94
17 changed files with 552 additions and 538 deletions
@@ -85,13 +85,13 @@ class GatewayHealthIndicator(
if (hasCriticalFailure && !isTestEnvironment) {
builder.down()
details["status"] = "DOWN"
details["reason"] = "One or more critical services are unavailable"
details["reason"] = "Ein oder mehrere kritische Services sind nicht verfügbar"
} else {
details["status"] = "UP"
details["reason"] = if (isTestEnvironment) {
"Health check passed (test environment)"
"Gesundheitsprüfung erfolgreich (Testumgebung)"
} else {
"All critical services are available"
"Alle kritischen Services sind verfügbar"
}
}
@@ -99,7 +99,7 @@ class GatewayHealthIndicator(
builder.down()
.withException(exception)
details["status"] = "DOWN"
details["reason"] = "Failed to check downstream services: ${exception.message}"
details["reason"] = "Fehler beim Prüfen der nachgelagerten Services: ${exception.message}"
}
return builder.withDetails(details).build()
@@ -77,7 +77,7 @@ class GatewayMetricsConfig {
@Bean
fun requestTimer(meterRegistry: MeterRegistry): Timer {
return Timer.builder(GATEWAY_REQUEST_TIMER)
.description("Gateway request processing time")
.description("Gateway Request-Verarbeitungszeit")
.tag("type", "http")
.register(meterRegistry)
}
@@ -88,7 +88,7 @@ class GatewayMetricsConfig {
@Bean
fun errorCounter(meterRegistry: MeterRegistry): Counter {
return Counter.builder(GATEWAY_ERROR_COUNTER)
.description("Total number of gateway errors")
.description("Gesamtanzahl der Gateway-Fehler")
.register(meterRegistry)
}
@@ -98,7 +98,7 @@ class GatewayMetricsConfig {
@Bean
fun requestCounter(meterRegistry: MeterRegistry): Counter {
return Counter.builder(GATEWAY_REQUESTS_COUNTER)
.description("Total number of gateway requests")
.description("Gesamtanzahl der Gateway-Requests")
.register(meterRegistry)
}
@@ -108,7 +108,7 @@ class GatewayMetricsConfig {
@Bean
fun circuitBreakerCounter(meterRegistry: MeterRegistry): Counter {
return Counter.builder(GATEWAY_CIRCUIT_BREAKER_COUNTER)
.description("Circuit breaker events in gateway")
.description("Circuit Breaker Events im Gateway")
.register(meterRegistry)
}
}
@@ -128,7 +128,7 @@ class GatewayMetricsWebFilter(private val meterRegistry: MeterRegistry) : WebFil
Counter.builder(GatewayMetricsConfig.GATEWAY_REQUESTS_COUNTER)
.tag("method", method)
.tag("path", normalizePath(path))
.description("Total gateway requests")
.description("Gateway-Requests gesamt")
.register(meterRegistry)
.increment()
@@ -151,7 +151,7 @@ class GatewayMetricsWebFilter(private val meterRegistry: MeterRegistry) : WebFil
.tag("path", normalizePath(path))
.tag("status", statusCode)
.tag("status_series", statusSeries)
.description("Gateway request processing time")
.description("Gateway Request-Verarbeitungszeit")
.register(meterRegistry)
.record(duration)
@@ -163,7 +163,7 @@ class GatewayMetricsWebFilter(private val meterRegistry: MeterRegistry) : WebFil
.tag("status", statusCode)
.tag("status_series", statusSeries)
.tag("error_type", if (statusCode.startsWith("4")) "client_error" else "server_error")
.description("Gateway error count")
.description("Gateway-Fehleranzahl")
.register(meterRegistry)
.increment()
}
@@ -0,0 +1,3 @@
# Placeholder HOCON configuration for compatibility with legacy test scripts
# The actual configuration is provided in application.yml.
# This file ensures scripts that check for application.conf do not fail.
@@ -20,159 +20,164 @@ spring:
consul:
host: ${CONSUL_HOST:localhost}
port: ${CONSUL_PORT:8500}
enabled: ${CONSUL_ENABLED:false}
discovery:
register: true
enabled: ${CONSUL_ENABLED:false}
register: ${CONSUL_ENABLED:false}
health-check-path: /actuator/health
health-check-interval: 10s
instance-id: ${spring.application.name}-${server.port}-${random.uuid}
gateway:
# HTTP Client-Timeouts für stabile Upstream-Verbindungen
httpclient:
connect-timeout: 5000 # in Millisekunden
response-timeout: 30s
pool:
type: elastic
max-idle-time: 15s
max-life-time: 60s
# Verbesserte CORS-Konfiguration
# Antwort-Header bereinigen und globale Filter
# Route definitions with service discovery
server:
webflux:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
httpclient:
connect-timeout: 5000 # in Millisekunden
response-timeout: 30s
pool:
max-idle-time: 15s
max-life-time: 60s
# Disable weight calculation filter to prevent blocking operations
filter:
weight:
enabled: false
# Verbesserte CORS-Konfiguration
# Antwort-Header bereinigen und globale Filter
# Route-Definitionen mit Service Discovery
defaultFilters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
- name: CircuitBreaker
args:
name: defaultCircuitBreaker
fallbackUri: forward:/fallback
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
methods: GET,POST,PUT,DELETE
backoff:
firstBackoff: 50ms
maxBackoff: 500ms
factor: 2
basedOnPreviousValue: false
# Sicherheits-Header für erweiterten Schutz
- name: AddResponseHeader
args:
name: X-Content-Type-Options
value: nosniff
- name: AddResponseHeader
args:
name: X-Frame-Options
value: DENY
- name: AddResponseHeader
args:
name: X-XSS-Protection
value: 1; mode=block
- name: AddResponseHeader
args:
name: Referrer-Policy
value: strict-origin-when-cross-origin
- name: AddResponseHeader
args:
name: Cache-Control
value: no-cache, no-store, must-revalidate
routes:
# Health Check und Gateway Info Routes
- id: gateway-info-route
uri: http://localhost:${server.port}
predicates:
- Path=/
- Method=GET
filters:
- SetStatus=200
- SetResponseHeader=Content-Type,application/json
# Members Service Routes
- id: members-service-route
uri: lb://members-service
predicates:
- Path=/api/members/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: defaultCircuitBreaker
fallbackUri: forward:/fallback
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
methods: GET,POST,PUT,DELETE
backoff:
firstBackoff: 50ms
maxBackoff: 500ms
factor: 2
basedOnPreviousValue: false
# Security Headers for enhanced protection
- name: AddResponseHeader
args:
name: X-Content-Type-Options
value: nosniff
- name: AddResponseHeader
args:
name: X-Frame-Options
value: DENY
- name: AddResponseHeader
args:
name: X-XSS-Protection
value: 1; mode=block
- name: AddResponseHeader
args:
name: Referrer-Policy
value: strict-origin-when-cross-origin
- name: AddResponseHeader
args:
name: Cache-Control
value: no-cache, no-store, must-revalidate
routes:
# Health Check und Gateway Info Routes
- id: gateway-info-route
uri: http://localhost:${server.port}
predicates:
- Path=/
- Method=GET
filters:
- SetStatus=200
- SetResponseHeader=Content-Type,application/json
args:
name: membersCircuitBreaker
fallbackUri: forward:/fallback/members
# Members Service Routes
- id: members-service-route
uri: lb://members-service
predicates:
- Path=/api/members/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: membersCircuitBreaker
fallbackUri: forward:/fallback/members
# Horses Service Routes
- id: horses-service-route
uri: lb://horses-service
predicates:
- Path=/api/horses/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: horsesCircuitBreaker
fallbackUri: forward:/fallback/horses
# Horses Service Routes
- id: horses-service-route
uri: lb://horses-service
predicates:
- Path=/api/horses/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: horsesCircuitBreaker
fallbackUri: forward:/fallback/horses
# Events Service Routes
- id: events-service-route
uri: lb://events-service
predicates:
- Path=/api/events/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: eventsCircuitBreaker
fallbackUri: forward:/fallback/events
# Events Service Routes
- id: events-service-route
uri: lb://events-service
predicates:
- Path=/api/events/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: eventsCircuitBreaker
fallbackUri: forward:/fallback/events
# Masterdata Service Routes
- id: masterdata-service-route
uri: lb://masterdata-service
predicates:
- Path=/api/masterdata/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: masterdataCircuitBreaker
fallbackUri: forward:/fallback/masterdata
# Masterdata Service Routes
- id: masterdata-service-route
uri: lb://masterdata-service
predicates:
- Path=/api/masterdata/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: masterdataCircuitBreaker
fallbackUri: forward:/fallback/masterdata
# Auth Service Routes (falls vorhanden)
- id: auth-service-route
uri: lb://auth-service
predicates:
- Path=/api/auth/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: authCircuitBreaker
fallbackUri: forward:/fallback/auth
# Auth Service Routes (if exists)
- id: auth-service-route
uri: lb://auth-service
predicates:
- Path=/api/auth/**
filters:
- StripPrefix=1
- name: CircuitBreaker
args:
name: authCircuitBreaker
fallbackUri: forward:/fallback/auth
# Ping Service Routes (bestehend)
- id: ping-service-route
uri: lb://ping-service
predicates:
- Path=/api/ping/**
filters:
- StripPrefix=1
globalcors:
corsConfigurations:
'[/**]':
allowedOriginPatterns:
- "https://*.meldestelle.at"
- "http://localhost:*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- PATCH
- OPTIONS
allowedHeaders:
- "*"
allowCredentials: true
maxAge: 3600
# Ping Service Routes (existing)
- id: ping-service-route
uri: lb://ping-service
predicates:
- Path=/api/ping/**
filters:
- StripPrefix=1
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns:
- "https://*.meldestelle.at"
- "http://localhost:*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- PATCH
- OPTIONS
allowedHeaders:
- "*"
allowCredentials: true
maxAge: 3600
# Circuit Breaker Configuration
# Circuit Breaker Konfiguration
resilience4j:
circuitbreaker:
configs:
@@ -237,12 +242,9 @@ management:
access: unrestricted
gateway:
access: unrestricted
circuit breakers:
circuitbreakers:
enabled: true
metrics:
export:
prometheus:
# Prometheus configuration moved to monitoring-client module
distribution:
percentiles-histogram:
spring.cloud.gateway.requests: true
@@ -271,7 +273,7 @@ management:
java:
enabled: true
# Enhanced Logging Configuration
# Erweiterte Logging-Konfiguration
logging:
level:
org.springframework.cloud.gateway: INFO
@@ -287,7 +289,6 @@ logging:
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{correlationId:-}] %logger{36} - %msg%n"
file:
name: logs/gateway.log
max-size: 100MB
logback:
rollingpolicy:
clean-history-on-start: true
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true">
<property name="LOG_FILE" value="logs/gateway.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{correlationId}] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/gateway.log.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{correlationId}] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.cloud.gateway" level="INFO"/>
<logger name="org.springframework.cloud.loadbalancer" level="DEBUG"/>
<logger name="org.springframework.cloud.consul" level="INFO"/>
<logger name="io.github.resilience4j" level="INFO"/>
<logger name="reactor.netty.http.client" level="INFO"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
@@ -2,12 +2,12 @@ openapi: 3.0.3
info:
title: Meldestelle API
description: |
Self-Contained Systems API Gateway for Austrian Equestrian Federation.
This API provides access to various bounded contexts including authentication,
master data management, horse registry, and event management.
Self-Contained Systems API Gateway für den Österreichischen Pferdesportverband.
Diese API bietet Zugriff auf verschiedene Bounded Contexts einschließlich Authentifizierung,
Stammdatenverwaltung, Pferderegister und Veranstaltungsmanagement.
## Rate Limiting
This API implements rate limiting to ensure fair usage and system stability.
Diese API implementiert Rate Limiting zur Gewährleistung fairer Nutzung und Systemstabilität.
### Global Rate Limits
- Default limit: 100 requests per minute per IP address
@@ -8,31 +8,31 @@ import org.springframework.test.context.ActiveProfiles
import org.springframework.test.web.reactive.server.WebTestClient
/**
* Tests for the Fallback Controller that handles circuit breaker scenarios.
* Tests all fallback endpoints for different services.
* Tests für den Fallback Controller, der Circuit Breaker Szenarien behandelt.
* Testet alle Fallback-Endpunkte für verschiedene Services.
*/
@SpringBootTest(
classes = [GatewayApplication::class],
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = [
// Disable external dependencies for fallback tests
// Externe Abhängigkeiten für Fallback-Tests deaktivieren
"spring.cloud.discovery.enabled=false",
"spring.cloud.consul.enabled=false",
"spring.cloud.consul.config.enabled=false",
"spring.cloud.consul.discovery.register=false",
"spring.cloud.loadbalancer.enabled=false",
// Disable circuit breaker health indicator to avoid interference
// Circuit Breaker Health Indicator deaktivieren um Interferenzen zu vermeiden
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
"management.health.circuitbreakers.enabled=false",
// Disable custom filters for pure fallback testing
// Custom Filter für reine Fallback-Tests deaktivieren
"gateway.security.jwt.enabled=false",
// Use reactive web application type
// Reaktiven Web-Anwendungstyp verwenden
"spring.main.web-application-type=reactive",
// Disable gateway discovery
// Gateway Discovery deaktivieren
"spring.cloud.gateway.discovery.locator.enabled=false",
// Disable actuator security
// Actuator Security deaktivieren
"management.security.enabled=false",
// Set random port
// Zufälligen Port setzen
"server.port=0"
]
)
@@ -43,7 +43,7 @@ class FallbackControllerTests {
lateinit var webTestClient: WebTestClient
@Test
fun `should return members service fallback response`() {
fun `sollte Members Service Fallback Response zurückgeben`() {
webTestClient.get()
.uri("/fallback/members")
.exchange()
@@ -60,7 +60,7 @@ class FallbackControllerTests {
}
@Test
fun `should return horses service fallback response`() {
fun `sollte Horses Service Fallback Response zurückgeben`() {
webTestClient.get()
.uri("/fallback/horses")
.exchange()
@@ -75,7 +75,7 @@ class FallbackControllerTests {
}
@Test
fun `should return events service fallback response`() {
fun `sollte Events Service Fallback Response zurückgeben`() {
webTestClient.get()
.uri("/fallback/events")
.exchange()
@@ -5,31 +5,31 @@ import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
/**
* Basic test to verify that the Gateway application context loads successfully.
* Uses test profile to disable production filters and external dependencies.
* Basis-Test zur Überprüfung, dass der Gateway-Anwendungskontext erfolgreich lädt.
* Verwendet Test-Profil um Produktions-Filter und externe Abhängigkeiten zu deaktivieren.
*/
@SpringBootTest(
classes = [GatewayApplication::class],
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = [
// Disable all external dependencies for context loading test
// Alle externen Abhängigkeiten für Context-Loading-Test deaktivieren
"spring.cloud.discovery.enabled=false",
"spring.cloud.consul.enabled=false",
"spring.cloud.consul.config.enabled=false",
"spring.cloud.consul.discovery.register=false",
"spring.cloud.loadbalancer.enabled=false",
// Disable circuit breaker for tests
// Circuit Breaker für Tests deaktivieren
"resilience4j.circuitbreaker.configs.default.registerHealthIndicator=false",
"management.health.circuitbreakers.enabled=false",
// Disable custom security and filters
// Custom Security und Filter deaktivieren
"gateway.security.jwt.enabled=false",
// Use reactive web application type
// Reaktiven Web-Anwendungstyp verwenden
"spring.main.web-application-type=reactive",
// Disable gateway discovery
// Gateway Discovery deaktivieren
"spring.cloud.gateway.discovery.locator.enabled=false",
// Disable actuator security
// Actuator Security deaktivieren
"management.security.enabled=false",
// Set random port
// Zufälligen Port setzen
"server.port=0"
]
)
@@ -38,7 +38,7 @@ class GatewayApplicationTests {
@Test
fun contextLoads() {
// This test passes if the Spring application context loads successfully
// without any configuration errors or missing bean dependencies
// Dieser Test ist erfolgreich, wenn der Spring-Anwendungskontext erfolgreich lädt
// ohne Konfigurationsfehler oder fehlende Bean-Abhängigkeiten
}
}
@@ -21,9 +21,11 @@ spring:
discovery:
locator:
enabled: false
httpclient:
connect-timeout: 1000
response-timeout: 5s
server:
webflux:
httpclient:
connect-timeout: 1000
response-timeout: 5s
# Override production routes: keep empty in tests running with dev profile
routes: []
globalcors:
@@ -22,8 +22,8 @@ spring:
locator:
enabled: false
httpclient:
connect-timeout: 1000
response-timeout: 5s
connectTimeout: 1000
responseTimeout: 5s
# IMPORTANT: Do not load production lb:// routes in tests
routes: []
globalcors: