Compartir vía


Creación de instancias de inicialización

En el ejemplo de inicialización se extiende el ejemplo de Pooling al definir una interfaz, IObjectControl, que personaliza la inicialización de un objeto mediante su activación y desactivación. El cliente invoca métodos que devuelven el objeto al grupo y que no devuelven el objeto al grupo.

Nota

El procedimiento de instalación y las instrucciones de compilación de este ejemplo se encuentran al final de este tema.

Puntos de extensibilidad

El primer paso para crear una extensión de Windows Communication Foundation (WCF) es decidir el punto de extensibilidad que se va a usar. En WCF, el término EndpointDispatcher hace referencia a un componente en tiempo de ejecución responsable de convertir los mensajes entrantes en invocaciones de método en el servicio del usuario y para convertir valores devueltos de ese método a un mensaje saliente. Un servicio WCF crea un EndpointDispatcher para cada punto de conexión.

EndpointDispatcher proporciona la extensibilidad (para todos los mensajes recibidos o enviados por el servicio) del ámbito del extremo mediante la clase EndpointDispatcher. Esta clase le permite personalizar varias propiedades que controlan el comportamiento de EndpointDispatcher. Este ejemplo se centra en la propiedad InstanceProvider que señala al objeto que proporciona las instancias de la clase de servicio.

IInstanceProvider

En WCF, EndpointDispatcher crea instancias de una clase de servicio utilizando un proveedor de instancias que implementa la interfaz IInstanceProvider. Esta interfaz tiene solo dos métodos:

Agrupación de objetos

La clase ObjectPoolInstanceProvider contiene la implementación para el grupo de objetos. Esta clase implementa la interfaz IInstanceProvider para interactuar con el nivel de modelo de servicio. Cuando EndpointDispatcher llama al método GetInstance, en lugar de crear una nueva instancia, la implementación personalizada busca un objeto existente en un grupo en memoria. Si hay uno disponible, se devuelve. De lo contrario, ObjectPoolInstanceProvider comprueba si la propiedad ActiveObjectsCount (número de objetos devueltos desde el grupo) ha alcanzado el tamaño máximo del grupo. Si no, se crea una nueva instancia y se devuelve al autor de la llamada y, como consecuencia, se incrementa ActiveObjectsCount. De lo contrario, se pone en la cola una solicitud de creación de objetos para un período configurado de tiempo. Se muestra la implementación para GetObjectFromThePool en el código de ejemplo siguiente.

private object GetObjectFromThePool()
{
    bool didNotTimeout =
       availableCount.WaitOne(creationTimeout, true);
    if(didNotTimeout)
    {
         object obj = null;
         lock (poolLock)
        {
             if (pool.Count != 0)
             {
                   obj = pool.Pop();
                   activeObjectsCount++;
             }
             else if (pool.Count == 0)
             {
                   if (activeObjectsCount < maxPoolSize)
                   {
                        obj = CreateNewPoolObject();
                        activeObjectsCount++;

                        #if (DEBUG)
                        WritePoolMessage(
                             ResourceHelper.GetString("MsgNewObject"));
                       #endif
                   }
            }
           idleTimer.Stop();
      }
     // Call the Activate method if possible.
    if (obj is IObjectControl)
   {
         ((IObjectControl)obj).Activate();
   }
   return obj;
}
throw new TimeoutException(
ResourceHelper.GetString("ExObjectCreationTimeout"));
}

La implementación ReleaseInstance personalizada agrega la instancia liberada de nuevo al grupo y disminuye el valor de ActiveObjectsCount. EndpointDispatcher puede llamar a estos métodos desde subprocesos diferentes y, por consiguiente, se necesita tener acceso sincronizado a los miembros de nivel de clase en la clase ObjectPoolInstanceProvider.

public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
    lock (poolLock)
    {
        // Check whether the object can be pooled.
        // Call the Deactivate method if possible.
        if (instance is IObjectControl)
        {
            IObjectControl objectControl = (IObjectControl)instance;
            objectControl.Deactivate();

            if (objectControl.CanBePooled)
            {
                pool.Push(instance);

                #if(DEBUG)
                WritePoolMessage(
                    ResourceHelper.GetString("MsgObjectPooled"));
                #endif
            }
            else
            {
                #if(DEBUG)
                WritePoolMessage(
                    ResourceHelper.GetString("MsgObjectWasNotPooled"));
                #endif
            }
        }
        else
        {
            pool.Push(instance);

            #if(DEBUG)
            WritePoolMessage(
                ResourceHelper.GetString("MsgObjectPooled"));
            #endif
        }

        activeObjectsCount--;

        if (activeObjectsCount == 0)
        {
            idleTimer.Start();
        }
    }

    availableCount.Release(1);
}

