Ativação do host baseando-se na memória

by Israel Aece 20. October 2009 09:03

O WCF fornece uma opção que nos permite configurar a porcentagem mínima de memória disponível na máquina antes de ativar o serviço. Para customizar isso, recorremos a propriedade (atributo) MinFreeMemoryPercentageToActivateService da classe (elemento) ServiceHostingEnvironmentSection. Essa propriedade recebe um número inteiro, que corresponde a quantidade de memória disponível que a máquina onde o serviço está rodando deverá ter disponível.

Como essa configuração recorre a métodos não gerenciados para fazer essa verificação, a aplicação (serviço) deverá estar rodando em full-trust. Caso contrário, uma exceção do tipo SecurityException será disparada e, consequentemente, o serviço não estará disponível para receber requisições.

Tags: , ,

WCF

Host de Serviços em Aplicações Windows

by Israel Aece 24. September 2009 21:42

Uma das grandes vantagens do WCF é que qualquer aplicação pode servir como hosting para ele. Isso quer dizer que além do tradicional IIS, podemos recorrer à outros tipos, como Windows Services, Console, Windows Forms ou WPF. Cada uma tem as suas vantagens e desvantagens, mas isso já foi abordado neste artigo.

Cada uma dessas opções de hosting, possuem implementações diferentes, e aplicações Windows Forms e WPF tem um sofrimento maior em relação às outras. Isso se deve ao fato de que aplicações Windows não gerenciam apenas o serviço, mas também há uma interface (formulários) para controlar. Ao utilizar um host próprio, temos que recorrer à classe ServiceHost que gerenciará a vida e a execução do serviço. O problema já começa aqui, ou seja, onde criar e manter a instância desta classe?

Antes de prosseguirmos, precisamos analisar o que são os Synchronization Contexts. Synchronization Contexts permite executarmos uma determinada tarefa em uma outra thread, diferente da qual estamos atualmente, representando uma espécie de “canal” entre as duas threads envolvidas, fazendo tudo o que for necessário para isso. Dentro do namespace System.Threading existe uma classe chamada SynchronizationContext, que representa esse “canal”, e o WCF interage com ele através da propriedade boleana UseSynchronizationContext do atributo ServiceBehaviorAttribute, que por padrão é True.

Na configuração padrão, quando criamos o ServiceHost e invocamos o método Open, o WCF irá verificar se há um synchronization context definido e o utilizará. Quando não existir ou a propriedade UseSynchronizationContext é definida explicitamente como False, as operações serão executadas em uma outra thread, que são extraídas do ThreadPool.

Como o Windows Forms cria automaticamente o SynchronizationContext no construtor de um formulário, quando criamos a instância da classe ServiceHost dentro dele, o WCF irá executar todas as operações nesta mesma thread (que também atende aos comandos do usuário (message loop)), pois o contexto já está criado. O exemplo abaixo ilustra isso:

public partial class CadastroDeClientes : Form
{
    private ServiceHost _host;

    public CadastroDeClientes()
    {
        //Neste momento, o context já está criado
        this._host = new ServiceHost(typeof(ServicoDeClientes), new Uri[] { });
    }
}

O problema desta técnica é que você sobrecarregará a thread de UI, já que ela terá que se preocupar com as operações do serviço e com os eventos tradicionais do formulário. Se você optar por abrir o host antes da chamada de um formulário, como por exemplo, dentro do método Main da aplicação, você ainda não terá o contexto estabelecido. Neste caso, qualquer manipulação que você, eventualmente, faça nos controles do formulário dentro das operações do serviço, precisarão de um tratamento especial, pois você não poderá manipular os controles em uma thread diferente da qual eles foram criados.

Finalmente, se você estiver com a propriedade UseSynchronizationContext definida como True (que é o padrão), e estiver consumindo o serviço no mesmo formulário que possui o ServiceHost criado, você terá problemas. Isso se deve ao fato de que a chamada para o serviço bloqueia a thread de UI, enquanto o WCF posta a mensagem para essa mesma thread para invocar o serviço (que está ocupada). Sendo assim, o deadlock será garantido.

