Monday, September 25, 2017

Microsoft 70-487: Host and manage services

Exam Objectives

Manage services concurrency (single, multiple, reentrant); create service hosts; choose a hosting mechanism; choose an instancing mode (per call, per session, singleton); activate and manage a service by using AppFabric; implement transactional services; host services in an Azure worker role


Quick Overview of Training Materials

[PluralSight] WCF End-to-End -

Manage Services Concurrency


Services can be configured to allow or restrict multithreading of the service.  This is controlled with the "ConcurrencyMode" property on the [ServiceBehavior] attribute.  There are three possible values for this property:

  • Single - only a single thread can run the service at a time
  • Reentrant - basically the same as Single, except for the case of callbacks.  This prevents deadlocks when using callback contracts (I actually ran into this issue on the demo below, but changing all the operations to "one way" solved the issue for me... either way).
  • Multiple - multiple threads can process requests to the service concurrently.


One thing I found confusing is the relationship between the concurrency mode and the instancing mode.  For instance, even though "PerCall" instancing puts each service instance in it's own thread, each request to the endpoint is queued when using Single concurrency.  Weird, right?  Well I wanted a demo to visuallize this relationship, and came up with a pretty good one.  I'm not going to paste all the code in here, you can find it on GitHub.  I will post once of the annotated services though, since this is the critical step to configure concurrency behavior:


    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,
                     ConcurrencyMode = ConcurrencyMode.Multiple,
                     UseSynchronizationContext = true)]
    public class PerCall_Multi_Service : BaseService
    {
    }


This demo is the "ConcurrentServices" and "ConcurrentClient" projects, the other two are for the "Choose an Instancing Mode" demo:

Two Clients Running Simultaniously

The service, with the various combinations


Basically all the service does is call a notification method on the callback contract with the column number, which causes the client to update a shared buffer.  The client processes this buffer once a second and writes out the contents.  It's all very thready and asynchronous.

So I got curious about the exception I'd seen earlier suggesting I change the concurrency mode to "Reentrant" or "Multiple", and decided I would try making it work with "Reentrant" services in place of the "Single" service.  I also made the adjustment of starting the buffer processing thread before I started the service calls.  The result was eye opening:



Without "IsOneWay" on the operation contracts, everything was acting totally single threaded.  I tried changeing the bindings to netTcpBinding, that didn't help.  Tried setting concurrency on the callback contract to Multiple (thinking that maybe it was blocking), that made no difference.  So I set everything back to IsOneWay, and I noticed that Reentrant was not acting like Single.  So I modified the demo to use Reentrant services alongside the other two types to getting a full picture:



So, Reentrant acts just like "Multiple" on a PerCall instance, and acts kinda-sorta single like on PerSession and Singleton... but with some overlap, which I thought was really weird.  The MS docs for ConcurrencyMode.Reentrant say, explicitly, that it is basically supposed to act like Single.  One thing is does say, is that the lock for a service is release on an outgoing call, which makes me wonder if what is happening is that the requests are queuing up, and each time an outgoing call unlocks the service, a different inbound call gets through?  But that would kinda defeat the purpose... Maybe my implementation is just screwed up lol, who knows...

If the notification methods on ICallback are not set to one-way, then Single mode will throw a deadlock exception.  If the Process method on IService is not set to one-way, everything acts single threaded.

An answer on SO got me thinking about what actually triggers Reentrant mode to unlock the service instance.  It is an outbound WCF call that triggers it... and I thought "well, I wonder what happens if the outbound call isn't made from the same thread?"  Well, it was easy enough to test.  I changed the service so that the calls to the callback happened in a Task:

        public void Process(int id)
        {
            ICallback client = OperationContext.Current.GetCallbackChannel<ICallback>();
            try
            {
                Task.Run(() => client.NotifyBegin(id));
                Thread.Sleep(Rand(2000, 6000));
                Task.Run(() => client.NotifyEnd(id));
            } catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }


And just like that... *poof*... Reentrant mode starts acting mostly as advertised.  The PerCall instances still have Reentrant acting like Multiple, but the PerSession and Singleton instances were neatly Single style.

Ok, I think we're good


If I was a complete masochist, I might try to understand the source code for the ConcurrencyBehavior class... but I'm not... so I won't.  Rewatched the WCF Advanced Topics course, and Skonnard talks about the behavior you can get with Reentrant mode, which aligns with what I was seeing.  The outgoing call unlocks the service, and an incoming call gets in.  Interesting...



