NGINX.COM
Web Server Load Balancing with NGINX Plus

The API management (APIM) market is a crowded space. The latest Gartner Magic Quadrant for Full Life Cycle API Management ranks 22 vendors, with 7 in the Leaders quadrant. Competing in an existing market requires establishing a strong position to differentiate and stand out from the competition. So what makes NGINX’s API management solution different?

At NGINX our goal is to make lightweight, high‑performance software. NGINX Open Source has become the go‑to web server for the world’s busiest sites because its event‑driven architecture scales better than web servers that spawn a process or thread for each connection. NGINX Open Source is also the industry’s most ubiquitous API gateway, an infrastructure component that processes API traffic in APIM solutions like those from Apigee, Axway, IBM DataPower, Kong, Red Hat 3scale, and Torry Harris.

The NGINX Controller API Management Module is a high‑performance APIM solution. In this blog we’ll compare its performance to Kong, a competitor known for its high performance. Kong is built on NGINX, and uses Lua to implement its API functionality, whereas the API Management Module relies completely on native, high‑performance capabilities that are implemented as NGINX Plus modules. NGINX’s API management solution is built on an innovative architecture that decouples the data plane and control plane. All API calls are handled directly by NGINX Plus acting as the API gateway (data plane) without requiring any interaction with the control plane. This results in high‑performance API traffic mediation for both north‑south and east‑west traffic.

A sneak peak at the results: the NGINX Controller API Management Module outperforms Kong by 2x.

A special thank you to Intel for providing the hardware and lab space used to do this testing. Full test and hardware details can be found in the Appendix.

Latency Added to a Single Request

In this test we compared the additional latency introduced by the API manager to a single request, for files of 0 KB and 1 KB in size. We used curl to send the single HTTP request (details).

Kong increases latency compared to the API Management Module: by 44% for a 0-KB file and 24% for a 1-KB file.

API Calls Per Second

A standard HTTP scalability metric is requests per second (RPS). The equivalent metric in the context of APIM is API calls per second. We used wrk to send a continuous stream of requests for either a 0-KB or 1-KB file, on 300 connections for 3 minutes (details).

The API Management Module outperformed Kong – handling 2.6x as many API calls per second for 1-KB responses.

CPU Usage

The API Management Module introduces less latency and handles more API calls per second than Kong because it uses the CPU more efficiently. We measured CPU usage at increasing numbers of API calls per second. We used a single core, so the absolute number of API calls per second is much lower than in the previous test, where we used 22 cores (details).

Kong effectively caps out at 5,000 API calls per second – latency spikes noticeably at call volumes any higher than that, indicating the system is fully loaded. At 5,000 calls per second, CPU usage is 93%, 73% higher than the API Management Module.

API Calls Per Second with JWTs

The final test measures how well each API gateway validates JSON Web Tokens (JWTs), the preferred method for authenticating API requests. Your most important API endpoints will likely be authenticated, so this test more closely approximates a real‑world configuration.

We used the same JWT, signed using HS256, with both gateways (details).

The API Management Module handles more than 2x as many JWT‑authenticated API calls per second as Kong.

How NGINX Controller Works

NGINX Controller is a control‑plane solution that manages the NGINX Plus data plane. NGINX Controller enables you to manage the entire lifecycle of NGINX Plus, as a load balancer, API gateway, or a proxy in a service mesh environment. With NGINX Controller’s API Management Module, you can define, publish, secure, monitor, and analyze APIs.

Under the hood, NGINX Controller generates NGINX Plus configuration which is published to the underlying NGINX Plus data plane. The core configuration loader has a very efficient mechanism for storing configurations in memory, enabling the API Management Module to deliver high performance for APIs.

Conclusion

Many companies already use NGINX Open Source as their API gateway. Capital One has been able to scale to over 12 billion API calls per day using NGINX Open Source. In fact, many of our competitors, including Kong, use NGINX Open Source under the hood in their APIM solutions. Our lightweight design – based on native, high‑performance NGINX configuration along with NGINX Plus modules – enables the API Management Module to scale better than NGINX‑derivative competition.

