Hoy examinaremos una de las capas más complejas del sistema, la capa Request. Comparando esta capa con las anteriores (Conexión y Sesión) que son básicamente pasa-mano, esta capa agrega buena parte de la complejidad del sistema. ¿Por qué? Porque es la encargada del manejo de pedidos y respuestas concurrentes entre los clientes y el servidor.
En las decisiones tomadas en el diseño de esta capa primó la escalabilidad: capacidad del servidor de procesar gran cantidad de pedidos y conservando tiempos de respuestas bajos. (Aclaro que no se busca desarrollar el RPC con más rendimiento del mundo, me conformo con uno que sea útil para cargas medias. Habrá un artículo dedicado al rendimiento del sistema).
Al diseñador!
Primero vamos a analizar el lado del servidor. Lo que buscamos es la capacidad de recibir y procesar pedidos concurrentemente, y claro, enviar las respuestas correspondientes. Siguiendo con la filosofía de las capas inferiores, vamos a tener la interfaz RequestPacketListener para escuchar pedidos, RequestPacket y se van a responder con la clase ResponsePacket.

Diseño de la capa Pedido, lado servidor
La clase principal es RequestManager. Al implementar SessionPacketListener tiene la capacidad de recibir los mensajes de la capa Session y procesarlos. “Procesarlos” significa notificar al RequestPacketListener asociado. Ahora, para que esto sea concurrente, nos vamos a valer de la clase ThreadPoolExectutor para despachar los pedidos. Este Executor tiene un pool de threads -workers- que serán los que en definitiva invocan a requestPacketArrived() del RequestPacketListener para notificar la llegada del pedido. Antes de iniciar el procesamiento, a estos workers se les setea el contexto, que es el Map<String, Object> de Session; esto se hace mediante la clase WorkerContext.
Ahora el cliente. En principio esta capa del lado del cliente podría ser simplemente otro pasamano, pero como queremos que un mismo cliente pueda realizar varios pedidos concurrentemente, tenemos que dale un poco más de pienso. Además, vamos a implementar un send() sincrónico. Así, podemos escribir:
ResponsePacket response = requestManager.send(request);
Entonces ya esto se está pareciendo cada vez más a un RPC.
Pero, cómo hacer para que una operación que es naturalmente asíncrona (el send de sockets), sea sincrónica? Simple, utilizando semáforos (¡busy-waiting está prohibido!). Por cada pedido se va a crear un semáforo (Future) sobre el cual vamos a esperar la respuesta luego de enviar el pedido al servidor. Cuando la respuestas llega se notifica y el atributo data está cargado. Como queremos que un mismo cliente pueda realizar varios pedidos concurrentemente, vamos a tener una colección de estos semáforos.
El atributo key en las clases RequestPacket y ResponsePacket es para encontrar al destinatario de la respuesta.

Diseño de la capa Pedido, lado cliente
Bueno, ahora si, esto ya es un RPC: mediante el RequestManager, podemos realizar pedidos al servidor y esperar la respuesta. Mientras el cliente espera, el servidor procesa la respuesta con el RequestPacketListener.
Entonces, para implementar un método remoto, tenemos que crear un RequestPacketListener que haga lo que necesitamos y desde el cliente, mandar el RequestPacket con el método send de la clase RequestManager:
RequestPacket req = new RequestPacket(key, “obtener_valor_de_pi”); ResponsePacket resp = requestManager.send(req); double pi = (Double)resp.getData();
Y en el servidor:
public class RemoteExecution implements RequestPacketListener {
public ResponsePacket requestPacketArrived(RequestPacket requestPacket) {
if (requestPacket.getPayload().equals(“obtener_valor_de_pi”)) {
return new ResponsePacket(requestPacket.getKey(), 3.1416);
}
return null;
}
}
Si bien este mecanismo es válido, está lejos de lo que queríamos; que era tener la misma sintaxis que la invocación a métodos locales:
double pi = mathService.getPI();
Y su implementación:
public MathService {
public double getPI() {
return 3.1416;
}
}
Si podemos lograr esto último, estamos en la situación que queríamos desde el principio. En el próximo artículo vamos a ver la capa Servicio que, entre otras cosas, nos va a resolver este problema.
Hasta el próximo!
