Evaluating Apache APISIX vs. Spring Cloud Gateway

Given the number of API Gateways available on the market, I'm regularly asked which is better. Better is a very subjective term. However, there's no denying that if you're advocating for a product, you should know your product and its competitors. In this post, I'd like to share my understanding of Spring Cloud Gateway and how it compares to Apache APISIX.

I'm cautious when comparing products because most comparisons I read are heavily biased. That's a risk, especially when working on one of the products one is comparing. I'll also avoid "benchmarketing" - when you benchmark products in a context that favors your own; I'll focus on the so-called Developer Experience.

image:/assets/resources/spring-cloud-gateway-apache-apisix/blind-men-g35dae97dc_640.png[The mythical elephant and the blind me,640,417]

As a safeguard, I've asked my friend Iván López, a Spring Cloud Gateway user, to proofread the post. Iván reviewed the post as my friend, not as a VMWare developer. Despite all my best efforts, I may appear not as objective as I'd like. I accept it, and that's fine.

First steps with Spring Cloud Gateway

All API Gateways that I know about provide a Docker image. For example, Apache APISIX provide three flavours: Debian, CentOS, and recently, Red Hat. At this point, you can start deploying the images in your containerized architecture.

Spring Cloud Gateway's approach is radically different. It's just a regular dependency on a regular Spring project:

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
      <version>4.0.6</version>
</dependency>

You can leverage all standard ways to create the project, including the popular start.spring.io, as for any regular Spring project. This developer-oriented approach is pervasive in everything related to Spring Cloud Gateway.

Concepts and abstractions

Apache APISIX features a rich model:

In particular, you can create an Upstream abstraction and share it across different routes. Likewise, Plugin Config allows you to create a reusable combination of plugins.

Here's the Spring Cloud Gateway model:

The APISIX model is richer, with abstractions and the possibility of reuse.

Configuration

