Tutorial III de HPC Pack SOA: modo interactivo
En tutorial II analizamos cómo tratar con un servicio que consume mucho tiempo mediante el empleo de una sesión duradera. Sin embargo, el modo por lotes no es el único modo de cálculo en el mundo de HPC. Algunos cálculos se pueden finalizar en unos segundos a unos minutos. El usuario final puede esperar una respuesta casi en tiempo real.
Este modo conlleva diferentes desafíos en comparación con el modo por lotes. El tiempo de respuesta es más crítico. Por lo tanto, no se puede omitir la sobrecarga de inicio de la sesión. En un clúster de HPC típico, se tarda unos segundos en iniciar una nueva sesión. Si hay otros trabajos que se ejecutan en el clúster, la sesión recién creada debe esperar hasta que los recursos estén disponibles, lo que hace que el tiempo de inicio sea mucho mayor. Afortunadamente, HPC Pack proporciona una manera de tratar esta situación y reducir el costo innecesario.
Consulte la de ejemplo de código que acompaña para seguir los pasos de este artículo.
Implementación del servicio
Usamos el mismo servicio que en tutorial II: el servicio de factorización prime. Para satisfacer los requisitos en tiempo real, solo pasaremos números pequeños al servicio.
Este es el contrato de servicio:
[ServiceContract]
public interface IPrimeFactorization
{
[OperationContract]
List<int> Factorize(int n);
}
Y esta es la implementación del servicio:
public List<int> Factorize(int n)
{
List<int> factors = new List<int>();
for (int i = 2; n > 1;)
{
if (n % i == 0)
{
factors.Add(i);
n /= i;
}
else
{
i++;
}
}
return factors;
}
Implementación del cliente
Para ahorrar el tiempo de inicio de un nuevo trabajo, el cliente debe reutilizar la sesión existente en lugar de crear una nueva, ya que la creación de una nueva sesión significa iniciar un nuevo trabajo. Para reutilizar la sesión existente, es necesario crear la sesión de la siguiente manera:
const string headnode = "head.contoso.com";
const string serviceName = "PrimeFactorizationService";
SessionStartInfo info = new SessionStartInfo(headnode, serviceName);
//Enable session pool
info.ShareSession = true;
info.UseSessionPool = true;
Puede observar que hay dos nuevas propiedades de SessionStartInfo que se asignan aquí.
Establecer ShareSession en true significa que cualquier usuario puede enviar solicitudes al agente, no solo la que crea la sesión.
Al establecer UseSessionPool en true, se garantiza que cada nuevo cliente use la sesión existente en lugar de crear otra. El grupo de sesiones se mantiene en el lado servidor; garantiza que cuando un cliente se conecta al mismo servicio con la marca establecida en true, siempre devuelve la misma sesión siempre que todavía esté activa.
Ahora podemos crear la sesión. No queremos usar una sesión duradera porque puede afectar al rendimiento.
//create an interactive session
using (Session session = Session.CreateSession(info))
{
Console.WriteLine("Session {0} has been created", session.Id);
…
}
Cree un cliente de agente para enviar solicitudes y obtener respuestas.
En el caso del código anterior, ahora tenemos una situación en la que puede haber muchos clientes de agente en una sola sesión. En este caso, se debe asignar un identificador único al cliente.
//in one session, each broker client should have a unique id
string ClientId = Guid.NewGuid().ToString();
using (BrokerClient<IPrimeFactorization> client = new BrokerClient<IPrimeFactorization>(ClientId, session))
{
Console.WriteLine("BrokerClient {0} has been created", ClientId);
Random random = new Random();
int num = random.Next(1, Int32.MaxValue);
//Send request
FactorizeRequest request = new FactorizeRequest(num);
client.SendRequest<FactorizeRequest>(request, num);
client.EndRequests();
//Get response
foreach (BrokerResponse<FactorizeResponse> response in client.GetResponses<FactorizeResponse>())
{
int number = response.GetUserData<int>();
int[] factors = response.Result.FactorizeResult;
Console.WriteLine("{0} = {1}", number, string.Join<int>(" * ", factors));
}
}
Ahora ejecute el cliente dos veces. Verá que los clientes comparten el mismo identificador de sesión, como resultado del grupo de sesiones habilitado. Además, el primer cliente se ejecuta mucho más tiempo que el segundo, lo que indica que el nuevo cliente reutiliza la sesión creada.
Dado que GetResponses es una función sincrónica, el cliente se bloqueará y se mantendrá esperando los resultados. Esto no es una situación de bienvenida en un sistema en tiempo real, así que vamos a probar otra manera de obtener respuestas.
Podemos establecer un de devolución de llamada asincrónica para el cliente mediante SetResponseHandler de la siguiente manera:
//use this event sync main thread and callback
AutoResetEvent done = newAutoResetEvent(false);
//set callback function. this handler will be invoke before service replies.
client.SetResponseHandler<FactorizeResponse>((response) =>
{
int number = response.GetUserData<int>();
int[] factors = response.Result.FactorizeResult;
Console.WriteLine("{0} = {1}", number, string.Join<int>(" * ", factors));
//release the lock
done.Set();
});
Por lo tanto, después de enviar normalmente solicitudes, el cliente puede continuar con otro trabajo. Cuando las respuestas estén listas, se llamará al controlador de respuesta para mostrar los resultados.
Implementación y prueba del servicio
Puede seguir este tutorial para implementar y probar el caso paso a paso.
Puede ejecutar varios clientes. La salida será similar a la siguiente:
Observe que todos los clientes comparten el mismo identificador de sesión.
Crecimiento automático del clúster y reducción automática
Una situación común del modo interactivo es ejecutar un servicio de larga duración que atiende a varios clientes. Para responder a cada solicitud lo antes posible, debemos mantener la sesión activa. Pero, por otro lado, tener un trabajo de SOA ocupa un gran número de recursos durante los tiempos fuera del pico es desperdiciado.
HPC Pack tiene una característica para aumentar y reducir los recursos en función del número de solicitudes. Si no hay ninguna solicitud, reducirá el número de recursos al número mínimo especificado por el trabajo. Al recibir solicitudes, aumentará automáticamente los recursos para controlarlos.
Nota: una sesión agotará el tiempo de espera si no hay ningún cliente que se conecte a él durante un período de tiempo. Para que una sesión sea un servicio de larga duración, puede cambiar el sessionIdleTimeout de