maandag 3 juni 2013

Onderzoek naar Windows Azure Service Bus (deel 1)

Software voor bedrijven is altijd een heikel punt geweest. Ze willen niet dat derden bij hun kostbare data komen. Vandaar dat die applicaties in het algemeen in een zwaar geïsoleerde omgeving (het intranet) draaien. Nadeel van deze benadering is dat je erg veel geld kwijt bent aan de softwarepakketten, infrastructuur en updates. Vanwege de groeiende populariteit van het internet worden steeds meer diensten aangeboden in de cloud. Die zijn veelal goedkoper omdat ze op één plaats worden geïnstalleerd en onderhouden en meerdere klanten kunnen van die specifieke dienst gebruik maken. Dan kun je wel eens een probleem krijgen met de integratie van jouw intranet applicatie met zo'n dienst. Wellicht wil je juist wat intranetdiensten naar buiten openen om bijvoorbeeld mobiele applicaties te ondersteunen. Dan heb je geen zin om allemaal gaten in jouw firewall te schieten. Hier komt nu Windows Azure Service Bus om de hoek kijken.

Windows Azure Service Bus is een technologie om applicaties eenvoudig met elkaar te kunnen laten communiceren en wel zo dat de voordelen van de intranet omgeving, zoals veiligheid, betrouwbaarheid en snelheid niet aangetast worden.
In Windows Azure Service Bus kun je drie soorten van communicatie onderscheiden:
  1. Service Bus Relay. Hierbij moeten toepassingen, waar ook ter wereld, kunnen communiceren met jouw (on-premise) service, waarvan het adres in het algemeen niet bekend of onbereikbaar is.  
  2. Queues. Wanneer je een stortvloed aan berichten krijgt die door één server niet meer te behappen zijn, maar wel behandeld moeten worden. Zo'n queue is een wachtrij waarin de berichten voor een langere tijd kunnen worden opgeslagen totdat ze afgehandeld kunnen worden door een server.
  3. Topic/Subscriptions. Hierbij heb je te maken met softwarepakketten die geïnteresseerd zijn in bepaalde onderwerpen. Denk hierbij aan een verkooponderwerp. Wanneer een verkoop plaats vindt, zullen andere toepassingen, zoals een inventarisatiesysteem of een boekhoudsysteem, hiervan op de hoogte willen worden gebracht, zodat ze hierop gepaste actie kunnen onderemen in hun systeem.
In dit eerste artikel wil ik kijken naar Service Bus Relay.
Service Bus Relay maakt het mogelijk om services, zoals een WCF service, bereikbaar te maken voor toepassingen buiten het intranet (zie afbeelding 1).

Afbeelding 1. Service Bus Relay. Applicaties buiten het intranet kunnen communiceren met jouw WCF-service die in de beveiligde omgeving draait. Bron: Windows Azure
Service Bus Relay neemt hierbij de rol van een Message Broker aan, dat wil zeggen, externe applicaties sturen hun berichten naar de Message Broker die vervolgens het bericht naar de echte WCF-service doorstuurt. Eventuele reacties worden via de Message Broker weer naar de applicatie teruggestuurd. Service Bus Relay ondersteunt een scala aan communicatiepatronen, zoals request/response, fire and forget en duplex communicatie. Laten we maar meteen beginnen met het lastigste scenario, de duplex communicatie.

Bij duplex communicatie kan niet alleen de client contact opnemen met de service, maar de service kan ook contact opnemen met de client. Chatservers zullen dit principe in het algemeen toepassen. Wanneer een client iets te melden heeft, stuurt hij dat bericht naar de service die op zijn beurt het bericht doorstuurt naar alle andere clients. Eerst maar eens zo'n service in elkaar schroeven.

[ServiceContract(Namespace = "http://blog.pasit.nl/relay",
                         CallbackContract = typeof(IClientContract))]
public interface IMessenger
{
    [OperationContract(IsOneWay = true)]
    void SendMessage(string input);

    [OperationContract(IsOneWay = true)]
    void Register();
}