Apache APISIX has two deployment modes (actually three, but let's not get into details): traditional and standalone.

In traditional mode, APISIX stores its configuration in etcd. APISIX offers a rich API to access and update the configuration, the Admin API. In standalone mode, the configuration is just plain YAML. It's the approach for GitOps practitioners: you'd store your configuration in a Git repo, watch it via your favorite tool (e.g., Argo CD or Tekton), and the latter would propagate the changes to APISIX nodes upon changes. APISIX reloads its configuration every second or so.

Here's a sample:

upstreams:
  - id: 1
    nodes:
      "catalog:8080": 1
  - id: 2
    nodes:
      "pricing:8080": 1

routes:
  - uri: /v1/products*
    upstream_id: 1
    plugins:
      proxy-rewrite:
        regex_uri: ["/v1(.*)", "$1"]
  - uri: /prices*
    upstream_id: 2
    plugins:
      referer-restriction:
        whitelist:
          - catalog.me

global_rules:
  plugins:
    prometheus:
      prefer_name: true

Spring Cloud Gateway supports all configuration options of regular Spring projects, and they are many. However, "flat" configurations, such as .properties file(s) and environment variables, are error-prone:

spring.cloud.gateway.routes[0].id=products
spring.cloud.gateway.routes[0].uri=http://catalog:8080
spring.cloud.gateway.routes[0].predicates[0]=Path=/v1/products*
spring.cloud.gateway.routes[1].id=pricing
spring.cloud.gateway.routes[1].uri=http://pricing:8080
spring.cloud.gateway.routes[1].predicates[0]=Path=/prices*
spring.cloud.gateway.routes[1].predicates[1]=Header=Referer, http://catalog.me

IMHO, one should stick to a hierarchical configuration, such as YAML - and remember that I'm not too fond of YAML. Here's the same configuration as above:

spring.cloud.gateway.routes:
  - id: products
    uri: http://catalog:8080
    predicates:
      - Path=/v1/products*
    filters:
      - StripPrefix=1
  - id: pricing
    uri: http://pricing:8080
    predicates:
      - Path=/prices*
      - Header=Referer, http://catalog.me

I believe the YAML version leaves less space for errors, especially regarding indices.

Be warned that Spring applications don't reload their configuration by default when the latter changes. While it's possible - and indeed, a couple of options are available - it requires coding. Here's a good tutorial on how to achieve it.

As for Apache APISIX, you can also create update and delete routes dynamically via the /actuator endpoint. However, the API doesn't offer a PATCH method: you need to update the whole route in case of updates.

Features comparison

Apache APISIX implements features with plugins, while Spring Cloud Gateway implements them with filters. While an exhaustive, detailed feature-by-feature comparison is beyond the scope of a single blog post, we can still get a good overview.

FeatureSpring GatewayApache APISIX
Request headers manipulationAddRequestHeader AddRequestHeadersIfNotPresent RemoveRequestHeader SetRequestHeader MapRequestHeader SecureHeaders FallbackHeaders SetRequestHostHeader PreserveHostHeader AddRequestParameter RemoveRequestParameterproxy-rewrite
Path manipulationStripPrefix PrefixPath RewritePath SetPathproxy-rewrite
Response headers manipulationAddResponseHeader DedupeResponseHeader RewriteLocationResponseHeader RemoveResponseHeader RewriteResponseHeader SetResponseHeader SetStatusresponse-rewrite
RedirectionRedirectToredirect
JSON gRPC transcodingJsonToGrpcgrpc-transcode
Body manipulationModifyRequestBody ModifyResponseBody Only available via coderesponse-rewrite
ResiliencyCircuitBreaker Retryapi-breaker
RequestRateLimiterlimit-count limit-conn limit-request
-fault-injection
CachingLocalResponseCacheproxy-cache

Apache APISIX and Spring Cloud Gateway offer more or less the same features set. Regarding common features, the Spring way is much more granular, with a dedicated filter for each operation. In contrast, APISIX provides a single plugin with many configuration options but for rate-limiting.

Some plugins are Spring-specific, e.g., SaveSession - APISIX has no such integration. Conversely, APISIX provides many plugins for authentication with different third-party services, e.g., KeyCloak, OpenId Connect, etc. Spring Cloud Gateway achieves it via the Spring Security dependency, a whole subject.

If a feature is unavailable out of the box, developing a custom plugin in Lua for APISIX, in a JVM language for Spring is possible.

Observability

Observability implementations differ wildly between Spring Cloud Gateway and Apache APISIX.

The first relies on the Actuator, which offers plenty of observability-related features. To use it in any Spring Boot project, just add the dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>3.1.0</version>
</dependency>

To serve metrics for Prometheus consumption, add the following Micrometer dependency:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    <version>1.11.0</version>
</dependency>

On the other hand, Apache APISIX uses the same plugin system for features for observability:

  1. For tracing: zipkin, skywalking, and opentelemetry

  2. For metrics: prometheus, node-status, and datadog

  3. For logging: too many to list exhaustively, but integrates with Kafka, Elasticsearch, Splunk, Google Cloud, ClickHouse, etc.

Both products cover the three pillars of Observability and provide many integrations with third-party backends.

Documentation

IMHO, Spring's documentation is second to none; Spring Cloud Gateway's is no exception. It belongs to the Spring portfolio and offers the same structure: an overview, the reference documentation (exhaustive), a get-started guide, and two sample projects organized by version.

The only reproach I have is that everything is developer-oriented. API Gateways are infrastructure components and should cater to Ops people. For example, configuration snippets should highlight YAML and only show code as a second option.

I won't comment on Apache APISIX's documentation because I know the issues too well. If you have any constructive feedback, however, I'm all ears. Please comment below.

Usability

Usability is quite subjective, but I didn't notice significant differences in my sample demo. Here's the general design, pretending to mimic a microservices architecture.

I've slightly changed the implementation, though, to leverage the gateway. Instead of the catalog calling the pricing/stocks components, I call the gateway to forward the call. Additionally, I want to prevent external callers from accessing pricing and stocks: only the catalog should be allowed.

Many implementations of this requirement are possible. In real-world scenarios, I'd probably use a TLS-based authentication. For this demo, I chose to pass a configurable header.

Prometheus scraps the metrics of the gateway. Apache APISIX offers a dedicated port and thread, so regular routing and observing are decoupled. You can also customize the path to the endpoint. Spring Cloud Gateway uses the same port but a specific path, /actuator, which you can customize. You can also change the port of the whole actuator via the management.server.port property.

The project offers two branches, apisix and spring. To use it, check out one of the two branches.

Launch the project:

docker compose up

Then, test it:

curl localhost:8080/products

Both branches should yield the same result.

I've added a Grafana dashboard. Note that the Spring one doesn't output anything usable, but it's my fault that I could not configure it properly.

Conclusion

Spring Cloud Gateway and Apache APISIX are two (API) Gateways offering more or less the same set of features. However, their approach is radically different.

Spring Cloud Gateway stems from the Spring framework and the Spring Boot platform and essentially focuses on developers already familiar with Spring. If you're in such a scenario, it's easy to get into as one feels right at home, with a slight warning. For performance reasons, Spring Cloud Gateway implements non-blocking I/O with Spring WebFlux, which relies Project Reactor. It will be a challenging ride if you need to code non-trivial logic using code and you're not familiar with Mono and Flux.

On the other side, while using it without prior knowledge of Spring internals is possible, it will be a massive step for non-Spring developers. It will be an even more gigantic step if they are not JVM developers and a complete leap of faith if they aren't developers at all, i.e., Ops.

Apache APISIX is more suited to regular Ops profiles, providing the product in a packaging they're familiar with: Docker images and Helm charts for Kubernetes.

Thanks Iván López for his kind review. He also pointed out to me misconceptions I had about Spring Cloud Gateway. Muchas gracias amigo!

The complete source code for this post can be found on GitHub:

To go further:


Originally published at A Java Geek on June 18th, 2023

22
Subscribe to my newsletter

Read articles from Nicolas Fränkel directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nicolas Fränkel
Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.