Create Service Hosts


Internet Information Services (IIS)


I've come to regard anything dealing with IIS as entering a world of pain.  It took me a solid 6 hours to get the ConcurrentServices demo to work on a standalone IIS install.  I put IIS on another machine on my network so I could get the true sense of what it would be like, and let me tell you... it hurts.

Up until now, all my demos have been stand alone console apps (Self-Hosted, as they say), with the service and client living on the same machine.  This insulates you from many of the very real complications that arise in a more realistic setting.  The first step I took to create a demo for IIS was to create a new "WCF Service Application" in Visual Studio.  The main thing this does is give you an ".svc" file.



One of the problems I ran into early had to do with trying to get IIS to play nice with my existing configuration.  IIS basically ignores the base addresses you have configured.  This was a problem for me, since I had multiple service hosts (9 in fact) living in the same config.  It is possible to get IIS to play nice when you have multiple bindings defined, you just have to set the appropriate attribute, "multipleSiteBindingsEnabled", to true:

  <system.serviceModel>   
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" 
                               multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>


The first thing to do on the IIS side is create a new "application" under the Default Web Site.  We'll give it an alias (which will become part of the address) and a physical location on disk for the application files (our .svc file along with the supporting binaries):



By default, at least in IIS7, this application is configured to use .NET 2.0 (yuck!).  I had 4.0 installed on this machine, so I figured I could live with that.  While I could have sworn I set this somewhere else, the only reference I could find to versions was the Application Pool setting, which you can change when you create the application, or modify later by selecting "Basic Settings":



