PHP Application server - GSoC proposal

After, not so good feedback after present my idea on the PHP Dev maillist (probably for my “English”) so I’ve decide to write a blog post about it, with the main goal of clarify all those dark sides.

Objectives

  1. Simple principle of work.
  2. Migration painless process, people should be able to copy existence code and put on the worker and it should work.
  3. Easy scalability, just add more workers on need.
  4. Provide a simple and effortless way to deploy application across many servers.

Some concepts

  1. Client: The client has a list of workers, he chooses one randomly (similar to memcached), and query a function or queue a job.
  2. Worker: C or C++ application with PHP embed, which listens a TCP port (probably 25887). The worker can accept request  from anywhere, but only a single IP (from the master process) can submit and deploy
  3. Master process: PHP web page, where the sysadmin can upload PHP code and “deploy” (submit to every worker).

System overview

phpserver - system overview
The graphic above show how the system will look like, the clients (PHP scripts that runs on the webserver) can connect to any worker and request a function or queue a procedure. In the case of a function the client expects a response, otherwise it will not. The master process is the only one allowed to submit PHP code to the workers, with this the system has a really easy way to deploy applications.

Advantages

  1. Single point of source code distributions.
  2. There is not a central point (aka manager process), the client speaks directly to a worker, this keeps it really simple and fault tolerant. In traditional models if the manager crash, nothing works.
  3. I am planning to add a Cache layer on the client, to avoid call same functions with same parameters, if the function is cacheable, in order to reduce the network latency.
  4. It can be used to process large amount of data in parallel, as hadoop does.

Why

The big question, Why should I use this to scale if there are plenty of others well tested method to scale?

Answer this question without fanaticism it’s quite hard, but I will try. Basically my goal it’s provide an easy, painless and efficient way to scale, basically those three words are the differences, in theory you will be able to migrate a site by simple cut-and-paste from your application to the application server (through the master web-admin-page), also the master web-admin-page will be able to generate Proxy functions with the same name as the old name (the function that was cut and pasted on the appserver), so for your application (on the webserver) nothing had changed. Also that proxy function will have cache support, if the function’s return can be cached.

Part of it’s efficient would be the fact that there is not human readable protocol (no json, xml, http), just a method for fast serialization and deserialization, representing in a binary format the data.

Will it be secure, ssl, authentication or something?

The answer is no, part of the simplicity and efficient it’s avoid what is unnecessary since it is supposed that the appserver will run in a secure private network, so why loose CPU cycles checking out security?, of course any function could receive, in example, user and password, and check before execute it, but it’s out of appserver’s scope.

What would be the advantage over similar projects

  1. Speed, since it will be coded in C with threads and PHP embed
  2. Fault tolerant, since no central point, no-worker it’s indispensable.
  3. Add worker on hot, (I still need to figure out how to let know to clients about new workers),.
  4. Cache on client by the default (If a function it’s configured has return a cacheable object).

MVC Hecho en casa - Simple, lindo y efectivo

Este post va dedicado a mi amiga Piojita, que gracias a sus ganas de aprender tengo ánimos e ideas para escribir acá, ojalá puedan ser de utilidad a los que por miedo no preguntan

En estos días está muy de moda todo lo que sea MVC, que no es otra que escribir aplicaciones en tres niveles, Modelo (acceso a los datos), Vista (la presentación al usuario) y el Controlador (Encargada de los eventos, y en muchos casos es el nexo entre el modelo y la vista). Como Rasmus dice en su post The non-framework PHP MVC Framework, que en su época trajo polémica, utilizar frameworks tiene ventajas y desventajas, para mi gusto más desventajas que ventajas.

Primero que nada los frameworks webs convencionales, realizan muchas tareas automáticamente, y abstrae muchos detalles al programador que si bien ahora un montón de tiempo, no es efectiva, ya que asúme varías cosas por defecto. Cuando digo que no es efectiva hablo de que lo que se gana en tiempo, se pierde en el rendimiento de la aplicación, ¿de que nos sirve hacer una aplicación web en 5 minutos si sólo puede tener hasta 5 visitas en simultáneo? ¿No sería acaso mejor tardar 40 minutos y que soporte 30 visitas en simultáneo?, quizá  parezca una  exageración pero se acerca mucho a la realidad.

No estoy diciendo que utilizar frameworks es algo malo, depende de las necesidades del sitio web a ser realizado. <ironic>Si fuésemos radicales cada uno escribiría programas en asembler que corra con en nuestro propio sistema operativo,</ironic>

Mi idea con esta entrada es mostrar que no sólo utilizando frameworks que fuerzan a uno a utilizar uno u otro modelo de programación es posible escribir aplicaciones web en poco tiempo manteniendo la simpleza y mantenibilidad del código como primer meta. En otras palabras escribiré, o al menos esbozaré, un mini-framework MVC en PHP.

