Como habrá podido comprobar, el nuevo gema ruby couchbase ha sido lanzado recientemente. La versión 1.2.2 es principalmente una versión de mantenimiento con varias correcciones de errores, pero todavía se puede probar una nueva característica experimental: la integración con Máquina de eventos biblioteca. Este post te dará una rápida introducción sobre cómo empezar a usar Couchbase Server con tus aplicaciones basadas en el modelo asíncrono EventMachine.
La integración de EventMachine sólo es accesible (actualmente) en sistemas tipo UNIX (como Linux, Solaris, BSD). Debido a que utiliza fibras, también requiere MRI ruby versión 1.9 o posterior.
Configure su Sandbox
El primer paso es instalar la librería libcouchbase que maneja todos los detalles de bajo nivel del protocolo Couchbase. Puedes seguir el guía de instalación en la página oficial. Aquí sólo replicaré los pasos necesarios para una caja GNU/Linux típica (estoy usando Debian inestable):
-
Instale la clave PGP del repositorio:
$ wget -O- http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add - -
Configurar la fuente del repositorio. Aquí estoy usando el enlace para Ubuntu 12.04, pero en general no importa porque vamos a usar el plugin EventMachine, que viene incorporado en la propia gema. Los paquetes están en diferentes repositorios de paquetes construidos usando el mismo código base; la única diferencia es la versión de las librerías IO (libevent, libev) incluido en la versión de la distribución.
$ sudo wget -O/etc/apt/sources.list.d/couchbase.list http://packages.couchbase.com/ubuntu/couchbase-ubuntu1204.list -
Instala las cabeceras de libcouchbase, la biblioteca central y los símbolos de depuración. Una vez más, es posible que desee instalar herramientas de línea de comandos o uno de los IO backends, pero eso no es necesario para la tarea que nos ocupa.
$ sudo apt-get update
$ sudo sudo apt-get install libcouchbase-dev libcouchbase2-core libcouchbase-dbgYa está.
Ahora tienes que instalar Servidor CouchbaseSiga las instrucciones del sitio oficial. Después de la instalación obtendrá la consola de administrador que se ejecuta en http://localhost:8091 y también la API REST accesible en el mismo puerto. Siga los pasos de configuración iniciales y, finalmente, asignará un bucket con el nombre "default".
-
Por último, hay que instalar la gema. Es tan fácil como escribir esto en el terminal:
$ gem install couchbase
Construyendo extensiones nativas. Esto podría llevar un tiempo...
Instalado con éxito couchbase-1.2.2
1 gema instalada
Instalando la documentación de ri para couchbase-1.2.2...
Instalando la documentación RDoc para couchbase-1.2.2...
Creación de la aplicación
Para demostrar la integración, vamos a construir una sencilla aplicación de chat utilizando EventMachine y el registro de añadir para todos los eventos allí a un cubo Couchbase. Es extremadamente fácil construir una aplicación asíncrona con EventMachine y para demostrarlo voy a poner el código fuente completo en este post (también se encuentra en ejemplos/chat-em de las fuentes de la gema).
@@clientes = []
def post_init
@nombredeusuario = nil
send_data("*** ¿Cuál es tu nombre?n")
fin
def recibir_datos(datos)
si @nombredeusuario
broadcast(datos.strip, @nombredeusuario)
si no
nombre = data.gsub(/s+|[[]]/, '').strip[0..20]
if nombre.vacío?
send_data("*** ¿Cuál es tu nombre?n")
si no
@nombredeusuario = nombre
@@clients.push(self)
broadcast("#{@nombredeusuario} se ha unido")
send_data("*** Hola, #{@nombre_usuario}!n")
fin
fin
fin
def desvincular
@@clients.delete(self)
broadcast("#{@nombredeusuario} se ha marchado") if @nombredeusuario
fin
def broadcast(mensaje, autor = nil)
prefijo = autor ? "" : "***"
@@clientes.each do |cliente|
unless cliente == self
client.send_data("#{prefijo} #{mensaje}n")
fin
fin
fin
fin
EventMachine.run do
# pulsa Control + C para parar
Signal.trap("INT") { EventMachine.stop }
Signal.trap("TERM") { EventMachine.stop }
EventMachine.start_server("0.0.0.0", 9999, ChatServer)
fin
Este es el típico servidor EventMachine basado en EM::Connection. Para aquellos que no conocen el significado de estos métodos redefinidos aquí es un extracto de la documentación oficial:
EventMachine::Connection es una clase que es instanciada por el bucle de procesamiento de EventMachine cada vez que se crea una nueva conexión. (Las nuevas conexiones pueden ser iniciadas localmente a un servidor remoto o aceptadas localmente desde un cliente remoto). Cuando un objeto Connection es instanciado, mezcla la funcionalidad contenida en el módulo definido por el usuario especificado en las llamadas a connect o start_server. Los módulos manejadores definidos por el usuario pueden redefinir cualquiera o todos los métodos estándar definidos aquí, así como añadir código adicional arbitrario que también se mezclará.
EventMachine gestiona un objeto heredado de EventMachine::Connection (y que contiene el código de usuario mezclado) por cada conexión de red que esté activa en un momento dado. El bucle de eventos llamará automáticamente a los métodos de los objetos EventMachine::Connection cada vez que se produzcan eventos específicos en las conexiones correspondientes, como se describe a continuación.
Esta clase nunca es instanciada por el código de usuario, y no publica un método de inicialización. Los métodos de instancia de EventMachine::Connection que pueden ser llamados por el bucle de eventos son: #post_init, #connection_completed, #receive_data, #unbind, #ssl_verify_peer (si se utiliza TLS), #ssl_handshake_completed
Todos los demás métodos de instancia definidos aquí son llamados únicamente por código de usuario.
El protocolo es muy simple y orientado a la línea. Para cada conexión EventMachine creará una instancia de ChatServer, que primero preguntará el nombre del nuevo participante y luego emitirá todos sus mensajes al grupo. Puedes usar tu herramienta favorita que te permita comunicarte a través de un protocolo de texto arbitrario, como telnet por ejemplo o nc. Aquí hay un ejemplo de sesión entre puntos finales.
Intentando 127.0.0.1... *** ¿Cuál es tu nombre?
Conectado a localhost. Alice
El carácter de escape es "^]". *** ¡Hola, Alice!
*** ¿Cuál es tu nombre? ??? *** bob se ha unido
hola a todos
*** ¡Hola, Bob! ¡Hola, Bob! ¿Cómo estás?
hola a todos ^C
¡Hola, Bob! ¿Cómo estás? ??? ~ $
*** Alice se ha ido ***
^] ???
telnet> Conexión cerrada. ???
~ $ ???
Ahora es el momento de añadir un poco de Couchbase. Imagina que me gustaría mantener todos los mensajes en una base de datos distribuida tan eficientemente como pueda. Couchbase es la respuesta :). Para ello necesito:
Implementar un método de registro en la clase ChatServer, que debe aceptar el mensaje y un autor opcional (para los eventos del sistema será nil):
Couchbase.bucket.incr("log:key", :initial => 1) do |res|
entrada = {
'time' => Time.now.utc,
'autor' => autor || "[sistema]",
'message' => mensaje
}
Couchbase.bucket.set("log:#{res.value}", entrada)
fin
fin
Luego añado una llamada a log(message, author) en el método broadcast justo antes de iterar todos los clientes conectados. Y envuelvo EventMachine.start_server con Couchbase::Bucket#on_connect callback, para ejecutar el servidor justo después de que el cliente se haya conectado. La ejecución del bucle resultante tendrá este aspecto:
# pulsa Control + C para parar
Signal.trap("INT") { EventMachine.stop }
Signal.trap("TERM") { EventMachine.stop }
Couchbase.connection_options = {:async => true, :engine => :eventmachine}
Couchbase.bucket.on_connect do |res|
if res.success?
EventMachine.start_server("0.0.0.0", 9999, ChatServer)
si no
puts "No se puede conectar a Couchbase Server: #{res.error}"
fin
fin
fin
Eso es todo por ahora. En el futuro podemos ampliar este ejemplo para utilizar técnicas más modernas como em-synchrony y quizás websockets. Esté atento a las actualizaciones de este blog.
Puntos extra
Sólo el registro puede no ser tan interesante, con Couchbase Server puedes realizar análisis simples con consultas View usando la maravilla incremental Map-Reduce de Couchbase. Por ejemplo, aquí está la función Map para obtener todas las entradas en orden cronológico.
if (doc.message) {
if (doc.author == "[system]" && doc.time) {
emit(new Date(doc.time), "*** " + doc.message);
} else {
emit(new Date(doc.time), " " + doc.message);
}
}
}
Y la salida JSON.
{"id": "log:1″, "key": "2013-02-11T19:08:05.000Z", "value": "*** alice se ha unido"},
{"id": "log:2″, "key": "2013-02-11T19:08:18.000Z", "value": "*** bob se ha unido"},
{“id”:”log:3″,”key”:”2013-02-11T19:08:38.000Z”,”value”:” hi everyone”},
{"id": "log:4″, "key": "2013-02-11T19:08:48.000Z", "value":" ¡hola, bob! ¿cómo estás?"},
{"id": "log:5″, "key": "2013-02-11T19:08:58.000Z", "value": "*** alice se ha ido"},
{"id": "log:6″, "key": "2013-02-11T19:09:01.000Z", "value": "*** bob se ha ido"}
]}
Bueno, eso es todo por ahora. Disfrute de esta nueva característica experimental. Será totalmente compatible en una futura versión. Si tiene algún problema, por favor, envíe un mensaje a la sección Seguimiento de problemas del proyecto RCBC. Las correcciones y contribuciones también son siempre bienvenidas y es de código abierto bajo licencia Apache 2.0. Encontrará el fuentes en github.