TQ
dev.com

Blog about software development

Subscribe to RSS

High performance C# web service using EvHttpSharp

04 Apr 2016 - by 'Maurits van der Schee'

In a previous post I announced a attempts at high performance web server implementations in some popular languages (Java, Go, C# and JavaScript). Today I will show you a C# implementation and give you instructions on how to get it running on your own machine.

Source code for HttpListener example

Below you find a nice and short web server written in C# that I found online.

using System; 
using System.IO; 
using System.Net; 
using System.Text; 
using System.Threading; 

class WebServer { 
    HttpListener _listener; 

    public WebServer(string address) { 
        _listener = new HttpListener(); 
        _listener.Prefixes.Add(address); 
    } 

    public void Start() {                        
        _listener.Start(); 
        while (true) {
            HttpListenerContext request = _listener.GetContext(); 
            ThreadPool.QueueUserWorkItem(ProcessRequest, request); 
        }
    } 

    void ProcessRequest(object listenerContext) { 
        var context = (HttpListenerContext)listenerContext; 
        context.Response.StatusCode = (int)HttpStatusCode.OK; 
        context.Response.AddHeader("Content-Type","text/html; charset=utf-8");
        var msg = Encoding.UTF8.GetBytes("<h1>Hello World</h1>"); 
        context.Response.ContentLength64 = msg.Length; 
        context.Response.OutputStream.Write(msg, 0, msg.Length);
        context.Response.OutputStream.Close();
    } 

    static void Main(string[] args) {
        (new WebServer("http://localhost:8000/")).Start();
    }
} 

Save this file as "hello.cs" in your project folder.

Running the HttpListener example

Install and run on a Debian based Linux using:

sudo apt-get install mono
mono-csc hello.cs
./hello.exe

The first line installs mono. The second command compiles and the last command should start the server.

Benchmarking HttpListener

Like last time I am benchmarking the performance using Apache Bench:

ab -n 200000 -c 100 http://localhost:8000/

I could not get this example to use more than 20% CPU. I tried compiling and running on Windows, but there it also did not use the full CPU.

Source code for EvHttpSharp example

I tried many things without success, until I found the EvHttpSharp project on Github and created the following:

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using EvHttpSharp;

class WebServer {
  EventHttpMultiworkerListener _listener;
  string _host;
  int _port;

  public WebServer(string host,int port, int workers) {
    _listener = new EventHttpMultiworkerListener(RequestHandler, workers);
    _host = host;
    _port = port;
  }

  public void Start() {
    _listener.Start(_host, (ushort) _port);
  }

  private void RequestHandler(EventHttpRequest req)
  {
    ThreadPool.QueueUserWorkItem(_ =>
    {
      var headers = new Dictionary<string,string>(){{ "Content-Type","text/html; charset=utf-8" }};
      var msg = Encoding.UTF8.GetBytes("<h1>Hello World</h1>");
      req.Respond(System.Net.HttpStatusCode.OK, headers, msg);
    });
  }

  static void Main(string[] args) {
    LibLocator.Init(null);
    var w = new WebServer("127.0.0.1",8000,Environment.ProcessorCount);
    w.Start();
  }
}

Save this file as "evhello.cs" in your project folder.

Running the EvHttpSharp example

Install and run on a Debian based Linux using:

sudo apt-get install mono
sudo apt-get install libevent-core-2.0-5 libevent-extra-2.0-5 libevent-pthreads-2.0-5
git clone git@github.com:kekekeks/evhttp-sharp.git
xbuild /p:Configuration=Release evhttp-sharp/EvHttpSharp/EvHttpSharp.csproj
cp evhttp-sharp/EvHttpSharp/bin/Release/EvHttpSharp.dll .
mono-csc -i:EvHttpSharp.dll evhello.cs
./evhello.exe

So the first line installs mono. The second line installs the EvHttpSharp dependencies. Then we clone the EvHttpSharp project from Github. Build the project in "Release" mode. Next we copy the compiled DLL to the project directory. The second-last line compiles the source and includes the EvHttpSharp DLL in the build and the last line simply executes the webserver.

Benchmarking EvHttpSharp

Again I am benchmarking the performance using Apache Bench:

ab -n 200000 -c 100 http://localhost:8000/

Now we do get 100% CPU load and comparable performance to the Java Jetty example. I tried running this on Windows as well, but without success.

Lots of people have asked me how fast this example is. I suggest you test that for yourself, but to give you a rough idea: on my box this performs >20k requests per second with 100% CPU load. Just as good as a single line of PHP stating "echo 'Hello World';" served by "php -S localhost:8000".

Conclusion

There may be something obvious I overlooked that makes the HttpListener example handle more requests per second. If I did, please let me know. Also it must be noted that HttpListener will probably perform good enough for most use cases. On the other hand it seems that it does not allow for an extreme high amount of requests per second. This is clearly caused by not fully using the CPU, which may in itself not be a bad choice for an application web server. But if you do have many lightweight (or cachable) requests (for instance a micro-service) and hit a mysterious request per second boundary, then you may want to consider an EvHttpSharp based solution.