Si quires hacer tus aplicaciones con procesos en múlti-hilo, esté es el primer paso…. (Spring 4.3, Vertx 3.5)
Primero quisiera decir que no es un tema fácil de entender así que daré por hecho ciertos conocimientos y me centraré únicamente en la implementación.
Puntos importantes:
- Configuración Boostrap de Spring
- Uso de Spring Factory
- Configuración de puertos
- Anotaciones importantes @Component, @Aoutowire,@PostConstructor, @Scope(“SCOPE_PROTOTYPE”)
Primero que nada empezaremos con la configuración de Spring Factory. Si te preguntas porque la necesitamos es porque ciertamente necesitamos los servicios de Spring dentro de Vertx y como ya debemos saber Vertx crea múltiples instancias a contrario de Spring que crea singletons para sus servicios y es por eso que muchas veces tenemos conflictos a la hora de implementar Vertx en Spring, Un vez mencionado esto entendemos que necesitamos el “ApplicationContext” de Spring dentro de las clases de que usará Vertx para tener un deploy.
package mx.edu.ebc.core.init
import io.vertx.core.Verticle
import io.vertx.core.spi.VerticleFactory
import org.springframework.beans.BeansException
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.stereotype.Component
@Component
class SpringVerticleFactory implements VerticleFactory, ApplicationContextAware {
ApplicationContext applicationContext;
@Override
public boolean blockingCreate() {
return true;
}
@Override
public String prefix() {
return "ebc"; // Nombre para tomar la referencia de tus clases
}
@Override
public Verticle createVerticle(String verticleName, ClassLoader classLoader) throws Exception {
String clazz = VerticleFactory.removePrefix(verticleName);
return (Verticle) applicationContext.getBean(Class.forName(clazz));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Posterior a esto pasaremos a la creación de la clase boostrap la cual en donde lograremos definir los verticles que ocuparemos a lo largo de nuestra aplicación, así como sus opciones de despliegue. Lo importante de esta clase es que la haremos como un componente de spring el cual se ejecutara justo de haber levantado la aplicacion junto con el contexto ya que es requerido.
Shell
Si queremos utilizar la shell de vertx basta con añadir el siguiente código a la clase Bootstrap:
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setTelnetOptions(
new TelnetTermOptions().
setHost("localhost").
setPort(4000)
)
)
service.start()
En la clase mencionada se debe de hacer la anotación @PostConstruct al metodo que tiene que construir en este caso el encargado de hacer nuestros despliegues del verticle mientras de igual manera se inyecta el VerticleFactory el cual correspopnde a la interfaz implementada en nuestra clase SpringVerticleFactory.
Despliegue de un verticle
Para hacer un despliegue de un verticle se deberá realizar de la siguiente manera.
vertx.deployVerticle("${verticleFactory.prefix()}:${CourseVerticle.class.name}")
A continuación dejo el código completo de la clase Bootstrap.
package mx.edu.ebc.core.init
import javax.annotation.PostConstruct
import mx.edu.ebc.core.verticle.*
import org.springframework.context.ApplicationContext
import io.vertx.core.Vertx
import io.vertx.core.spi.VerticleFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.context.annotation.Scope
import io.vertx.core.DeploymentOptions
import io.vertx.ext.web.handler.StaticHandler
import io.vertx.ext.web.Router
import io.vertx.ext.web.handler.sockjs.SockJSHandler
import io.vertx.ext.web.handler.sockjs.BridgeOptions
import io.vertx.ext.web.handler.sockjs.PermittedOptions
import io.vertx.ext.shell.*
import io.vertx.ext.shell.term.*
@Component
class Bootstrap {
@Autowired
VerticleFactory verticleFactory
@PostConstruct
void deployVerticles(){
println "*"*100
Vertx vertx = Vertx.vertx()
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setTelnetOptions(
new TelnetTermOptions().
setHost("localhost").
setPort(4000)
)
);
service.start();
vertx.registerVerticleFactory(verticleFactory)
DeploymentOptions opts_1 = new DeploymentOptions()
opts_1.setWorker(true)
opts_1.setInstances(5)
DeploymentOptions opts_2 = new DeploymentOptions()
opts_2.setWorker(true)
opts_2.setInstances(50)
vertx.deployVerticle("${verticleFactory.prefix()}:${CourseVerticle.class.name}", opts_1)
vertx.deployVerticle("${verticleFactory.prefix()}:${ProcessVerticle.class.name}", opts_1)
vertx.deployVerticle("${verticleFactory.prefix()}:${EnrollmentCourseVerticle.class.name}", opts_2)
//vertx.deployVerticle(new CourseVerticle(applicationContext), options)
//vertx.deployVerticle(new MainVerticle(applicationContext))
//vertx.deployVerticle(new ProcessVerticle(applicationContext))
//vertx.deployVerticle(new PingVerticle(applicationContext))
//vertx.deployVerticle(new PongVerticle(applicationContext))
def verticles = vertx.deploymentIDs()
println "Verticles: ${verticles.dump()}"
def server = vertx.createHttpServer()
def router = Router.router(vertx)
BridgeOptions bridgeOptions = new BridgeOptions()
bridgeOptions.addOutboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.create.course"))
.addOutboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.list.course"))
bridgeOptions.addInboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.create.course"))
.addInboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.list.course"))
bridgeOptions.addInboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.warn.course"))
bridgeOptions.addInboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.finish"))
.addOutboundPermitted(new PermittedOptions().setAddress("mx.edu.ebc.moodle.finish"));
def ebHandler = SockJSHandler.create(vertx).bridge(bridgeOptions)
router.route("/eventbus/*").handler(ebHandler)
server.requestHandler(router.&accept).listen(9090)
println "Vert.x is deployed "
}
}
Servidor para web sockets js
Si deseas levantar un servidor para la comunicación de tu front deberás hacerlo en la clase mencionada antes como se muestra en el código.
Ejemplo de clase para desplegar:
Importante mencionar que estas clases deberán llevar la anotación @Scope(SCOPE_PROTOTYPE) para que Spring logre hacer múltiples instancias ya que así lo requiere vertx.
package mx.edu.ebc.core.verticle
import io.vertx.core.AbstractVerticle
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
import groovy.json.JsonSlurper
import mx.edu.ebc.api.service.EnrollmentService
import mx.edu.ebc.moodle.model.MoodleCourse
import static org.springframework.beans.factory.config.ConfigurableBeanFactory.*
import io.vertx.core.DeploymentOptions
@Component
@Scope(SCOPE_PROTOTYPE)
class EnrollmentCourseVerticle extends AbstractVerticle {
@Autowired
EnrollmentService enrollmentService
@Override
void start() throws Exception {
super.start()
vertx.eventBus().consumer("mx.edu.ebc.moodle.enrollment.course") { message ->
println message.body().shortname
List<MoodleCourse> moodleCourse = []
moodleCourse << new MoodleCourse(
shortname: "${message.body().shortname}+T10",
idnumber:"${message.body().shortname}+T10",
category: "Vertx-test",
fullname: message.body().subject,
summary:"",
format:"",
startdate:"",
groupmode:"",
enablecompletion:"",
coursetemplate:"MER23432-P"
)
enrollmentService.createCoursesInMoodle(moodleCourse)
vertx.eventBus().send("mx.edu.ebc.moodle.bulid","Termine el curso ${message.body().shortname} ")
}
}
}
Conclusión
La verdad es que esta implementan me agrado bastante ya que al principio tuve muchas complicaciones para hacer las instancias de vertx ya que todo se ejecutaba de manera secuencial, lo cual no era el punto de la tecnología. Pero finalmente una vez ya trabajando me sorprendió su funcionamiento.
Pd: Si usas java, ya dejalo!!! usa groovy y descubre una forma más divertida de programar 😃