Jul 17

Puñetitas con SpringBoot y RestTemplateBuilder

Cuando en Spring Boot queremos hacer un cliente REST, podemos usar RestTemplate. Esta clase permite hacer llamadas REST al servidor.
Para evitar en todas las llamadas poner la URL completa, se puede poner la URL base de esta forma

RestTemplateBuilder builder = new RestTemplateBuilder();
RestTemplate restTemplate = builder.rootUri(“http://loquesea.com/”).build();

// y a partir de aqui, para una llamada GET por ejemplo
restTemplate.getForObject(“/entidad”, ….);

En getForObject() no hace falta poner la URL completa, basta empezarla con / y el rootUri que añadiemos anteriormente se añade al principio.

Pues bien, esto funciona bien, pero cuando hice mi primera prueba, iluso de mí, se me ocurrió poner esto

RestTemplateBuilder builder = new RestTemplateBuilder();
builder.rootUri(“http://loquesea.com/”)
RestTemplate restTemplate = builder.build();

peeero … ¡¡eso no funciona!!. ¿Por qué?. Metiéndome en el código del método rootUri() me asombra ver

/**
* Set a root URL that should be applied to each request that starts with {@code ‘/’}.
* See {@link RootUriTemplateHandler} for details.
* @param rootUri the root URI or {@code null}
* @return a new builder instance
*/
public RestTemplateBuilder rootUri(String rootUri) {
return new RestTemplateBuilder(this.detectRequestFactory, rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.interceptors);
}
}

Lo advierte el comentario, devuelve una nueva instancia, por lo que la rootUri no se guarda en la instancia actual. Así que esta llamada solo funciona si se pone todo seguido como en el primer ejemplo rootUri(..).build(), o si nos guardamos en una nueva variable lo que devuelve rootUri() para luego llamar a buld() de lo devuelto.

En fin, despista un poco.

Jun 16

Comunicación Consola Servidor : Hazelcast, Vert.x, REST

Logo HazelcastEn el mismo proyecto del post anterior, estaba replanteandome como hacer que la interfaz de usuario (consola, aplicación java de escritorio) se conecte con el servidor (ejecutable java que no es servidor web ni de aplicaciones). Habitualmente abrimos un socket TCP/IP y definimos la mensajería. Tenemos librerías ya preparadas para esto, es eficiente y lleva muchos años funcionando. Pero a todos nos gusta jugar. Para este proyecto, me estoy planteando usar algún otro tipo de comunicación.

En otros proyectos que he visto (uno de los cuales lo he heredado), usan servicios web REST para todo lo que sea petición/respuesta desde la consola al servidor. Cuando el servidor quiere enviar algo de motu propio a todas las consolas, se usa ActiveMQ como cola de mensajes. Como el proyecto está con Spring Framework, hay soporte para las dos cosas haciendo fácil su codificación. Este mecanismo también está funcionando sin problemas.

Pero lo dicho, quiero seguir jugando.

Lo del servicio REST … ¿podemos eliminarlo usando sólo colas de mensajes o algo parecido? ¿Y en vez de una cola de mensajes ActiveMQ podemos usar algo más sencillo y ligero?

Pues me plantee hacerlo primero todo con Hazelcast. Si pongo modelos compartidos entre consola y servidor y con el mecanismo que tiene Hazelcast de publicación/suscripción a base de topics, quizás me bastaría sólo Hazelcast para reemplazar los web services REST y a ActiveMQ.

Pues no. Por un lado, Hazelcast no ofrece un mecanismo de petición/respuesta. Si uso un topic como petición de forma que el servidor se suscribe a ese topic de petición y cuando la consola quiere algo, publica en ese topic qué quiere, el servidor no tiene forma de responder. Habría que crear un topic temporal de respuesta, de forma que la consola que ha hecho la petición se suscribe a ese topic temporal y recoge la respuesta. Esto lo ofrece ActiveMQ de forma transparente, pero con Hazelcast habría que currárselo.

Y me surgió un segundo tema. Si en base de datos tenemos guardadas una serie de cosas y la consola las quiere, lo normal es pedírselas al servidor y el servidor en respuesta las devuelve. ¿Cómo lo hace en el caso de Hazelcast? Tendría que meterlo en algún tipo de colección de Hazelcast en memoria. ¿Y qué pasa sin son muchos datos?. Pues que hay que cargarlos todos en memoria, o inventar un mecanismo de cargas parciales, con paginados, etc, etc.

Así que descarté el uso exclusivo de Hazelcast. Lo he dejado, pero principalmente porque me vale para el modelo “vivo” de datos. En la aplicación se verá un mapa con barcos moviéndose. Estos barcos moviéndose en lo que llamo modelo “vivo” de datos, los datos que actualmente se presentan en todas las consolas y que el servidor se encarga de ir actualizando para todas. Esto es una ventaja frente a lo que teníamos. Sin Hazelcast (o un equivalente), el servidor tenía que enviar todos los cambios de ese modelo “vivo” a través de eventos por socket TCP/IP o ActiveMQ y las consolas debías actualizar su copia de dicho modelo en base a esos eventos. Con Hazelcast, el servidor toca la colección compartida y la consola ve dicha colección actualizada en todo momento. Es Hazelcast el que se “come” por debajo todo el tema de eventos de cambios del modelo y la actualización en el lado de la consola. La consola puede suscribirse a cambios en esa colección compartida, hazelcast ofrece esa posibilidad, pero para hacer otras cosas que necesite, como borrar el icono del barco en el mapa o desplazarlo, pero no para actualizar esa misma colección de barcos que ya está actualizada

Bien, no me vale Hazelcast para todo. ¿Qué tal Vert.x?. Tiene un EventBus, más sencillo de manejar que ActiveMQ, contempla la petición/respuesta, usa (o puede usar) Hazelcast por debajo por lo que encaja con el Hazelcast que me he planteado usar. Así que eso me planteé. Dejo Hazelcast para el modelo vivo y usar EventBus de Vert.x para peticion/respuesta y publicación/suscripción con Topics.

Me pongo a hacer mis pruebas y … ¡¡Sorpresa!!. Vert.x no garantiza el orden en la entrega de los eventos. Si garantiza que si un publicador envía eventos, un suscriptor concreto los recibe en el orden que el publicador los ha enviado. Pero no garantiza nada más. Si un publicador publica en varios topic y cada topic tiene un suscriptor distinto, los suscriptores pueden recibir los eventos en cualquier orden, no necesariamente en el orden en que el publicador los ha enviado. Y eso me hizo polvo. Para petición/respuesta a veces se necesita que las cosas vayan en orden, que no me contesten antes a lo que he pedido después. Así que que para petición/respuesta descarté Vert.x, seguro que puedo hacer apaños para garantizar el orden, pero va en contra de la filosofía de Vert.x y me parece una fuente importante de bugs si los desarrolladores se despistan.

¿Y para publicación/suscripción con topics, sin respuesta?. Bien, tengo Hazelcast que también lo hace. No voy a meter un framework por encima sólo para eso. Lo dejo aparcado por si en un momento dado Hazelcast se me queda cojo en este asunto y Vert.x me lo solucionara, pero lo dicho, queda aparcado.

¿Cómo hago al final lo de petición/respuesta?. Pues al final he dejado los web services REST.

¿Qué creo que he ganado con todo este cambio aparte de jugar y aprender? Principalmente el tema del modelo “vivo”. La colección compartida de Hazelcast entre consola y servidor me ahorra enviar eventos de cambios en ese modelo en el servidor, recogerlos en la consola y reconstruir en consola con código el mismo modelo que tiene el servidor.

En cuanto a topics, AciveMQ frente a Hazelcast, desde luego es mucho más potente y con más posibilidades ActiveMQ, pero es más complejo y no necesito todas esas posibilidades, tiro de momento por la opción más simple de Hazelcast.

 

Jun 10

Estructura de Plugins con OSGI / Spring Boot

osgi

spring boot

Estoy empezando una aplicación java, de escritorio, desde cero. Una de las cosas que pretendo hacer es que tenga plugins que se puedan instalar de forma adicional, no solo en el lado de la consola (javax.swing), sino también funcionalidades en el lado del servidor (java de escritorio).

Me he planteado dos opciones. Una es usar OSGI, que es el framework más estándar y conocido para hacer una aplicación modular con plugins. Pero aunque no está pensado para ello, se me ha ocurrido otra forma de hacerlo usando Spring Boot (por ir a lo más moderno).

Con OSGI, cada jar lleva un fichero de manifiesto donde indica qué paquetes java exporta a otros jar y qué paquetes java necesita de otros jar. Si el fichero de manifiesto no exporta o importa paquetes, el desarrollador no puede usar los paquetes que no han sido exportados/importados, el mismo IDE debidamente montado no le deja.

Con Spring Boot no hay esta opción de importar y exportar paquetes, pero se puede “apañar” con disciplina. Debemos montar una estructura de proyectos/subproyectos donde haya jar que sólo tengan interfaces o estructuras de datos. Los demás jar donde está el código sólo pueden depender de estos jar de interfaces/estructuras de datos. Esto se puede montar correctamente con maven/gradle. Puede quedar quizás una estructura de proyecto/subproyectos un poco engorrosa, pero si organizada y que impide al desarrollador usar en un proyecto cosas que no debe de otro (siempre que no se líe a tocar dependencias maven/gradle)

Para enganchar las distintas clases/servicios entre si de distintos jar, OSGI ofrece lo que llama Declarative Services. Cada jar registra en OSGI los servicios que ofrece (clases que implementan una interfaz java y que ofrecen alguna funcionalidad) y cada jar pide a OSGI los servicios que necesita (servicios a través de interfaces java y que necesita que alguien se las dé ya implementadas) . OSGI se encarga de “enganchar” en el arranque, automáticamente, a unos con otros en función de lo que ofrecen y necesitan.

Con Spring Boot, está el mecanismo que tiene de inyección de dependencias y anotaciones. Si una clase necesita un servicio, le basta con poner un atributo con la interfaz de ese servicio anotada con @Autowired. Si Un jar ofrece un servicio, basta que le ponga a la clase que lo ofrece la anotación @Service (o cualquiera de las alternativas, como @Component u otras). Spring Boot se encarga de ver qué servicios se ofrecen e inyectarlos en las clases que lo necesitan.

De una forma o de otra, el mecanismo de plugin sería que la aplicación principial definiera la interfaz que deben implementar los plugins y los servicios que deben ofrecer para integrarse correctamente en la aplicación. Por ejemplo, en la interfaz de usuario la interfaz que deben implementar los plugins pueden tener métodos como getActions() para añadir dichas acciones a un menú o barra de herramientas, getIcon() para poner el icono de dicho plugin en algún sitio, getPanel() para que el plugin pueda proporcionar su propio panel visual, etc. La aplicación principal pediría al framework todas las clases que ofrezcan este servicio (implementen esta interfaz) y los plugin presentes registrarían sus servicios en el framework.

¿Qué ventajas/inconvenientes tiene cada mecanismo?

OSGI es el estándar para plugins, y además permite el añadir plugins en caliente. Debidamente configurado, bastaría situar el jar con el plugin en un directorio concreto de la aplicación que está en ejecución y el plugin se cargaría automáticamente, inyectando en caliente los servicios que ofrece al que los necesite. Sin embargo, la pega de OSGI es es que es algo retorcido a la hora de desarrollar. Su objetivo es limitarnos para obligarnos a hacer el desarrollo modular y esas limitaciones a veces son un poco engorrosas.

Spring Boot por otro lado es muy sencillo de usar. Un par de anotaciones (@Autowired y @Service) y está todo apañado (por supuesto, siempre hay peguillas en el mundo real y casos extraños). Las pegas son que no permite el despliegue de plugins en caliente y que no obliga a desarrollo modular limitando visibilidad, por lo que tenemos que obligar a ese desarrollo modular a bases de subproyectos maven/gradle debidamente configurados.

Otra posible pega de Spring Boot es que en el arranque busca por todas las clases las anotaciones mencionadas para hacer el enganche. Si es un proyecto grande con muchas clases, el proceso de escaneo de todas las clases y sus anotaciones puede volverse lento. OSGI no tiene ese problema, porque cada jar, con código (o un xml si optamos por esa opción), pide o registra servicios en OSGI, no es OSGI el que se dedica a recorrer todo el código.

Y una ventaja clara de Spring Boot sobre OSGI es la cantidad de funcionalidades adicionales que de forma sencilla nos vienen de regalo. Por ejemplo, montar web services o soporte con bases de datos en Spring Boot es casi inmediato. En OSGI, aunque existe algo, es muy, muy engorroso de poner en marcha. Si conoces ambos frameworks, pero no has levantado nunca web services con ellos, en Spring Boot puedes buscar un tutorial y tener tu web service “hola mundo” en 10 minutos. Con OSGI, te puedes pasar una o dos semanas buscando los plugins que te dan soporte para crear web services y tenerlos funcionando, sin haber implementado todavía tu hola mundo. Spring Boot es seguramente más pesado, pero si quieres funcionalidad adicional es mucho más fácil.

¿Mi elección?

Para este proyecto que estoy empezando y que no tiene ningún requisito (ni visos de tenerlo) de despliegue de plugins en caliente, me he tirado por Spring Boot. Por su sencillez y porque voy a usar web services para comunicación entre consola y servidor, aunque sea java de escritorio.

Apr 22

¿Es nuevo esto del machine learning?

Pajarito de Weka

Me ha dado por mirar temas de machine learning. Como también he oído mucho de deep learning, lo primero he intentado ver es la diferencia entre uno y otro. Pues parece que machine learning es el término general para a partir de unos datos de entrada encontrar correlaciones y poder hacer predicciones a futuro sobre estos datos, usando determinados algoritmos. Y deep learning es un subconjunto de machine learning en el que el algoritmo a usar es el de redes neuronales.

En machine learning en general se usan cosas como árboles de decisión, redes bayesianas, clustering, etc, etc. En deep learning se usan redes neuronales.

Y una vez entendido el asunto, me he dicho … seguro que hay cosas en java hechas. Y sí, hay bastantes. La que más me han llamado la atención, por la parte de machine learning, es Weka. He estado jugando con Weka y no es muy compleja de usar, me ha parecido bien. También he visto, aunque no jugado todavía, TensorFlow, esta de redes neuronales (deep learning) y al menos la web tiene muy buena pinta y TensorFlow es de google, que siempre hace que al menos merezca la pena mirarla. De todas formas he sido “malo” y le puesto la mosca detrás de la oreja a un compañero de trabajo, que sí ha jugado con ella y me ha dado buenas referencias tras hacer sus experimentos.

Pero tras leer un poco sobre esto y jugar con weka … me he decepcionado un poco. Weka no me ha costado mucho porque he visto que tenía una cosa que se llama árboles de decisión como algoritmo para clasificar muestras y he podido hacer rápidamente mis pruebas porque sé cómo funciona un árbol de decisión. ¿Y por qué lo sé?. Porque allá por 1993 (hace ya 25 años) estuve intentando hacer el doctorado de mi carrera (no lo terminé, por eso “intentando”) y ya entonces existían los árboles de decisión. También por aquella época existían las redes neuronales (ahora por lo visto llamadas deep learning).

Entonces .. . ¿hemos evolucionado algo?. Pues mi conclusión es que en conceptos/algoritmos me da la impresión de que no, posiblemente se han mejorado o se han variado, pero básicamente sigue siendo lo mismo que había al menos hace 25 años. Lo que sí ha evolucionado un montón es que ahora hay un mogollón más de datos, tenemos más potencia de CPU y tenemos más posibilidades de hacer cálculo distribuido en cluster de servidores, por lo que todos estos algoritmos tienen ahora mucha más potencia y posibilidades. De hecho, aunque no he encontrado una referencia clara, en algunos sitios si mencionan que la palabra “deep” es porque ahora se pueden poner muchas más capas en las redes neuronales.

Y lo que más ha evolucionado, en 1993 internet no era lo que es hoy y me tuve que hacer yo desde cero el código de los árboles de decisión, estudiando e implementando las fórmulas de entropía del conjunto de muestras. También me hice desde cero mis redes neuronales. Ahora hay mucha gente que ha hecho librerías que tienen todo esto implementado, como Weka o TensorFlow, y están fácilmente accesibles y documentadas en internet. Sólo nos queda por hacer la parte difícil, elegir bien los parámetros de interés de nuestras muestras y entrenar el algoritmo 🙂

Apr 11

Jugando con Docker

Logo Docker Descubrí y jugué con Docker hace ya bastante tiempo, pero ahora me ha dado otra vez y estoy en ello.

He hecho alguna pruebecilla con Docker y Multicast, con Docker y Postgres/Postgis … y ahora ando liado a ver cómo hacer imágenes Docker desde Maven.

Ya en su día hice una prueba y me funcionó bien, use el plugin de maven spotify / docker-maven-plugin y más o menos me gustó. En el pom.xml ponías a base de tags cómo construir la imagen y todo bien. Sin embargo, la documentación pone que ese plugin no se mantiene más y que se use en su lugar spotify / dockerfile-maven. Pues bien, he estado jugando un muy poco con él y lo que he visto no me ha gustado nada. En vez de poner cómo construir la imagen a base de tags en el pom.xml, te obliga a usar un Dockerfile. Nada malo hasta ahí. En los ejemplos de la documentación el fichero Dockerfile está en el raíz del proyecto maven y funciona bien. Estupendo. Pero a mí me gusta tener libertad y organizar las cosas a mi manera, así que pongo el Dockerfile en src/main/docker/Dockerfile. Y ahí empiezan mis problemas. Para meter dentro el jar (y sus dependencias), tengo que poner en el Dockerfile algo como

COPY ../../../target/mijar.jar /path/dentro/de/mi/imagen

Queda feo, pero podría vivir con ello. El problema es que cuando ejecutas el plugin, da un error de que el directorio target está fuera del contexto del Dockerfile. Obliga (no sé si el plugin o el Dockerfile, creo que el plugin) a que todos los ficheros que quieres copiar estén donde el Dockerfile o en subdirectorios por debajo. Eso me obliga a poner el Dockerfile en el raíz para poder coger cosas del subdirectorio target, como mi propio jar. Es su forma de funcionar, pero no me gusta. No he encontrado (tampoco me he matado buscando) una forma de solucionar esto. De momento voy a mirar el fabric8io / docker-maven-plugin, que de momento no he probado, pero solo ver el detalle de la documentación (sigue el link que aparece en esa página), ya me ha causado muy buena impresión.

Así que … ¡¡ ha probar se dicho !!

Dec 17

Por qué me está gustando IntelliJ IDEA

 Hace ya un porrón de años tuve que elegir un IDE para trabajar con Java. Miré Eclipse y Netbeans, de aquella no existía IntelliJ y me acabé decantando por eclipse. Y desde entonces, más de 10 años ya, trabajando con eclipse.

Sin embargo, algunos compañeros míos usan IntelliJ y están muy contentos con él. También encontré unas estadísticas en las que IntelliJ se estaba usando cada vez más mientras que Eclipse parece que cada vez menos, aunque actualmente andan muy a la par.

Así que me decidí a probar IntelliJ …. y de momento me está convenciendo.

Me está costando acostumbrarme a los atajos de teclado o encontrar los comandos del IDE que quiero hacer, pero sólo es por la costumbre y poco a poco me voy haciendo a los nuevos atajos. Aparte de esto ¿por qué me está gustando?

Lo primero es el auto-compilar. Eclipse auto-compila siempre. IntelliJ por defecto no lo  hace, pero es una opción que se puede poner. El autocompilado de eclipse a veces hace el trabajo lento o pesado. En eclipse me veía frecuentemente esperando por barras del progreso de cosas que el eclipse estaba haciendo por debajo. En IntelliJ de momento no me está pasando.

Soy fan del editor vi. IntelliJ lo lleva por defecto, pero se puede activar/desactivar. Siempre tengo el gvim instalado en windows para determinadas tareas, ahora parece que no me va a hacer falta.

IntelliJ lleva la ventana de comandos del sistema empotrada por defecto. Habitualmente me gusta ejecutar los comandos de gradle, maven, git y subversion desde línea de comandos, porque me da la impresión de tener más control de lo que estoy haciendo. Lo de tener la ventana de comandos integrada en el IDE me está ayudando mucho. Sí, eclipse tiene un plugin opcional para poner.

Otra ventaja que resulta más o menos interesante es que el autocompletar de IntelliJ es más inteligente. Incluso autocompleta el nombre de la variable que estamos declarando, ofreciéndonos sugerencias. Por ejemplo, si tengo una clase MiClase y despues de poner el tipo MiClase le digo a IntelliJ que me diga el posible nombre de variable, me pone miClase.

Pero todo esto no dejan de ser detalles (bueno, quizás lo de las esperas de eclipse no sea tan tonto). Una de las cosas que me ha convencido totalmente de IntelliJ es que IntelliJ entiende por separado los classpath de test y de main. Tanto en gradle como en maven, hay directorios src/main/java y src/test/java y hay dependencias de compilado y dependencias de test. Tanto gradle como maven no incluyen para nada las dependencias de test en compilado o runtime, salvo que se estén haciendo test. Pues bien, eclipse no hace esta distinción en absoluto. En tu código de src/main/java puedes usasr clase que sean de test o de dependencias de test. En eclipse va, pero al compilar luego fuera de eclipse con gradle o maven, da error. IntelliJ sin embargo sí tiene en cuenta esta separación. Tiene su "classpath" de "proyecto_main" y "proyecto_test".

En fin, llevo ya un par de semanas con IntelliJ y me está gustando más que eclipse. Es posible que tenga que volver a eclipse cuando quera utilizar alguna cosa que no venga en la edición gratuita de IntelliJ.

Dec 10

Qué es Spring Boot

Hace unos meses estuve jugueteando con Spring Boot, pero el ponerme a hacer código a lo loco mirando la documentación y algunos tutoriales, no me dio una idea de lo que realmente es Spring Boot.

La idea de Spring Boot es que podamos hacer aplicaciones Spring, pero con cero configuración. No tenemos que hacer ningún fichero de configuración, ni ficheros XML. A Spring Boot, usando una herramienta propia, le decimos qué tipo de aplicación queremos (web, con jpa, con postgres, etc) y él solo nos genera una proyecto maven/gradle con todo lo necesario y que se autoconfigura en el arranque.

Por ejemplo, si decimos que queremos una aplicación web, Spring Boot automáticamente nos embebe un Tomcat y nos lo configura con el servlet de Spring. Por supuesto, si no nos gusta la configuración por defecto (no queremos Tomcat, por ejemplo), sí podemos cambiar cosas para usar jetty, jboss o el que nos guste.

¿y cómo hace esta magia Spring Boot?

Pues añadiendo en nuestro proyecto Maven/Gradle dependencias estilo "spring-boot-starter-****". Por ejemplo, si añadimos la dependencia "spring-boot-starter-web", es la que se encarga de añadir todo lo necesario para embeber un Tomcat y el que nos crea (sin que ni siquiera lo veamos en nuestro proyecto) el fichero web.xml de nuestra aplicación correctamente configurado para Spring. Si añadimos "spring-boot-starter-data-jpa" junto con la dependencia de algún driver de base de datos, como por ejemplo h2, es Spring Boot el que automáticamente en el arranque nos configurará automáticamente los DataSource para esa base de datos y todo lo necesario para acceder a esa base de datos usando JPA.

La magia de Spring Boot va incluso más allá. Una vez añadida la dependencia "spring-boot-starter-data-jpa" y la del driver de nuestra base de datos, sólo tenemos que crear nuestras clases de base de datos con las consabidas anotaciones @Entity, @Id, etc …  y una interfaz (adecuadamente anotada) que herede de la interfaz de Spring CrudRepository<MyBean, Long>, siendo MyBean la clase persistente que nos hemos creado y cuyo id es un Long. Y listo, no tenemos que implementar la interfaz, Spring Boot lo hace por nosotros en tiempo de ejecución, dándonos todos los métodos típicos de consulta/inserción/modificación y borrado de MyBean en base de datos.

Dentro de nuestro proyecto Spring Boot, en src/main/resources hay un fichero, en principo vacío, de nombre applicacion.properties. En ese fichero podemos poner properties propias de Spring Boot para cambiar las configuraciones por defecto de Spring Boot. Un ejemplo tiípico es cambiar el puerto por defecto 8080 de tomcat por otro. Basta añadir en ese fichero la linea server.port=8000 siendo 8000 el puerto que queremos en vez de 8080. Aquí tienes todas las posibles propiedades a poner y valores por defecto.

 

En fin, estoy con ello todavía, pero promete mucho y merece la pena echarle un ojo. Lo comentado aquí es un pequeño resumen de lo que se dice en este vídeo

 

 

Nov 29

git flow

git-flow

( Imagen de Our Workflow: git-flow

Hace poco, viendo un vídeo, descubrí git-flow.

Siempre había oído, tanto en git como en subversion, que hay que tener una rama de desarrollo, una rama para versiones estables, hacer ramas para cada nueva funcionalidad que se va a implementar, etc, etc. Pero siempre me ha parecido muy lioso. Los comandos de crear ramas y juntarlas tanto en subversion como en git, si bien no son especialmente complicados, si son varios comandos a ejecutar para cada una de esas tareas a hacer y requieren que todos los del equipo seamos muy rigurosos para hacerlo bien.

Pues bien, hay una herramienta para git, llamada git-flow, que justamente nos facilita la ejecución de esos comandos, haciendo que el proceso de crear las ramas para cada nueva funcionalidad, para cada nueva release, para corregir bugs, sea bastante más sencilla.

Con git-flow hay dos ramas principalmente. La rama master, en la que están las versiones estables de nuestro software, convenientemente etiquetadas, y la rama development, en la que se hace el desarrollo. La creación de estas ramas es muy sencilla una vez instalado git-flow, basta ejecutar

$ git flow init

Si queremos empezar a trabajar en una nueva funcionalidad, debemos crear una rama para ella que parte de la rama development. Nuevamente, con git-flow es inmediato

$ git flow feature start NOMBRE

donde NOMBRE es el nombre que queramos para la rama. Yo suelo poner el número de tarea de jira. El comando de git-flow crea la rama feature/NOMBRE y nos hace el checkout de ella para que podamos empezar a trabajar. Podemos subirla al servidor si queremos que más desarrolladores trabajen en ella, o dejarla en nuestro git local si sólo vamos a trabajar nosotros. Una vez terminada y probada la funcionalidad, hay que llevarla a la rama development, que se hace con el siguiente comando

$ git flow feature finish NOMBRE

Esto hace el merge de nuestra funcionalidad sobre la rama de desarrollo … y borra la rama de la funcionalidad, es decir, se borra la rama feature/NOMBRE, aunque los commit que hicimos en ella se han pasado a la rama de desarrollo.

Si quisieramos hacer una nueva entrega de nuestro software, es decir, pasar una nueva versión de la rama de desarrollo a la rama estable master, el comando de git-flow es

$ git flow release start VERSION

Esto crea una nueva rama release/VERSION donde podemos tener congelada este nuevo candidato a versión, hacer los cambios que consideremos necesarios para pasar a nueva versión (por ejemplo, cambiar en número de versión en los ficheros pom.xml o build.gradle, cambiar niveles de log, ficheros de configuración o lo que sea). Una vez que nuestra nueva versión está "fetén", para llevarla a la rama master el comando es

$ git flow release finish VERSION

Esto lleva los cambios de release/VERSION tanto a la rama master como a la rama de desarrollo. Etiqueta la rama master con una etiqueta que nos pide y que puede ser en número de versión o lo que queramos. Y finalmente, borra la rama release/VERSION.

Finalmente, si en la rama estable master encontráramos un bug que hubiera que corregir, git flow permite crear una rama directamente sobre la master para corregir el bug. El comando sería

$ git flow hotfix start BUG

Esto crea una rama hotfix/BUG para que corrijamos el bug. Una vez corregido, hay que llevar los cambios a master y suele ser buena idea llevarlos también a la rama de desarrollo, para que el bug quede corregido definitivamente. El comando es

$ git flow hotfix finish BUG

Esto lleva los cambios del bug tanto a master como a development, y borra la rama del bug.

Y esto es lo básico, bastante sencillo. Hay más comandos de git flow, y con opciones, pero con lo comentado aquí, así de sencillo, se tiene ya un flujo de trabajo sobre git bastante completito.

 

Aug 27

¿Por qué Microservicios?

Monolítico vs MicroserviciosHe leído este artículo ¿Por qué Microservicios? y ahí van mis opiniones.

Un resumen del artículo

Si ya te lo has leído, te puedes saltar este apartado, pero por si te da pereza leerlo entero en inglés, comenta que las pegas de una arquitectura tradicional monolítica y basada en capas son:

  1. En una arquitectura monolítica cada desarrollador tiene que "cargar" con todo el código de todo el mundo, no sólo con la parte en la que está especializado. Extraer del control de versiones, compilar completo, desplegar al completo, etc, etc.
  2. Los distintos módulos dependen mucho unos de otros. Un pequeño cambio en uno puede afectar a varios módulos distintos.
  3. En una arquitectura en capas (por ejemplo, interfaz de usuario, capa de negocio y capa de persistencia), los desarrolladores suelen especializarse en una de las capas, Cuando hay algún problema, por no esperar por los desarrolladores de la otra capa, el desarrollador mete en su capa cosas que no deberían serlo. Por ejemplo, accesos directos a base de datos desde la interfaz de usuario.
  4. El equipo de soporte no suele ser el mismo que el de desarrollo, por lo que durante el mantenimiento del software, el equipo de soporte  puede requerir la ayuda del equipo de desarrollo.
  5. En una arquitectura de capas, los desarrolladores de cada capa suelen especializarse en su capa y desconocen las otras. Cuando hay errores en el código, suele haber problemas para saber exactamente en qué capa está el problema y suele ser necesaria la colaboración de los desarrolladores de todas las capas para localizarlo y corregirlo.

A continuación el artículo enumera las ventajas e inconvenientes de una arquitectura basada en microservicios. Cada microservicio es un desarrollo independiente de los demás microservicios, llevado por uno o más desarrolladores que sólo se preocupan de su microservicio. Las ventajas que enumera son

  1. Cada microservicio es independiente de los demás, así que puede desarrollarse en su propio lenguaje de programación. En una arquitectura monolítica, todo debe estar desarrollado en el mismo lenguaje.
  2. El microservicio suele ser pequeño, por lo que el desarrollador se centra en él y lo conoce muy bien.
  3. Los microservicios hablan entre sí por algún protocolo sencillo y "tonto", como REST. Al contrario que en una arquitectura SOA donde los servicios hablan a través de un ESB, que suele tener demasiada inteligencia.
  4. Cada microservicio puede tener su propia base de datos, por lo que incluso uno puede tener una base de datos SQL, otro una base de datos NoSQL, etc.

Y por supuesto, también menciona algunas pegas, que resumidas vienen a ser que el conjunto es más difícil de depurar (es difícil seguir con un debugger las llamadas entre microservicios distintos), no hay un log centralizado y las llamadas entre servicios vía REST suelen ser más lentas que las llamadas internas dentro de una arquitectura monolítica.

mis opiniones

Aunque el artículo está bien y cuenta cosas interesantes, no veo muy claros algunos razonamientos que hace.

En una arquitectura monolítica se puede hacer, además de capas, módulos independientes y separados, con interfaces claras para comunicarse entre ellos. Si cada uno de estos módulos contiene todas las capas y se organiza como un proyecto separado, tenemos parte de las ventajas de los microservicios sin necesidad de microservicios:: Un desarrollador no tiene que cargar con el código de todo el mundo (salvo para el despliegue de su módulo en la aplicación completa), cada desarrollador conocería todas las capas de su módulo, que sería más  pequeño que la aplicación completa, fácil de depurar.  Ahora, siempre es cierto que una cosa son las intenciones (hacer el módulo aislado con una interfaz clara) y otra es la realidad (los desarrolladores usan los módulos de otros saltándose esa interfaz, simplemente porque tienen las clases a mano aunque sea como librería. Esto es más difícil con microservicios.

Lo de que los módulos dependen unos de otros y un pequeño cambio afecta a varios, me suena más a problema de diseño que a problema de la arquitectura en sí. Si cada módulo tiene una interfaz clara definida, no debería haber problemas si cambias el módulo mientras no cambies la interfaz. Lo mismo pasaría con los microservicios si decides cambiar la interfaz REST que ofrecen, afectará a otros módulos.

Lo de que el equipo de soporte no suele ser el mismo que el de desarrollo …. bueno, creo que no tiene nada que ver con la arquitectura que se use. Hay el mismo problema si unos desarrolladores desarrollan los microservicios y luego son otros los que los mantienen.

Que un problema en una arquitectura en capas involucre simultánemente a desarrolladores de cada capa … lo cambiamos porque un problema en una arquitectura en microservicios involucre simultáneamente a desarrolladores de cada microservicio involucrado.

Y en cuanto a las ventajas, bien, cada microservicio se puede desplegar y testear por separado. Eso es una ventaja importante, pero relativamente cierta, ya que ese microservicio puede tener que llamar a otros que también necesitarían ser desplagados y desplegar a su vez los que estos últimos necesiten). Es cierto que se pueden usar lenguajes y bases de datos distintas para cada microservicio, pero esta ventaja es "cuestionable". ¿Se te ocurre algún tipo de aplicación en el que haya partes que sea claramente mejor desarrollarlas en un lenguaje y otras en otro y que haya partes para las que claramente sea mejor una base de datos y para otras partes otra. Posiblemente, a poco que pienses, sí se te ocurre, pero … ¿es una aplicación más o menos estándar de las que se suelen pedir algún cliente?. En cuanto a bases de datos distintas, también se puede hacer así en un sistema monolítico, cada módulo que se desarrolle podría tener su propia base de datos.

Hay sin embargo otras ventajas que tienen los microservicios que no menciona el artículo

La principal, desde mi punto de vista, es que la aplicación es fácilmente "clusterizable". Un desarrollo monolítico corre en  un sólo servidor. Los microservicios podemos ejecutarlos cada uno en un servidor distinto si es necesario.

Otra ya la he mencionado, si un microservicio se desarrolla por separado de los demás y no se meten en el proyecto dependencias del código de otros microservicios, es imposible saltarse las fronteras. Con módulos bien definidos, como al final un módulo depende de la interfaz de otro, también ve sus clases internas (hablo de java, donde si ves un jar, ves todas sus clases), por lo que un desarrollador por descuido o por las prisas, puede saltarse la frontera.

Y una última ventaja, es la reusabilidad. Un microservicio podemos usarlo tal cual posiblemente en otro proyecto, siempre que nos valga tal cual, simplemente con arrancarlo. Con módulos podemos también reusarlos, pero tienen que estar pensados para eso, ya que pueden necesitar configuraciones distintas en otro proyecto, como acceder a otra base de datos, Por supuesto, esto es relativamente cierto, ya que tanto un microservicio como un módulo puede necesitar para funcionar otros microservicios u otros módulos, que también debemos traernos.

resumiendo

Como ventajas de los microservicios veo principalmente cosas como el arranque distribuido en varios servidores o la reutilización en la práctica es más fácil que la de un módulo de una aplicación monolítica.

No me parece que soluciones los problemas de desarrollo tradicionales al hacer software, que creo que son más generales al software que específicos de la arquitectura. Si los módulos tienen una dependencia fuerte más allá de la interfaz, es que están mal diseñados/desarrollados. Si los desarrolladores se especializan en capas, deberían especializarse en módulos y en todas las capas de su módulo. Si cuando los desarrolladores se especializan en un tema hay problemas para resolver los bug, porque nunca se sabe en qué parte está, es común tanto si hablamos de capas, como si hablamos de microservicios que colaboran (¿en qué microservicio está el problema?), como si hablamos de módulos que colaboran.

 

Jul 02

Apache Cassandra vs MongoDB

 Llevamos haciendo pruebas unos días para comparar Apache Cassandra y MongoDB. Nuestro objetivo inicial es insertar datos más o menos grandes (unos 80KBytes)  a razón de 5 inserciones por segundo. No es nada del otro mundo, a poco que nos pongamos posiblemente hasta una base de datos SQL normal nos valga. Sin embargo, queríamos jugar con estas dos bases de datos noSQL y ver qué pasa.

En un mismo servidor hemos montado ambas bases de datos y realizado las inserciones A veces simultáneamente en las dos bases de datos, a veces sólo en una.

En Cassandra configuramos el tiempo de vida de los datos en unas 5 horas, de forma que los datos más viejos de 5 horas se borrarán automáticamente. Mongo tiene dos opciones, una, al igual que Cassandra es la de tiempo de vida, pero otra que nos parece más interesante es la de espacio en disco duro. Así que configuramos Mongo para que borrara automáticamente datos viejos cuando la colección ocupara 10 Gigas.

Pues bien, en estas condiciones, tanto antes de empezar borrados automáticos como después, ambas bases de datos van sobradas. Pueden perfectamente insertar datos y permitir las consultas simultáneamente. Sin embargo, echando un ojo al consumo de recursos, vemos que mongo consume bastante menos en CPU y aunque cassandra tampoco consume demasiado, sí tiene unos picos de consumo de CPU bastante importantes periódicamente. Seguramente algunas de las tareas de mantenimiento del propio cassandra que saltan de vez en cuando.

Logo mongodbCuando empieza el borrado, vemos en disco que mongo tiene sus 10 Gigas y no se pasa ni un pelo. Vemos sin embargo que cassandra sigue creciendo y que hay que esperar a alguna de esas tareas de mantenimiento para que el disco realmente reduzca su espacio.

En cuanto a consultas, mucho mejor mongo. En cassandra nos pasan dos cosas. Un simple select count(*) da timeout, sin necesidad de que haya más allá de un par de miles de registros. La consulta en cassandra de los datos, poniendo bien los índices en la condición y tal va bien, pero nos da esos misteriosos problemas que da a veces cassandra diciendo que no hay bastantes réplicas (¿1 necesaria, 0 disponibles? ¿con un solo servidor que está arrancado?) y sucede cuando consultas en el periodo de tiempo que acaba de borrar por pasarse el tiempo de vida, porque si consultamos dentro de la zona termporal en la que todavía hay datos, entonces los devuelve sin errores.

Bueno, seguro que se puede arreglar y optimizar, pero sin haberse matado en configuraciones, mongo va bien y cassandra hace alguna cosa rara.

Vamos más allá con la prueba. Se supone que cassandra es más rápido en inserciones, así que decidimos insertar datos de pocos bytes (unos 40 bytes), pero del orden de 250 por segundo. Vemos que ambas bases de datos aguantan el ritmo. Vamos subiendo a 500 por segundo, 750 por segundo, etc, etc…. Sorpresa. Mongo va mejor que Cassandra. Mongo aguanta cerca de 1000 por segundo (en nuestro servidor, con nuestro disco, si lo haces en otro será otro resultado), mientras que cassandra ya no puede con 750.

Insisto, no nos hemos matado en optimizar, inserts a pelo, nada de batch, ni bulk inserts ni similar. Lo tenemos pendiente de probar.

De todas formas, y antes estas primeras impresiones, creo que nos quedamos con mongo, al menos para nuestras necesidades. Nos quedan pruebas en cluster,, donde siempre según los de Cassandra, Cassandra es mejor en el sentido que de que si pones el doble de servidores, tienes el doble de todo, mientras que con mongo no es así  En cualquier caso, haremos las pruebas por hacerlas, puesto que en producción no tendremos clusters.