As we have seen, a key problem with the client-server architecture is the difficulty of scaling resources available to the server as the number of clients grows.

The fat client architecture is one approach to managing this problem by moving more functionality into the clients. The peer-to-peer architectural pattern extends this approach by moving all services and data in the system into the clients themselves so that every peer is both a client and a server.

A consequence of this architecture is that responsibility for hosting and managing services is shared amongst all the clients. As the demand for resources grows due to an increase in clients, there is an equivalent increase in the resources available for servers, because each peer fulfils both roles.

Consequently, a peer-to-peer architecture is particularly useful when a system has responsibility for extensive computational processing, or hosting large amounts of information, but is constrained by the physical capabilities of the hardware. In addition, peer-to-peer networks are useful in adversarial situations when a single point of failure might be threatened by an attacker. Good examples are:

  • file sharing networks (enabling robust sharing of material),
  • block-chain (for validating transactions when participants don’t trust each other)
  • sensor networks in remote locations, where power failures may occur.

Peer Discovery

One key challenge in peer-to-peer architectures is service distribution and discovery: if services are distributed to any available or participating node, how do you know where to look for other peers offering the same service. In addition, if anyone can participate in a peer-to-peer system, how do you determine which of the potential peers to trust?

There are several adaptations that can be made to the peer-to-peer pattern to accommodate these problems, including:

  • Using a globally known registry to record the addresses of peers in the system.
  • Searches across the peer based network.
  • A hybrid of both patterns

In this approach, a peer first queries the registry to discover what other peers are available. The registry effectively acts as a super-peer, to which all other peers refer first for resource discovery.

Each peer maintains its own registry of known other peers that have previously contacted it to request resources. When this happens the requesting peer also reports the resources it has available. When one peer performs a search for resources held by another peer it asks all the peers it knows for the resource. These peers then pass on the request to their known peers until the resource is discovered or the request times out. In this approach, several registries are deployed, each of which contains a list of available peers. Each registry attempts to maintain an independent and up-to-date list of available peers by querying the other registries.

All of these styles involve trade-offs. The use of a globally known registry shares some of the limitations of the client-server pattern: if the registry becomes unavailable then none of the peers will be able to discover new peers that might have the resources they need. On the other hand, the peer based search has no guarantee of finding the required resources if the search times out before the right peer is reached. The hybrid approach mitigates but does not eliminate the disadvantages of the other two approaches while introducing significant additional architectural complexity.

[A]synchronous communication mechanisms

So far, we have assumed that communication between components is direct, synchronous and instantaneous. Sometimes these assumptions are inappropriate for inter-component communication because:

  • Underlying communication channels may be unreliable or subject to delays that disrupt the expected flow of computation in a component.
  • The client must choose which components will provide information for a computation before processing begins. because only a single request can be made at a time. This means that the minimum time for the computation is typically a factor of the number of information requests made
  • Components are forced to request information from other components in a pre-determined sequence because they need to wait for a response from each component in turn. However, some applications may not need responses from every request, provided a sufficient number of responses are obtained
  • The overall system design cannot exploit computational parallelism to improve response time. A component may be unnecessarily idle while it waits for a message to be delivered to another component. It could be handling messages received from other components, for example.

Sync vs Async Example

Consider, for example a component developed to search for the best possible price for a home insurance premium, given some constraints (property value, contents value, minimum excess, whether the purchaser wants legal cover and so on). The component needs to contact a large number of insurance providers and request a quotation according to the specifications of the customer. The component will then select the top five quotations and present them to the customer for review.

The diagram illustrates the communication between the client component and the insurance providers if the client makes its requests using synchronous communication. The time taken to identify the best quotation will be equal to the sum of times taken by each of the individual insurance providers polled. In addition, if any of the providers are unable to provide a response (due to network failure, for example) additional time is added to the duration of the computation without benefit. The synchronous communication also means that the component cannot deal with other quotation requests until the first one is completed.

Alternatively, the quotation component could contact each of the brokers asynchronously, as illustrated in the diagram. In this arrangement, the client sends a request to each insurance provider without waiting for an immediate reply. Once all the requests have been sent, the quotation component then waits for a minimum number of insurance providers to respond with a quotation and then sends an initial estimate to the customer. As further quotations arise the estimate can be updated as required.

Information processing patterns address challenges of efficient information management within a system. This may be for processing large amounts of data or for supporting effective coordination between components.