API Gateway patterns – Kompleksowy przewodnik po wzorcach 2019

TL;DR: API Gateway to centralny punkt wejścia do mikrousług, który implementuje wzorce routingu, security, rate limiting i monitoring. W 2019 najlepsze opcje to Spring Cloud Gateway, Netflix Zuul 2 i Kong. Wzorce Backend for Frontend i Circuit Breaker są kluczowe dla skalowalnych systemów.

Dlaczego API Gateway to game-changer w mikrousługach?

W świecie mikrousług każdy serwis ma własny endpoint, własne zasady bezpieczeństwa i własne API. Bez centralnego punktu kontroli szybko powstaje chaos – klienty muszą znać dziesiątki URL-i, implementować własną logikę retry, a zarządzanie security staje się koszmarem.

API Gateway rozwiązuje te problemy, stając się jedynym punktem wejścia do całego ekosystemu mikrousług. To jak recepcjonista w biurowcu – wie gdzie skierować każde zapytanie i pilnuje, żeby tylko uprawnione osoby miały dostęp.

Co się nauczysz z tego artykułu:

  • Jakie wzorce API Gateway są najważniejsze w praktyce
  • Jak zaimplementować Backend for Frontend pattern
  • Kiedy używać Circuit Breaker i jak go skonfigurować
  • Praktyczne porównanie Spring Cloud Gateway vs Netflix Zuul
  • Best practices dla rate limiting i security
  • Jak monitorować i debugować API Gateway w produkcji
Wymagania wstępne: Podstawowa znajomość Spring Boot, REST API i koncepcja mikrousług. Przydatna znajomość Spring Cloud.

Podstawowe wzorce API Gateway

API Gateway implementuje kilka kluczowych wzorców, które rozwiązują typowe problemy w architekturze rozproszonej. Poznajmy najważniejsze z nich.

1. Gateway Router Pattern

Najprostszy i najważniejszy wzorzec – gateway odbiera request i przekierowuje go do odpowiedniego mikrousługi na podstawie URL-a, nagłówków lub innych kryteriów.

# Spring Cloud Gateway configuration
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://user-service:8081
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=2
        - id: order-service  
          uri: http://order-service:8082
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=2
Pro tip: Używaj path rewriting z StripPrefix, żeby mikrousługi nie musiały znać prefiksów API Gateway. Service otrzymuje czysty path bez /api/users.

2. Backend for Frontend (BFF) Pattern

Różne typy klientów (web app, mobile app, external API) potrzebują różnych formatów danych i różnych endpoints. BFF pattern tworzy dedykowane gateway dla każdego typu klienta.

@RestController
@RequestMapping("/mobile/api")
public class MobileGatewayController {
    
    @Autowired
    private UserService userService;
    
    @Autowired  
    private OrderService orderService;
    
    // Endpoint zoptymalizowany dla mobile - zwraca tylko potrzebne dane
    @GetMapping("/user/{id}/summary")
    public MobileUserSummary getUserSummary(@PathVariable Long id) {
        User user = userService.getUser(id);
        List recentOrders = orderService.getRecentOrders(id, 5);
        
        return MobileUserSummary.builder()
            .userName(user.getName())
            .avatar(user.getAvatarUrl())
            .recentOrdersCount(recentOrders.size())
            .lastOrderDate(recentOrders.get(0).getCreatedAt())
            .build();
    }
}
BFF pattern jest szczególnie przydatny, gdy mobile app potrzebuje mniej danych niż web app (oszczędność transferu), albo gdy external API wymaga innego formatu odpowiedzi.

Circuit Breaker Pattern w API Gateway

Gdy jeden z mikrousług nie odpowiada, Circuit Breaker zapobiega kaskadowym awariom. Zamiast czekać na timeout, gateway szybko zwraca error lub fallback response.

@Component
public class OrderServiceClient {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @HystrixCommand(
        fallbackMethod = "getOrdersFallback",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
        }
    )
    public List getOrders(Long userId) {
        return restTemplate.getForObject(
            "http://order-service/users/" + userId + "/orders", 
            OrderList.class
        ).getOrders();
    }
    
    public List getOrdersFallback(Long userId) {
        // Zwracamy cached data lub pusty wynik
        return cacheService.getCachedOrders(userId)
            .orElse(Collections.emptyList());
    }
}
Uwaga: Netflix Hystrix wchodzi w maintenance mode w 2019. Rozważ migrację do Resilience4j – nowszej alternatywy z lepszym API.

Authentication i Authorization patterns

API Gateway to idealny punkt do centralizacji uwierzytelniania. Zamiast implementować security w każdym mikrousługi, robimy to raz w gateway.