Tags: , ,

WCF

Registrando a URL HTTP para serviços WCF

by Israel Aece 10. September 2009 08:48

Quando você está desenvolvendo um serviço WCF, e o usuário que utiliza para rodar a aplicação de hosting não tem privilégios administrativos, provavelmente você irá se deparar com a seguinte mensagem de erro:

AddressAccessDeniedException: HTTP could not register URL http://+:8080/Servico.  Your process does not have access rights to this namespace.

Ao rodar a aplicação que efetuará o hosting do serviço, o WCF recorre ao HTTP.sys (componente para comunicação HTTP sem uso do IIS) para criar a URL onde o serviço será exposto. Depois da URL criada, essas requisições serão encaminhadas para o respectivo serviço que efetuou o registro. No entanto, para efetuar o registro, você precisa de privilégios administrativos que, eventualmente, você não tenha.

Para resolver esse problema você pode rodar como "Administrador" (mas você já sabe a implicação disso), ou recorrer a uma ferramenta de linha de comando chamada netsh.exe. Dado a URL e o usuário, ele permite o acesso à URL à este usuário, através de ACLs. Abaixo o código que você deve executar (como "Administrador") para conceder o acesso:

netsh http add urlacl url=http://+:80/Servico user=IANB01\IsraelAece

Tags: ,

WCF

WCF Vídeo - Deployment

by Israel Aece 9. September 2009 07:51
Deployment Deployment

Este vídeo exibe como podemos proceder para efetuar a distribuição de um serviço WCF, de uma forma superficial. Como exemplo, migraremos o hosting de uma aplicação Console para um Windows Service. Além disso, ele ainda aborda algumas práticas para a reutilização do proxy entre aplicações.

Formato: WMV - Duração: 00:38:05 - Tamanho: 50MB

Tags: , , ,

WCF

WCF Vídeo - Hospedando um Serviço

by Israel Aece 18. August 2009 08:08
Hospedando um Serviço Hospedando um Serviço

Criar o contrato e a classe que representa o serviço não é o suficiente para que ele funcione. O hosting é o responsável por gerenciar a execução do serviço, que determinará como, onde e o que será disponibilizado aos consumidores. Este vídeo irá mostrar as possibilidades de hosting que temos no WCF. Para maiores detalhes, consulte este artigo.

Formato: WMV - Duração: 00:41:44 - Tamanho: 52MB

Tags: , , , ,

WCF

WCF – Roteamento de Mensagens

by Israel Aece 5. August 2009 22:10

Ao desenvolver um serviço WCF, disponibilizamos um endpoint de acesso ao mesmo, permitindo que clientes o consumam diretamente. Independentemente da tarefa que ele venha a desempenhar, fica sob responsabilidade do mesmo, através de algum ponto de extensibilidade ou até mesmo em sua implementação, efetuar alguma customização ou reutilização em termos de infraestrutura, como segurança, caching, etc.

Esse tipo de customização visa centralizar alguns processos, facilitando a reutilização e gerenciamento dessas tarefas. Outro ponto importante no desenvolvimento de serviço, é a questão do balanceamento de carga. Ao publicar um serviço e muitos clientes passarem a consumí-lo, provavelmente o servidor onde ele ficar hospedado não comportará esse aumento. Neste caso, cria-se um segundo servidor para distribuir a execução do serviço, e através de algum software ou hardware, faz a configuração necessária para direcionar as requisições de acordo com a sua capacidade/disponibilidade.

Esses são alguns dos típicos cenários para o uso de roteamento de mensagens. A ideia do roteador é receber uma mensagem e encaminhá-la, podendo ou não fazer alguma verificação. Até a versão atual do WCF (3.5), é necessário uma grande quantidade de código para a criação deste roteador, enquanto na versão 4.0, que está por vir, já trará esse serviço nativamente, e é o que veremos no decorrer deste artigo.

