Cloud Native 19 min read

Why Every Microservice Architecture Needs an API Gateway

The article explains that without a gateway each microservice must duplicate authentication, rate‑limiting, logging and other cross‑cutting concerns, leading to maintenance overhead and security risks, and shows how a gateway centralises these functions while providing routing, load‑balancing, circuit‑breaking and observability, backed by real‑world code examples and a comparative analysis of popular gateway solutions.

Java Companion
Java Companion
Java Companion
Why Every Microservice Architecture Needs an API Gateway

Why a Gateway Is Essential

When a monolith is split into dozens of services, each service gets its own port, authentication logic, rate‑limiting switch and log format. A client request for an order may need to call member, order and product services, all directly exposed to the public network, creating a chaotic architecture.

The gateway places a unified entry point in front of all services, handling authentication, routing, rate‑limiting, logging and other cross‑cutting concerns that are unrelated to business logic.

Problems Without a Gateway

1. Repeated Authentication Logic

Each of the 20 services must parse JWT, verify signatures and check a blacklist, resulting in duplicated code that must be updated in every service when the logic changes, creating security gaps.

Sharing a common library inflates every service’s JAR and forces a full rebuild of all dependent services for any change.

2. Scattered Cross‑Cutting Concerns

Logging, rate‑limiting and CORS are implemented differently across teams (JSON vs plain text logs, Guava RateLimiter vs custom sliding window), making global configuration and troubleshooting difficult.

3. Lack of Global Operational View

Features such as gray releases, A/B testing or global request blocking require modifications in every service; a gateway allows a single configuration to take effect globally.

4. Nginx Is Not a Full Replacement

Nginx excels at static routing but cannot perform JWT parsing, circuit breaking or dynamic routing from a service registry. Configuration changes require reloads, which is impractical for frequent updates.

What a Gateway Is

A gateway consists of two capabilities: gateway = routing + filter‑chain Routing: Receives external requests and forwards them to the appropriate microservice based on path, headers or parameters.

Filter Chain: A series of filters that process requests on entry and exit, handling authentication, rate‑limiting, logging, circuit breaking, CORS, etc.

Microservice gateway architecture diagram: client accesses multiple backend services through an API gateway that handles routing, authentication, rate‑limiting, monitoring, etc.
Microservice gateway architecture diagram: client accesses multiple backend services through an API gateway that handles routing, authentication, rate‑limiting, monitoring, etc.

Core Responsibilities

1. Unified Authentication

The gateway parses the JWT token, checks the Redis blacklist, extracts user information and injects it into request headers before forwarding.

// Extract token from request header
String token = request.getHeaders().getFirst("Authorization");
// Check blacklist in Redis
Mono<Boolean> logoutInRedis = reactiveStringRedisTemplate
    .opsForValue()
    .get(LOGOUT_REDIS_KEY_PREFIX + pureToken)
    .map(s -> true)
    .switchIfEmpty(Mono.just(false));
// Parse JWT
Jws<Claims> claimsJws = Jwts.parser()
    .setSigningKey(secretBase64)
    .parseClaimsJws(pureToken);
Claims body = claimsJws.getBody();
String subject = body.getSubject();
String channel = body.get("channel", String.class);

After validation, the gateway removes any external custom headers and injects the verified values:

// Clean possible forged headers
httpHeaders.remove("X-Subject");
httpHeaders.remove("X-Channel");
httpHeaders.remove("X-Primary-Account-Id");
// Inject parsed user info
requestBuilder.header("X-Subject", subject);
requestBuilder.header("X-Channel", channel);
requestBuilder.header("X-Primary-Account-Id", primaryAccountId);
Security note: the gateway must clear external X-Subject (and similar) headers before injecting its own values to prevent header spoofing.

2. Routing and Load Balancing