JWT Token Validation Pattern

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Autowired
    private JwtTokenProvider tokenProvider;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        
        String token = extractTokenFromRequest(request);
        
        if (token != null && tokenProvider.validateToken(token)) {
            // Dodaj user info do headers dla downstream services
            String userId = tokenProvider.getUserIdFromToken(token);
            String roles = tokenProvider.getRolesFromToken(token);
            
            // Forward headers to microservices
            request.setAttribute("X-User-Id", userId);
            request.setAttribute("X-User-Roles", roles);
        } else {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return;
        }
        
        filterChain.doFilter(request, response);
    }
    
    private String extractTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

Pro tip: Przekazuj user context przez custom headers (X-User-Id, X-User-Roles) do mikrousług. Mikrousługi mogą ufać tym headerom, bo przychodzą z zaufanego API Gateway.

Rate Limiting patterns

Rate limiting chroni przed nadmiernym ruchem i atakami DDoS. W API Gateway można implementować różne strategie w zależności od potrzeb.

PatternOpisUżycie
Fixed WindowX requestów na minutęProste API publiczne
Sliding WindowX requestów w ostatniej minucieBardziej równomierne ograniczenia
Token BucketMożliwość burst trafficAPI z okresowymi skokami ruchu
Per-User LimitingRóżne limity dla różnych użytkownikówPłatne tiers API
@Component
public class RateLimitingFilter implements GlobalFilter {
    
    private final RedisTemplate redisTemplate;
    
    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String clientId = extractClientId(request);
        String key = "rate_limit:" + clientId;
        
        return checkRateLimit(key)
            .flatMap(allowed -> {
                if (allowed) {
                    return chain.filter(exchange);
                } else {
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                    response.getHeaders().add("X-RateLimit-Retry-After", "60");
                    return response.setComplete();
                }
            });
    }
    
    private Mono checkRateLimit(String key) {
        return Mono.fromCallable(() -> {
            // Sliding window algorithm with Redis
            long now = System.currentTimeMillis();
            long windowStart = now - 60000; // 1 minute window
            
            // Remove old entries
            redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
            
            // Count current requests
            Long count = redisTemplate.opsForZSet().count(key, windowStart, now);
            
            if (count < 100) { // 100 requests per minute limit
                // Add current request
                redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
                redisTemplate.expire(key, Duration.ofMinutes(1));
                return true;
            }
            
            return false;
        });
    }
}

Monitoring i Observability patterns

API Gateway zbiera metryki ze wszystkich requestów, co daje doskonały wgląd w całą architekturę. Kluczowe metryki to latencja, error rate, throughput i status poszczególnych mikrousług.

Distributed Tracing

@RestController
public class GatewayController {
    
    @Autowired
    private Tracer tracer;
    
    @GetMapping("/api/user/{id}/profile")
    public Mono getUserProfile(@PathVariable String id) {
        
        Span span = tracer.nextSpan()
            .name("gateway-get-user-profile")
            .tag("user.id", id)
            .start();
            
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
            // Request propaguje trace context do mikrousług
            return webClient
                .get()
                .uri("http://user-service/users/" + id)
                .retrieve()
                .bodyToMono(UserProfile.class)
                .doOnSuccess(profile -> span.tag("user.name", profile.getName()))
                .doOnError(error -> span.tag("error", error.getMessage()));
        } finally {
            span.end();
        }
    }
}
Spring Cloud Sleuth automatycznie dodaje trace headers do outgoing requests. Mikrousługi kontynuują trace bez dodatkowego kodu, jeśli używają Spring Boot.

Porównanie rozwiązań w 2019

RozwiązanieZaletyWadyKiedy wybierać
Spring Cloud GatewayReactive, Performance, Spring ecosystemMłode, mniej pluginówNowe projekty, Spring shops
Netflix Zuul 1Stabilny, dużo dokumentacjiBlocking I/O, performance issuesLegacy systems, gdzie stability > performance
Netflix Zuul 2Non-blocking, dobry performanceNowy, mniej examplesHigh-traffic applications
KongFeature-rich, plugins, UINie-Java, licencja dla enterpriseMulti-platform, gdy potrzebujesz UI

Production-ready deployment patterns

W produkcji API Gateway to single point of failure, więc wymaga szczególnej uwagi na wysoką dostępność i performance.

High Availability Setup

# docker-compose.yml for HA Gateway
version: '3.8'
services:
  gateway-1:
    image: api-gateway:latest
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - EUREKA_INSTANCE_INSTANCE_ID=gateway-1
    depends_on:
      - redis
      - eureka
      
  gateway-2:
    image: api-gateway:latest  
    ports:
      - "8081:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - EUREKA_INSTANCE_INSTANCE_ID=gateway-2
    depends_on:
      - redis
      - eureka
      
  load-balancer:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - gateway-1
      - gateway-2
Uwaga: Upewnij się, że rate limiting używa zewnętrznego store (Redis) zamiast in-memory cache. Inaczej każda instancja gateway będzie miała własne liczniki.

Health Checks i Graceful Shutdown

@RestController
public class HealthController {
    
    @Autowired
    private List healthIndicators;
    