Todos os tipos necessários para criarmos este roteador estão abaixo de um novo namespace, chamado System.ServiceModel.Routing (assembly System.ServiceModel.Routing.dll). Assim como a implementação manual que existia antes da versão 4.0, o roteador será disponibilizado como um serviço WCF qualquer, mas implementando alguns contratos (Interfaces) específicos, que determinarão como as mensagens serão encaminhadas para o respectivo serviço. A classe responsável por representar o serviço de roteamento é chamada de RoutingService, que por sua vez, implementa as seguintes Interfaces: ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter e IDuplexSessionRouter.

Cada uma dessas Interfaces descrevem as funcionalidades suportadas pelo serviço de roteamento. A primeira delas, ISimplexDatagramRouter, traz suporte ao processamento assíncrono de uma mensagem, suportando tipos “one-way”; já a Interface ISimplexSessionRouter, possibilita o processamento de mensagens que requerem sessões; a Interface IRequestReplyRouter possibilita ao serviço de roteamento, processar mensagens do tipo requisição-resposta, podendo ou não suportar sessões e, finalmente, a Interface IDuplexSessionRouter, que permite ao roteador processar mensagens “duplex” (aquelas que suportam callbacks).

Todas essas Interfaces estão implementadas na classe RoutingService, podendo ela tratar qualquer requisição, para os mais variados tipos de mensagens. A idéia de ter isso tudo isolado em Interfaces é que, eventualmente, você possa vir a criar um serviço de roteamento que suporte apenas um dos tipos.

A implementação e a forma como criamos e hospedamos um serviço não muda em nada. Continuamos criando os contratos, criação da classe que representa o serviço e o hosting do mesmo, com os respectivos endpoints. Em princípio, as aplicações que consomem o serviço também não mudam em nada. A única – grande – diferença é que entre essas duas partes haverá um intermediário, que como vimos acima, será o responsável por encaminhar as mensagens do cliente para o serviço e as mensagens do serviço para o cliente.

Uma vez que o serviço estiver construído, é necessário criarmos um serviço que servirá como roteador. Como vimos acima, a classe que representa isso é a RouterService, e podemos hospedá-la em qualquer hosting suportado pelo WCF. Já as Interfaces, que também foram comentadas acima, serão utilizadas para construir os endpoints do roteador, nos obrigando a escolher a Interface correta, em sincronia com o tipo de mensagem exposto pelo serviço efetivo.

Como o roteador será um serviço qualquer, também temos que configurar o(s) endpoint(s) e behavior(s). Como sabemos, uma das características do endpoint é o endereço, e neste caso, ele será utilizado pelos clientes para enviar a mensagem para qualquer um dos serviços que estão atrás do roteador, que por sua vez, se baseará em filtros para encaminhar a mensagem ao serviço correto.

Para o exemplo teremos dois serviços: um responsável pelo gerenciamento dos usuários e outro pelo gerenciamento de clientes, e ambos estarão acessíveis através do roteador. Um dos serviços (“ServicoDeUsuarios”) foi criado e disponibilizado utilizando o binding BasicHttpBinding, e as mensagens são do tipo requisição-resposta. Já o segundo serviço (“ServicoDeClientes”) possuirá apenas um método do tipo “one-way”, sendo disponibilizado através do binding WSHttpBinding. Dessa forma, o roteador será criado com dois endpoints distintos, onde o primeiro deles é configurado com o binding BasicHttpBinding e com o contrato IRequestReplyRouter, enquanto o segundo, utilizará o binding WSHttpBinding e o contrato definido como ISimplexDatagramRouter. Abaixo temos a configuração parcial do roteador:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="System.ServiceModel.Routing.RoutingService"
               behaviorConfiguration="routerConfig">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9997/Router"/>
          </baseAddresses>
        </host>
        <endpoint address="rr"
                  binding="basicHttpBinding"
                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />
        <endpoint address="ow"
                  binding="wsHttpBinding"
                  contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="routerConfig">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <routing filterOnHeadersOnly="false" 
                       routingTableName="RouterMapping" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <!-- Outras Configurações -->
  </system.serviceModel>
</configuration>

