Communication Protocols
The communication protocol fundamentally shapes how a service is designed and implemented. With so many options available, selecting the right one requires a thorough understanding of how each protocol works.
Hypertext Transfer Protocol (HTTP)
Hypertext Transfer Protocol (HTTP) is built on top of Transmission Control Protocol (TCP) and is one of the most widely used communication protocols in modern systems.
The concept is straightforward: a client sends a request and receives the corresponding response.
HTTP/1.0
The initial version of HTTP establishes a separate TCP connection for each request.
Establishing a TCP connection is resource-intensive, especially when using SSL/TLS. This becomes inefficient when clients need to make multiple requests, as a large number of connections must be established.
HTTP/1.1
HTTP/1.1 improved efficiency by keeping a connection open for a short period before closing it. This behavior is controlled by the Keep-Alive header, which determines how long the connection remains active.
HTTP has several potential drawbacks:
- Synchronous limitation: HTTP generally requires the client to wait for a request to complete, making it inefficient for long-running tasks that are better handled asynchronously.
- One-way initiation: Requests always originate from the client, so the server cannot independently initiate communication with the client.
However, its simplicity and lightweight nature make it highly effective in many scenarios. This protocol is particularly useful for simplifying communication between clients and servers, such as:
- Client-facing services.
- Public APIs exposed to external systems.
Polling
Short Polling
To approximate bidirectional communication using HTTP , a naive approach is to have clients continuously send requests to the server to check for new notifications.
This approach is known as Short Polling . It is highly inefficient in terms of bandwidth, as hundreds of requests may be sent just to retrieve a single notification.
Long Polling
To improve efficiency, the server can hold a request for a short period before responding. This brief delay significantly reduces the number of unnecessary requests. This pattern is known as Long Polling .
Long Polling is a traditional technique for delivering near-real-time notifications from the server. Because requests still originate from clients, Long Polling is well-suited for:
- Decoupling the server from the client. Clients determine when to retrieve messages, so the server does not need to “seek” active client connections within the system.
- Backpressure-aware clients, allowing clients to control their polling behavior independently, such as introducing delays between polls or specifying how many messages to retrieve per request.
Long Polling is best implemented using a stateless service. Because connections are short-lived, clients can easily switch between service instances and retrieve data from a shared store.
For example, instances of a stateless service can share and poll the same store.
However, this model does not provide true real-time communication. Because clients determine when to retrieve data, messages cannot necessarily be delivered immediately after they are created.
WebSocket
WebSocket is a newer technology than Long Polling . In short, a WebSocket server maintains long-lived connections, allowing both sides to actively exchange messages over the same connection.
In general, WebSocket provides lower latency than Long Polling because the connection is already established and ready for communication. It is particularly well-suited for bidirectional, low-latency applications such as gaming and chat services.
Some important drawbacks of WebSocket include:
- Availability and routing complexity: When a service cluster distributes client connections across multiple instances, an instance receiving a message may need to locate and forward it to the instance holding the target client’s connection. This additional coordination increases system complexity and can affect availability.
- Resource utilization: A WebSocket connection is long-lived and remains tied to a specific server, which can lead to uneven resource utilization. For example, one client may continuously interact with a single server while other instances remain underutilized, even though distributing the workload across instances would be more efficient.
Stateful Misconception
Does maintaining long-lived connections automatically make a service stateful? The answer is no.
The communication protocol itself does not determine whether a service is Stateful or Stateless . That property depends on how the service is implemented.
Returning to the chat example in the previous topic, we described it as a stateful service because user connections were maintained by specific servers.
Now, consider a different approach. Instead of forwarding messages directly between instances, each instance periodically retrieves messages from a shared store.
The service can now be stateless. Every instance behaves identically, so it does not matter which instance a client connects to.
In practice, WebSocket services are often implemented as stateful services because WebSocket is frequently used for real-time communication, where messages should be delivered immediately after they are created.
A polling-based approach is less suitable for such workloads because it introduces delays, even if those delays are brief. For strict real-time delivery, maintaining connection-related state is often necessary.
Server-Sent Events
As the name suggests, Server-Sent Events (SSE) provides unidirectional communication. It maintains a long-lived connection while allowing data to flow only from the server to the client.
Behind the scenes, Server-Sent Events is built on top of the HTTP protocol. As a result, developing and maintaining an SSE application is generally simpler than using WebSocket , because it can leverage existing HTTP infrastructure and tooling.
Additionally, unidirectional communication typically incurs less overhead than full-duplex communication. Server-Sent Events is therefore a good choice when an application only needs to push data from the server to the client, such as for live scores or news feeds.
Similar to WebSocket , Server-Sent Events maintains long-lived connections and therefore introduces similar challenges related to connection affinity and resource balancing.
Google Remote Procedure Call (gRPC)
gRPC
is a modern technology developed by Google
that supports both bidirectional and unidirectional communication
using Remote Procedure Call (RPC) over the
HTTP/2
protocol.
Remote Procedure Call (RPC)
When calling a conventional HTTP endpoint, an application must typically handle several details, such as the URI, headers, and parameters, to construct a valid request. Although this approach provides flexibility, it can also introduce complexity and increase the likelihood of errors.
GET /docs?name=README&team=dev HTTP/2In contrast, RPC provides a more structured communication model, requiring both the client and server to agree on a shared contract that defines the exposed operations.
This contract is typically used to generate client and server code, making remote interactions resemble calls to local functions.
For example, suppose the Chat Service exposes a Chat function.
The service contract can define the function and its request and response types for consumers.
// Exchange schema
message ChatRequest {
string content;
}
message ChatResponse {
string messageId;
}
// Service definition
service ChatService {
rpc Chat (ChatRequest) returns (ChatResponse);
}
// Service is called from the client side conveniently
var chatService = new ChatService();
var chatResponse = chatService.Chat(new ChatRequest("Hello Bro!"));Another advantage of RPC is efficient serialization. Formats such as JSON and XML are commonly used for data exchange because of their flexibility and broad compatibility, but text-based representations are generally larger and more expensive to process than compact binary formats. With a predefined schema, RPC frameworks can generate efficient binary serializers, such as Protocol Buffers.
One drawback of RPC is its reliance on shared contracts and strict schemas, which can create friction for clients we don’t control. For this reason, gRPC is more commonly used for controlled service-to-service communication than for public-facing services.
HTTP/2
HTTP/1.1 uses a connection between the client and server, with requests and responses transferred through that connection.
HTTP/2 improves this model by dividing a connection into independent streams, allowing multiple requests and responses to be transmitted concurrently.
For example:
- In the
HTTP/1.1
example,
dog.pngis requested only afterindex.htmlhas been fetched. - In the
HTTP/2
example, both requests can be transmitted concurrently through
Stream 1andStream 2, allowing the resources to be downloaded in parallel.
Behind the scenes, HTTP/2 still uses a single TCP connection, with frames associated with individual Stream IDs. Frames belonging to the same stream can be reassembled independently, allowing multiple logical streams to share a single connection concurrently.
Use Cases
Returning to gRPC , it is built on top of HTTP/2 and RPC , making it highly effective for handling multiple concurrent requests and streams.
Similar to WebSocket and Server-Sent Events , gRPC can also maintain long-lived connections.
However, more sophisticated communication patterns generally require additional processing and connection management. gRPC may therefore consume more computational resources when handling large numbers of concurrent streams and messages.
If a service does not require RPC semantics or multiplexed request-response streams and instead primarily needs ordered message exchange, WebSocket or Server-Sent Events may provide a simpler communication model, depending on whether communication needs to be bidirectional or server-to-client only.
Webhook
Webhook is an effective mechanism for handling long-running or asynchronous operations. Conceptually, it resembles a callback function in programming.
The client registers a callback endpoint, usually a URL , with the server. The server can later invoke that endpoint when an event occurs or a result becomes available.
For example, a client may register a callback address.
Whenever the server needs to notify the client,
it sends a request to site.com/callback.
This approach is particularly useful for tasks with unpredictable execution times, because it avoids wasting resources while clients wait for completion.
For example, during payment processing, a transaction may pass through multiple banking systems, potentially across different countries, and may therefore take an unpredictable amount of time to complete.
Use Cases
Webhook provides an efficient event-driven communication model. Data is transmitted only when an event occurs, eliminating the need for continuous polling or long-lived client connections.
This approach is commonly used by intermediary systems that communicate with numerous trusted external clients, such as Stripe Payments.
However, webhooks are generally impractical for directly serving end users, because end-user devices typically do not expose a stable publicly reachable address that can receive callbacks.
Furthermore, because the server initiates requests to client-provided endpoints, webhook systems must carefully address security concerns such as authentication, endpoint validation, and request verification.
HTTP Live Streaming (HLS)
We’ve highlighted some of the most popular protocols in the previous sections. While they are versatile and suitable for a wide range of use cases, they aren’t specifically optimized for streaming media such as video and audio.
HLS is a media streaming protocol developed by Apple for efficiently delivering video and audio content. Unlike protocols such as WebSocket , which typically rely on a persistent connection between the client and a server, HLS delivers media as a sequence of independent files over HTTP. This design makes it well suited for distributed delivery through web servers and CDNs.
HLS works through segmentation, splitting audio or video into small, independent segments, typically a few seconds long.
- These segments can be stored independently and distributed across multiple servers.
- A playlist, usually an
.m3u8file, describes the available segments and their locations.
To play a video, the client first fetches the playlist to determine which media segments are available and where to retrieve them. When seeking to a specific point in the video, the client can request the segment containing that point instead of downloading the entire media file.
For example, with the segments shown above, seeking to the 11th second would require Segment_3.mp4, since the first two segments cover the first 10 seconds.
In practice, clients usually buffer several sequential segments in advance to provide smooth, uninterrupted playback.
We’ll discuss the storage aspect in more detail in a later topic.