Helicon Zoo

What is Helicon Zoo

The core of Helicon Zoo technology is Helicon Zoo Module. It allows running all these engines and applications under IIS server. Basically, Helicon Zoo Module is a load balancing HTTP/FastCGI proxy with built-in process manager and some other features to help hosting applications under IIS. Although Zoo module was initially designed to run automatically and without additional configuration needed, in this document you will find some explanations about Helicon Zoo Module internals and configuration directives you may use to tune it.

Helicon Zoo Module is a fully native IIS 7+ module written in pure C++ using super fast and capacitive technologies, like IOCP. This also means previous versions like IIS 6 and Windows 2000 are not supported. The module provides ability to run various web engines and technologies side by side on a single IIS web site and to utilize whole power of multicore servers by automatically ajusting number of workers on multycore systems to reach best performance on high load applications. It can run FastCGI-compatible web engines as well as HTTP back-ends using TCP/IP or Named Pipes transport protocols.

Why do I need Zoo?

Ruby on Rains and Django are self-contained applications for request processing which cannot be exposed to the Internet as they are not suitable for production environments due to poor scalability and reliability. Another option is using proxy, but this requires 2 web servers—the first is Ruby app and the second is IIS or Nginx which will do proxying. FastCGI is a much simpler and faster interface than HTTP. So what Zoo does may be considered a more elegant way of proxying.

How Zoo works?

Zoo native module is an implementation of FastCgi protocol inside IIS7. FastCgi terminology distinguishes client and server. The server can be represented for example by Ruby on Rails application getting requests via FastCgi protocol and generating responses. Client in our case is Zoo native module which requests Ruby on Rails app via FastCgi protocol and sends data directly to the browser. So, Zoo native module acts as a mid-person between browser and Ruby on Rails app, it gets request from browser, gives them to Ruby on Rails, gets response from the latter and returns it to browser.

FastCgi is always an inter-process cooperation, where there is w3wp process and FastCgi server process (Ruby on Rails app). Advantages of this approach are: the processes may be from very different "worlds"; no matter if they are 32- or 64-bit ones; they may be on PHP, Ruby, Perl, Python or whatever; high reliability 'cause FastCgi server breakdown doesn't lead to web-server death. Drawbacks include the expenses on inter-process operations.

Here are 3 possible modes in which Zoo can operate:

1. TCP
Standard says nothing on how FastCgi server process should be created, so you can run it by yourself (the process may even work on a remote PC). In the simplest case FastCgi server opens and listens to TCP port, e.g. 127.0.0.1:8080, FastCgi client connects to it, and request-response is done via TCP socket. If the server supports multiple threads, several clients can connect to this port simultaneously and all requests will be processed in parallel. Zoo native module can start  FastCgi server, select free port and monitor the lifetime of child process. (see fullPath, arguments).

2. NamedPipes
FastCgi protocol can also work with NamedPipes (most likely for productivity). As there's no standard way to say the name of this NamedPipe to server and client the following scenario is used: native module creates NamedPipe, creates FastCgi server process and sends the handle of this NamedPipe as STDIN for this newly-born process. As NamedPipes don't allow multiple threads to read/write simultaneously, implementation of FastCgi server for NamedPipe is usually single-threaded. For better performance Zoo native module bears several worker processes (see maxInstances)

3. Socket
This one is a mixture of NamedPipes and genuine TCP. Zoo native module opens TCP socket and sends it to its child process via STDIN. Mojolicious works this way. This mode also lacks full-scale multitasking as the socket is sent to the child process being already opened.


Helicon Zoo insight

For advanced users only!

Normally, everything explained below is done automatically by the installer, so the following explanation is for those who want to know the internals and to customize the stuff.

Configuration sections registration

The sections themselves are defined in C:\Windows\System32\inetsrv\config\schema\heliconZoo_schema.xml as follows:

<configSections>
<sectionGroup name="system.webServer">

  <!-- This section may be used by users to put their custom settings into web.config. -->
  <section name="heliconZoo" overrideModeDefault="Allow" allowDefinition="Everywhere" />

  <!-- And this one is for mature admins. We have all languages and workers defined here. -->
  <section name="heliconZooServer" overrideModeDefault="Allow" allowDefinition="Everywhere" />

  </sectionGroup name="system.webServer">
</configSections>

Registration of the Zoo module inside IIS

Here's what needs to be put into applicationHost.config to register the module for FAST_CGI protocol. As it's a native module both x86 and x64 versions are registered.

<system.webServer>
  <globalModules>
    <add name="HeliconZoo_x64" image="%windir%\System32\inetsrv\HeliconZoo_x64.dll" preCondition="bitness64"  />
    <add name="HeliconZoo_x86" image="%windir%\System32\inetsrv\HeliconZoo_x86.dll" preCondition="bitness32"  />
  </globalModules>
</system.webServer>

Having that done we enable Zoo native module in applicationHost.config:

<location path="" overrideMode="Allow">
  <system.webServer>
  <modules>
  ...
  <add name="HeliconZoo_x64" preCondition="bitness64" />
  <add name="HeliconZoo_x86" preCondition="bitness32" />
  ...
  </modules>
  ...
</location >

and register all known languages/workers:

<system.webServer>
  ...

  <heliconZooServer>
    <engine name="python.2.7.pipe" fullPath="c:\python27\python.exe"
          arguments="-O %SystemDrive%\Zoo\Workers\python\zoofcgi.py" />
    <engine name="python.2.7.tcp" fullPath="c:\python27\python.exe"
          arguments="-O c:\python27\Scripts\django-admin.py runfcgi method=threaded host=%HOST% port=%PORT%"
          transport="tcp" />
    <engine name="ruby.1.9.pipe" fullPath="C:\Ruby19\bin\rubyw.exe"
          arguments="%SystemDrive%\Zoo\Workers\ruby\zack.rb" />
    <engine name="ruby.1.9.tcp" fullPath="C:\Ruby19\bin\rubyw.exe"
          arguments="%SystemDrive%\Zoo\Workers\ruby\zack.rb --host=%HOST% --port=%PORT%" transport="tcp" />
    <engine name="php.5.2.pipe" fullPath="%ProgramFiles%\PHP\v5.2\php-cgi.exe" />
    <engine name="php.5.3.pipe" fullPath="%ProgramFiles%\PHP\v5.3\php-cgi.exe" />
    <engine name="perl.5.12.pipe" fullPath="C:\strawberry\perl\bin\perl.exe"
          arguments="%SystemDrive%\Zoo\Workers\perl\llama.pl"  />
    <engine name="mojo.1.34.socket" fullPath="C:\strawberry\perl\bin\perl.exe"
          arguments="%APP_WORKER% fastcgi" transport="socket" />
  </heliconZooServer>
  ...

</system.webServer>

A small explanation of the above:

name="python.2.7.pipe" - the name to have a reference on in web.config
fullPath="c:\python27\python.exe"
arguments="-O C:\Windows\System32\inetsrv\Workers\python\zoofcgi.py"
maxInstances="0" - number of worker processes (streams for TCP) to handle requests. If set to 0, the max number of worker processes equals the number of processor cores
transport="tcp|namedpipe|socket"
host=localhost|%HOST% -%HOST%= 127.0.0.1
port=8888|%PORT% where %PORT% is a random vacant port.

These special variables can be used in command prompt and environment variables:

%APPL_PHYSICAL_PATH% - physical path to the app C:\inetpub\wwwroot\TestApp\
%APPL_VIRTUAL_PATH% - virtual path to the app /TestApp
%HOST% - host for TCP connection
%PORT% - port for TCP connection

And here's what users may want/need to change manually in web.config:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <heliconZoo>
      <application name="django.project.x64" >
        <environmentVariables>
          <add name="PYTHONPATH" value="%APPL_PHYSICAL_PATH%;%PYTHONPATH%" />
          <add name="DJANGO_SETTINGS_MODULE" value="settings" />
          <add name="django.root" value="%APPL_VIRTUAL_PATH%" />
        </environmentVariables>
      </application>
    </heliconZoo>
    <handlers>
      <add
          name="django.project.x64"
          scriptProcessor="python.2.7.pipe"
          path="*" verb="*"
          modules="HeliconZoo_x64" preCondition="bitness64"
          resourceType="Unspecified" requireAccess="Script" />
    </handlers>
  </system.webServer>
</configuration>

name – uniquie name, should be equal to application name parameter.
scriptProcessor – the registered worker which will handle the request.
<application> section defines environment variables to be transferred for the current application. This happens only once upon child process creation.

Deployment

It often happens that during application deployment on the hosting/production one needs to run some additional script which will create or synchronize a database, install some modules or do something else.

As Windows hostings are reluctant to give terminal access to their clients, we suggest the following scenario:

  1. the user creates a script to be executed on production, e.g. deploy.py
  2. upload it via FTP or with the help of WebDeploy
  3. make request to the production server, http://mysite.com/DEPLOY
  4. HeliconZoo processes this request in a particular way and executes deploy.py on the server
  5. deploy.py is automatically removed at the end

 

There are 2 specific environment variables that help deploy applications on a production server:
DEPLOY_URL
DEPLOY_FILE

Both variables point to URL and file relative to this specific application.

If these variables do not exist or there's no such file, the URL is processed as usual URL.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <heliconZoo>
      <application name="django.project.x64" >
        <environmentVariables>
        <add name="PYTHONPATH" value="%APPL_PHYSICAL_PATH%;%PYTHONPATH%" />
        <add name="DJANGO_SETTINGS_MODULE" value="settings" />
        <add name="django.root" value="%APPL_VIRTUAL_PATH%" />
        <add name="DEPLOY_URL" value="DEPLOY" />
        <add name="DEPLOY_FILE" value="deploy.py" />
      </environmentVariables>
    </application>
  </heliconZoo>
  ...
  </system.webServer>
</configuration>