Analisando essa primeira parte do arquivo de configuração do serviço de roteamento, podemos notar que o serviço que está sendo exposto é o RoutingService. Na sua definição vemos o baseAddress e dois endpoints. O endereço especificado no baseAddress, é o endereço que os clientes utilizarão para efetuar a comunicação com o roteador. Logo em seguida temos dois endpoints, onde o primeiro define o binding BasicHttpBinding e o contrato IRequestReplyRouter, ou seja, aceitará requisição através deste binding, suportando o tipo de mensagem requisição-resposta. Já o segundo, utiliza o binding WSHttpBinding com o contrato ISimplexDatagramRouter, ou seja, suporte à operações do tipo “one-way”.

Podemos reparar também que o serviço está referenciando um behavior chamado “routerConfig”. Dentro desta seção de configuração, além das opções comuns, como a disponibilidade de metadados, exceções, temos um novo behavior, chamado de RoutingBehavior (representando pelo elemento <routing />). Esse elemento possui apenas dois atributos: filterOnHeadersOnly e routingTableName. O primeiro atributo recebe um valor boleano indicando se poderemos ou não utilizar o corpo da mensagem para aplicar um determinado filtro (veremos mais sobre isso abaixo). O segundo atributo, define o nome de uma seção (que deve estar no mesmo arquivo de configuração), onde definiremos todos os filtros necessários para avaliar e, consequentemente, efetuar o encaminhamento da mensagem para o respectivo serviço.

Antes de falarmos efetivamente sobre os filtros, há uma seção muito importante e que é necessário efetuarmos a configuração da forma correta. Esta seção, delimitada pelo elemento <client />, muitas vezes é utilizada do lado de aplicações consumidoras, para especificar o endpoint que será utilizado por ela para efetuar a comunicação com o serviço. Neste contexto, esse elemento tem uma finalidade diferente, ou seja, de especificar o nome, endereço, binding e contrato dos serviços para os quais, eventualmente, o roteador encaminhará as mensagens. Abaixo podemos visualizar como fica a configuração dele:

<client>
  <endpoint name="Servico1"
            address="http://localhost:9998/usuarios"
            binding="basicHttpBinding"
            contract="*" />
  <endpoint name="Servico2"
            address="http://localhost:9999/clientes"
            binding="wsHttpBinding"
            contract="*" />
</client>

Na configurações destes endpoints, elencamos o endereço de cada serviço para qual o roteador enviará a mensagem, e a única e principal diferença em relação a uma configuração tradicional, é a presença o caracter “*” como contrato. Isso quer dizer que o serviço poderá receber a mensagem de qualquer contrato, obviamente, desde que passe pelos critérios que serão estabelecidos nos filtros.

A terceira e última parte do arquivo de configuração do roteador, consiste na definição dos filtros e como eles serão avaliados. No trecho de código abaixo, o elemento <routing /> agrupa dois sub-elementos que compõem o sistema de filtragem. O primeiro deles é a seção <filters />. Como o próprio nome diz, é uma coleção de filtros, onde cada filtro é represetado pelo elemento <filter />, que por sua vez, possui três atributos: name, filterType e filterData. O atributo name é autoexplicativo; já o atributo filterType especifica como será analisado o filtro. No exemplo abaixo, estou verificando a propriedade Action no header da mensagem. Finalmente, o atributo filterData é o valor a ser comparado com o qual foi extraído da mensagem.

O segundo sub-elemento é chamado de <routingTables />. Este elemento também possui uma coleção de entradas, onde ele relaciona um filtro à um endpoint cadastrado previamente (através do elemento <client />), por exemplo, se o “FiltroServico1” for avaliado como verdadeiro, a mensagem será encaminhada para o endpoint “Servico1”, e o mesmo acontecerá para o endpoint “Servico2” se o filtro “FiltroServico2” for atendido.

