All Windows servers (with the exception of external nodes that may be hosting the Platform Load Manager or Platform Coordinator exclusively) that take part in your Platform are capable of hosting WCF services, though that doesn’t necessarily mean all of them are used to host applications published to the instance. By default, WCF service deployments will not occur on servers hosting databases or user interfaces unless no other server is available. This preserves a separation of concerns for the servers on your grid, which is helpful on a production or production-like environment where servers have different hardware and software profiles.
This page introduces what happens when the Platform deploys a WCF service and how the Platform facilitates communication between multiple service instances. Before continuing, you should understand how the Platform deploys applications, the fundamentals of application on the Platform, and the different Platform roles and services.
New service instances are created when any of the following actions occur:
When a new instance of the service is created, the following workflow is executed:
C:\ApprendaPlatform\Container\LaunchPads\$serviceInstanceId(chosen from the root of
C:\to reduce the directory length from Windows’ folder path length).
Apprenda.WCFServiceHost.exe, which will host the service being created.
Apprenda.WCFServiceHost.exe.config. Among other things, the service is configured to work with the Platform, and any conditional configuration sections and tokens are changed to their appropriate “live” values.
Now that the service is running, it becomes available to fulfill requests.
Service instances are removed from the grid when any of the following actions occur:
The Platform maintains a sophisticated meta-typing system to help route service calls on the grid. This provides the following benefits:
Upon promotion of your application to the Sandbox and Published stages, the Platform catalogs details about your WCF service. Specifically, the Platform maintains an inheritance tree for versioning your services and to provide other system components with a way to logically reference them. For each WCF service, the Platform creates Service Types representing the abstract WSDL proposed by your service’s WCF Service Contract. As you evolve your application and service through the Platform’s versioning system, the Platform creates a new Service Implementation that is a concrete representation of the Service Type.
The identifiers are guaranteed to be unique, which allows the Platform to perform targeted, version-based routing and gives a uniquely identifiable logical system to target WCF service calls. Because of this, the Platform can guarantee clients that some instance of the service will satisfy a request without knowing the physical location of any instances. When you see Platform references (in documentation, portals, etc.) to metadata, it is typically referring to artifacts of this “typing system.”
The following illustration shows the relationship between service types and Service Implementations as multiple versions of the service are created as the application evolves:
Each Service Implementation is linked directly to the WCF binaries that resulted in its creation. In fact, the Platform treats the Service Implementation as a synonym for the WCF service itself. Understanding this hierarchy is critical to understanding .NET service request dispatching in the Platform. The metadata defined at this stage creates a logical representation that the Platform uses to target arbitrary WCF service instances deployed on the grid. How this targeting and routing works is covered below.
The Platform will configure all clients to point to the local Router, which runs on all Platform Windows servers, upon deployment. The following illustration indicates at a high level how service calls are routed on the grid.
Being a generic .NET service intermediary, the Router, upon receipt of the request, needs to determine what service the request is targeting. This is done via the Platform’s service metadata. The Apprenda Router accepts logical service targets and, from a pool of available physical targets, translates those logical service targets to single specific physical targets.
All service calls in the Platform are logically addressed through the Apprenda Router. The Apprenda Router accepts SOAP messages via well-known URL patterns that inform the Router of the logical service target. The URL pattern is of the following specific format:
Each Apprenda Router starts a number of listeners waiting on requests matching this pattern; all adhering to the protocol will have a specific understanding of the URL parts after the ‘listenPath.’
To help solidify an understanding of this pattern, consider the following example:
This URL is targeting an Apprenda Router’s SOAP 1.2-named pipe listening port via the ‘listenPath’ ‘services/soap12/pipe.’ The client targeting this URL is expecting that the request will be handled by a service instance of the specific ‘Service Implementation’ (because of the ‘i’) identified by the ID ‘36b08605-e79c-46c4-ab9f-bc113ccb0a93.’ Furthermore, because of the ‘r,’ the client is expecting that the request will generate a SOAP reply that the Apprenda Router has returned.
Targeting a ‘Service Type’ versus a ‘Service Implementation’ has implications. A ‘Service Implementation’ is a metadata leaf; no more specific logical representation exists. This means that a caller targeting a ‘Service Implementation’ can expect that a specific physical instance of the ‘Service Implementation’ will behaviorally manifest itself in a well-expected fashion since ‘Service Implementation’ maps 1-to-1 to a WCF class in a .NET assembly that defines very specific logic behind a WSDL contract. Targeting a ‘Service Type’ is not as concrete, since it simply acts as a WSDL abstraction that may have many specific implementations.
A request targeting a ‘Service Type’ is instead a request where selection of a more specific representation is left up to Apprenda. Every ‘Service Type’ keeps track of the ‘Service Implementation’ belonging to the newest version of the application owning the ‘Service Type’ as the default implementation. When the Router receives a request targeting a logical ‘Service Type,’ it will re-target the default ‘Service Implementation’ of that ‘Service Type.’
Having multiple ‘Service Implementations’ is important for allowing ‘Published’ and ‘Sandbox’ deployments for different versions of the same application to co-habit the same Platform. By having multiple ‘Service Implementations,’ the Platform can logically target different physical instances of different versions.
Circling back and following the path of a .NET service request originating from your client, the Router parses the targeted URL for the previously-described pattern, identifies the logical target, and checks its memory routing tables for the target. If the Apprenda Router has no address information for a physical instance matching the logical target specified, it immediately queries the Platform for a suitable service that is a representation of the logical criteria. If no service instance exists, the Router enters a “holding pattern,” keeping the SOAP message in its in-memory pipeline and dispatches a dynamic deployment request to the service grid for an instance matching the targeted logical type. Once the Apprenda Router is notified that an instance matching the targeted logical type has come online, it will flush the held message and add the new location information of the instance to its cache for future use.
The following illustration summarizes the workflow just described:
Besides offering an explanation of how service instances are located and automatically deployed to the grid, the previous explanation exemplifies one of the Platform’s high-availability mechanisms. Not explicitly mentioned in the workflow description above is that the Platform will detect and attempt to re-start non-responsive services and will make multiple attempts to deploy services, all while holding your original service request noticeable to the client in the form of a delay in getting the response.
While these considerations are not necessarily linked to your application, it can be useful to know how the Platform architecturally provides routing.
the Platform’s architecture is based on decentralized peer-based routing. Rather than proving a single, centralized “be-all” service bus and SOAP router, the Platform runs local routing services on each individual server. Client proxies, when under the Platform’s management, are dynamically re-routed to logical targets rather than physical targets. All service requests are, through inter-process communication, pushed to the local Router service. The Router service then takes over dispatching as described earlier, targeting a well-known service. This architecture provides a number of key advantages:
While there is seemingly a substantial overhead in adding this intermediary, care has been taken to optimize this process. Our performance testing shows that this is marginal after the first service request, and is minimal compared to the time to contact the target service itself.
It is required that the Developer create fully-stateless services. Besides permitting deployment using the fully-shared model, this is important because the Platform may, as a part of its normal operation, direct service calls to a new service instance. One example of this is how the Platform’s recoverability mechanics work: if a server with an instance of the service goes offline, the Platform will deploy a new instance to another machine automatically, as opposed to denying a service call.
See more about developing WCF Services for guest applications.
In a traditional on-premises deployment, a WCF service would be deployed and serve the needs of a single Tenant. In the Platform, a service will be shared by multiple Tenants, and all calls for a service, regardless of which underlying Tenant originated the call, are handled by any instance of the service on the grid. the Platform’s routing system will by default route service calls to all instances equally and randomly regardless of the underlying Tenant that makes the service call.