Modelo

El modelo es, probablemente, la parte más delicada del sistema ya que es la encargada de interactuar con las bases de datos. Siempre es bueno diseñar una buena API de datos persistentes, para no tener que escribir SQL en todas partes. Aparte de mantener el código ordenado, ayuda a la legibilidad del mismo, ya que sabemos si hay un error en base de datos, sabremos con seguridad de que parte del código hay que parchar.

A mi me gusta usar clases para definir una tabla y relaciones, sería algo así como lo siguiente (no es código funcional, solo un ejemplo).

<?php
class Users {
    function GetAll() {
        return $this->Select();
    }

    function Select($params=array()) {
        $sql = "SELECT * FROM users";
        if (is_array($params) && count($params)) {
            $where=array();
            foreach($values as $col=>$val) {
                $where[] = "$col='".addslashes($val)."'";
            }
            $sql .= " WHERE ".implode(" and ",$where);
        }
        return db_exec($sql);
    }

    function Update($id,$values) {
        if (!is_numeric($id)||!is_array($values)||!count($values))
            return false;
        $id  = addslashes($id);
        $cols = "";
        foreach($values as $col=>$val) {
            $cols .= "$col='".addslashes($val)."',";
        }
        $cols[strlen($cols)-1] = ' '; /* borrar el ultimo , */
        $sql = "UPDATE users SET $cols WHERE id = $id";
        db_exec($exec); /* call some db */
    }
}

?>

Algo un poco más automático es esta parte sería grandioso, es un poco molesto escribir mucho SQL, especialmente para las relaciones y esas cosas. Actualmente estoy trabajando, con un amigo brasilero, en una implementación de ActiveRecord (con API similar al de AppEngine) para PHP 5 (es que quiero hacer algo útil también con PHP5). No voy a dar más detalles hasta que esté terminado, pero doy la primicia, se podrán definir relaciones en las clases. Ejemplo:

<?php
class Author extends XXORM {
    function data() {
        $this->name = DB::String(array("size"=>50,"required"=>true));
        $this->book = DB::Relation("Books",DB::MANY);
    }
}

class Books extends XXORM {
    function data() {
        $this->author = DB::Relation("Author",DB::ONE);
        $this->title = DB::String(array("required"=>true,"size"=>50));
    }
}
?>

El proyecto, creara las estructuras de las tablas (si se ejecuta en modo development), también vendrá con una API para extender utilizando Hooks, especial para el cacheado de consultas y demás. Se podrán crear filtros de validaciones por campos, entre otras caracteristicas, si están interesados pueden seguirme twitter

La vista

Una de las características de PHP es que puede ser mezclada con HTML, pues entonces ¿para que el overhead de utilizar un template engine?

<html>
<head>
    <title><?php echo $title?></title>
</head>
<body>
    <?php foreach($users as $user): ?>
        <?php echo $user?>
    <?php endforeach;?>
</body>
</html>

Algunos consejos, evitar los short tags(<?=, <?), evitar utilizar las llaves ({}) para ello utilizar los : y los end*; asi como el ejemplo.

El controlador

El controlador no es nada mas que una función que la mayoría del tiempo llama a la vista y al modelo, aunque no se limíta solamente a eso (ej. enviar un e-mail, subir un archivo de un a ftp, etc).

<?php

function user_controller($params) {
    if (
            !is_array($params) ||
            !isSet($params[1]) ||
            !is_numeric($params[1])
        )
            return false;
    /* llamamos el modelo */
    $dbu   = new User;
    $users = $dbu->Select(array("id"=>$params[1]));
    /* incluimos la vista */
    /* la variable $users es visible para vista */
    include("vista.php");
}

?>

Ruteo de URL a Controladores

<?php
class Router {
    function AddRoute($reg_expr,$action) {
        if (!is_callable($action) && !is_file($action))
            return false;
        $this->route[$reg_expr] = $action;
    }

    function FindAction($url) {
        $routes = & $this->route;
        foreach((array)$routes as $route) {
            extract($route);
            if (preg_match($reg_expr,$url,$params)) {
                $ret = true;
                if (is_callable($action))
                    $ret = call_user_func($action,$params);
                else
                    include($action);
                return true;
            }
        }
        if (!isset($ret) && !$ret)
            $this->err();
        return false;

    }

    function err() {
        header("HTTP/1.1 404 Not Found");
        include("not-found.php");
    }
}

?>

Poniendo todo en orden

Hasta ahora vimos el MVC, separados, ahora ¿como juntamos todo eso?, pues sería algo así