El método ReleaseInstance proporciona una característica de inicialización de limpieza. Normalmente el grupo mantiene un número mínimo de objetos para la duración del grupo. Sin embargo, puede haber períodos de uso excesivo que requieren la creación de objetos adicionales en el grupo para alcanzar el límite máximo especificado en la configuración. Finalmente, cuando el grupo se vuelve menos activo, esos objetos adicionales pueden suponer una sobrecarga adicional. Por consiguiente, cuando activeObjectsCount llega a cero, se inicia un temporizador inactivo que activa y realiza un ciclo de limpieza.

if (activeObjectsCount == 0)
{
    idleTimer.Start();
}

Las extensiones de nivel de ServiceModel se enlazan utilizando los comportamientos siguientes:

  • Comportamientos del servicio: permiten la personalización del tiempo de ejecución completo del servicio.

  • Comportamientos del punto de conexión: permiten la personalización de un punto de conexión de servicio determinado, incluido EndpointDispatcher.

  • Comportamientos del contrato: permiten la personalización de las clases ClientRuntime o DispatchRuntime en el cliente o el servicio respectivamente.

  • Comportamientos de la operación: permiten la personalización de las clases ClientOperation o DispatchOperation en el cliente o el servicio respectivamente.

Con el objetivo de una extensión de agrupación de objetos, se puede crear un comportamiento de punto de conexión o de servicio. En este ejemplo, utilizamos un comportamiento de servicio, que aplica la capacidad de agrupación de objetos a cada punto de conexión del servicio. Los comportamientos de servicio se crean implementando la interfaz IServiceBehavior. Hay varias maneras de hacer que ServiceModel sea consciente de los comportamientos personalizados:

  • Utilizar un atributo personalizado.

  • Agregarlo de manera imperativa a la colección de comportamientos de la descripción del servicio.

  • Extender el archivo de configuración.

Este ejemplo utiliza un atributo personalizado. Cuando se construye ServiceHost, examina los atributos utilizados en la definición de tipo del servicio y agrega los comportamientos disponibles a la colección de comportamientos de la descripción del servicio.

La interfaz IServiceBehavior tiene tres métodos: Validate,AddBindingParameters, y ApplyDispatchBehavior. WCF llama a estos métodos cuando se está inicializando ServiceHost. Primero se llama a IServiceBehavior.Validate; permite inspeccionar el servicio para ver si hay incoherencias. Después se llama a IServiceBehavior.AddBindingParameters; este método solo se necesita en escenarios muy avanzados. En último lugar se llama a IServiceBehavior.ApplyDispatchBehavior, que es responsable de configurar el tiempo de ejecución. Los parámetros siguientes se pasan a IServiceBehavior.ApplyDispatchBehavior:

  • Description: este parámetro proporciona la descripción del servicio para todo el servicio. Esto se puede utilizar para inspeccionar los datos de la descripción sobre los extremos del servicio, los contratos, enlaces y otros datos asociados al servicio.

  • ServiceHostBase: este parámetro proporciona ServiceHostBase que se inicializa actualmente.

En la implementación IServiceBehavior personalizada, se crea una nueva instancia de ObjectPoolInstanceProvider y se asigna a la propiedad InstanceProvider en cada EndpointDispatcher que está adjuntada a ServiceHostBase.

public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
    if (enabled)
    {
        // Create an instance of the ObjectPoolInstanceProvider.
        instanceProvider = new ObjectPoolInstanceProvider(description.ServiceType,
        maxPoolSize, minPoolSize, creationTimeout);

        // Assign our instance provider to Dispatch behavior in each
        // endpoint.
        foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
        {
             ChannelDispatcher cd = cdb as ChannelDispatcher;
             if (cd != null)
             {
                 foreach (EndpointDispatcher ed in cd.Endpoints)
                 {
                        ed.DispatchRuntime.InstanceProvider = instanceProvider;
                 }
             }
         }
     }
}

