Introduction - grey-kristy/nimble GitHub Wiki

= Nimble Service Library v0.4 =

= Functionality and goals =

Goal: fast and agile python-based wsgi backend server deployment and creation

Current state:

nimble.server package:

  • decorator-based shared over the net methods creation

  • exception catching and restoring over the net

  • secret-based scheme for authorizing trusted clients

  • some common authorization helpers

  • FastCGI input-output processing based on Flup

  • Http proxy input-output processing based on GEvent

  • daemonizing and server process management

  • simultanious multiprotocol work based on PATH_INFO header

  • multifrontend support: flup and gevent are currently supported

nimble.protocols package:

  • two protocols out-of-the-box: specific and fast 'simple', general-purpose 'json'

nimble.client package:

  • simple RPC functionality (service module required for method binding)

nimble javascript library:

  • helper functions to operate nimble requests/responses in javascript with both protocols support

= Requirements =

  • python 2.5/2.6 (2.7 untested)

  • simplejson package for json protocol

  • [http://trac.saddi.com/flup Flup] - fast-cgi python daemon library

= Nimble server =

from nimble.server.base import Server
from nimble.server.tools import shared

class MyServer(Server):
    @shared
    def return_args(self, connection, arg1, *listArg):
        return connection.OK([arg1, listArg])

if __name__=="__main__":
    MyServer.application #standart wsgi application
    MyServer.run #start with no daemon support over nimble-defined frontend (flup, etc)
    MyServer.run_daemon #as run but in daemon mode, uses console arguments
    

= Nimble protocols =

==Simple==

request:

command<space>command_params
command is: text with no spaces
command_params are:
param1<space>param2<space>...
paramN is: text with no spaces

so, currently if you're not encoding-decoding your data structures (using standart 'pickle' module for example) your shared server method signature will be looking like that:

class MyServer(Server):
   @shared
   def method(self, connection, [arg1...argN], [*listArg]):
       pass

where arg1...argN have primitive type (strings, numbers and so on) and listArg can be a list as a varargs.

response:

result_code<space>results
result_code is: OK or ERROR
results are:
result11<space>result12...\nresult21<space>result22...
resultN is: text with no spaces and no '\n'

'simple' protocol client is designed for fast processing, required safe (quoted) string data items

==Json==

request:

{command:<shared method name or alias>, args:<arguments for the method>}

response:

{is_error:<boolean error status>, results:<any simplejson deserializable data structure>}

= Examples =

Example source (geoipservice.py):

 #!/usr/bin/env python2.6

 from nimble.server.base import Server
 from nimble.server.tools import shared, shared_as
 from nimble.client.base import ServerClient
 import GeoIP

 class GeoIPServer(Server):
     def __init__(self, *args, **kwargs):
         Server.__init__(self, *args, **kwargs)

         self.db = GeoIP.open("./GeoLiteCity.dat", GeoIP.GEOIP_STANDARD)

     @shared_as('LC')
     def get_location_coordinates(self, connection, ip):
         try:
             rec = self.db.record_by_addr(ip)
             return connection.OK([rec['latitude'], rec['longitude']])
         except:
             return connection.ERROR('SOME_ERROR')

     @shared
     def get_multiple_location_coordinates(self, connection, *ips):
         recs = [self.db.record_by_addr(ip) for ip in ips]
         return connection.OK([(rec['latitude'], rec['longitude']) for rec in recs])

class GeoIPServerClient(ServerClient):
   SERVER_CLASS=GeoIPServer

if __name__=='__main__':
    GeoIPServer.run_daemon(port=1111)

Example cmdline:

$ ./geoipservice.py start/restart/stop/debug [secret=value] [port=value] [optionX=value]

Traffic example (over 'simple' protocol):

In:

LC 12.23.12.12

Out:

OK 42.2345 21.2322

Example usage in http client (web browser, javascript by the prototype library or like that) (over 'simple' protocol):

new Ajax.Request('http://192.168.1.100/geoip/', 
                 { method: 'post',
                   postBody: 'LC 12.23.12.12',
                   onComplete: function(r) {alert(r.responseText);} 
                 }
);

Example usage of nimble.js + jquery

conn = new NimbleConnection('/geoip/');
var response_func = function(text) {
    try {
        server_result = conn.parse_response(text);
    } catch(error) {
        alert(error);
    }
};
$.ajax({type: "POST",
        url: conn.get_url(),
        data: conn.make_request('LC',['12.23.12.12']),
        dataType: 'text'
}).done(response_func);

or

var conn = new NimbleConnection('/geoip/');
var onsuccess = function(result) {
    alert(result);
};
var onerror = function(error) {
    alert(error);
};
conn.request_by_jquery('LC',['12.23.12.12'], onsuccess, onerror);

Example python client usage:

from geoipservice import GeoIPServerClient as Client
import nimble.protocols.json as json
client = Client('http://192.168.1.100/geoip/') #over default nimble protocol
lat, long = client.get_location_coordinates('1.1.1.1') # convert to float from string manually
client = Client('http://192.168.1.100/geoip/', default_protocol=json) #over your protocol
lat, long = client.get_location_coordinates('1.1.1.1')

Example nginx+flup setup:

server {
    listen 80;
    server_name 192.168.1.100;

    location /geoip {
	fastcgi_pass   127.0.0.1:1111;

	fastcgi_param  SCRIPT_FILENAME  /home/projects/geoip/$fastcgi_script_name;
	fastcgi_param  QUERY_STRING     $query_string;
	fastcgi_param  REQUEST_METHOD   $request_method;
	fastcgi_param  CONTENT_TYPE     $content_type;
	fastcgi_param  CONTENT_LENGTH   $content_length;
	fastcgi_param  SERVER_PORT      $server_port;
	fastcgi_param  SERVER_NAME      $server_name;
	fastcgi_param  SERVER_PROTOCOL  $server_protocol;
	fastcgi_param  REMOTE_ADDR	$remote_addr;
    }
}

Example nginx+gevent setup:

server {
    listen 80;
    server_name 192.168.1.100;

    location /geoip {
	proxy_pass   http://127.0.0.1:1111;
        proxy_set_header x-real-ip $remote_addr;
    }
}
⚠️ **GitHub.com Fallback** ⚠️