Clients only need the gateway address; the gateway resolves service instances from a registry (e.g., Nacos/Eureka) and performs load balancing.

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service   # lb:// uses the service registry for load balancing
          predicates:
            - Path=/api/user-service/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order-service/**
        - id: pay-service
          uri: lb://pay-service
          predicates:
            - Path=/api/pay-service/**

Backend services can scale up or down without client changes. The gateway also supports API aggregation, sending parallel requests to multiple services and merging the results.

Microservice gateway pattern diagram: client accesses multiple services through a single API gateway.
Microservice gateway pattern diagram: client accesses multiple services through a single API gateway.

3. Rate Limiting and Circuit Breaking

Rate limiting is implemented per‑API using a Redis + Lua token‑bucket algorithm, allowing fine‑grained QPS control.

-- Get current token count
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil then
  last_tokens = capacity
end
-- Refill tokens based on elapsed time
local delta = math.max(0, now - last_refreshed)
local filled_tokens = math.min(capacity, last_tokens + (delta * rate))
-- Allow if enough tokens
local allowed = filled_tokens >= requested

Using Lua ensures atomic execution in Redis, avoiding race conditions and offering better performance than application‑level distributed locks.

Circuit breaking (e.g., Hystrix) isolates failures per URL, preventing a faulty downstream service from cascading.

// Initialise circuit breaker per URL
Setter setter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName()))
    .andCommandKey(HystrixCommandKey.Factory.asKey(uriAsCommandKey));
// Publish event when circuit opens
if (command.isCircuitBreakerOpen()) {
  circuitBreakerOpenEventBus.post(command);
}

Sentinel (Spring Cloud Alibaba) provides a visual console for real‑time rule updates without redeployment.

4. Logging and Tracing

The gateway captures request and response logs asynchronously via MQ, avoiding main‑thread blocking.

// Request log → MQ async send
accessLogService.logRequestBody(exchange, bodyStr);
// Response log → MQ async send
accessLogService.logResponseBody(exchange, responseBody);

It also injects a TraceId into response headers, enabling end‑to‑end tracing with Zipkin/Sleuth.

Gateway Landscape Comparison

Nginx – Proven, high‑performance, stable; requires configuration reload and lacks business‑logic handling.

Spring Cloud Gateway – Spring ecosystem friendly, highly extensible; community resources relatively fewer.

Kong – Open‑source API gateway pioneer, large user base; depends on PostgreSQL and performance degrades with >1000 routes.

Apache APISIX – Cloud‑native, excellent performance, multi‑language plugins; documentation still maturing.

Envoy – CNCF graduated project, ideal for service‑mesh scenarios; C++ codebase and higher learning curve for extensions.

For Java stacks, Spring Cloud Gateway is the preferred choice; for high‑performance or polyglot environments, APISIX or Envoy may be considered.

Production Practices

Large systems often run separate gateways for consumer (C‑side) and merchant (B‑side) traffic due to differing authentication, rate‑limiting and routing requirements.

Authentication Differences

C‑side uses JWT + SSO logout; B‑side adds guest tokens and RSA signature verification.

// B‑side guest token example
private static final TokenDetail FAKE_GUEST_TOKEN = new TokenDetail()
    .setSub("-1")
    .setChannel("weixin")
    .setIsGuest(true)
    .setSource("mini_program");
// B‑side RSA signature verification
String clientId = request.getClientId();
String payload = request.getPayload();
String sign = request.getSign();
if (Math.abs(nowTimestamp - timestamp) > signApiV2Properties.getTtl()) {
  throw new SignApiV2AccessNotAllowException("request timestamp too late or early");
}
SignUtils.checkSign(request, "sign", clientProperties.getPublicKey());

Rate‑Limiting Strategies

C‑side uses precise API token‑bucket limits; B‑side adds intelligent limiting that reacts to downstream circuit‑breakers.

Routing Management

C‑side routes are stored in Apollo configuration; B‑side routes reside in Redis with dynamic API updates and local snapshots for failover.

Capability : Auth – C‑side : JWT + SSO logout; B‑side : JWT + Guest mode + RSA signature

Rate limiting : C‑side – API precise limiting; B‑side – Precise + intelligent limiting

Circuit breaking : C‑side – Unified error response; B‑side – URL‑level circuit breaker + fallback

Routing : C‑side – Apollo config center; B‑side – Redis dynamic routing + local snapshot

Logging : Both – MQ async send

FAQ

What should a gateway do? Authentication, rate‑limiting, routing, logging, CORS, circuit breaking, request rewriting and response standardisation – all cross‑cutting concerns unrelated to business logic.

What should it not do? Business logic, data aggregation, direct database access, or complex decision‑making; mixing business code with the gateway compromises stability.

When to split gateways? When different client groups have distinct authentication methods, traffic patterns or security requirements, or when traffic isolation is needed to prevent one side’s spikes from affecting the other.

Is the gateway a single point of failure? Yes; typical mitigation is to place Nginx or LVS in front, deploy multiple gateway instances, and use health checks from the service registry. Keep the gateway lightweight and perform capacity testing.

Conclusion

The gateway acts like a security guard and front desk for a microservice system: it handles identity verification and traffic distribution, allowing downstream services to focus on their own responsibilities. Without a gateway, standards diverge, maintenance costs rise, security gaps appear, and operational risk increases. Building a gateway early avoids costly retrofits later, but the gateway itself should remain a thin, non‑business layer.

Microservicesapi-gatewayloggingAuthenticationRate LimitingSpring Cloud GatewayCircuit Breaking
Java Companion
Written by

Java Companion

A highly professional Java public account

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.