Inicialização de instanciação
O exemplo de inicialização estende o exemplo de pool definindo uma interface, IObjectControl
que personaliza a inicialização de um objeto ativando-o e desativando-o. O cliente invoca métodos que retornam o objeto para o pool e que não retornam o objeto para o pool.
Nota
O procedimento de configuração e as instruções de compilação para este exemplo estão localizados no final deste tópico.
Pontos de extensibilidade
A primeira etapa na criação de uma extensão do Windows Communication Foundation (WCF) é decidir o ponto de extensibilidade a ser usado. No WCF, o termo EndpointDispatcher refere-se a um componente de tempo de execução responsável por converter mensagens de entrada em invocações de método no serviço do usuário e por converter valores de retorno desse método em uma mensagem de saída. Um serviço WCF cria um EndpointDispatcher para cada ponto de extremidade.
O EndpointDispatcher oferece extensibilidade de escopo de ponto de extremidade (para todas as mensagens recebidas ou enviadas pelo serviço) usando a EndpointDispatcher classe. Essa classe permite que você personalize várias propriedades que controlam o comportamento do EndpointDispatcher. Este exemplo se concentra na InstanceProvider propriedade que aponta para o objeto que fornece as instâncias da classe de serviço.
IInstanceProvider
No WCF, o EndpointDispatcher cria instâncias de uma classe de serviço usando um provedor de instância que implementa a IInstanceProvider interface. Esta interface tem apenas dois métodos:
GetInstance: Quando uma mensagem chega, o Dispatcher chama o GetInstance método para criar uma instância da classe de serviço para processar a mensagem. A frequência das chamadas para este método é determinada pela InstanceContextMode propriedade. Por exemplo, se a InstanceContextMode propriedade estiver definida como InstanceContextMode.PerCall, uma nova instância da classe de serviço será criada para processar cada mensagem que chegar, assim GetInstance será chamada sempre que uma mensagem chegar.
ReleaseInstance: Quando a instância de serviço termina de processar a mensagem, o EndpointDispatcher chama o ReleaseInstance método. Como no GetInstance método, a frequência das chamadas para este método é determinada pela InstanceContextMode propriedade.
O pool de objetos
A ObjectPoolInstanceProvider
classe contém a implementação para o pool de objetos. Esta classe implementa a IInstanceProvider interface para interagir com a camada de modelo de serviço. Quando o EndpointDispatcher chama o GetInstance método, em vez de criar uma nova instância, a implementação personalizada procura um objeto existente em um pool na memória. Se houver um disponível, ele é devolvido. Caso contrário, ObjectPoolInstanceProvider
verifica se a ActiveObjectsCount
propriedade (número de objetos retornados do pool) atingiu o tamanho máximo do pool. Caso contrário, uma nova instância é criada e retornada ao chamador e ActiveObjectsCount
é posteriormente incrementada. Caso contrário, uma solicitação de criação de objeto será enfileirada por um período de tempo configurado. A implementação para GetObjectFromThePool
é mostrada no código de exemplo a seguir.
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"));
}
A implementação personalizada ReleaseInstance
adiciona a instância liberada de volta ao pool e diminui o ActiveObjectsCount
valor. O EndpointDispatcher pode chamar esses métodos de threads diferentes e, portanto, o acesso sincronizado aos membros de nível de ObjectPoolInstanceProvider
classe na classe é necessário.
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);
}
O ReleaseInstance
método fornece um recurso de inicialização de limpeza. Normalmente, o pool mantém um número mínimo de objetos durante o tempo de vida do pool. No entanto, pode haver períodos de uso excessivo que exigem a criação de objetos adicionais no pool para atingir o limite máximo especificado na configuração. Eventualmente, quando o pool se torna menos ativo, esses objetos excedentes podem se tornar uma sobrecarga extra. Portanto, quando o atinge zero, um temporizador ocioso activeObjectsCount
é iniciado que aciona e executa um ciclo de limpeza.
if (activeObjectsCount == 0)
{
idleTimer.Start();
}
As extensões de camada ServiceModel são conectadas usando os seguintes comportamentos:
Comportamentos de serviço: permitem a personalização de todo o tempo de execução do serviço.
Comportamentos de ponto de extremidade: permitem a personalização de um ponto de extremidade de serviço específico, incluindo o EndpointDispatcher.
Comportamentos contratuais: permitem a personalização de uma ClientRuntime ou DispatchRuntime classes no cliente ou no serviço, respectivamente.
Comportamentos de operação: permitem a personalização de uma ClientOperation ou DispatchOperation classes no cliente ou no serviço, respectivamente.
Para a finalidade de uma extensão de pool de objetos, um comportamento de ponto de extremidade ou um comportamento de serviço pode ser criado. Neste exemplo, usamos um comportamento de serviço, que aplica a capacidade de pool de objetos a cada ponto de extremidade do serviço. Os comportamentos de serviço são criados implementando a IServiceBehavior interface. Há várias maneiras de tornar o ServiceModel ciente dos comportamentos personalizados:
Usando um atributo personalizado.
Adicioná-lo imperativamente à coleção de comportamentos da descrição do serviço.
Estendendo o arquivo de configuração.
Este exemplo usa um atributo personalizado. Quando o ServiceHost é construído, ele examina os atributos usados na definição de tipo do serviço e adiciona os comportamentos disponíveis à coleção de comportamentos da descrição do serviço.
A IServiceBehavior interface tem três métodos: Validate,
,
AddBindingParameterse .ApplyDispatchBehavior Esses métodos são chamados pelo WCF quando o ServiceHost está sendo inicializado. IServiceBehavior.Validate é chamado primeiro; permite que o serviço seja inspecionado quanto a inconsistências. IServiceBehavior.AddBindingParameters é chamado próximo; Este método só é necessário em cenários muito avançados. IServiceBehavior.ApplyDispatchBehavior é chamado por último e é responsável por configurar o tempo de execução. Os seguintes parâmetros são passados para IServiceBehavior.ApplyDispatchBehavior:
Description
: Este parâmetro fornece a descrição do serviço para todo o serviço. Isso pode ser usado para inspecionar dados de descrição sobre os pontos de extremidade do serviço, contratos, associações e outros dados associados ao serviço.ServiceHostBase
: Este parâmetro fornece o ServiceHostBase que está sendo inicializado no momento.
Na implementação personalizadaIServiceBehavior, uma nova instância de é instanciada e atribuída à InstanceProvider propriedade em cada EndpointDispatcher uma que está anexada ServiceHostBaseObjectPoolInstanceProvider
ao .
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;
}
}
}
}
}
Além de uma IServiceBehavior implementação, a ObjectPoolingAttribute
classe tem vários membros para personalizar o pool de objetos usando os argumentos de atributo. Esses membros incluem MaxSize
, MinSize
Enabled
e CreationTimeout
, para corresponder ao conjunto de recursos de pool de objetos fornecido pelo .NET Enterprise Services.
O comportamento de pool de objetos agora pode ser adicionado a um serviço WCF anotando a implementação do serviço com o atributo personalizado ObjectPooling
recém-criado.
[ObjectPooling(MaxSize=1024, MinSize=10, CreationTimeout=30000]
public class PoolService : IPoolService
{
// …
}
Ativação e desativação de ganchos
O objetivo principal do pool de objetos é otimizar objetos de curta duração com criação e inicialização relativamente caras. Portanto, ele pode dar um impulso de desempenho dramático para um aplicativo se usado corretamente. Como o objeto é retornado do pool, o construtor é chamado apenas uma vez. No entanto, alguns aplicativos exigem algum nível de controle para que possam inicializar e limpar os recursos usados durante um único contexto. Por exemplo, um objeto que está sendo usado para um conjunto de cálculos pode redefinir seus campos privados antes de processar o próximo cálculo. O Enterprise Services habilitou esse tipo de inicialização específica do contexto, permitindo que o desenvolvedor do objeto substitua Activate
e Deactivate
os métodos da ServicedComponent classe base.
O pool de objetos chama o Activate
método imediatamente antes de retornar o objeto do pool. Deactivate
é chamado quando o objeto retorna ao pool. A ServicedComponent classe base também tem uma boolean
propriedade chamada CanBePooled
, que pode ser usada para notificar o pool se o objeto pode ser agrupado ainda mais.
Para imitar essa funcionalidade, o exemplo declara uma interface pública (IObjectControl
) que tem os membros acima mencionados. Essa interface é então implementada por classes de serviço destinadas a fornecer inicialização específica do contexto. A IInstanceProvider implementação deve ser modificada para atender a esses requisitos. Agora, cada vez que você obtém um objeto chamando o GetInstance
método, você deve verificar se o objeto implementa IObjectControl.
Se isso acontecer, você deve chamar o Activate
método apropriadamente.
if (obj is IObjectControl)
{
((IObjectControl)obj).Activate();
}
Ao retornar um objeto para o pool, uma verificação é necessária para a CanBePooled
propriedade antes de adicionar o objeto de volta ao pool.
if (instance is IObjectControl)
{
IObjectControl objectControl = (IObjectControl)instance;
objectControl.Deactivate();
if (objectControl.CanBePooled)
{
pool.Push(instance);
}
}
Como o desenvolvedor do serviço pode decidir se um objeto pode ser agrupado, a contagem de objetos no pool em um determinado momento pode ficar abaixo do tamanho mínimo. Portanto, você deve verificar se a contagem de objetos ficou abaixo do nível mínimo e executar a inicialização necessária no procedimento de limpeza.
// 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());
}
}
Quando você executa o exemplo, as solicitações de operação e as respostas são exibidas nas janelas do console de serviço e do cliente. Pressione Enter em cada janela do console para desligar o serviço e o cliente.
Para configurar, compilar e executar o exemplo
Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para criar a solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.
Para executar o exemplo em uma configuração de máquina única ou cruzada, siga as instruções em Executando os exemplos do Windows Communication Foundation.