Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O Exemplo de inicialização estende o exemplo de Pooling 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.
Observação
O procedimento de instalação e as instruções de compilação dessa amostra estão no final deste tópico.
Pontos de extensibilidade
A primeira etapa na criação de uma extensão do WCF (Windows Communication Foundation) é decidir o ponto de extensibilidade a ser usado. No WCF, o termo EndpointDispatcher refere-se a um componente de runtime responsável por converter mensagens de entrada em invocações de método no serviço do usuário e para converter valores retornados 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 classe EndpointDispatcher. Essa classe permite que você personalize várias propriedades que controlam o comportamento do EndpointDispatcher. Esta amostra se concentra na propriedade InstanceProvider 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 interface IInstanceProvider. Essa interface tem apenas dois métodos:
GetInstance: quando uma mensagem chega, o Dispatcher chama o método GetInstance para criar uma instância da classe de serviço para processar a mensagem. A frequência das chamadas para esse método é determinada pela propriedade InstanceContextMode. Por exemplo, se a propriedade InstanceContextMode for definida como InstanceContextMode.PerCall, uma nova instância da classe de serviço é criada para processar cada mensagem que chega, portanto, GetInstance será chamado sempre que uma mensagem chegar.
ReleaseInstance: quando a instância de serviço termina de processar a mensagem, o EndpointDispatcher chama o método ReleaseInstance. Assim como no método GetInstance, a frequência das chamadas para esse método é determinada pela propriedade InstanceContextMode.
O pool de objetos
A classe ObjectPoolInstanceProvider contém a implementação do pool de objetos. Essa classe implementa a interface IInstanceProvider para interagir com a camada do modelo de serviço. Quando o EndpointDispatcher chama o método GetInstance, 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 retornará. Caso contrário, ObjectPoolInstanceProvider verifica se a propriedade ActiveObjectsCount (número de objetos retornados do pool) atingiu o tamanho máximo do pool. Se não tiver atingido, uma nova instância será criada e retornada ao chamador e ActiveObjectsCount será incrementada posteriormente. Caso contrário, uma solicitação de criação de objeto será enfileirada por um período configurado. A implementação de 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 valor ActiveObjectsCount. O EndpointDispatcher pode chamar esses métodos de threads diferentes e, portanto, o acesso sincronizado aos membros de nível da classe na classeObjectPoolInstanceProvider é obrigató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 método ReleaseInstance fornece um recurso de inicialização de limpeza. Normalmente, o pool mantém um número mínimo de objetos para 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. Quando o pool finalmente se torna menos ativo, esses objetos excedentes podem se tornar uma sobrecarga extra. Portanto, quando activeObjectsCount chega a zero, um temporizador ocioso é iniciado, disparando e executando um ciclo de limpeza.
if (activeObjectsCount == 0)
{
idleTimer.Start();
}
As extensões da camada do ServiceModel são conectadas usando os seguintes comportamentos:
Comportamentos do serviço: permitem a personalização de todo o runtime do serviço.
Comportamentos do ponto de extremidade: permitem a personalização de um ponto de extremidade de serviço específico, incluindo o EndpointDispatcher.
Comportamentos do contrato: permitem a personalização da classe ClientRuntime ou DispatchRuntime no cliente ou no serviço, respectivamente.
Comportamentos de operação: permitem a personalização da classe ClientOperation ou DispatchOperation no cliente ou no serviço, respectivamente.
Para os fins de uma extensão de pooling de objetos, é possível criar um comportamento de ponto de extremidade ou um comportamento de serviço. Neste exemplo, usamos um comportamento de serviço, que aplica a capacidade de pooling de objetos a todos os pontos de extremidade do serviço. Os comportamentos de serviço são criados pela implementação da interface IServiceBehavior. Há várias maneiras de tornar o ServiceModel ciente dos comportamentos personalizados:
Usando um atributo personalizado.
Adicionando-o imperativamente à coleção de comportamentos da descrição do serviço.
Estendendo o arquivo de configuração.
Esta amostra usa um atributo personalizado. Quando ServiceHost for construído, ele examinará os atributos usados na definição de tipo do serviço e adicionará os comportamentos disponíveis à coleção de comportamentos da descrição do serviço.
A interface IServiceBehavior tem três métodos: Validate,AddBindingParameters, e ApplyDispatchBehavior. Esses métodos são chamados pelo WCF quando ServiceHost está sendo inicializado.
IServiceBehavior.Validate é chamado primeiro; permite que o serviço seja inspecionado quanto a inconsistências.
IServiceBehavior.AddBindingParameters é chamado em seguida; esse método só é necessário em cenários muito avançados.
IServiceBehavior.ApplyDispatchBehavior é chamado por último e é responsável por configurar o runtime. Os seguintes parâmetros são passados para IServiceBehavior.ApplyDispatchBehavior:
Description: esse parâmetro fornece a descrição do serviço para todo o serviço. Pode ser usado para inspecionar dados de descrição sobre pontos de extremidade, contratos, associações e outros dados associados ao serviço.ServiceHostBase: esse parâmetro fornece o ServiceHostBase que está sendo inicializado no momento.
Na implementação personalizada IServiceBehavior, uma nova instância de ObjectPoolInstanceProvider é instanciada e atribuída à propriedade InstanceProvider em cada EndpointDispatcher anexado ao 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;
}
}
}
}
}
Além de uma implementação IServiceBehavior, a classe ObjectPoolingAttribute 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 pooling 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
{
// …
}
Ligando a ativação e a desativação
O objetivo principal do pooling de objetos é otimizar objetos de curta duração com criação e inicialização relativamente dispendiosas. Portanto, ele pode aumentar bastante o desempenho de um aplicativo, se usado corretamente. Como o objeto retorna ao 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 ao contexto, permitindo que o desenvolvedor de objetos substituísse os métodos Activate e Deactivate da classe base ServicedComponent.
O pool de objetos chama o método Activate pouco antes de retornar o objeto do pool.
Deactivate é chamado quando o objeto retorna para o pool. A classe base ServicedComponent também tem uma propriedade boolean chamada CanBePooled, que pode ser usada para notificar o pool se o objeto puder ser agrupado ainda mais.
Para imitar essa funcionalidade, o exemplo declara uma interface pública (IObjectControl) que tem os membros mencionados acima. Essa interface é, então, implementada por classes de serviço destinadas a fornecer a inicialização específica ao contexto. A implementação IInstanceProvider deve ser modificada para atender a esses requisitos. Agora, sempre que você obtém um objeto chamando o método GetInstance, deve verificar se o objeto implementa IObjectControl.. m caso afirmativo, você deve chamar o método Activate adequadamente.
if (obj is IObjectControl)
{
((IObjectControl)obj).Activate();
}
Ao retornar um objeto ao pool, uma verificação é necessária para a propriedade CanBePooled 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 de serviços 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 a amostra, as solicitações de operação e as respostas são exibidas no serviço e na janela do console do cliente. Pressione Enter em cada janela do console para desligar o serviço e o cliente.
Para configurar, compilar, e executar o exemplo
Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para compilar a solução, siga as instruções contidas em Como compilar as amostras do Windows Communication Foundation.
Para executar a amostra em uma configuração de computador único ou entre computadores, siga as instruções contidas em Como executar as amostras do Windows Communication Foundation.