[ServiceContract(Namespace = "http://blog.pasit.nl/relay")]
public interface IClientContract
{
    [OperationContract(IsOneWay = true)]
    void PushMessage(string message);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MessengerImp : IMessenger
{
    private List<IClientContract> subscribers =
                                              new List<IClientContract>();

    public void SendMessage(string input)
    {
        foreach (IClientContract subscriber in subscribers)
        {
            subscriber.PushMessage("From service: " + input);
        }
    }
    public void Register()
    {
        IClientContract subscriber =
          OperationContext.Current.GetCallbackChannel<IClientContract>();
        subscribers.Add(subscriber);
    }
}

IMessenger is de interface voor de service, IClientContract wordt door de client applicatie geïmplementeerd. MessengerImp is de service implementatie. Het idee is dat clients zich aanmelden bij deze service (via Register()) en wanneer een client iets te melden heeft, doet hij dat via SendMessage  (ik ga verder niet op alle implementatiedetails in).
De volgende stap is het maken van een host programma (zo'n uitvoerbaar programma dat processortijd en RAM voor de service aanlevert).

class Program
{
    static void Main(string[] args)
    {
        ServiceHost host = new ServiceHost(typeof(MessengerImp));
        ServiceEndpoint ep = CreateLocalServiceEndpoint();
        host.AddServiceEndpoint(ep);

        host.Open();
        Console.WriteLine("Service is running...");
        Console.ReadLine();
        host.Close();
    }
    static ServiceEndpoint CreateLocalServiceEndpoint()
    {
        string address = "net.tcp://localhost:9998/message";
        NetTcpBinding binding = new NetTcpBinding();
        Type contract = typeof(IMessenger);
        ServiceEndpoint ep = new ServiceEndpoint(
                                          ContractDescription.GetContract(contract),
                                          binding,
                                          new EndpointAddress(address));

        return ep;
    }
}

In CreateLocalServiceEndpoint() wordt nu een endpoint opgezet die luistert op poort 9998. 
De service is nu helemaal af. Nu kunnen we aan de slag gaan met de client. Daarvoor heb ik in ieder geval de interfaces (IMessenger en IClientContract) uit de eerste listing nodig.


[ServiceContract(Namespace = "http://blog.pasit.nl/relay",
                         CallbackContract = typeof(IClientContract))]
public interface IMessenger
{
    [OperationContract(IsOneWay = true)]
    void SendMessage(string input);

    [OperationContract(IsOneWay = true)]
    void Register();
}

[ServiceContract(Namespace = "http://blog.pasit.nl/relay")]
public interface IClientContract
{
    [OperationContract(IsOneWay = true)]
    void PushMessage(string message);
}

public class ClientImp : IClientContract
{
    public void PushMessage(string message)
    {
        Console.WriteLine(message);
    }
}

class Program
{
    static void Main(string[] args)
    {
        IMessenger proxy = CreateLocalProxy();
        proxy.Register();
        proxy.SendMessage("Hello World");
        Console.ReadLine();
    }
    static IMessenger CreateLocalProxy()
    {
        string address = "net.tcp://localhost:9998/message";
        NetTcpBinding binding = new NetTcpBinding();
        Type contract = typeof(IMessenger);
        ClientImp imp = new ClientImp();
 
        DuplexChannelFactory<IMessenger> factory =
                    new DuplexChannelFactory<IMessenger>(
                           imp, binding, address.ToString());

        return factory.CreateChannel();
    }
}

ClientImp is de implementatie van IClientContract die wordt aangeroepen als de service iets te melden heeft. Verder is het wederom het Endpoint opbouwen in CreateLocalProxy(). Dit is de traditionele benadering. Nu wil ik de communicatie via Service Bus Relay laten verlopen. Daarvoor moet ik eerst een Service Bus namespace aanmaken in Windows Azure. In het portalmenu selecteer je "SERVICE BUS" en vervolgens klik je op de enorme "+" links onderin beeld. Je krijgt dan het scherm uit afbeelding 2. Hierin selecteer je "Quick Create" en je kunt de namespace en region opgeven.


Afbeelding 2. Het instellen van de namespace.
Wanneer de namespace is aangemaakt, kunnen we proberen om de WCF-service via Service Bus Relay te laten communiceren. Hiervoor moeten we eerst de Windows Azure Service Bus SDK installeren. Een versie komt al mee met de Windows Azure SDK, maar via NuGet kun je ook de laatste versie installeren.
Het enige dat aan de eerdere implementatie verandert, zijn de endpoints, zowel bij de client als bij de service (beide moeten immers identiek zijn). Hiervoor levert de Windows Azure Service Bus SDK een aantal Relay bindingen, zoals NetTcpRelayBinding en WsHttpRelayBinding die je in zult moeten zetten. Daarnaast zullen client en service beide een een security token moeten configureren (shared secret) om te kunnen communiceren. De informatie voor dit token vind je in de Azure Portal onder het menu "Service Bus". Onderin beeld zie je een knop "ACCESS KEY". Wanneer je daar op drukt, krijg je het venster uit afbeelding 3 waarin de default issuer en de default key getoond worden. Beide heb je nodig. (overigens kun je ook andere issuers en keys aanmaken. Dat is zelfs aan te bevelen).
De endpoint configuratie op de service en de client wordt nu:

static ServiceEndpoint CreateServiceBusEndpoint()
{
    Uri address = ServiceBusEnvironment.CreateServiceUri(
                          "sb""pasitns""MessengerImp");
    NetTcpRelayBinding binding = new NetTcpRelayBinding();
    Type contract = typeof(IMessenger);

    ServiceEndpoint ep =
             new ServiceEndpoint(ContractDescription.GetContract(
                    contract), binding, new EndpointAddress(address));
    TransportClientEndpointBehavior tokenBehavior =
                         new TransportClientEndpointBehavior();
    tokenBehavior.TokenProvider =
              TokenProvider.CreateSharedSecretTokenProvider(
                   "owner""AQsRfnCYMvxIBEqmM/7wrHpkNKtV=");
    ep.EndpointBehaviors.Add(tokenBehavior);
    return ep;
}
Wanneer je nu de applicatie start, krijg je de foutmelding uit afbeelding 4. 


Afbeelding 4. Het gaat niet helemaal goed.
Het probleem is dat Windows Azure Service Bus via een speciale poort wil communiceren. Voor NetTcpRelayBinding is dat poort 9350. Deze zullen we in de firewall moeten openzetten, maar daarna loopt het ook meteen. In deze demonstratie heb ik de service in een virtuele omgeving laten draaien en de client erbuiten. Normaal is het een hele toestand om de zaak aan het draaien te krijgen, maar met Windows Azure Service Bus werkt het meteen (afbeelding 5).


Afbeelding 5. Het resultaat van de communicatie tussen de service op de virtuele omgeving en de client op de Windows omgeving.
Dit is nog maar één toepassing. Het is ook mogelijk om Windows Azure Service Bus Relay in te zetten als een soort van discovery service. Ideaal voor services die op een staging omgeving, waarvan het adres onmogelijk te voorspellen is, draaien. 

Geen opmerkingen:

Een reactie posten