Latency is one of the most important metrics for end‑user experience. High latency reduces app responsiveness, frustrating users. The API Management Module adds 20–30% less latency to user requests compared to Kong. It also uses system resources more efficiently, using 40% less CPU than Kong for the same workload.


Appendix

Topology

All tests were done using three separate machines connected by 10 GbE links in a simple, flat Layer 2 network.

Hardware Used

The following hardware was used for the testing. All three machines were identical. Hyperthreading was not used. In previous testing we did not observe a major difference in performance from hyperthreading.

CPU Network Memory
Intel® Xeon(R) CPU E5‑2699 v4 @ 2.20GHz, 22 cores Intel Ethernet Controller 10‑Gigabit X540‑AT2 128 GB

Software Used

The following software was used to do the testing:

  • NGINX Controller API Management Module version 2.
  • Kong Open Source version 1.0.0. We did not test Kong Enterprise, but none of its additional features are relevant to our tests (for example, Kong Open Source and Kong Enterprise use the same JWT plug‑in).
  • curl version 7.61.0.
  • Docker version 18.09.1. To make management easier, we ran both Kong and the API Management Module inside Docker containers. We found that Docker reduces performance by about 30%, but because we’re comparing relative performance, the benefits of using Docker outweighed the performance hit.
  • mpstat in version 12.0.1 of the systat package.
  • wrk version 4.1.0, installed according to these instructions.

NGINX Configuration

The following NGINX configuration was used for the testing.

upstream my_upstream {
    keepalive 60;
    server API-server:80;
    keepalive_requests 3000000;
    keepalive_timeout 300;
}

server {
    listen 8000;

    access_log off;
    keepalive_requests 3000000;
    keepalive_timeout 300;
 
    tcp_nodelay on;

    location /test {
        set $apimgmt_environment 4;
        set $apimgmt_definition 3;
        set $upstream my_upstream;
        set $upstream_protocol http;
        rewrite ^ /_devel_4 last;
    }
    location = /_devel_4 {
        internal;
        set $apimgmt_definition_name my_api;
        set $apimgmt_environment_name devel;
        proxy_intercept_errors on;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass $upstream_protocol://$upstream/0kb;      # 0-KB file
        #proxy_pass $upstream_protocol://$upstream/1kb.bin; # 1-KB file
    }
}

We made the following settings to maximize performance. As detailed in the next section, we made similar settings in the Kong configuration.

  • keepalive_requests and keepalive_timeout were set to a high number to minimize overhead from setting up TCP connections.
  • tcp_nodelay was enabled, improving performance slightly by disabling Nagle’s algorithm.
  • access_log was disabled. Enabling it reduces performance by about 10%.
  • We used the two proxy_pass directives to request the appropriate file size.

Kong Configuration

We added the following configuration directives to kong.conf.default, matching the settings in the NGINX configuration discussed in the previous section.

nginx_http_tcp_nodelay=on
nginx_http_keepalive_requests=3000000
nginx_http_keepalive_timeout=300
proxy_access_log=off

We ran the following Kong API calls to create a route to the API server. The first command creates a service named test and the second creates the route /test pointing to the server.

$ curl -X POST http://localhost:8001/services/ \
  --data 'name=test' \
  --data 'url=http://API-server:80/0kb'

$ curl -X POST http://localhost:8001/services/test/routes \
  --data 'paths[]=/test'

The following commands display the configuration of the service and the route, respectively. We pipe the output to the jq tool to make it easier to read the JSON output.

$ curl localhost:8001/services/ | jq '.'
{
  "next": null,
  "data": [
    {
      "host": "172.20.40.32",
      "created_at": 1556770191,
      "connect_timeout": 60000,
      "id": "f4629d56-550b-4b37-aa59-66d931aa6f37",
      "protocol": "http",
      "name": "test",
      "read_timeout": 60000,
      "port": 80,
      "path": "/0kb",
      "updated_at": 1556770191,
      "retries": 5,
      "write_timeout": 60000
    }
  ]
}