    @GetMapping("/health")
    public ResponseEntity> health() {
        Map health = new HashMap<>();
        boolean allUp = true;
        
        for (HealthIndicator indicator : healthIndicators) {
            Health indicatorHealth = indicator.health();
            health.put(indicator.getClass().getSimpleName(), 
                      indicatorHealth.getStatus().getCode());
            
            if (!Status.UP.equals(indicatorHealth.getStatus())) {
                allUp = false;
            }
        }
        
        health.put("overall", allUp ? "UP" : "DOWN");
        
        return ResponseEntity
            .status(allUp ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE)
            .body(health);
    }
}

@Component
public class DownstreamServicesHealthIndicator implements HealthIndicator {
    
    @Override
    public Health health() {
        try {
            // Check if critical downstream services are reachable
            checkUserService();
            checkOrderService();
            return Health.up().build();
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

Częste problemy i rozwiązania

Pułapka: Timeout configuration chaos. Gateway ma własne timeouts, ale HTTP client też. Mikrousługa też może mieć timeouts. Rezultat: request timeoutuje po 30 sekundach zamiast po 3.

Rozwiązanie: Ustaw timeouts kaskadowo - gateway timeout > HTTP client timeout > mikrousługa timeout. Przykład: 5s > 4s > 3s.

Typowy błąd: Logowanie wszystkich requestów w produkcji. API Gateway obsługuje tysiące requestów per second - logi szybko zajmą dysk i spowolnią system.

Lepsze podejście: Loguj tylko errors, slow requests (>2s) i sample requestów (np. co 100-ty). Używaj structured logging (JSON) dla łatwego parsowania.

Security best practices

API Gateway to pierwsza linia obrony - każda luka tutaj wpływa na całą architekturę.

Pro tip: Implementuj CORS policy na poziomie gateway, nie w każdym mikrousługi. Centralne zarządzanie CORS jest łatwiejsze i mniej podatne na błędy.
@Configuration
public class CorsConfiguration {
    
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowCredentials(true);
        corsConfig.addAllowedOriginPattern("https://*.yourdomain.com");
        corsConfig.addAllowedHeader("*");
        corsConfig.addAllowedMethod("*");
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig);
        
        return new CorsWebFilter(source);
    }
}
Czy API Gateway wprowadza single point of failure?

Tak, ale można to zminimalizować przez load balancer i multiple instances. W praktyce korzyści (centralne security, monitoring, routing) przeważają nad ryzykiem. Alternatywą jest service mesh, ale to znacznie bardziej skomplikowane.

Ile latencji dodaje API Gateway?

Dobrze zoptymalizowany gateway dodaje 1-5ms latencji. Spring Cloud Gateway w benchmarkach osiąga <1ms overhead. Ważniejsze są korzyści: cache responses, circuit breaker, compression - które mogą faktycznie poprawić performance.

Backend for Frontend vs jeden gateway - kiedy co wybierać?

Jeden gateway jeśli klienty mają podobne potrzeby. BFF gdy mobile app potrzebuje innych danych niż web app, lub gdy masz external API z innymi SLA. BFF zwiększa complexity, ale daje lepsze developer experience.

Jak testować API Gateway?

Unit tests dla filters i custom logic. Integration tests z WireMock dla downstream services. Contract tests (Pact) dla API contracts. Performance tests dla throughput i latencji. Chaos engineering dla circuit breaker logic.

Czy warto używać Netflix Zuul w 2019?

Zuul 1 - tylko dla legacy systems. Zuul 2 - ok dla high-performance apps, ale mało examples. Spring Cloud Gateway - najlepsza opcja dla nowych projektów Spring. Ma reactive support i aktywny development.

Jak implementować versioning API przez gateway?

Header-based: route na podstawie Accept-Version header. URL-based: /v1/users vs /v2/users. Query param: /users?version=2. Header-based jest cleanest, ale URL-based jest najprostszy dla klientów.

Jak debugować problemy performance w gateway?

Enable actuator metrics. Dodaj custom timers dla każdego route. Sprawdź connection pool settings dla HTTP client. Monitor GC metrics. Używaj profiler (JProfiler, async-profiler) w staging. Trace slow requests z distributed tracing.

Następne kroki:

Poznałeś wzorce API Gateway i wiesz jak je implementować. Teraz rozwiń wiedzę o architekturze mikrousług:

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Stwórz API Gateway z Spring Cloud Gateway, który będzie routować requesty do dwóch mikrousług (user-service i order-service). Dodaj JWT authentication, rate limiting (100 requests/minute), circuit breaker z Hystrix, i monitoring z Micrometer. Przetestuj, że rate limiting działa poprawnie i circuit breaker otwiera się po kilku failed requests.

Masz doświadczenie z API Gateway w produkcji? Podziel się swoimi insights w komentarzach - które wzorce sprawdziły się najlepiej w Twoich projektach?

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry