Bjoern is 4x faster than Nginx - how to tweak Nginx to handle Bjoern proxy_requests without performance loss?

7,745

I think that the answer is given in the article below.

https://news.ycombinator.com/item?id=2036661

For example, let's consider this thought experiment: Someone here mentioned Mongrel2 getting 4000 req/sec. Let's replace the name "Mongrel2" with "Server A" because this thought experiment is not limited to Mongrel2, but all servers. I assume he's benchmarking a hello world app on his laptop. Suppose that a hypothetical Server B gets "only" 2000 req/sec. One might now (mistakenly) conclude that:

  • Server B is a lot slower.

  • One should use Server A instead of Server B in high-traffic production environments.

Now put Server A behind HAProxy. HAproxy is known as a high-performance HTTP proxy server with minimal overhead. Benchmark this setup, and watch req/sec drop to about 2000-3000 (when benchmarked on a typical dual core laptop).

What just happened? Server B appears to be very slow. But the reality is that both Server A and Server B are so fast that doing even a minimum amount of extra work will have a significant effect on the req/sec number. In this case, the overhead of an extra context switch and a read()/write() call to the kernel is already enough to make the req/sec number drop by half. Any reasonably complex web app logic will make the number drop so much that the performance difference between the different servers become negligible.

Share:
7,745

Related videos on Youtube

user965091
Author by

user965091

I'm done with SO, I may occasionally come here to quickly look for questions though. I can't really bear how some users behave. I already have to bear some people where I work at because they think they are superior to me.

Updated on September 18, 2022

Comments

  • user965091
    user965091 over 1 year

    I was trying to put Bjoern behind Nginx for easy load-balancing and DoS/DDoS attack mitigation.

    To my dismay I not only discovered that it drops connections like chips (it varies between 20% and 50% of total connections), but it seems actually faster when not put behind it.

    This was tested on a machine with 6GB RAM and Dual-Core 2Ghz cpu.

    My app is this:

    import bjoern,redis
    
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    
    val = r.get('test:7')
    
    def hello_world(environ, start_response):
        status = '200 OK'
        res = val
        response_headers = [
            ('Content-type','text/plain'),
            ('Content-Length',str(len(res)))]
        start_response(status, response_headers)
        return [res]
    
    # despite the name this is not a hello world as you can see
    bjoern.run(hello_world, 'unix:/tmp/bjoern.sock')
    

    Nginx configuration:

    user www-data;
    worker_processes 2;
    worker_rlimit_nofile 52000; # worker_connections * 2
    pid /run/nginx.pid;
    
    events {
        multi_accept on;
        worker_connections 18000;
        use epoll;
    }
    
    http {
    
        charset                         utf-8;
        client_body_timeout             65;
        client_header_timeout           65;
        client_max_body_size            10m;
        default_type                    application/octet-stream;
        keepalive_timeout               20;
        reset_timedout_connection       on;
        send_timeout                    65;
        server_tokens                   off;
        sendfile                        on;
        server_names_hash_bucket_size   64;
        tcp_nodelay                     off;
        tcp_nopush                      on;
    
    
        error_log /var/log/nginx/error.log;
    
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
    }
    

    and virtual host:

    upstream backend {
    server unix:/tmp/bjoern.sock;
    }
    
    server {
        listen 80;
    
        server_name _;
    
        error_log  /var/log/nginx/error.log;
    
    
    
        location / {
            proxy_buffering     off;
            proxy_redirect      off;
            proxy_pass         http://backend;
        }
    }
    

    The benchmark of Bjoern put behind Nginx through unix socket I get is this:

    Benchmarking 127.0.0.1 (be patient)
    Completed 1000 requests
    Completed 2000 requests
    Completed 3000 requests
    Completed 4000 requests
    Completed 5000 requests
    Completed 6000 requests
    Completed 7000 requests
    Completed 8000 requests
    Completed 9000 requests
    Completed 10000 requests
    Finished 10000 requests
    
    
    Server Software:        nginx
    Server Hostname:        127.0.0.1
    Server Port:            80
    
    Document Path:          /
    Document Length:        148 bytes
    
    Concurrency Level:      1000
    Time taken for tests:   0.983 seconds
    Complete requests:      10000
    Failed requests:        3
       (Connect: 0, Receive: 0, Length: 3, Exceptions: 0)
    Non-2xx responses:      3
    Total transferred:      3000078 bytes
    HTML transferred:       1480054 bytes
    Requests per second:    10170.24 [#/sec] (mean)
    Time per request:       98.326 [ms] (mean)
    Time per request:       0.098 [ms] (mean, across all concurrent requests)
    Transfer rate:          2979.64 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0   15   4.8     15      35
    Processing:    11   28  19.2     19     223
    Waiting:        7   24  20.4     16     218
    Total:         16   43  20.0     35     225
    
    Percentage of the requests served within a certain time (ms)
      50%     35
      66%     38
      75%     40
      80%     40
      90%     79
      95%     97
      98%    109
      99%    115
     100%    225 (longest request)
    

    10k requests per second, lesser failed requests this time but still..

    When Bjoern is hit directly benchmarks results are the following:

    After changing bjoern.run(hello_world, 'unix:/tmp/bjoern.sock') to bjoern.run(hello_world, "127.0.0.1", 8000)

    Benchmarking 127.0.0.1 (be patient)
    Completed 1000 requests
    Completed 2000 requests
    Completed 3000 requests
    Completed 4000 requests
    Completed 5000 requests
    Completed 6000 requests
    Completed 7000 requests
    Completed 8000 requests
    Completed 9000 requests
    Completed 10000 requests
    Finished 10000 requests
    
    
    Server Software:        
    Server Hostname:        127.0.0.1
    Server Port:            8000
    
    Document Path:          /
    Document Length:        148 bytes
    
    Concurrency Level:      100
    Time taken for tests:   0.193 seconds
    Complete requests:      10000
    Failed requests:        0
    Keep-Alive requests:    10000
    Total transferred:      2380000 bytes
    HTML transferred:       1480000 bytes
    Requests per second:    51904.64 [#/sec] (mean)
    Time per request:       1.927 [ms] (mean)
    Time per request:       0.019 [ms] (mean, across all concurrent requests)
    Transfer rate:          12063.77 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.3      0       4
    Processing:     1    2   0.4      2       5
    Waiting:        0    2   0.4      2       5
    Total:          1    2   0.5      2       5
    
    Percentage of the requests served within a certain time (ms)
      50%      2
      66%      2
      75%      2
      80%      2
      90%      2
      95%      3
      98%      4
      99%      4
     100%      5 (longest request)
    

    50k requests per second and not even a failed request in this case.

    I have extensively tweaked system variables such as somaxconn etc, if not I think I wouldn't get that many requests with Bjoern alone anyway.

    How is it possible that Bjoern is so massively faster than Nginx?

    I'm really concerned about not being able to use Nginx and benefit from the things outlined in the first line and hope you can help me find where the culprit is.

    The short and concise question is: How to proxy_pass Bjoern to Nginx without losing in terms of performance? Should I just stay with Bjoern and achieve load-balancing and DoS/DDoS attack mitigation another way?

    • Michael Hampton
      Michael Hampton almost 10 years
      These tests are not comparable; they are testing two completely different things. One is simply serving a static piece of text. The other is actually doing work.
    • user965091
      user965091 almost 10 years
      @MichaelHampton Can you pinpoint precisely where? They are both executing the same script. Shouldn't Bjoern be slower then as it's the one "doing the work" while Nginx "proxying" it?
    • Michael Hampton
      Michael Hampton almost 10 years
      Bjoern isn't doing anything more than serving "hello world". If you have nginx do the same thing, then it will be faster too.
    • user965091
      user965091 almost 10 years
      @MichaelHampton And what is Nginx doing then, sitting idle?
    • Michael Hampton
      Michael Hampton almost 10 years
      You are having nginx proxy all its connections! You already knew this, why did you ask?
    • user965091
      user965091 almost 10 years
      @MichaelHampton My thought was that something like this was happening: client -> nginx -> bjoern -> nginx -> client. So the conclusion is not to put Bjoern behind Nginx as it will degrade performance and just decide between Bjoern and Nginx?
    • Michael Hampton
      Michael Hampton almost 10 years
      Effort is wasted when you are going in the wrong direction!
    • user965091
      user965091 almost 10 years
      @MichaelHampton Thank you for the tip! I'm already in the right path after the answer which clarified the issue.