The bindings themselves have to be configured in IIS.  After futzing with the firewall on the client machine failed to yeild results, I gave up trying to get WSDualHttpBinding to work (IIS kept bitching about a BasicHttpBinding I never set, though changing to net.tcp didn't fix my problems anyway...), and decided to try net.tcp instead.  The first thing I had to do was enable net.tcp as an allowed protocol:



Once the protocol is enabled, we can create some additional bindings.  There are several that come by default (including http on port 80 for all IP addresses).  I'm not 100% on the meaning of the way the tcp ports are specified, I just know that what is below is what I eventually got to work.  These addresses will become our collection of "baseAddresses" later when we are creating the ServiceHost instance:



Because of the issues I was having with getting IIS to start up my services, I decided to create my own ServiceHostFactory subtype (an idea I got from WCF Advanced Topics).  There are two overloads for the method CreateServiceHost(), one which takes a Type, and the other takes a string (which is the fully qualified name of the type).  I eventually figured out that IIS was calling the string version (tracing was a lifesaver):

public override ServiceHostBase CreateServiceHost(string constructorString, 
                                                  Uri[] baseAddresses)
{
    Trace.WriteLine("Called with constructor string: " + constructorString);
    Type serviceType = types[constructorString];
    return CreateServiceHost(serviceType, baseAddresses);
}

protected override ServiceHost CreateServiceHost(Type serviceType, 
                                                 Uri[] baseAddresses)
{
    Trace.WriteLine("Creating service of type " + serviceType.FullName + "...");
    int port = map[serviceType.FullName];
    Trace.WriteLine("...on port " + port);
    var host = new ServiceHost(serviceType, 
        baseAddresses.Where(b => b.Port == port).FirstOrDefault());

    //Add metadata behavior
    ServiceMetadataBehavior smb1 = new ServiceMetadataBehavior();
    //smb1.HttpGetEnabled = true;
    host.Description.Behaviors.Add(smb1);

    //Add endpoint
    NetTcpBinding binding = new NetTcpBinding();
    binding.ReceiveTimeout = TimeSpan.FromMilliseconds(60000);
    binding.SendTimeout = TimeSpan.FromMilliseconds(60000);
    binding.CloseTimeout = TimeSpan.FromMilliseconds(60000);
    binding.OpenTimeout = TimeSpan.FromMilliseconds(60000);
    binding.Security.Mode = SecurityMode.None;

    string address = "status";
    host.AddServiceEndpoint(typeof(IService), binding, address);
    
    PrintServiceDescription(host);

    return host;
}


I loaded a couple dictionary with all the ports and Types, keyed by the fully qualified service names, since IIS kept complaining about not being able to find the types, smh.

The .svc file is what represents the service within IIS.  It contains a single directive that must define, at a minimum, the Type of the service being hosted, and can additionally contain attributes pointing to a code behind file (which IIS can compile on the fly), a language hint (VB vs C#),  whether debuging is enabled, and what factory class to use to build the ServiceHost.  The last hurdle I had to overcome to get the ConcurrentServices project to run in IIS was figuring out that I needed to define one .svc file for each service I wanted to host.  Since I had 9 ServiceHost instances that I wanted to create, I needed 9 .svc files.  They were all basically identical:

<%@ ServiceHost Debug="true" Factory="IISHosted.Factory" 
    Service="ConcurrentServices.Services.Singleton_Single_Service"%>


So the IIS project defines PCM.svc (for the PerCall instance mode, Multiple concurrency service), PCS (Single concurrency), etc.  The way IIS addresses the services uses the Application alias along with the .svc name to create the complete path.  So the base addresses look like this:

net.tcp://<machineName>:<port>/concurrency/<service>.svc


Ultimately the client was basically the same as the ConcurrentClient demo.  I did add a couple of additional states to the messageBuffer processor to deal show when channels were in various states (Created, Opening, or Faulted) to try and help with the debugging process, but once everything was put together the right way they didn't really show up.

Figured out later that I probably could have just put all the service activation code into the relevant section in the config file rather than create a bunch of .svc files... doh!



Windows Process Activation Service (WAS)


According to the documentation,  WAS generalizes the process model of IIS 6 to allow for non-HTTP protocols to be used in a manage hosting environment.  It turns out that, as part of my struggle to get IIS 7 to play nice, I actually installed the WAS feature in my 2008 R2 instance:



So WAS allows you to use Tcp, MSMQ, and Named Pipes bindings to communicate with services hosted in IIS.  So without even realizing it, I was using WAS in the IIS demo.



Self-Hosted 


The ServiceHost class around which WCF services are built can be instantiated and run in any managed .NET application.  The implication of this is that a Windows Forms application, or a console application, can spin up a service and communication with the outside world.  Since almost all of the demo's I've created have been examples of self-hosted console services, it wouldn't really add value to create another one, but for completeness I'll cover the basics.

The ServiceHost class is the cornerstone of the service.  The WCF Advanced Topics course, in the "Runtime" section, does an excellent job presenting a high level overview of how the ServiceHost operates.  One thing that is important to understand is that the ServiceHost is not the actual service.  The service instances that are constructed and destroyed (according to their Instancing Mode) live inside the ServiceHost.  So in my example above, even those the PerCall service is standing up and tearing down service instances for every call, this is all happening in the context of one persistent service.

The demo's I created for the Configure with the API post touch on some of the more useful methods on the ServiceHost class.



Windows Service Host


Working through the How To document provided by Microsoft, I'm struck by how little is different from hosting in a Windows service compared to just creating a self hosted service in a console app. Basically just create a service that derives from ServiceBase in the System.ServiceProcess namespace, override the OnStart and OnStop methods to create and destroy the service host, and then install it using installutil.exe in the command line.



Once installed, you can manage the service like any other windows service.  You can see the services in Task Manager (though there is, apparently, no easy way to start Task Manager with the elevated privileges required to start or stop the service), or mmc with the Services snap-in.  Below you can see my simple EchoService, which I just started successfully:



And in welcome contrast to the IIS fiasco, I was immediately able to pull down the metadata from the service:




Choose a Hosting Mechanism


The Hosting Options article provides an excellent rundown of the strengths of each of the hosting options.  I'm basically just paraphrasing that article here with a sprinkle of my own (limited) insights, lol, so I would definitely recommend reading the article, it's like a 5 minute read.


Self-Hosting


WCF services can be hosted in any managed application (console, WPF, etc.), which provides for a great deal of flexibility.  Most of my demos of WCF services have been Self-Hosted console applications precisely because they are so easy to spin up and use.

The two scenarios MS points out as common are:

  • Console applications used during development.  The simplicity of this set up allows for easy debugging and tracing of the service.  
  • Rich console applications (WPF and WinForms) that are consuming and exposing services. They give the example of a peer-to-peer collaboration application




Managed Windows Service


This hosting option basically wraps a self-hosted WCF service in a Windows service, allowing the life cycle of the service to be controlled by the operating system.  Services hosted this way can start automatically when the machine restarts, and can be started and stopped (provided they are written correctly) from the Services snap-in for mmc.  This mechanism would be useful when a long running service is required on a local machine.



IIS/WAS


IIS 5 and 6 only supported HTTP based traffic.  This changed when WAS was introduced, as it created a bridge between IIS and non-HTTP based protocols (net.tcp, MSMQ, named pipes, etc.).

Modern versions of IIS offer many capabilities.  IIS manages the lifecycle of the ServiceHost for deployed services, which means code to create the instance need not be part of the code (unless you write a factory).  The article lists the following benefits of using IIS to host services:

  • Process recycling
  • Idle shutdown
  • Process health monitoring
  • Message based activation
  • Integration with ASP.NET


The big drawback of using IIS is the extra complexity (as I painfully demonstrated above). However, IIS hosting is going to be the go to solution for enterprise solutions. For enterprises with a mature IIS stack, these complications are probably less of a hurdle, since the ops guys probably know the ins and out of IIS better than I do lol.



Azure


Azure hosting isn't covered in the "Hosting Services" article, but the advantages to using Azure for WCF hosting are going to be pretty much the same as the benefits for using Azure over other on-premises services... scalability, availability, security, etc.  Within Azure, the mechanism I see reference the most for hosting WCF is "Azure Cloud Services", though there were some murmurings that the various compute roles being deprecated, but it seems these fears never came to fruition.

Azure App Service is another option for hosting a WCF service, as I found at least one example of this being done successfully. Since App Services includes a free tier, it could be an interesting option for experimenting with cloud based services.

It is also entirely possible to spin up a VM and host IIS in it, but then we're just talking about an implementation detail of the infrastructure, and from the service perspective we're just hosting in IIS.



Choose an Instancing Mode


The [ServiceBehavior] attribute also allows us to control how our service is instantiated. The default for most bindings is to create a new instance for each session, though for bindings that don't support session, this falls back to per-call instantiation. By setting the InstanceContextMode property, we can control which strategy is used to control the service instance life cycle. The three options are:

  • Single - One singleton instance of the service for all requests 
  • PerSession - One instance for each connection or channel. 
  • PerCall - A new instance is created for every request 


The WCF Advanced Topics includes better graphics than the one's I drew up, but I think I get the idea across:



To demonstrate this concept I created a simple service with one operation. The class holds a static counter variable, and every time the constructor is called, this number is incremented. The current count is assigned to an instance variable as an id, and the one operation in the contract returns this id. Thus, we would expect to see a Single instance service return the same id for every call by every client, a PerSession service return the same id for each call but unique for each client, and a PerCall service to return a different id for every single request. Here is the base service:

    public class BaseContract : IConService
    {
        public BaseContract()
        {
            id = getId();
        }

        public string Status()
        {
            return id;
        }


        private static int count;
        private string id;

        private static string getId()
        {
            object thisLock = new object();
            lock (thisLock)
            {
                count++;
            }

            return count.ToString();
        }
    }


Each of three service classes (SingleService, SessionService, and CallService) extend this base class, with the only differentiator being what the InstanceContextMode is set to:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class SingleService : BaseContract
    {
    }


The client makes a call to Status() on each:

    class Program
    {
        static void Main(string[] args)
        {
            using(var callClient = new CallServiceReference.ConServiceClient())
            using(var sessionClient = new SessionServiceReference.ConServiceClient())
            using(var singleClient = new SingleServiceReference.ConServiceClient())
            {
                string command = null;
                do
                {
                    Console.WriteLine("Single: " + singleClient.Status());
                    Console.WriteLine("Session: " + sessionClient.Status());
                    Console.WriteLine("Call: " + callClient.Status() + "\n\n");

                    Console.WriteLine("Hit 'x' to go again, anything else to quit...");
                    command = Console.ReadKey().KeyChar.ToString();

                    Console.WriteLine("\n\n");

                } while (command == "x");
            }
        }
    }


When I start the service and two clients, we see that the id for the Single service never changes, the Session service is different for both but also never changes once set, and the Call service changes every request, as we expected:




Implement Transactional Service


I covered the basics of transactions in my Implement Transactions Post, so I'm just going to cover the incremental changes necessary to make WCF Services work with System.Transaction, and WS-AtomicTransaction based transactions.  My primary reference is the Transactions section from the WCF documentation.  The Service Transaction Behavior documentation also proved critical to me getting the demo to work right.

There are a few pieces that have to be in place for transactions to work with a WCF service.  First off, transactions require a session, so the ServiceContract must be configured with SessionMode = SessionMode.Required.  In addition, any operations that are going to participate in a transaction must have a [TransactionFlow] attribute with the option set to either Mandatory or Allowed.

    [ServiceContract(SessionMode=SessionMode.Required)]
    public interface IEchoService
    {
        [OperationContract]
        [TransactionFlow(TransactionFlowOption.Mandatory)]
        string Echo(string message);
    }


On the service implementation, we need to configure an OperationBehavior with TransactionScopeRequired = true.  We can also set the TransactionAutoComplete property to true or false, depending on our needs.  If we leave it false, we have to complete the transaction manually.  This tripped me up, as I couldn't initially figure out how to do this.  I tried wrapping a new TransactionScope around the current transaction, tried casting the current transaction to a CommitableTransaction... totally wrong.  To complete the transaction manaually, call the SetTransactionComplete() method on the current operation context:


    public class EchoService : IEchoService
    {
        [OperationBehavior(TransactionScopeRequired = true, 
                           TransactionAutoComplete = false)]
        public string Echo(string message)
        {
            var ti = Transaction.Current.TransactionInformation;
            var id = ti.DistributedIdentifier.ToString();

            //If "TransactionAutoComplete = true", this line isn't necessary
            OperationContext.Current.SetTransactionComplete();
            
            return "Id(" + id + "), Status(" + ti.Status + "): " + message;
        }
    }


When creating the service host, it is important to note that the binding must support transactions.  I first tried to create the service host with a BasicHttpBinding, but this doesn't support transactions (probably because it doesn't support sessions).  So I had to switch to a WSHttpBinding, and set the TransactionFlow property to "true":

   static void Main(string[] args)
    {
        using (var host = new ServiceHost(typeof(EchoService), 
                          new Uri("http://localhost:60999")))
        {
            //metadata exchange
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(smb);

            host.AddServiceEndpoint(typeof(IMetadataExchange),
                                    MetadataExchangeBindings.CreateMexHttpBinding(),
                                    "mex");

            //Service endpoint
            var binding = new WSHttpBinding();
            binding.TransactionFlow = true;
            host.AddServiceEndpoint(typeof(IEchoService),
                                    binding,
                                    "echo");

            host.Open();
            PrintServiceDescription(host);

            Console.ReadKey();
        }
    }


Finally, in the client, we have to make the call to the service from within a TransactionScope:


    static void Main(string[] args)
    {
        using (var client = new EchoServiceReference.EchoServiceClient())
        {
            string msg = null;
            do
            {
                Console.Write("Enter a message, or blank to exit: ");
                msg = Console.ReadLine();
                using (TransactionScope scope = new TransactionScope())
                {
                    Console.WriteLine(client.Echo(msg));
                    scope.Complete();
                }                                      
            } while (msg != "");

        }
    }


And now we have a transactional service:



One other point of interest is the transaction protocol in use.  In this case, when I drilled into the scope before the end of the "using" statement, I found it was actually using the OleTx protocol.  This surprised me, since I thought the WSHttpBinding would use the WSAtomicTransaction protocol, but the WS Transaction Flow documentation notes that WCF clients can (will?) use OleTx.  I'm not going to build a Java client to test this out, and not just because Java isn't on the test.  Setting up WS-AtomicTransaction support looks waaaaaay too involved for it to be worthwhile in this context lol.



AppFabric and Azure Worker Roles


According to the Wikipedia page for AppFabric, Microsoft is ending support for the Windows Server based AppFabric offering.  It is effectively being replaced by Azure Service Fabric.

For historical purposes, the Hosting WCF Service in Windows Server AppFabric article does a good job on explaining how it was done, but I don't think there is much value in me rehashing it here.

Because this post is already ridiculously long, I'm moving the discussion of Worker Roles and Azure Service Fabric to the Creating WCF Service on Windows Azure post, which is relatively thin otherwise, and really I think it makes sense to group all the WCF + Azure stuff together in one place.



No comments:

Post a Comment