<routing>
  <filters>
    <filter name="FiltroServico1"
            filterType="Action"
            filterData="http://tempuri.org/IUsuarios/Adicionar" />
    <filter name="FiltroServico2"
            filterType="Action"
            filterData="http://tempuri.org/IClientes/Notificar" />
  </filters>
  <routingTables>
    <table name="RouterMapping">
      <entries>
        <add filterName="FiltroServico1" endpointName="Servico1" />
        <add filterName="FiltroServico2" endpointName="Servico2" />
      </entries>
    </table>
  </routingTables>
</routing>

Observação: Se existir dois endpoints do tipo “one-way” ou “duplex”, e que apontam para um mesmo filtro, que por sua vez, foi atendido, a mensagem será encaminhada para ambos endpoints.

Action é um dos filtros possíveis. Você pode utilizar o filtro do tipo XPath, para que através de uma query XPath, você consiga efetuar validações/consultas mais complexas, podendo inclusive analisar o corpo da mensagem. Com essa opção, você terá uma grande flexibilidade, já que conseguirá extrair mais informações e ter maior precisão na hora de avaliar/aplicar o filtro. Outro tipo de filtro é o MatchAll, que como o próprio nome já diz, acatará todas as mensagens. Basicamente, um filtro nada mais é do que uma classe que herda de MessageFilter, e sendo assim, você pode criar os teus próprios filtros, controlando como eles serão aplicados.

É importante dizer que os filtros são avaliados de acordo com a prioridade. Para determiná-la, podemos utilizar o atributo priority na coleção de filtros do elemento <routingTables />. Para o exemplo deste artigo, isso não faz muito sentido, já que temos dois filtros e cada um deles lidará com um tipo de mensagem específico, mas em um cenário onde você conseguir detectar a frequência de acesso, você pode determinar a prioridade para tirar melhor proveito em termos de performance, evitando que ele gaste tempo na avaliação dos outros filtros. Ao encontrar um filtro que atenda a requisição, a mensagem é encaminhada para o endpoint correspondente, caso contrário, uma exceção será disparada.

Outras Funcionalidades

Ainda há alguns outras funcionalidades que estão diretamente ligadas ao sistema de roteamento de mensagens. A primeira delas é a capacidade de aplicar filtros no corpo da mensagem. Como falado acima, tudo o que você precisa fazer é definir o atributo filterOnHeadersOnly para False e vasculhar o corpo da mensagem em busca dos parâmetros necessários para avaliar/aplicar o filtro, e para isso, você pode utilizar a seção <namespaceTable />. É através dela que conseguimos estabelecer a relação de namespaces com seus respectivos prefixos, para que assim, consiga encontrar e navegar pelos elementos que estão dentro da mensagem.

Como vimos acima, relacionamos um filtro à um determinado endpoint, e caso esse filtro seja atendido, a mensagem será encaminhada para o endpoint correspondente. Mas e se houver alguma falha de comunicação, como por exemplo, timeout? Para isso, a Microsoft também disponibilizou um elemento chamado <alternateEndpoints />. Através dele, podemos relacionar uma lista de endpoints, e caso a mensagem falhe, automaticamente o WCF tentará reencaminhar para o outro endpoint desta lista, até que algum deles processe com sucesso. Além deste elemento, tudo o que precisamos fazer é relacionar a lista de endpoints ao filtro, através do atributo alternateEndpoints, como vemos abaixo:

<routing>
  <!-- Outras Configurações -->
  <routingTables>
    <table name="RouterMapping">
      <entries>
        <add filterName="FiltroServico1"
             endpointName="Servico1"
             alternateEndpoints="alternateEndpoints" />
      </entries>
    </table>
  </routingTables>
  <alternateEndpoints>
    <list name="alternateEndpoints">
      <endpoints>
        <add endpointName="Servico1Servidor2"/>
        <add endpointName="Servico1Servidor3"/>
      </endpoints>
     </list>
  </alternateEndpoints>
</routing>

Para finalizar, outra funcionalidade que temos é a capacidade que o serviço de roteamento tem para trocar o binding que está sendo utilizado na comunicação entre o cliente e o roteador e entre o roteador e o cliente. Isso quer dizer que você pode definir que a comunicação com que o cliente terá com o roteador seja através do binding WSHttpBinding, enquanto a mensagem será encaminhada para o serviço através do binding NetTcpBinding.

