|
|
Browse by Tags
All Tags » Indigo » Service Architecture (RSS)
-
Some tips for building support for versioning into the naming of data contracts. First, the primary route for versioning should be through the namespace part of the contract rather than the member name part of the contract. Versioning the contract through member names tends to leak across the service boundary more forcefully. The programming experience of the service often makes a member name directly visible while a namespace is more or less invisible. Second, choose a single consistent scheme for identifying the version. Two popular schemes are the date of the contract and a sequential numbering system of major and minor versions. Both schemes provide the basic element required of a versioning identity, which is an unambiguous total order among the different versions. However, multiple schemes should not be mixed together for a single contract and preferably not for a single system as well. The date scheme, http://company.com/year/month/name, has issues around granularity but can be very evocative since you probably already associate dates in your mind with other events. The issue with granularity is that you have to plan ahead for a maximum update frequency. In the previous example, two updates in the same month would collide with the same name, suggesting that a contract that is updated frequently might include additional levels of refinement, such as the day of the month. However, unnecessarily fine granularity makes the name cumbersome. The numbering scheme, http://company.com/major/minor/name, gives less of a clue about what the version corresponds to but has fewer issues with granularity. Updates can happen as frequently as you want since you can just keep picking new numbers. However, you still have to give some thought to granularity when deciding how many numbering components to include. For example, a single version number may be sufficient if no distinction is needed between major and minor updates. Next time: Finding a Client Channel Read More...
|
-
The WCF Security Guide content that I've mentioned a few times before is now done with early drafts and has been rolled up into a beta release of the full book. There's a ton of content in the real thing on top of what you've been seeing in the drafts. You can download the beta of the full security guide from CodePlex now. If you want to know what I think about the guide, here's the foreword I wrote for them: The computer industry has come to a realization – based on many years of slowly learning from painful experiences – that computer networks are hostile environments. Nevertheless, computer users demand as part of their basic expectations that applications take advantage of the ubiquitous and continuously available connectivity at their disposal to deliver a rich connected experience. It is now your task to design and assemble the loosely coupled service components that you have available in a way that blunts threats and thwarts attacks on the user’s precious assets. Your applications must withstand the hazards of living in a hostile networked environment. To make that possible, you must understand the risks that your applications face and you must be certain that the remedies you put in place properly mitigate the dangers of those risks. As someone who has been through several rounds of security and threat modeling for Windows Communication Foundation, I can say without hesitation that knowledge and experience are your greatest assets for designing secure Web service applications. The trick is to gain as much of that knowledge as possible from the painful experiences of other people rather than painful experiences of your own. J.D. Meier and team have done a fantastic job of assembling and digesting countless practical experiences into a convenient and centralized resource. Practitioners of service-oriented development with WCF will want to use this guide as both a means of learning about the fundamentals of Web service security and a reference for getting specific, step-by-step instructions for dozens of the most common security problems. I enjoy that this guide collects together several different approaches for learning about and implementing security solutions. By combining a variety of formats – scenarios, how-to articles, and guidelines are only a sample of the offered modes – solutions are both reinforced and made more easily discoverable through different entry points. The reason that I’m so excited to see Improving Web Services Security: Scenarios Read More...
|
-
I'm a big fan of using service virtualization to solve a variety of problems with developing and managing web services. The Managed Services Engine is a solution built on top of WCF to supply a repository-based runtime and management tool for service virtualization. I hope to someday put the solutions team out of business by making service virtualization easier to do in the product. For now though, the Managed Services Engine is one of the better web service virtualization systems that I've seen. You can get their new June CTP on CodePlex , which replaces the previous beta release from last October. Read More...
|
-
FaultException supports both an untyped variant, for when you don't have any particularly interesting detail to provide, and a typed variant, for when you do. Don't use a subclass of Exception as the type of a typed FaultException. Here's why. When you use a typed FaultException, you are creating a fault contract between the client and service about data that gets exchanged and a common type system that the two share. By using a CLR exception type, you are unnecessarily forcing that common type system to reflect details of the CLR type system. That type dependency will make it more complicated in the future to move your services and clients to other platforms and possibly even to other versions of the framework. When moving to another version of the framework, you may change the exception profile of your application and start receiving exception types that are more specific or different than the exception types that you received before for a particular error. If you pass those exceptions through to the client, then those platform implementation details are now leaked across the service boundary. Finally, there's no guarantee that the interesting information in a CLR exception will be preserved across the boundary. Most of the interesting contents of exceptions are not serializable. Instead of creating a FaultException with a subclass of Exception, you should define a fault contract that is meaningful for your application or business user. This fault contract can be crafted according to the needs of your service and will remain independent of any particular platform or technology choices that you may need to make later. Next time: Faster Known Types in Orcas Read More...
|
-
One of the things that happens when a new technology framework is introduced with new patterns and best practices is that people wonder what's going to happen with the frameworks that they use today. When WCF was announced a few years ago, users of Remoting were asking this question a lot. The answer then (I have actual quotes from people) was that the introduction of WCF does not mean that Remoting is dead. A lot of scenarios that were formerly done with Remoting can now be done better with WCF, but Remoting does not go away. I still see people asking this question, and the answer has not changed. I would not be surprised to see applications continue to come out with support for Remoting, but I would expect that those applications would represent a smaller percentage of the overall market. There are going to be some times when Remoting is actually the preferred choice. Just like we say with ASMX, if you have an existing application that works today with Remoting, then there's no reason that you have to go out and rewrite it. There should be a preference to use WCF in new applications, but there's no reason to do a conversion unless you actually need to make use of the new features. Remoting also has benefits in some particular scenarios. The fastest standard transport for WCF today is the named pipe transport. Remoting can beat the performance of named pipes if you're communicating between two endpoints within the same process. WCF would be preferable if you want the flexibility to change the scope of the endpoints later or to use a consistent programming model at different scopes, but Remoting is useful if what you care about is performing that scenario as fast as possible. Next time: SVC Files and Services Read More...
|
-
You have talked in the past about how a service has both local settings and settings that are shared through policy. How can I transmit all settings through policy to the client? The two types of settings are clearly distinguishable. Shared settings are required to have agreement between the client and server for the two to interoperate. Examples of shared settings are the protocols and formats being used to transmit messages. Local settings are not required to have agreement between the client and server for the two to interoperate. Examples of local settings are the limits for the time and space allowed to process a message. Local settings can not only be in disagreement between the client and server, but they frequently do not make sense to share between the two. The messages sent between the client and server are rarely symmetric. The processing resources available to the client and server are rarely the same. The security concerns of the client and server are rarely in agreement. You can transmit local settings by creating your own policy assertions that both sides implement. This will involve a lot of hassle, particularly if the service wants to have local setting values that are different than those sent in the policy. Finally, the client will need to absolutely trust the service because you are asking the user to run with settings that were supplied by a third party. Why do you want to do this using policy? It seems that if you have such a level of trust with the client, then you probably already have more direct ways of pushing configuration and executables to the client machine. Next time: Enabling Performance Counters Read More...
|
-
A delegate is a special type that can be bound at execution time to a method invocation. Normally you'd think of method invocations as being synchronous, but delegates can be executed either synchronously in the obvious way or asynchronously by introducing an extra thread of execution. An asynchronous delegate invocation uses the standard BeginInvoke and EndInvoke pattern, with the option to provide a callback for when the work is complete. You would expect that the asynchronous delegate pattern would be an easy way to make asynchronous calls to a web service. You would be wrong. [ServiceContract] public interface IService { [OperationContract] string Echo( string text); } public class Service : IService { public string Echo( string text) { Thread.Sleep(5000); return text; } } public delegate string EchoDelegate( string text); public class BeginInvokeDelegate { static void Callback(IAsyncResult result) { Console.WriteLine( "In callback." ); } static void Main( string [] args) { string uri = "http://localhost:8000/" ; ServiceHost service = new ServiceHost( typeof (Service)); service.AddServiceEndpoint( typeof (IService), new BasicHttpBinding(), uri); service.Open(); ChannelFactory<IService> factory = new ChannelFactory<IService>( new BasicHttpBinding(), new EndpointAddress(uri)); IService proxy = factory.CreateChannel(); EchoDelegate d = new EchoDelegate(proxy.Echo); IAsyncResult result = d.BeginInvoke( "foo" , new AsyncCallback(Callback), null ); Console.WriteLine( "Returned." ); Console.WriteLine(d.EndInvoke(result)); factory.Close(); service.Close(); Console.ReadLine(); } } The code above is using a ChannelFactory against a synchronous version of the Echo interface. The client and service are decoupled so it would have been legal to generate a client proxy that expressed the Echo interface using an asynchronous representation regardless of how Echo is implemented on the server. Instead, the code attempts to make the service call asynchronous by wrapping it in a delegate and using BeginInvoke. If you run this code, then you'll see that the call to BeginInvoke does not complete until after the Echo service call has returned. This defeats the purpose of using the asynchronous pattern. The problem is that BeginInvoke knows about and only works with specific types of proxy objects, which do not include the proxy objects generated by ChannelFactory. The right way to make an asynchronous service call is to generate a proxy that has an asynchronous representation Read More...
|
-
How can I speed up message processing when using MSMQ with WCF? For small gains, it is generally possible to eke out a few percentage points of performance by tuning parameters and settings according to the application domain knowledge you have. For large gains, you are likely going to have to think about larger design issues. In particular, a type of design issue that you should consider in your quest for large gains is to question what features are truly needed to build your application. This article is about a few of those feature decisions that you can make when using MSMQ together with WCF. Since these design decisions require building your application with a restricted set of features, there's no guarantee that these techniques are going to be applicable for you. It really just depends on the queue features you need versus the features you don't. I'm not going to mention features not primarily related to the queue, such as whether to use message security, although obviously the same type of analysis can be applied. Use the NetMsmqBinding instead of the MsmqIntegrationBinding. Of the two bindings, NetMsmq is faster than MsmqIntegration in most cases when similar conditions are applied (for example, both running without security). I have an earlier article describing the differences between the two MSMQ bindings . Disable transactional delivery of messages. The ExactlyOnce option controls whether messages are delivered without being lost or duplicated. Making a delivery guarantee requires that the queue be transactional and issue transactions for transfers. If you need best effort rather than exactly once, turning off transactions noticeably improves performance. Disable durable message storage. If you've already turned off transactions, then you can go significantly farther as well. The Durable option controls whether the queue survives restarting the MSMQ service. If you are using the queue to get asynchronous communication rather than reliable delivery, then leaving messages in a volatile store is another noticeable performance improvement. Pack more messages into the same transaction. Often you can't go as far as turning transactions completely off. A less dramatic step is to use the same transaction for multiple receive operations. The TransactedBatchingBehavior allows you to group messages up to a maximum batch size in a single transaction to amortize creating a transaction across multiple receive calls. Next time: Streaming and ToString Read More...
|
-
How do I push back against clients that are tying up the external connections of my service? The amount of service connection resources used by the client can be thought of as a product of two dimensions. The first dimension is the number of connections that the client has open. The second dimension is the length of time that the client holds the connections open. This is a typical time-space product for measuring utilization. Another way to slice the problem might be network link capacity for space and transfer duration for time. The product that you're going to optimize for is problem dependent, but I'll use number of connections for the example. Each of the dimensions has a quota value that we can use to push back against clients. We push back against space usage by throttling the number of concurrent sessions or instances. This really only makes sense if you have some way of identifying a particular client across multiple sessions because otherwise the client can just knock out other competitors to grab more resources. A typical way of identifying the client is by requiring authentication. You could also do some kind of traffic shaping at the network level although that's best done in front of your service rather than on the same machine. We push back against time usage by limiting the connection lifetime (such as through operation and receive timeouts). The general solution to this problem is to identify the factors that make up the product, pick quotas that protect those factors, and then tune quota values. The balance between the factors is another problem dependent piece. For example, you may have to keep the time quota above a certain value due to the latency of the network connections you're using. However, this is just constraining the range for the corresponding space quota that will let you hit your target value for the product. Next time: Initializing the Context Read More...
|
-
I have a service contract with a few operations that take large inputs and do a lot of processing. If I configure the service quotas with small values to prevent too many of the expensive operations from happening at once, then the overall throughput is very bad. If I configure the service quotas with large values, then the expensive operations could be called many times and the server will run out of resources. I've left the question off of this one because I'm not actually going to talk about this problem. Instead, the description was a good excuse to talk about the design of service contracts. Long-time programmers will recognize the discussion on chatty versus chunky interface design. Chatty interfaces break operations up into small units of work. It takes a lot of chatty method calls to get something done, but because everything is nicely componentized, it's possible to reassemble the pieces in ways that the original designer didn't think of. Chunky interfaces map large, user-scale operations to a single method call. It only takes a few chunky method calls to get something done, but the operations chosen for the interface are really all you can do. In the non-distributed application world, it often doesn't matter whether your interfaces are chatty or chunky. There were some exceptions. For example, operating system calls have a kernel-mode transition cost so it's beneficial to get a lot of work done with a single call. Every iteration between kernel and user mode results in paying the transition cost. For distributed applications, there is a similar large transition cost called the network. Each trip back to the network introduces a lot of latency relative to the typical computational costs of an operation. COM programming really threw the distinction in your face because it was suddenly a lot harder to predict whether a given method call would incur an expensive transition. You had to program defensively and use lots of chunky interfaces to avoid unpredictable costs at runtime. Typically, calls between application tiers have to be done through chunky interfaces. Calls within an application tier can afford to be chattier. HTTP applications in particular tend to be extremely chunky. Often, requesting a resource will return every single piece of information about that resource and you don't have to make any trips to the server again. This trend has swung back around recently but mostly because the resources are too large to transmit at once (maps of the Read More...
|
-
How do I construct callbacks to work over a load balancer without affinity? Let's construct a scenario to demonstrate this question. I have three machines; call them X, Y, and Z. X and Y are together behind a network load balancer. This is a server to server communication scenario, where two servers are attempting to talk over a duplex contract. One of the load-balanced servers, X or Y, is going to first act as the client. Pretend that X is the relevant server in this case. X calls a service with a callback contract to Z. At some point in the future, Z is going to respond on that callback to the load-balanced group. If X passed its real address to Z, then Z has no problem making the callback. If X gives the load-balancer address, then Z will sometimes pick X and sometimes pick Y. The load balancer is not affinitized to a particular machine. The interesting case is where we haven't pinned X as the instance to respond to. What can we do to make sure that the request by X is correlated with the response by Z, regardless of whether that response goes to X or Y? Well, either one of two things needs to happen. Z can stuff all of the necessary context information into the response message so that any server could process the response without having to know about the previous conversation. This is essentially turning a stateful problem into a stateless problem that sends a whole bunch more data. This has turned out to be a pretty interesting solution from the HTTP developer front. X and Y can share a common, durable store of correlation information. This is typically a database, but we don't have to be specific about how X and Y share state between themselves. If you picked something in between going totally stateless and having durable state management, then there would be some interesting implications. There would be situations in which the receiving server would need to invent correlation information out of thin air in order to properly interpret the message. You can fake this some of the time, but sooner or later you'll get caught. Next time: Poison Message Handling Read More...
|
-
Today's article is about a classic flooding attack that relies on a receiver who isn't careful about where it sends responses. The whole article is about this next picture, so if you don't have images then you're going to miss out. The way that the attack works is that it relies on a receiver who sends replies or faults without having verified the reply address. In the normal case, replies go to some address that is controlled by the requestor. This is okay if we know who the requestor is and trust them. However, now suppose that we don't trust the requestor, perhaps because we've failed to authenticate them. An evil requestor can claim that replies should go off to some third-party address. There are a lot of systems where a very small request, remember it doesn't have to be valid at all, generates a much larger reply. In essence, the evil requestor is amplifying its attack power because it only needs the bandwidth for a 10 byte request to cause 1000 bytes to be thrown at the target machine. Stopping this attack is quite simple, although it can cause problems with the design of a distributed system. The receiver only sends replies to an address that is known to be owned by the requestor. That is a very simple rule to follow. If the requestor is authenticated, then the receiver trusts its claim of owning the reply address. If the requestor is not authenticated, then the receiver only sends replies along an associated back channel. Any replies that were supposed to be directed to any machine other than the requestor's are discarded. The attack is now stopped, although it breaks scenarios where the requestor wasn't authenticated but the requestor also wasn't being evil. Next time: Reducing Memory Usage with Large Messages Read More...
|
-
I'm building an always-on service that gets its messages from a front-end queue. How do I design this service to be scalable? There are two directions to go in when talking about scalability. There's scaling up, handling more messages while continuing to use a single machine, and then there's scaling out, handling more messages by using multiple machines. We'll talk about the two individually even though you may be using a combination of techniques to improve scalability. Scaling up is the simpler of the two although it isn't as interesting to talk about here. The WCF model works very naturally and automatically with scaling up. No matter how powerful the machine gets, it rarely makes sense to replicate an always-on service on a single machine. Rather than duplicating processes, single machine scalability in WCF is achieved by adding more threads to a single process. In other words, you'll get automatic scaling up until the point that you start hitting service model quota limits. This won't take long as the default service model limits are designed for a single processor machine with 10 concurrent clients. However, you can easily in the binding start increasing the number of threads pumping messages and the number of threads processing calls until you hit the hardware limitations of the machine. This is a balancing act to reach a point where resources are never idle, but they're also not overcommitted and facing contention. Scaling out involves replicating the server process to multiple machines. Typically the machines are tied together with network load balancing so that the distribution of load on the farm is controlled by the administrator rather than the user. Each of the machines needs their quotas tuned as above, although homogeneous hardware will let you get away with using a homogeneous binding on all of the machines. An asymmetric farm, with machines of varying capabilities, is really hard to tune. Queues come in to the picture because they generally favor scaling up rather than scaling out. Having a local queue has a number of advantages, including better transaction support unless you happen to be running a Vista or later operating system. However, it is generally much cheaper to scale out than scale up. A quick way to judge which way you need to scale next with your queue is to look at the bottlenecked resource. If you have exhausted the amount of network bandwidth you have moving messages out of the queue, then you should be thinking about moving Read More...
|
-
I'm making multiple calls to a service and all of the other calls sit waiting until the first call completes. How do I make the service process multiple calls at the same time? If a service doesn't accept multiple, simultaneous calls from your client, then you have to figure out whether the problem is actually on the client or service. On the client side, what typically goes wrong here is that the first call blocks all other progress. After the first call completes, you can see multiple calls going out to the service. This is caused by the first call being responsible for opening the connection to the service. You can choose to either open a connection to the service manually by calling Open on the client, or otherwise the first call will automatically open a connection if there isn't one already. Until the connection to the server is established, none of the other calls can proceed and that causes the blocking behavior. The solution is to always open the client connection yourself ahead of time by calling Open so that this process is hopefully complete by the time you want to start making calls. If every call blocks, even after the first one, then this is probably due to the service configuration. There are two knobs that control whether multiple calls are allowed into the service called InstanceContextMode and ConcurrencyMode. You can set both of these knobs through a service behavior. InstanceContextMode controls how often we'll create a new instance. There will only ever be one context if you pick Single and there can be multiple contexts if you pick PerSession or PerCall. PerSession is probably the sweet spot but requires your channel stack to serve up sessionful channels, as from buffered TCP or from reliable messaging. It can be expensive to make your channel stack sessionful if it does not already support sessions from the protocols that you've picked. ConcurrencyMode controls how many threads we'll let into a service instance. There will only ever be one thread per instance if you pick Single or Reentrant, although Reentrant allows a thread to leave from a call and later come back in. There can be more than one thread per instance if you pick Multiple. You really don't have a choice about ConcurrencyMode because you either wrote the service to support multiple threads or you didn't. I don't see a compelling reason for your service to support multiple threads but restrict it to only have one. You should now be able to spot how InstanceContextMode and Read More...
|
-
There are two architectural models for moving messages through a system. Pull messaging models require the receiver to actively suck messages out of the pipeline in order to achieve flow. If the receiver is not willing to perform work, then there's nothing the sender can do to move messages around. Push messaging models automatically generate work on the receiving end when a sender transmits a message. It's then the responsibility of the receiver to process these work items before system capacity is reached (in many systems, there is some programming ease-of-use to make the work items automatically expire after some period of negligence by the receiver). Real messaging systems have many architectural layers stacked on top one another, some of which are push and some of which are pull. Just the process of sending a packet of data using TCP requires several protocol layers of pushing and pulling. You may have noticed that the channel stack of WCF represents a pull model. Actually, you may not have noticed this because the service layer of WCF represents a push model. Even some parts of the channel stack push instead of pull. For instance, the arrival of new client connections at an endpoint is a push operation. Clients will attempt to connect even if the service is not ready to listen for them. However, the service has to engage in some pulling to transform those client connection attempts into transport channels. The boundary from a push layer to a pull layer is a queue. Quota settings on the queue define the system capacity for how much outstanding work is tolerated and how long work should wait in the queue before being expired. The boundary from a pull layer to a push layer is a message pump. Quota settings on the message pump define how aggressively the pump operates. The channel stack uses a pull model because it's very convenient from a framework perspective. Pull models allow the user to explicitly control resources without having to define lots of complicated policy. Work only happens in a pull model when the user takes action, so the messaging system can simply idle when it doesn't have the capacity to accept additional work. Pull models are also convenient for error handling because there is always user code to which we can report any errors. If we don't have user code, then we need policy to dictate how errors are handled. Whenever you're writing channels, you need to keep the pull model in mind or else you will end up writing lots of fragile layer Read More...
|
|
|
|