$ curl localhost:8001/services/test/routes | jq '.'
{
  "next": null,
  "data": [
    {
      "created_at": 1556770191,
      "methods": null,
      "id": "a7b417af-ccd4-48f7-b787-ae19490194dc",
      "service": {
        "id": "f4629d56-550b-4b37-aa59-66d931aa6f37"
      },
      "name": null,
      "hosts": null,
      "updated_at": 1556770191,
      "preserve_host": false,
      "regex_priority": 0,
      "paths": [
        "/test"
      ],
      "sources": null,
      "destinations": null,
      "snis": null,
      "protocols": [
        "http",
        "https"
      ],
      "strip_path": true
    }
  ]
}

Testing Methodology

Latency Added to a Single Request

We used curl for the single‑request latency testing. To set a baseline, we first made a request to the API server without an API gateway in front of it. We then made the same request with each API gateway in front of the API server to measure the latency they introduced.

$ curl -w "@curl-latency.txt" -o /dev/null -s http://target-server

The contents of curl-latency.txt:

    time_namelookup:  %{time_namelookup}\n
       time_connect:  %{time_connect}\n
    time_appconnect:  %{time_appconnect}\n
   time_pretransfer:  %{time_pretransfer}\n
      time_redirect:  %{time_redirect}\n
 time_starttransfer:  %{time_starttransfer}\n
                    ----------\n
         time_total:  %{time_total}\n

The chart in Latency Added to a Single Request shows time_total, in milliseconds. Here’s a sample output:

$ curl -w "@curl-latency.txt" -o /dev/null -s http://192.0.2.1/api-endpoint
    time_namelookup:  0.000035
       time_connect:  0.000364
    time_appconnect:  0.000000
   time_pretransfer:  0.000401
      time_redirect:  0.000000
 time_starttransfer:  0.001701
                    ----------
         time_total:  0.001727

API Calls Per Second

We tested API calls per second with wrk, a scalable benchmarking tool we use often. We tried various combinations of parameters, and this one maximized performance for both the API Management Module and Kong:

$ wrk -t 22 -c 300 -d 180 http://target-server

The command creates 22 wrk threads (1 per core) and a total of 300 connections across the threads. The -d parameter specifies the duration of the test, in our case 180 seconds (3 minutes). Sample output:

$ wrk -t 22 -c 300 -d 180 http://192.0.2.1/api-endpoint
Running 3m test @ http://192.0.2.1/api-endpoint
  22 threads and 300 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    13.96ms    7.84ms 279.85ms   77.37%
    Req/Sec     0.96k   298.23     1.88k    68.55%
  3769861 requests in 3.00m, 36.25GB read
Requests/sec:  20934.34
Transfer/sec:    206.16MB

CPU Usage

We tested CPU usage with mpstat (a standard Linux tool for that purpose) while running the wrk command for API calls per second. Sample output (spread across two lines for legibility):

$ mpstat
Linux 4.18.0-13-generic (nbdw38) 	04/29/2019 	_x86_64_	(88 CPU)

03:34:50 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  ...
03:34:50 PM  all    0.04    0.00    0.02    0.00    0.00    0.03    0.00  ...

      ... %guest  %gnice   %idle
      ...   0.00    0.00   99.91

API Calls Per Second with JWTs

We tested JWT performance with the wrk command for API calls per second, with the addition of the -H parameter to insert the JWT in the Authorization: Bearer HTTP header. We generated the JWT and JSON Web Keys (JWKs) using the instructions in this blog post, storing the JWT in a file named test.jwt.

$ wrk -t 22 -c 300 -d 180 -H "Authorization: Bearer `cat test.jwt`" \
  http://target-server
Hero image
eBook: Continuous API Management, 2nd Edition

Download this O’Reilly ebook to learn how maximize the value of APIs by managing them as products through a continuous lifecycle.



About The Author

Faisal Memon

Software Engineer

About F5 NGINX

F5, Inc. is the company behind NGINX, the popular open source project. We offer a suite of technologies for developing and delivering modern applications. Together with F5, our combined solution bridges the gap between NetOps and DevOps, with multi-cloud application services that span from code to customer.

Learn more at nginx.com or join the conversation by following @nginx on Twitter.