<?php
/* la clase de ruteo URL -> Controlador*/
include("routes.php");
/* los modelos de datos */
include("model.php");
/* controladores */
include("controllers.php");

/* */
$router = new Router;
/* ruta hacia un controlador */
$router->addRoute('/user\/([0-9]+)$/i',"user_controller");
/* ruta hacia una vista, por no se necesite un */
/* controlador */
$router->addRoute("/","principal.php");
#... Demas reglas

/*
 * buscamos el controlador, basado en el URI,
 * suponiendo que tenemos el URL Rewrite activado
 * un uri com "/user/11" haria match, y se ejecutaria
 * un controlador.
 */
$route->FindAction($_SERVER['REQUEST_URI']);
?>

Como verán, un poco de orden en la forma de programar, utilizando un mini-framework, que en realidad son buenos hábitos a la hora de programar, fácilita nuestro trabajo (y de los que mantendrán nuestro código), a la vez de hacerlo efecto (¿para que derrochar valiosos ciclos de procesador?).

How to unlock the audio device on fedora

If use Fedora (I didn’t get yet this problems on Debian nor FreeBSD), probably you got stuck with the famous “Audio Device  Locked” (or some similar text) when your viewing a flash-video on firefox. It is really annoying restart the computer or Xorg to solve that little problem, so I decide to Googling about its answer, despite I didn’t found anything, then I’ve tried a simple command that works, so I am sharing here a solution.

Execute the follow command as root,

$ kill -9 `lsof -t /dev/snd/*  2> /dev/null`

Warning, the previous command will close all the audio programs (i.e xmms, amarok, etc).

I love UNIX, I can’t figure out live without it.

Concatenando videos

Si son como yo, de los que se pasa bajando videos de sitios (ya que no es un delito), se habran encontrado con los videos que vienen en dos partes (especialmente los conciertos musicales). Bueno, aquí comparto en este post relámpago como hacerlo:

#!/bin/bash
#el archivo de salida
SALIDA="concat.avi"
#  si tienen poco espacio en disco pal-vcd es mejor
ARGS="-target pal-dvd"
# archivo temporal, si o si tiene que ser mpg
TMP="concat.mpg"
> $TMP

for i in "$@"
do
    ffmpeg -i "$i" $ARGS - >> $TMP
done
ffmpeg -i "$TMP" $SALIDA
rm -rf $TMP

Su utilización es sencilla

$ ./concat.sh video-part-1.avi video-part-2.avi video-part3.avi

Los “ayudadores” del calamar

El título les parecerá que este entrada sería una mera perdida de tiempo, que sería un off-topic total o una entrada de la GreenPeace, pero no es así es que quería ser nacionalista y escribir en Español (Squid helpers).

Hoy en día necesité  autenticar un Squid-cache contra el Active Directory de Windows, y como a la primera no me funcionó el helper LDAP que viene por defecto escribí un pequeño script en python que funciona, capaz a alguien le sea útil.

#!/usr/bin/env python  

import ldap, sys
from syslog import *

LDAP_SERVER='ldap://' + sys.argv[1]
try:
    ldap_client = ldap.initialize(LDAP_SERVER)
except:
    syslog(LOG_ERR,"Couldn't connect to the LDAP")
    sys.exit()

while (1):
    try:
        (user,passwd) = sys.stdin.readline().strip().split(" ")
        ldap_client.simple_bind_s(user,passwd)
        syslog(LOG_INFO,"%s login" % user)
        # Login success
        print "OK"
    except:
        syslog(LOG_INFO,"%s login failed" % user)
        # Sorry, an error
        print "ERR"
        sys.stdout.flush() # Requerido en algunas distros
ldap_client.unbind()

Es un simple script como lo ven, puede estar escrito en cualquier lenguaje (que tenga stdin y stdout) como PHP, C, Ruby, Haskell, Bash, pero elegí python para mostrar que tambíen hablo otro lenguajes (aparte de PHP y C). Tambíen pueden autenticar contra cualqueir cosa (ejemplo contra una DB como hacen acá) y por ejemplo asignar usuario por tiempos. Por último, para configurar el helper (asumiendo que el 192.168.1.1 es el servidor de ActiveDirectory):

auth_param basic program /path/to/login.py 192.168.1.1

# Aquí vi que algunos muggles que ponian 20, es muy alto, y no vale la pena
# a no ser que se tenga miles de usuarios.
auth_param basic children 5 

auth_param basic realm Type your password
# tiempo que será valido el login, para no hacer
# overhead tendría que ser un tiempo razonable
auth_param basic credentialsttl 5 minutes

Espero que mis días como sysadm lleguen a su fin, extraño los días de programar para vivir, no es lo mismo que pase a ser solo un hobbie.