|
|
Browse by Tags
All Tags » Networking » Indigo (RSS)
-
When I create a channel to a service, how do I know when the service is ready to process the data for that channel? A channel doesn't really know what the service is doing. The service might be actively processing the data being sent over the channel. Or, the service might not. There is a constant tension in the system between components that want to push data and components that want to pull data. Components that push data actively work as long as there is data available until a back pressure builds up in the system that resists their ability to push. This back pressure is typically the result of some queue or buffer that has filled up and is no longer able to accept the things being pushed into it. Components that push include several transport and protocol channels, as well as the service dispatcher that pushes messages to the service implementation. Components that pull data remain quiescent even if data is available until there is someone that actually wants to consume some data. Many transport and protocol channels pull messages rather than push. A single implementation may feature both push and pull modes. For example, the TCP channel has a small portion that works in a push mode for connection establishment and transferring some initial data while the application portion of TCP works in a pull mode. Depending on the visibility of these push and pull modes, you might be able to tell from the behavior of a particular protocol what the service is doing. For example, if you're using just TCP, then the initial transmissions needed to open a connection can only be completed once a little bit of pulling has been done on the receiving application side, thus telling you that the service is doing some active processing. On the other hand, if you're using a ReliableMessaging channel or a OneWay channel, then those protocols have a visible running state where they are in push mode. You can't actually tell whether the service is working until you fill up some of the buffers in the protocol stack and start getting push back in the form of rejected messages. That means the service is not working as fast as you're sending data. A queued channel would be an extreme example of push mode. A message queue allows you to push large amounts of data when the service is not even running. Therefore, to know in the general case whether the service is ready to process for you, you need to be able to ask that question to the service rather than to the channel. Next time: Composing Read More...
|
-
How do I find the address of a client connection to make a trust decision? Don't base security decisions on the perceived client address. Any address that we have comes from the underlying socket implementation and could be spoofed. The data that the socket has is sourced by the client. You should be using a source of information that has a verification process that the server trusts, such as a certificate, to distinguish clients. Next time: Reader Trends Read More...
|
-
Simplicity is elusive. A few weeks ago I learned that part of transaction flow, propagating information about the source machine between the client and server, is more complicated than I thought. It's not that the details were inherently complicated but rather that they were inconsistent. The information was passed in a certain header of the message, except when using a particular transaction protocol and transport protocol together. Someone noticed that a few bytes could be saved by optimizing this particular combination and in that case put the information in a different header in a different format. Efficiency was achieved at the cost of forcing everyone trying to understand the protocol to think harder about it. Most of the protocols that have stood the test of time on the web have sacrificed efficiency to achieve simplicity. Consider all of the protocols out there that are large, bloated, and redundant but easy to think about and tolerant to misunderstandings. What keeps them alive against their slimmer and more advanced competition other than that they were able to attract many people to speak them? Next time: Naming Contracts for Versioning Read More...
|
-
As I mentioned on the 30th anniversary of IP , an early and fundamental split was made in TCP to distinguish point-to-point messaging from end-to-end messaging. The split is based on a philosophy that the communication endpoints should be in control and that there should be a minimal amount of functionality and responsibility distributed along the communication path in between. The end-to-end philosophy shows up in how TCP systems perform throttling. Duplication of throttling makes the system more complicated. Duplication of throttling also makes the system less efficient because more throttling decisions have to be made by intermediaries that have less information about the system than the endpoints. TCP uses flow control to establish how fast data can be transmitted from the sender to the receiver. Rather than having the communication path adjust the flow rate, TCP uses congestion control algorithms to infer how the sender should change its transmission strategy to best use the network. In the basic implementation of TCP, the data stream is overlaid by a transmission window that defines which packets can be sent. As packets are received and acknowledged, the transmission window slides along the data stream. The endpoints grow and shrink the size of the transmission window as needed to adjust the flow rate. Initially the transmission window is very small because the state of the communication path is unknown. However, the window size adapts itself over time to an optimal rate of transmission. Some of the changes to the TCP/IP stack in Windows Vista were to accelerate the rate of adaptation and reduce the amount of time spent using a sub-optimal transmission rate. The end-to-end philosophy also shows up in how the WCF TCP channel avoids trying to do throttling. The service knows more about how it's going to use the network than the networking stack does. Consequently, if you want to control how quickly things are sent and received, the best place to control that is in the application. The only bottleneck in the TCP channel is how quickly it can pass information back and forth between the application and the wire. The default quota settings for the channel are sufficient for networks up to 100 Mbps. You'll want to increase the ConnectionBufferSize quota from its default of 8 KB to something like 64 KB on faster networks. Note that if the throttling is desired for fairness between applications rather than within an application, then the best place to do the throttling Read More...
|
-
There needs to be some concept of cleanup that takes place when an asynchronous request can't be completed. For example, when a service is shut down or a socket is closed you know that any asynchronous operation waiting on that resource will never get a result. Something needs to happen to the asynchronous operations to prevent them from lingering forever. The asynchronous callback pattern has no built-in concept for cancelling a request that you made previously. Even if you build in the concept of cancellation, by its fundamental nature an asynchronous call is going to race to completion against any actions you take. It would be possible to examine a request, see that it hasn't been completed, attempt to cancel the request, and then find out that it completed anyway because completion occurred during that same interval of time. Consequently, cleanup has to be initiated by the request itself rather than by the caller. There are two typical patterns that you see asynchronous operations implement to perform cleanup. The fencepost pattern is where the operation cleans up by completing successfully but giving a distinguished value that indicates that the operation had no result, such as by returning a null object. The exception pattern is where the operation cleans up by storing an exception caught on the worker thread, signaling completion, and then throwing the stored exception on the user thread that goes to pick up the result. The fencepost pattern is typically used for expected cases, such as shutting down, whereas the exception pattern is typically used for unexpected cases, such as IO failures. As an example, consider a service waiting for incoming client connections on a socket. The service will keep several asynchronous requests ready at a time waiting to accept clients. When the socket is shut down, those asynchronous requests need to be cleaned up and they'll do that by returning null. On the other hand, if the socket had encountered a read error instead, then cleanup would have been done by throwing an exception for each outstanding request. Next time: Controlling HTTP Connection Limits Read More...
|
-
Being thread-safe is different than being concurrent. The channel interfaces are thread-safe so that multiple callers can use them at the same time without getting garbled messages. However, if multiple callers try to send messages on a single channel at the same time then a few different things might happen. The channel might be fully concurrent and process the sends in an overlapped fashion. The channel might be partially concurrent and process a limited number of the sends at a time. The other sends are blocked until one of the earlier requests completes. The channel might be singly concurrent and process the sends in a sequential fashion. A connection-oriented channel like TCP tends to be singly concurrent. Interleaving messages requires having a sophisticated framing protocol and the performance cost is rarely worth handling this special case. Datagram channels on the other hand tend to be at least partially concurrent. For example, HTTP in theory is fully concurrent to any degree that you'd like but the HTTP specification recommends limiting clients to two connections to any particular server, resulting in partial concurrency. Whenever you have less than full concurrency you run the risk of deadlock with certain application designs. That's because the logical operation order of the application might say that caller 1 must complete a send before caller 2 can complete, but the physical operation order of the channel says that caller 1 cannot start a send before caller 2 completes. I've seen customers encounter this problem when using HTTP and callbacks. The original request is waiting for the callback operation to complete but the callback thread cannot make any progress because all of the connections are tied up by existing callers, such as the original request. This is a case where the performance gain of sharing connections has led to unsafe behavior. The solution is to increase the concurrency of the system so that at least one call from any of the ongoing chains of operations can complete (there can be callbacks on top of callbacks in complicated knots so the problem is not as simple to solve as increasing the concurrency factor to two). Concurrency can be increased directly at the networking level or by isolating networking resources into separate pools at the application level. Next time: Windows and UPN Format Credentials Read More...
|
-
How do I detect when the other side of a TCP connection has gone away? Does TCP keep-alive take care of this for me? Although we take it for granted that change can be quickly detected for closely connected components, it turns out to be surprisingly difficult to detect change when two machines are isolated by more than a simple wire. Even a really big change to the system, like one of the machines disappearing, is hard to spot. Detecting that the other side has disappeared is a common request because, on a server, knowing that the client has dropped the connection allows you to clean up resources much faster. The TCP transport sometimes gives you quick notifications by aborting the session that the connection has been dropped. However, there's no guarantee that the transport will be able to detect that the other side has gone away. That's because notification of a TCP connection reset has to travel just like any other piece of data and can be lost or redirected along the way, if it was sent at all. The only sure thing is that the next time you attempt an IO operation, you'll find out if the channel was still good or not. If you're unhappy waiting for the next IO operation, then you can make IO operations happen faster. The basic concept is to have a cheap IO operation that does nothing but bounce between the two parties. This is sometimes called a heartbeat and is exactly what takes place when you talk about TCP keep-alive. However, the standard keep-alive interval for TCP is 120 minutes, which is probably worse than your current latency for detecting change. By default, a service gets bored waiting after about 10 minutes and gives up. The chance of a keep-alive happening between the time that a client disconnects and the service notices it is pretty small. If you want something faster but don't want to change the timeouts, then you can take the basic concept into your own hands. One approach is to create a keep-alive method on your service contract that does nothing but let you trigger IO operations at a frequency you desire. Another approach is if you control both ends and don't want to change your service contract, then you can do the same thing in a protocol channel and swallow those messages so that the service never has to see them. Next time: Collections without CollectionDataContract Read More...
|
-
How can I use reliable messaging together with a load balancer? The point of reliable messaging is to help ensure that messages get from one place to another. This means that the protocol notices when messages that were expected to be delivered go missing. On the other hand, the point of a load balancer is to make sure that messages are spread out and that there aren't too many messages going to the same place. You can see how these two goals might come into conflict at times. Many load balancers do offer a compromise to make these two goals simultaneously achievable though. A perfect load balancer would take the total sum of messages delivered and divide those messages evenly up among the available processing nodes. However, assume that the number of messages in any one client session is relatively small compared to the total number of messages processed. A load balancer could instead apportion groups of messages among the available processing nodes where the total sum of messages is still split roughly equally. This division according to groups would allow a feature like reliable messaging to work because the same server would be used to process all of the messages in the reliable session. The feature that this division method represents is typically called "sticky sessions" or some other phrase for affinitization in the load balancer. Next time: Local Settings and Policy Read More...
|
-
Does WCF ever cache the DNS lookup for a service address? How do I clear this cache? There is just a tiny amount of caching that I know about. The TCP and MSMQ transports use a shared cache for recently used addresses. I think for MSMQ that this is only a factor for custom dead letter queue addresses. In any case, this cache is to speed up lookups in the case that you're hitting the same address over and over again very quickly. There's no way to clear the cache, but the lifetime of entries is two seconds so you'd have to be changing DNS settings extremely often to notice this. If you're getting stale lookups, then it's probably an operating system or server cache that is causing that. Next time: Moving Services and User Principals Read More...
|
-
My web service needs to periodically broadcast messages to clients. The service is an Internet-facing application hosted inside of IIS. What’s the best way to do this? The big limitation in this scenario is that your clients might be behind a firewall and non-addressable. There are basically two architecture camps for broadcasting messages to clients over the Internet. The push architecture camp has the clients maintain a continuous connection to the server and pushes data out at each update. The pull architecture camp has the clients periodically poll the server to see if there’s any new data. Both of these architectures are widely used and they trade latency versus resources off of each other. There are a few other architectures that work locally as well but aren’t as useful over the Internet, such as multicasting and callbacks. I’m just going to pick one of these and talk about using a push architecture. The basic way to build a push architecture is to have clients connect to the server and then the server holds the connection open indefinitely to send messages. If your service is hosted in IIS version 6 or below, then you don’t have a lot of choice about the network protocol. Pushing data from the server to client is difficult with HTTP because the protocol is built on top of the request-reply model. A typical way of using HTTP to push is to make an empty request and send back a response using the chunked transfer encoding. Chunked transfers allow an HTTP server to send the response in pieces without having to specify the total length of the response up front. Normally, the client knows the message is done when the connection is closed or the pre-announced content length is reached. Neither of those options work in this case. Instead, the server needs to define some framing protocol so that the client can tell when the individual messages are done. The easiest way to get chunked HTTP transfers in WCF is to use the HTTP transport with streaming enabled. If your service is hosted in IIS 7 (the Vista/Longhorn version of IIS), then you are able to pick other network protocols for your service, such as TCP. TCP is inherently duplex so it works across the Internet for server-initiated transfer of messages without having to connect back through a firewall. Since TCP is duplex, you can write a duplex contract that gives you a very nice programming model for sending the broadcasts. This is a lot less work on your part than the equivalent setup needed with HTTP. Read More...
|
-
I have a long-running service operation that needs to receive a response. What options do I have for designing my web services? The problem that most people run into with long-running operations is that the operation eventually hits some quota value and times out. For instance, if you want to run the operation over an HTTP connection, you have to configure the HTTP transport to not give up waiting before the operation completes. This configuration consists of setting the send and receive timeouts of the channel to some maximum bound for the operation time. Setting the timeouts correctly requires you to know what a maximum bound is for your operation. If you don’t know of a maximum bound or the expected operation time is poorly defined, then it becomes very difficult to set a reasonable quota value. Quota values that are too small cause the operation to be unnecessarily aborted. Quota values that are too large weaken your security and makes you more vulnerable to malicious attackers tying up your resources. Large operation times also mean that you’re more susceptible to dropped connections or network glitches that cause the operation to be aborted. One alternative to holding a transport channel open during the entire operation is to use an asynchronous delivery mechanism (this shouldn’t be confused with asynchronous method calls). Asynchronous delivery, such as a queue, allows you to drop messages off without having a continuously stable network connection to the other party. Reliable messaging is also a way to recover from dropped connections. However, neither of these methods helps you bound your maximum operation time for request-reply style messaging. The basic solution to unbounding your operation time is to decouple the request and response of the operation into a pair of one-way operations. A one-way operation on the service is used to receive the request and a one-way operation on the client is used to receive the response. Correlation is still needed to associate the request and response together. This correlation is no longer defined by the use of a single network connection but instead is some property associated with the message. Each one-way call has a well-bounded and predictably short operation time. The one-way model is not perfect. Transports that do not support symmetric message transfer, such as HTTP, are harder to use because you may need to address client machines and poke holes through firewalls. However, that is a tradeoff you may need Read More...
|
-
A uniform resource locator (URL) is a standardized pointer to a resource. URLs not only tell you where information is located, they also tell you how to interact with that information. A cousin of the URL is the URN (the N stands for Name). URNs identify a resource by name without regard to the resource location. URLs are the opposite. They identify the location of a resource without regard to what that resource is actually called. URLs don't have an overly rigid syntax, although most URL schemes follow the same general form. scheme://username:password@host:port/path?query#fragment There are other pieces that can go into URLs although these are seen less frequently in WCF applications. Usernames, passwords, and fragments are rare enough that I'm not going to talk about them here. These are primarily used in the HTTP and FTP schemes with an interactive client, rather than against a web service. The most easily recognizable portion of a URL is the scheme. A scheme indicates the protocol that is needed to access a resource. Our standard transports come with the schemes "http", "https", "net.tcp", and "net.pipe". You'll sometimes see other schemes for transports and you can pick your own scheme when you're writing a channel and make the binding element. The host and port specify where connections should be established to access a resource. If you're using a hosting environment, like IIS, or discovery then the location of the server is generally determined by configuration information outside the service. Someone then has to tell the client where the server is located. In some cases, particularly when you've got a transport that's purely on-machine, neither the client nor server cares about the actual connection location. The path has traditionally indicated where a resource resides on the machine, using a hierarchical notation very similar to a file path. There's no requirement to actually map paths to files. When you're using the net.tcp scheme, the path really is just the name of a service without regard to where the files for that service are located. Automatically generated client-side paths for net.tcp services are even less connected to the concept of a file because they contain a random GUID to make each conversation have a unique address. The query string is used by some schemes to pass parameters to the service. There's no standard for formatting a query and there are very few restrictions on what can go in the query string. Servers that process a query Read More...
|
|
|
|