1.03k likes | 1.22k Views
Design Patterns, Practices, and Techniques with the Azure AppFabric Service Bus. Juval Lowy IDesign www.idesign.net. ©2011 IDesign Inc. All rights reserved . About Juval Löwy. Software architect Consults and trains on .NET architecture and technology
E N D
Design Patterns, Practices, and Techniques with the Azure AppFabric Service Bus Juval Lowy IDesign www.idesign.net ©2011 IDesign Inc. All rights reserved
About Juval Löwy • Software architect • Consults and trains on .NET architecture and technology • Microsoft's Regional Director for the Silicon Valley • Recent book • Programming WCF Services 3rd Edition (2010 O’Reilly) • Participates in the .NET/WCF design reviews • Publishes at MSDN and other magazines • Recognized Software Legend by Microsoft • Contact at www.idesign.net
Agenda • What is the service bus • Brief • Techniques • Publishing to registry • Discrete events • Discovery • Explorer • Structured buffers • Response service • Bonus Material • Time permitting
Azure AppFabric Service Bus • While designed to address connectivity issues also provides • Scalability • Availability • Security • Lower technology entrance barrier by making advanced scenario main-stream and mundane
Why Service Bus • Internet connectivity is hard • Firewalls • Load balancers • Sessions • NAT • Private IPs • Discovery and registry • Virtualization • Proxy servers • Security • IT in general • … LB NAT Router H/W Firewall S/W Firewall Service Client
Why Service Bus • Scalability throughput and availability challenges • Opening your Intranet • Requires DMZ • Clients credentials management • Commonplace solutions • Cumbersome • Not real-time • Potentially insecure
Why Service Bus • Solution • Do not connect clients to services directly • At least not initially • Use a relay service • Relay service in the cloud • Neutral territory • Only requires outbound calls to establish connection • Allowed in most environments • Will relay client calls to service • Additional benefits of scalability, security, administration
2 1 4 3 Service Bus Relay 1. Service connects and authenticates against relay Relay figures out how to best call back to service 2. Client connects and authenticates against relay 3. Client sends message to service 4. Relay forwards message to service Relay Service Service Client
Azure AppFabric Service Bus • Ready-made service bus relay service • DMZ in the sky • Only allows authenticated authorized calls • Repels attacks and hides service • Relay service in cloud routing messages • Microsoft massive data centers • Additional features • Security • Access rules • Service registry
Relay Service Address • Address format [base address]/[optional URI]/.../[optional URI] • Bus address format [schema]://[namespace].servicebus.windows.net/ • Schema format is sb or http or https • Binding dependent
Services Registry • Can view ATOM feed of listening services in namespace • Or one of its sub URI http://[namespace].servicebus.windows.net/[URI]
Services Registry • Can control publishing to registry publicenum DiscoveryType { Public, Private } publicclass ServiceRegistrySettings : IEndpointBehavior { public ServiceRegistrySettings(); public ServiceRegistrySettings(DiscoveryType discoveryType); public DiscoveryType DiscoveryMode {get;set;} publicstring DisplayName {get;set;} }
Services Registry • Must use programmatic setting IEndpointBehavior registeryBehavior = new ServiceRegistrySettings(DiscoveryType.Public); ServiceHost host = new ServiceHost(typeof(MyService)); foreach(ServiceEndpoint endpoint in host.Description.Endpoints) { endpoint.Behaviors.Add(registeryBehavior); } host.Open();
Discoverable Host • My DiscoverableServiceHost automates registry • Used like regular host publicclass DiscoverableServiceHost : ServiceHost,IServiceBusProperties { public DiscoverableServiceHost(object singletonInstance,params Uri[] baseAddresses); public DiscoverableServiceHost(Type serviceType,params Uri[] baseAddresses); //More members }
IServiceBusProperties • All my service bus helpers support my IServiceBusProperties publicinterface IServiceBusProperties { TransportClientEndpointBehavior Credential {get;set;} Uri[] Addresses {get;} }
Service Bus Explorer • View with my Service Bus Explorer • Can administer buffers as well
Service Bus Bindings • The three main bindings • NetTcpRelayBinding • NetOnewayRelayBinding • NetEventRelayBinding
TCP Relay Binding • Binding of choice in most cases • Best performance and throughput • Minimum overhead for service • Unlimited message size • Up to configured limits • Request-reply messages through relay
TCP Relay Binding • Multiple clients / single service • As with regular WCF • Maintains transport session • Clients gets the same instance • Not interoperable • Uses sb for transport <endpoint address = "sb://MyNamespace.servicebus.windows.net/..." binding = "netTcpRelayBinding" contract = "..." />
One-Way Relay Binding • No reply messages • All operations must be one-way • Message goes into a buffer • Messages limited to 64 KB • Used sb for scheme <endpoint address = "sb://MyNamespace.servicebus.windows.net/..." binding = "netOnewayrelayBinding" contract = "..."/>
Event Relay Binding • Specialization of one-way relay • Allows any number of services to monitor buffer • N:M communication • Can safely listen concurrently on nested URIs • For whatever reason • Clients may still call over NetOnewayRelayBinding • Services must use NetEventRelayBinding publicclass NetEventRelayBinding : NetOnewayRelayBinding {...}
Subscriber Subscriber Subscriber Service Bus as Events Hub Publisher Publisher Events Hub
Service Bus as Events Hub • Light weight pub/sub system • No administrative support • No per-operation subscription • Endpoint level only
Service Bus as Events Hub • Subscriber still receives events it may not care about simply because it has a matching endpoint [ServiceContract] interface IMyEvents { [OperationContract(IsOneWay = true)] void OnEvent1(); [OperationContract(IsOneWay = true)] void OnEvent2(int number); [OperationContract(IsOneWay = true)] void OnEvent3(int number,string text); }
Service Bus as Events Hub • To manage events at the operation level need to map URIs to operations not endpoints <endpoint name = "OnEvent1" address = "sb://MyNamespace.servicebus.windows.net/IMyEvents/OnEvent1" binding = "netOnewayBinding" contract = "IMyEvents" /> <endpoint name = "OnEvent2" address = "sb://MyNamespace.servicebus.windows.net/IMyEvents/OnEvent2" binding = "netOnewayBinding" contract = "IMyEvents" /> <endpoint name = "OnEvent3" address = "sb://MyNamespace.servicebus.windows.net/IMyEvents/OnEvent3" binding = "netOnewayBinding" contract = "IMyEvents" />
Service Bus as Events Hub • Have as many hosts as subscribed operations all targeting same service type • At run-time must recycle hosts and programmatically add each desired endpoint to specific host • Tedious repetitive code • Expensive • Pay for connections
Service Bus as Events Hub • Streamline with my ServiceBusEventsHost
publicclass ServiceBusEventsHost : DiscoverableServiceHost { public ServiceBusEventsHost(Type serviceType,Uri baseAddress); public ServiceBusEventsHost(Type serviceType,Uri[] baseAddresses); /*Additional constructors */ //Can optionally specify binding publicvirtual NetOnewayRelayBinding RelayBinding {get;set;} publicvoid SetBinding(string bindingConfigName); //Subscription management publicvoid Subscribe(); publicvoid Subscribe(Type contractType); publicvoid Subscribe(Type contractType,string operation); publicvoid Unsubscribe(); publicvoid Unsubscribe(Type contractType); publicvoid Unsubscribe(Type contractType,string operation); }
Service Bus as Events Hub • ServiceBusEventsHostused like regular host • Requires base address(es) • Appends contract name to each base address • Can accept binding to use • Defaults for secure binding • No need for config file • Can look up binding from config • Can subscribe or unsubscribe all operations on all contracts • Can subscribe or unsubscribe all operations on contract • Can subscribe or unsubscribe specific operation on contract
[ServiceContract] interface IMyEvents { [OperationContract(IsOneWay = true)] void OnEvent1(); [OperationContract(IsOneWay = true)] void OnEvent2(int number); [OperationContract(IsOneWay = true)] void OnEvent3(int number,string text); } class MySubscriber: IMyEvents {...} string baseAddress = "sb://MyNamespace.servicebus.windows.net/"; ServiceBusEventsHost host = new ServiceBusEventsHost(typeof(MySubscriber),baseAddress); host.Open(); host.Subscribe(); host.Unsubscribe(typeof(IMyEvents),"OnEvent2"); host.Subscribe(); host.Unsubscribe(); host.Close();
Service Bus as Events Hub • Subscriptions stored in dictionary • Maps subscribed operations per contract • Can add admin support • Use a tool to manage subscriptions outside scope of host/service • Manage subscriptions against host as needed
Service Bus as Events Hub • ServiceBusEventsHost adds endpoint per base address per contract • [base address]/[contract name] • Monitors all messages to that address and below • Publishers send messages to endpoint whose address contains operation name • [base address]/[contract name]/[operation]
Service Bus as Events Hub • ServiceBusEventsHost uses operation selector interceptor to rout message to subscribed operation • Attached as endpoint behavior to all dispatchers publicinterface IDispatchOperationSelector { string SelectOperation(ref Message message); }
//Partial listing without error handling publicclass ServiceBusEventsHost : ServiceBusHost { //Managing the subscriptions Dictionary<string,List<string>> Subscriptions {get;set;} public ServiceBusEventsHost(Type serviceType,Uri[] baseAddresses) : base(serviceType,baseAddresses) { Subscriptions = new Dictionary<string,List<string>>(); foreach(Uri baseAddress in BaseAddresses) { Type[] contracts = GetServiceContracts(); foreach(Type contract in contracts) { AddServiceEndpoint(contract,RelayBinding, baseAddress.AbsoluteUri + contract); Subscriptions[contract.Name] = new List<string>(); } } IEndpointBehavior selector = new EventSelector(Subscriptions); foreach(ServiceEndpoint endpoint in Description.Endpoints) { endpoint.Behaviors.Add(selector); } }
publicvoid Subscribe(Type contractType,string operation) { if(Subscriptions[contractType.Name].Contains(operation) == false) { Subscriptions[contractType.Name].Add(operation); } } publicvoid Unsubscribe(Type contractType,string operation) { if(Subscriptions[contractType.Name].Contains(operation)) { Subscriptions[contractType.Name].Remove(operation); } } //Uses reflection to get all service contracts Type[] GetServiceContracts() {...}
class EventSelector : IDispatchOperationSelector,IEndpointBehavior { readonly Dictionary<string,List<string>> m_Subscriptions; public EventSelector(Dictionary<string,List<string>> subscriptions) { m_Subscriptions = subscriptions; } publicstring SelectOperation(ref Message message) { string[] slashes = message.Headers.Action.Split('/'); string contract = slashes[slashes.Length-2]; string operation = slashes[slashes.Length-1]; if(m_Subscriptions[contract].Contains(operation)) return operation; else returnnull; } void IEndpointBehavior.ApplyDispatchBehavior( ServiceEndpoint endpoint,EndpointDispatcher endpointDispatcher) { endpointDispatcher.DispatchRuntime.OperationSelector = this; } ... } }
Service Bus as Events Hub • Publisher can use plain one-way relay proxy • Careful to match expected endpoints layout • Automate proxy with my ServiceBusEventsClientBase • Appends contract name and operation to base address • No need for config file • Only needs base address • Always uses one way relay binding • Used like regular proxy
publicabstractclass ServiceBusEventsClientBase<T> : ...ClientBase<T> where T : class { public ServiceBusEventsClientBase(string baseAddress) : this(baseAddress,new NetOnewayRelayBinding()) {} public ServiceBusEventsClientBase(string baseAddress,NetOnewayRelayBinding binding) : base(binding,ToEventAddress(baseAddress)) {} /* More constructors */ static EndpointAddress ToEventAddress(string baseAddress) { returnnew EndpointAddress(baseAddress + typeof(T).Name); } }
Service Bus as Events Hub class MyEventsProxy : ServiceBusEventsClientBase<IMyEvents>,IMyEvents { public MyEventsProxy(string baseAddress) : base(baseAddress) {} publicvoid OnEvent1() { Channel.OnEvent1(); } publicvoid OnEvent2(int number) { Channel.OnEvent2(number); } publicvoid OnEvent3(int number,string text) { Channel.OnEvent3(number,text); } }
Discovery • Discovery was designed for the Intranet • Discovery is useful • Loosely-coupled clients and services • Dynamic addresses • Easy deployment • Service bus may support discovery in future
Discovery • Would be nice to combine benefit of loose deployment of discovery with unhindered connectivity of service bus • Can substitute events binding for UDP • Discovery requests • Announcements • Mimic WCF discovery behavior
Solution Architecture • Streamline with my helpers • IServiceBusDiscovery for discovery requests • Events relay binding • Supported by discoverable services • Provides reply address of client [ServiceContract] publicinterface IServiceBusDiscovery { [OperationContract(IsOneWay = true)] void DiscoveryRequest(string contractName,string contractNamespace, Uri[] scopesToMatch,Uri responseAddress); }
Solution Architecture • IServiceBusDiscoveryCallback for receiving services responses • Exposed by clients • Over one-way relay binding [ServiceContract] publicinterface IServiceBusDiscoveryCallback { [OperationContract(IsOneWay = true)] void DiscoveryResponse(Uri address,string contractName,string contractNamespace, Uri[] scopes); }
Operation Event Service Client 3 1 IServiceBusDiscovery Discovery Requests Relay Service Relay Service Relay Service IServiceBusDiscoveryCallback 2
Discoverable Host • My DiscoverableServiceHost • Used like regular host • To enable must add discovery behavior and discovery endpoint • Forward looking and compatible
Discoverable Host publicclass DiscoverableServiceHost : ServiceHost,IServiceBusProperties { public Uri DiscoveryAddress {get;set;} public NetEventRelayBinding DiscoveryRequestBinding {get;set;} public NetOnewayRelayBinding DiscoveryResponseBinding {get;set;} public DiscoverableServiceHost(object singletonInstance,params Uri[] baseAddresses); public DiscoverableServiceHost(Type serviceType,params Uri[] baseAddresses); }
Discoverable Host • Creates internal host for private service class DiscoveryRequestService implementing IServiceBusDiscovery • Monitors discovery requests • Address defaults to URI "DiscoveryRequests" • Configurable via DiscoveryAddress property • Uses plain events binding to receive requests • Configurable via DiscoveryRequestBinding property • Calls back client using IServiceBusDiscoveryCallback • Uses plain one way binding • Configurable via DiscoveryResponseBinding property
Discoverable Host Uri baseAddress = newUri("sb://..."); ServiceHost host = new DiscoverableServiceHost(typeof(MyService,baseAddress); //Address is dynamic host.AddServiceEndpoint(typeof(IMyContract),new NetTcpRelayBinding(), Guid.NewGuid().ToString()); host.Open();
Discovery Client • For client use my ServiceBusDiscoveryClient • Modeled after DiscoveryClient publicclass ServiceBusDiscoveryClient : ClientBase<IServiceBusDiscovery>, IServiceBusProperties { protected Uri ResponseAddress {get;} public ServiceBusDiscoveryClient(string serviceNamespace,...); public ServiceBusDiscoveryClient(string endpointName); public ServiceBusDiscoveryClient(NetOnewayRelayBinding binding,EndpointAddress address); publicFindResponse Find(FindCriteria criteria); }