Además de una implementación IServiceBehavior, la clase ObjectPoolingAttribute cuenta con varios miembros para personalizar el grupo de objetos mediante los argumentos de atributo. Estos miembros incluyen MaxSize, MinSize, Enabled y CreationTimeout, para que coincida con el conjunto de características de agrupación de objetos proporcionada por .NET Enterprise Services.

El comportamiento de agrupación de objetos se puede agregar ahora a un servicio WCF al anotar la implementación del servicio con el atributo ObjectPooling personalizado que se acaba de crear.

[ObjectPooling(MaxSize=1024, MinSize=10, CreationTimeout=30000]
public class PoolService : IPoolService
{
  // …
}

Activación y desactivación de enlace

El objetivo principal de la agrupación de objetos es optimizar los objetos de corta duración con una creación e inicialización relativamente cara. Por consiguiente, puede aumentar considerablemente el rendimiento de una aplicación si se utiliza correctamente. Dado que el objeto se devuelve desde el grupo, al constructor se llama solo una vez. Sin embargo, algunas aplicaciones requieren cierto nivel de control para que puedan inicializar y limpiar los recursos utilizados durante un contexto único. Por ejemplo, un objeto que está siendo utilizado por un conjunto de cálculos puede restablecer los campos privados antes de procesar el cálculo siguiente. Enterprise Services habilitó este tipo de inicialización específica del contexto permitiendo al desarrollador de objetos invalidar los métodos Activate y Deactivate de la clase base ServicedComponent.

El conjunto de objetos llama al método Activate justo antes de devolver el objeto. Se llama a Deactivate cuando el objeto vuelve al conjunto. La clase base ServicedComponent también tiene una propiedad boolean llamada CanBePooled, que se puede utilizar para notificar al grupo si el objeto puede agruparse más adelante.

Para imitar esta funcionalidad, el ejemplo declara una interfaz pública (IObjectControl) que tiene los miembros mencionados anteriormente. Las clases de servicio implementan esta interfaz con objeto de proporcionar inicialización específica de contexto. Se debe modificar la implementación IInstanceProvider para cumplir estos requisitos. Ahora, cada vez que obtiene un objeto llamando al método GetInstance, debe comprobar si el objeto implementa IObjectControl.. Si lo hace, debe llamar al método Activate de forma adecuada.

if (obj is IObjectControl)
{
    ((IObjectControl)obj).Activate();
}

Al devolver un objeto al grupo, se requiere una comprobación para la propiedad CanBePooled antes de volver a agregar el objeto al grupo.

if (instance is IObjectControl)
{
    IObjectControl objectControl = (IObjectControl)instance;
    objectControl.Deactivate();
    if (objectControl.CanBePooled)
    {
       pool.Push(instance);
    }
}

Dado que el programador del servicio puede decidir si se puede agrupar un objeto, el recuento de objetos en el grupo a una hora determinada puede estar por debajo del tamaño mínimo. Por consiguiente, debe comprobar si el recuento de objetos ha caído por debajo del nivel mínimo y realizar la inicialización necesaria en el procedimiento de limpieza.

// Remove the surplus objects.
if (pool.Count > minPoolSize)
{
  // Clean the surplus objects.
}
else if (pool.Count < minPoolSize)
{
  // Reinitialize the missing objects.
  while(pool.Count != minPoolSize)
  {
    pool.Push(CreateNewPoolObject());
  }
}

Al ejecutar el ejemplo, las solicitudes y las respuestas de operación se muestran tanto en la ventanas de la consola del cliente como del servicio. Presione ENTRAR en cada ventana de la consola para cerrar el servicio y el cliente.

Configurar, compilar y ejecutar el ejemplo

  1. Asegúrese de que ha realizado el procedimiento de instalación única para los ejemplos de Windows Communication Foundation.

  2. Para compilar la solución, siga las instrucciones que se indican en Compilación de los ejemplos de Windows Communication Foundation.

  3. Para ejecutar el ejemplo en una configuración de una sola máquina o de varias máquinas, siga las instrucciones que se indican en Ejecución de los ejemplos de Windows Communication Foundation.