blaapps - servidor de aplicaciones
Post on 12-Jun-2015
505 Views
Preview:
DESCRIPTION
TRANSCRIPT
SERVIDOR DEAPLICACIONES DISTRIBUIDAS
Víctor Jiménez Cerrada golo@capitangolo.nethttp://www.blaapps.org
Hola! Soy Víctor Jiménez Cerrada, desarrollador de BlaappsBlaapps es un framework para desarrollar apliaciones distribuidas.
Q & A
Conclusiones
Demo
Blaapps
Servidores de Aplicaciones30"
Q & A
Conclusiones
Demo
Blaapps
Servidores de Aplicaciones
Para entrar en contexto, veamos qué son los servidores de aplicaciones...
SERVIDORES DE APLICACIONES
En la actualidad existen diversos servidores de aplicaciones...
OBJETIVO: REDUCIR COSTES
Tareas Comunes:
•Persistencia
•Colas de mensajes
•Seguridad
•Conexiones
•...
Aplicación
Aplicación
Aplicación
Una empresa utiliza el servidor de aplicaciones para reducir costes de desarrollo.El servidor de aplicaciones realiza tareas comunes para varias aplicaciones.
UN POCO DE HISTORIA…
1800-19601970's 1980's 1990's
Tarjetas Perforadas
∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎ ∎
Hardware
En las primeras máquinas se utilizaban tarjetas perforadas para darle las instrucciones.
UN POCO DE HISTORIA…
1800-19601970's
1980's 1990's
Programación Estructurada
open file;while (reading not finished) { read some data; if (error) { }}process read data;
Hardware
La PE permitió dar instrucciones en un lenguaje más humano.Se sigue trabajando con la máquina.
UN POCO DE HISTORIA…
1800-19601970's
1980's 1990's
Sistemas Operativos
Hardware
open file;while (reading not finished) { read some data; if (error) { }}process read data;
S.O.
Con la difusión de los sistemas operativos el desarrollador se abstrae del Hardware.
UN POCO DE HISTORIA…
1800-1960 1970's1980's
1990's
Programación Orientada a Objetos
HardwareS.O.
La POO permite crear abstracciones aún más cercanas a la forma de pensar humana.
UN POCO DE HISTORIA…
1800-1960 1970's 1980's1990's
Frameworks
HardwareS.O.
Fra
me
wo
rk
La mayoría de las apps realizan las mismas tareas:Conexión a base de datos, gestión de las acciones del usuario...Un Framework implementa estas funciones para el desarrollador. El desarollador sólo se centra en su aplicación.
OBJETIVO: REDUCIR COSTES
Tareas Comunes:
•Persistencia
•Colas de mensajes
•Seguridad
•Conexiones
•...
Aplicación
Aplicación
Aplicación
Los servidores de aplicaciones son Frameworks.De nuevo, el objetivo es reducir costes.A parte de que se desarrolla menos código, desarrollar se hace más fácil, por tanto se necesitan menos expertos.
OBJETIVO: REDUCIR COSTES
Si mi plataforma es tan fácil de desarrollar, que hasta un mono puede aprenderla...
OBJETIVO: REDUCIR COSTES
Por el precio de un experto, puedo pagar a varios monos.
Q & A
Conclusiones
Demo
Blaapps
Servidores de Aplicaciones
Ahora hablemos de Blaapps
Muchas veces veo los Servidores de Aplicaciones como grandes dinosaurios: Grandes y pesados.Tienen muchas funcionalidades, son muy robustos y por ello son ideales para aplicaciones empresariales...
Sin embargo Blaapps, es como una rana feliz.
BLAAPPS
• Ligero
• Rápido
• Fácil de utilizar
•Modular
Es ligero y muy rápido. (Arranca en menos de 3 segundos)Es fácil de utilizar (Ahora lo veremos)Y es flexible, puede aumentar de funcionalidad mediante módulos...
BLAAPPS
BLAAPPS CORE
Sistemas mínimos:
• Cargador de Módulos
• Configuración
• Registros
•Mensajería
•MBean Server
Blaapps está formado por 4 sistemas principales...
BLAAPPS MODULES
Funcionalidades extra:
•Objetos Remotos (RMI)
• Persistencia (JPA)
De momento hay desarrollados dos módulos...
¿CÓMO SE USA?Blaapps Core
Antes hemos comentado que Blaapps es fácil de usar, veámoslo...
@Module(! name = "HiWorld",! description = "An example module",! version = @Version(revision=5) // 0.5.0.0)public class HiWorld {
! @Log ! private Logger log;
! @AfterDeployment! public void sayHello() {! ! log.info("Hello world!");! }
! ...}
Esto sería un Módulo de blaapps.
@Module(! name = "HiWorld",! description = "An example module",! version = @Version(revision=5) // 0.5.0.0)public class HiWorld {
! @Log ! private Logger log;
! @AfterDeployment! public void sayHello() {! ! log.info("Hello world!");! }
! ...}
@Module(! name = "HiWorld",! description = "An example module",! version = @Version(revision=5) // 0.5.0.0)public class HiWorld {
! @Log ! private Logger log;
! @AfterDeployment! public void sayHello() {! ! log.info("Hello world!");! }
! ...}
Inyección de dependencias
@Module(! name = "HiWorld",! description = "An example module",! version = @Version(revision=5) // 0.5.0.0)public class HiWorld {
! @Log ! private Logger log;
! @AfterDeployment! public void sayHello() {! ! log.info("Hello world!");! }
! ...}
Ciclo de vida de un módulo.
blaapps librariesblaapps librariesblaapps libraries
blaapps
HiWorld.jar
bin
conf
deploy
lib
blaapps-launcher.jar
blaapps libraries
blaapps.properties
Meteríamos esa clase en blaapps/deploy/HiWorld.jar
$ cd blaapps/bin$ java -jar blaapps-kernel-launcher.jar ../conf/
y arrancaríamos blaapps...
$ cd blaapps/bin$ java -jar blaapps-kernel-launcher.jar ../conf/Loading Libraries/opt/blaapps/bin/../lib[...]Libraries LoadedINFO [Blaapps-Core]: Starting Deployer...INFO [Blaapps-Core]: Loading Module HiWorldINFO [HiWorld]: Hello World!INFO [Blaapps-Core]: Module HiWorld LoadedINFO [Blaapps-Core]: Deployer Started
y arrancaríamos blaapps...
$ cd blaapps/bin$ java -jar blaapps-kernel-launcher.jar ../conf/Loading Libraries/opt/blaapps/bin/../lib[...]Libraries LoadedINFO [Blaapps-Core]: Starting Deployer...INFO [Blaapps-Core]: Loading Module HiWorldINFO [HiWorld]: Hello World!INFO [Blaapps-Core]: Module HiWorld LoadedINFO [Blaapps-Core]: Deployer Started.quitINFO [Blaapps-Core]: Deployer Shutdown...INFO [Blaapps-Core]: Unloading ModuleINFO [Blaapps-Core]: Module HiWorld UnloadedINFO [Blaapps-Core]: Deployer Stopped$
y arrancaríamos blaapps...
• @Module
• @Version
• @Dependency
CORE ANNOTATIONSDeployer
Tres son las anotaciones necesarias para definir la metainformación de un módulo...
Logger log = Logger.getLogger(“HiWorld”);log.info(“Hola Mundo!”);
INFO [HiWorld]: Hola Mundo!
CORE ANNOTATIONSRegistros
Se puede obtener un logger programáticamente...
@LogLogger log;log.info(“Hola Mundo!”);
INFO [HiWorld]: Hola Mundo!
CORE ANNOTATIONSRegistros
Pero Blaapps también puede crearlo por nosotros...
@Inbox("evento")private void onMessage(Message message)
@Inbox("evento")private MessageStorage inbox;
CORE ANNOTATIONSMensajería
Las colas de mensajes están formadas por dos partes: Los buzones de recepción y de envío.Los buzones de recepción se pueden crear mediante la anotación @Inbox.
MailBoxMBean mailbox = ! ! ! ! MainDeployerFactory.getServerMessageQueue();
Message message = new Message(this, true, atmnt1);
mailbox.sendMessage(“evento”, message);
CORE ANNOTATIONSMensajería
Para enviar un mensaje al servidor, utilizamos el enviador del servidor...
@Confprivate Properties conf;
• Configuración por defecto
defaults.properties
• Configuración del usuario
carpeta "conf"
CORE ANNOTATIONSConfiguración
Blaapps permite cargar la configuración de un módulo con @Conf@Conf junta la configuración por defecto de ese módulo con la definida por el usuario.
@ManagementServerprivate MBeanServer server;
public PersistenceModule() {! ObjectName persistenceName = new ObjectName(name);! server.registerMBean(this, persistenceName);}
• clase ManagementProxy
CORE ANNOTATIONSConfiguración
Blaapps inicia un MBeanServer para registrar elementos de gestión.Todos los módulos publican una interfaz de gestión.La clase ManagementProxy permite utilizar de manera fácil un MBean.
¿CÓMO SE USA?Blaapps Modules
Pasemos a ver los módulos de blaapps
MODULE ANNOTATIONSPersistencia
• JPA Annotations
@PersistenceContext@Entity
@Module( !name = "test",! ! ! ! version = @Version(revision=5),! ! ! ! description = "Testing persistence",! ! ! ! dependency = @Dependency(! ! ! ! ! ! parentName = "Blaapps-Persistence",! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! policy = DependencyPolicy.REQUIRED! ! ! ! ))public class Module01 {
! @PersistenceContext! private EntityManager em;!
Primero vamos a hablar del módulo de persistencia.Permite usar JPA en un módulo para acceder a una base de datos.Acepta las anotaciones JPA.
• JPA Annotations
@PersistenceContext@Entity
@Module( !name = "test",! ! ! ! version = @Version(revision=5),! ! ! ! description = "Testing persistence",! ! ! ! dependency = @Dependency(! ! ! ! ! ! parentName = "Blaapps-Persistence",! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! policy = DependencyPolicy.REQUIRED! ! ! ! ))public class Module01 {
! @PersistenceContext! private EntityManager em;!
Persistencia
Para poder usar las clases del módulo de persistencia, tenemos que indicarlo como dependencia.
MODULE ANNOTATIONSObjetos Remotos
• RMI Objects
@BStateless@BRemote
El módulo de objetos remotos ofrece un mecanismo sencillo de publicación de clases RMI
@Module(! name="test-remote-1",! ! ! ! version=@Version(revision=5),! ! ! ! description = "Testing Remote",! ! ! ! dependency = @Dependency(! ! ! ! ! ! ! parentName = "Blaapps-Remote",! ! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! ! policy = DependencyPolicy.REQUIRED! ! ! ! ))public class Module01 {!! ! ...
Objetos Remotos
En el módulo en el que definamos los objetos RMI, tenemos que indicar Blaapps-remote como dependencia.
public interface DummyRemote! ! ! ! ! ! ! ! extends Remote, Serializable {
! int getFive() throws RemoteException;
}
@BStateless(DummyRemote.class)public class DummyRemoteObject implements DummyRemote {
! @Override! public int getFive(){! ! return 5;! }!
}
Objetos Remotos
En este módulo definimos una interfaz pública y una clase que la implementa.La clase debe ser compatible con RMI.Marcamos con @BStateless el interfaz público que implementa.
@Module(! name = "test-remote-2",! ! ! ! version = @Version(revision=5),! ! ! ! description = "Testing Remote",! ! ! ! dependency = {! ! ! ! ! ! @Dependency(! ! ! ! ! ! ! ! parentName = "Blaapps-Remote",! ! ! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! ! ! policy = DependencyPolicy.REQUIRED),! ! ! ! ! ! @Dependency(! ! ! ! ! ! ! ! parentName = "test-remote-1",! ! ! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! ! ! policy = DependencyPolicy.REQUIRED)! ! ! ! })public class Module02 {
! @BRemote("test-remote-1")! private DummyRemote dummy;
! @AfterDeployment! public void testRemote(){! ! if (dummy.getFive() == 5) {! ! ! // Remote Module Works
! ! }! }
Objetos Remotos
En otro módulo, donde queramos usar el objeto remoto recién definido,marcamos como dependencias "Blaapps-remote" y el módulo anterior.
Objetos Remotos
@Module(! name = "test-remote-2",! ! ! ! version = @Version(revision=5),! ! ! ! description = "Testing Remote",! ! ! ! dependency = {! ! ! ! ! ! @Dependency(! ! ! ! ! ! ! ! parentName = "Blaapps-Remote",! ! ! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! ! ! policy = DependencyPolicy.REQUIRED),! ! ! ! ! ! @Dependency(! ! ! ! ! ! ! ! parentName = "test-remote-1",! ! ! ! ! ! ! ! version = @Version(revision=5),! ! ! ! ! ! ! ! comparator = Comparation.EQUAL,! ! ! ! ! ! ! ! policy = DependencyPolicy.REQUIRED)! ! ! ! })public class Module02 {
! @BRemote("test-remote-1")! private DummyRemote dummy;
! @AfterDeployment! public void testRemote(){! ! if (dummy.getFive() == 5) {! ! ! // Remote Module Works
! ! }! }
Con la anotación @BRemote inyectamos una referencia a un objeto remoto
3 RETOS
• Aislamiento
• Inyección de Dependencias
•Nuevas Instancias
Durante el desarrollo de blaapps el equipo de desarrollo se ha encontrado con 3 grandes retos.El primero de ellos fue el asegurar el aislamiento entre módulos.
AISLAMIENTOProblema
Usuarios Jabber
collections-commons-3.2.1.jar
collections-commons-2.1.1.jar
Si dos módulos utilizan distintas versiones de la misma librería o clase,se tiene que asegurar el que cada módulo utilice su versión.
AISLAMIENTOClassLoaders
Usuarios JabberUsuarios ClassLoader Jabber ClassLoader
Blaapps ClassLoader
Bootstrap ClassLoader
lib lib
Una forma de solucionarlo es encerrando a cada módulo en un hilo con su propio class loader.
AISLAMIENTOClassLoaders
Usuarios JabberUsuarios ClassLoader Jabber ClassLoader
Blaapps ClassLoader
Bootstrap ClassLoader
lib lib
lib
Problema: ¿Qué pasa si blaapps utiliza una versión diferente de la librería?
AISLAMIENTOClassLoaders
Usuarios JabberUsuarios ClassLoader Jabber ClassLoader
Blaapps ClassLoader
Bootstrap ClassLoader
lib lib
lib
Problema: ¿Qué pasa si blaapps utiliza una versión diferente de la librería?
AISLAMIENTOClassLoaders
Usuarios JabberUsuarios ClassLoader Jabber ClassLoader
Blaapps ClassLoader
Bootstrap ClassLoader
lib lib
lib
Problema: ¿Qué pasa si blaapps utiliza una versión diferente de la librería?
AISLAMIENTOClassLoaders
Usuarios JabberUsuarios ClassLoader Jabber ClassLoader
Blaapps ClassLoader
Bootstrap ClassLoader
lib lib
lib
Problema: ¿Qué pasa si blaapps utiliza una versión diferente de la librería?
AISLAMIENTOIsolationClassLoader
Usuarios JabberUsuarios IsolationClassLoader
Jabber IsolationClassLoader
Blaapps IsolationClassLoader
Bootstrap ClassLoader
lib lib
lib
Solución: Creamos un classloader propio que invierta el orden en el que se buscan las clases.
AISLAMIENTOIsolationClassLoader
Usuarios JabberUsuarios IsolationClassLoader
Jabber IsolationClassLoader
Blaapps IsolationClassLoader
Bootstrap ClassLoader
lib lib
lib
Solución: Creamos un classloader propio que invierta el orden en el que se buscan las clases.
AISLAMIENTOIsolationClassLoader
Usuarios JabberUsuarios IsolationClassLoader
Jabber IsolationClassLoader
Blaapps IsolationClassLoader
Bootstrap ClassLoader
lib lib
lib
Solución: Creamos un classloader propio que invierta el orden en el que se buscan las clases.
AISLAMIENTOIsolationClassLoader
Usuarios
Permisos
Usuarios IsolationClassLoader
Permisos IsolationClassLoader
Blaapps IsolationClassLoader
Bootstrap ClassLoader
Usuarios
UsuariosPermisos
Además, si un módulo depende de otro, su classloader mantiene sus clases, y una copia de las clases de otro módulo.Se tiene que forzar a los módulos a interactuar con sus interfaces públicos.Problema: ¿Qué pasa si se tiene que usar una misma versión de una clase en dos módulos?
AISLAMIENTOSharedClassLoader
Usuarios
Permisos
Usuarios IsolationClassLoader
Permisos IsolationClassLoader
Usuarios
UsuariosPermisos
Shared ClassesUsuarios Shared Clases Permisos Shared Clases
Solución: Se mantiene un espacio de clases compartidas, donde el desarrollador puede publicar clases.Las clases compartidas sólo se cargan una vez en memoria.
AISLAMIENTOSharedClassLoader
Usuarios
Permisos
Usuarios IsolationClassLoader
Permisos IsolationClassLoader
Usuarios
UsuariosPermisos
Shared ClassesUsuarios Shared Clases Permisos Shared Clases
Solución: Se mantiene un espacio de clases compartidas, donde el desarrollador puede publicar clases.Las clases compartidas sólo se cargan una vez en memoria.
AISLAMIENTOSharedClassLoader
Usuarios
Permisos
Usuarios IsolationClassLoader
Permisos IsolationClassLoader
Usuarios
UsuariosPermisos
Shared ClassesUsuarios Shared Clases Permisos Shared Clases
Solución: Se mantiene un espacio de clases compartidas, donde el desarrollador puede publicar clases.Las clases compartidas sólo se cargan una vez en memoria.
EntityManager em = new EntityManager(?????);
• ¿Quién gestiona la configuración?
INYECCIÓN DE DEPENDENCIAS
Si el desarrollador es el responsable de crear conexiones a la base de datos, también tiene que gestionar la configuración.Blaapps se encarga de crear conexiones a datos, pero, ¿cómo obtiene el desarrollador una?Uno de los retos de Blaapps ha sido facilitar la creación de este tipo de objetos.
EntityManager em = Persistence.getEM();
INYECCIÓN DE DEPENDENCIAS
Inversión del control
Una Factoría.Problema: Genera código acoplado.
EntityManager em;
public void setEm(EntityManager em) {! this.em = em;}
INYECCIÓN DE DEPENDENCIAS
Inversión del control
Inversión de control.Problema: ¿Cómo sabe Blaapps que tiene que inyectar?
@PersistenceContextEntityManager em;
INYECCIÓN DE DEPENDENCIAS
Programación Orientada a Aspectos
Inyección de dependencias -> POAProblema: ¿Cómo hacer un set a ese campo?
for (Field field : clazz.getDeclaredFields()) {! if (! ! field.isAnnotationPresent(PersistenceContext.class)!&&! ! EntityManager.class.isAssignableFrom(field.getType())! ) {! ! field.setAccessible(true);! ! field.set(obj, createEntityManager());! }}
INYECCIÓN DE DEPENDENCIAS
API Reflection
Solución: API Reflection
NUEVAS INSTANCIASNotificando clases cargadas
ClassLoaderEjecuciónloadClass()
lib.jar
Blaapps necesita saber cuándo se cargan nuevas clases, y se crean nuevos objetos.De esta manera, registra componentes e inyecta campos.Blaapps sabe cuándo se carga una clase, se lo chiva el ClassLoader.El ClassLoader manda un mensaje cada vez que se carga una clase.
NUEVAS INSTANCIASNotificando clases cargadas
ClassLoaderEjecuciónloadClass()
lib.jar
MessageQueue
classLoaded()
Blaapps necesita saber cuándo se cargan nuevas clases, y se crean nuevos objetos.De esta manera, registra componentes e inyecta campos.Blaapps sabe cuándo se carga una clase, se lo chiva el ClassLoader.El ClassLoader manda un mensaje cada vez que se carga una clase.
NUEVAS INSTANCIASNotificando clases cargadas
ClassLoaderEjecuciónloadClass()
lib.jar
MessageQueue
classLoaded()
Deployer Deployer Deployer
Blaapps necesita saber cuándo se cargan nuevas clases, y se crean nuevos objetos.De esta manera, registra componentes e inyecta campos.Blaapps sabe cuándo se carga una clase, se lo chiva el ClassLoader.El ClassLoader manda un mensaje cada vez que se carga una clase.
NUEVAS INSTANCIAS
?Notificando nuevas instancias
Pero... ¿Cómo sabemos cuándo se crea una instancia de un Objeto?No hay ninguna forma de saberlo.Bueno, una sí, si nos lo dice el propio objeto...
NUEVAS INSTANCIASJavassist
ClassLoaderEjecuciónloadClass()
lib.jar
public class Class {! public Class(){
! }}
Javassist
inject()
Code
MessageQueue
newInsance()
En lugar de cargar en memoria la clase directamente, usaremos javassist.Javassist es una librería que nos permite alterar los bytecodes de una clase.Cada vez que se carga una clase, se añade un código al principio constructor.Cuando se ejecuta, se lanza una notificación de que se ha creado la instancia.
NUEVAS INSTANCIASJavassist
ClassLoaderEjecuciónloadClass()
lib.jar
public class Class {! public Class(){
! }}
Javassist
inject()Code
En lugar de cargar en memoria la clase directamente, usaremos javassist.Javassist es una librería que nos permite alterar los bytecodes de una clase.Cada vez que se carga una clase, se añade un código al principio constructor.Cuando se ejecuta, se lanza una notificación de que se ha creado la instancia.
Q & A
Conclusiones
Demo
Blaapps
Servidores de Aplicaciones
Demo T
ime!
Q & A
Conclusiones
Demo
Blaapps
Servidores de Aplicaciones
LO APRENDIDO
• Funcionamiento Interior de Java
• ClassLoaders
• Threads
• Programación orientada a Aspectos
• Integración continua
• Maven, JUnit, Bitten, Trac
FUTURO
• Estabilidad
• Funcionalidad
• Comunidad
• α → β → RC → GA
• Pruebas de unidad
• Librerías externas
FUTUROEstabilidad
Actualmente se considera que blaapps está en estado Alpha.Se plantea pasar a Beta en la siguiente versión.
• Escritorio
•Web
• ¿Comunidad?
FUTUROFuncionalidad
La funcionalidad futura pasará por ampliar horizontes.Facilitar el desarrollo de todo tipo de aplicaciones. Servidor, web y Escritorio.Pero será la comunidad, aquellos que usen y desarrollen blaapps, los que decidan por dónde irá el proyecto.
FUTUROComunidad
http://www.blaapps.org
La comunidad ya dispone de una página web donde colaborar.
BLAAPPS
•Modular
• Ligero
• Rápido
• Fácil de utilizar
Blaapps es ligero y flexible. Y además utiliza tecnologías estándar.Migrar de Blaapps a un servidor mayor no es problema.Modificar Blapps para que escale no es problema.En definitiva, es ideal para proyectos que empiezan. Crea algo muy rápido, y luego hazlo crecer.
BLAAPPS
•Modular
• Ligero
• Rápido
• Fácil de utilizar
STARTUPS
Blaapps es ligero y flexible. Y además utiliza tecnologías estándar.Migrar de Blaapps a un servidor mayor no es problema.Modificar Blapps para que escale no es problema.En definitiva, es ideal para proyectos que empiezan. Crea algo muy rápido, y luego hazlo crecer.
Víctor Jiménez Cerrada golo@capitangolo.nethttp://www.blaapps.org
SERVIDOR DE APLICACIONES DISTRIBUIDAS
(BLAAPPS)
¡GRACIAS!
top related