Conclusão: Baseando-se nos problemas que vimos no início deste artigo, um roteador pode ajudar imensamente a resolvê-los e, felizmente, com este recurso disponível a partir da versão 4.0 do WCF, trará novas capacidades e funcionalidades para incorporarmos em nossas aplicações, tornando-as muitos mais poderosas, e resumindo alguns processos que antes eram complexos de serem realizados, em configurações extremamente simples.

RoteamentoDeMensagens.zip (38.73 kb)

Tags: , , ,

WCF

IIS, WCF e Partial Trust

by Israel Aece 9. July 2009 11:39

Há algum tempo eu falei sobre a possibilidade de invocar serviços WCF em aplicações parcialmente confiáveis. Mas ainda há um segundo cenário, que é a exposição de serviços através de aplicações ASP.NET Web Site/WCF Service. Para criar um serviço WCF, você não precisa necessariamente criar um tipo de projeto exclusivo como o WCF Service. Um projeto do tipo ASP.NET Web Site pode, tranquilamente, servir como host de um serviço WCF, e para isso, basta adicionar um arquivo *.svc e configurá-lo corretamente no Web.config.

Independentemente de qual das alternativas utilize, você poderá se deparar com uma restrição de segurança, e dependendo das funcionalidades (mais precisamente do binding e seus elementos) que utiliza, o serviço não rodará. Isso muitas vezes acontece quando você faz o deployment para um servidor, em que a configuração padrão do .NET Framework foi alterada (visando uma maior segurança). Uma das regras mais importantes que se deve ter ao configurar um servidor Web (IIS), é não permitir que as aplicações que rodem ali executem em "Full Trust". Para isso, se altera o arquivo Web.config (que está em nível de servidor), definindo o atributo level do elemento trust para "Medium" ou qualquer nível abaixo disso.

Neste ambiente, você poderá utilizar os bindings BasicHttpBinding, WSHttpBinding ou o WebHttpBinding, desde que eles estejam com a segurança desabilitada ou com a proteção em nível de transporte. O binding WSDualHttpBinding também não pode ser utilizado neste cenário, já que algumas tarefas que ele desempenha exige um nível de segurança mais elevado. Finalmente, para tentar resolver este problema, podemos fazer uso das técnicas mostradas pelo Juval Lowy neste artigo, disponibilizando alguns helpers para facilitar a criação de hosts em ambientes parcialmente confiáveis.

Tags: , ,

Security | WCF

ClientAccessPolicy.xml sem IIS

by Israel Aece 15. May 2009 11:50

Um problema que sempre ocorre quando estamos tentando consumir um serviço WCF no Silverlight, é a ausência do arquivo ClientAccessPolicy.xml dentro do diretório onde está hospedado o serviço. Para que ele seja consumido por uma aplicação Silverlight, você deve criar esse arquivo na raiz do serviço, com a mesma estrutura definida neste artigo. Esse arquivo permite que o Silverlight invoque serviços a partir de um dominío diferente de onde ele reside.

Mas, por algum motivo, você está criando uma aplicação Console ou até mesmo um Windows Service, que servirá como o host para o serviço. Lá você configura a classe ServiceHost expondo o serviço através do BasicHttpBinding. Com isso você já consegue referenciá-lo no Silverlight, mas quando executar a aplicação, você receberá um erro de comunicação. Sabemos que é a ausência do arquivo acima mencionado, mas como estamos utilizando um host que não é o IIS, como devemos proceder para disponibilizar esse arquivo?

O Carlos Figueira criou uma solução interessante para isso, onde definiu uma interface que, quando implementada na classe que representa o serviço, gera a estrutura do arquivo ClientAccessPolicy.xml (ali está também suportando o padrão de arquivo necessário para que o Flash se comunique com o serviço WCF). Com o uso do atributo WebGetAttribute, ele configura a propriedade UriTemplate, redirecionando as requisições de cada um desses arquivos para o método correspondente.

Tags: , , ,

WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host