Tuesday, 8 September 2009
Saturday, 28 March 2009
These notes follow on from a Jeff Prosise talk on this subject at DevWeek 2007.
Asynchronous ASP.NET Programming – Jeff Prosise
Most sites that access external data (databases, web services etc) are written are done so in a synchronous way, and when the number of users increases the site can suddenly slow right down to a crawl, or even fail totally.
When ASP.NET receives a request for a page, it grabs a thread from a thread pool and assigns that request to the thread. A normal, or synchronous, page holds onto the thread for the duration of the request, preventing the thread from being used to process other requests.
If a synchronous request becomes I/O bound - for example, if it calls out to a Web service or queries a database and waits for the call to come back - then the thread assigned to the request is stuck doing nothing until the call returns. That impedes scalability because the thread pool has a finite number of threads available.
If all request-processing threads are blocked waiting for I/O operations to complete, additional requests get queued up waiting for threads to be free. At best, throughput decreases because requests wait longer to be processed.
At worst, the queue fills up and ASP.NET fails subsequent requests with 503 "Server Unavailable" errors.
ASP.NET 2.0 however supports asynchronous pages.
When the request arrives, it's assigned a thread by ASP.NET. The request begins processing on that thread, but when the time comes to hit the external data, the request launches an asynchronous request and returns the thread to the thread pool. When the query completes, the asynchronous request calls back to ASP.NET, and ASP.NET grabs another thread from the thread pool and resumes processing the request.
While the query is outstanding, zero thread pool threads are consumed, leaving all of the threads free to service incoming requests. A request that's processed asynchronously doesn't execute any faster. But other requests execute faster because they don't have to wait for threads to become free. Requests incur less delay in entering the pipeline, and overall throughout goes up.
Asynchronous HTML Handler Pages
The second asynchronous programming model featured in ASP.NET is the asynchronous HTTP handler. An HTTP handler is an object that serves as an endpoint for requests. Requests for ASPX files, for example, are processed by an HTTP handler for ASPX files. Likewise, requests for ASMX files are handled by an HTTP handler that knows how to deal with ASMX services.
You can extend ASP.NET to support additional file types by writing custom HTTP handlers. But even more interesting is the fact that you can deploy custom HTTP handlers in ASHX files and use them as targets of HTTP requests. This is the proper way to build Web endpoints that generate images on the fly or retrieve images from databases. You simply include an <img> tag (or Image control) in the page and point it to an ASHX that creates or fetches the image. Targeting an ASHX file with requests is more efficient than targeting an ASPX file because an ASHX file incurs much less overhead at processing time.
By definition, HTTP handlers implement the IHttpHandler interface. Handlers that implement that interface do their processing synchronously
HTTP handlers don't have to be synchronous. By implementing the IHttpAsyncHandler interface, which itself derives from IHttpHandler, an HTTP handler can be asynchronous. When used correctly, an asynchronous handler utilizes ASP.NET threads more efficiently. This is done in the same manner as an asynchronous page. In fact, asynchronous pages leverage the asynchronous handler support that predated asynchronous pages in ASP.NET.
See http://msdn.microsoft.com/en-gb/magazine/cc163725.aspx and http://msdn.microsoft.com/msdnmag/issues/07/03/WickedCode/default.aspx for more information.
Friday, 27 March 2009
WCF provides a message-oriented programming model, with flexibility in its use.
All WCF messages are modelled with System.ServiceModel.Channels.Message.
They can be encoded as XML, JSON, binary etc. They can also optionally be mapped to/from .Net objects (see serializing, below).
The MessageVersion class allows the specifying of the versions of SOAP and WS-Addressing that you wish to use (if any).
As the message class has been designed to support streaming, the body of a message can only be processed once for a particular instance. You can copy the body if you need to process the message more than once. The message has a state property which will be either Created, Read, Written, Copied or Closed.
The message has a Headers property which can hold values about the message. It is typically the job of intermediaries to process the message headers. Intermediaries often need to store the results of their processing somewhere for future use in the processing pipeline, which is why a message also have a message properties collection. The properties are usually only used during local processing, and don't usually affect what happens to the message on the wire, but they are able to.
Message bodies can be read from by calling either GetBody
Action values of the OperationContract can be used to map messages to methods, and these can be either universal one-way or two-way operations. Operations can either be "*" or named specifically.
Data and Message Contracts
The data contract defines the format of the message body that will be passed in the message. It is done by annotating classes with the [DataContract] and [DataMember] attributes. An example is shown below:
public class Person
public string PersonID; //This property will not be in the message
public string Name;
public int Age;
A data contract will break if any of the following changes are made:
Changing the type Name or Namespace
Changing the Order of data members
Changing the Name of a data member
Changing the type of a data member
Changing IsRequired property from false to true
Message contracts map data contracts to SOAP envelopes. They allow full control to model SOAP messages and headers, by annotating the class with [MessageContract], [MessageBodyMember] and [MessageHeader] attributes on members.
Messages can be serialised into .Net objects using one of the supported serializers. This allows objects to be sent easily using WCF. Messages can be "typed" to .Net types by annotating the types with special mapping attributes. At runtime a serializer maps the .Net objects into messages.
The XmlSerializer is the original serializer from .Net 1.0, and is still fully supported, and offers backwards compatibility to web (ASMX) services and other Non-WCF services.
The DataContractSerializer is a new serialiser in .Net 3.0 that supports most .Net types including:
CLR built in types,
Byte Array, DateTime, TimeSpan, GUID, XmlQualifiedName, XmlElement, XmlNode arrays
Types marked with the DataContract or CollectionDataContract attribute
Types that implment IXmlSerializable
Arrays/Collection classes including List
, Dictionary and HastTable
Types marked with Serializable attribute or implement Iserializable
Note that the DCS only supports a subset of XSD
In .Net 3.5 the DataContractJSONSerializer was added, that perfoms the same as the DCS but with serializing to JSON instead of XML.
Message-oriented Design Techniques
Pure XML messaging approaches are gaining more traction these days. This is very like REST.
Schema-first design is common in integration scenarios, specially where collaboration and parallel development is required. Data/message contracts can be generated from schemas using SvcUtil.exe. XmlSerializer (and xsd.exe) is the recommended approach here.
Code-first design is more common when collaboration isn't needed, where the developer writes data contracts and lets WCF generate the schemas. DataContractSerializer (and SvcUtil.exe) is the recommended approach here.
Code Access Security & Sandboxing
Most of the important base features were added in 2.0, including a simple sandboxing API, security transparency and manifest based activation. These have been used in several current and future technologies such as ClickOnce, XBAP, and SilverLight.
Sandboxing is all about loading code dynamically yet restricting what it can and cannot do. This is supported using the AppDomain class with restricted permissions. The permissions that the code in the sandbox would use were defined by the permission set in Code Access Security (CAS).