N-Tier Architecture and Messaging Systems
N-Tier Architecture and Messaging Systems
AND MICROSERVICES
N-tier architecture
N-tier architecture divides an application into logical layers and physical tiers.
Layers are a way to separate responsibilities and manage dependencies. Each layer
has a specific responsibility. A higher layer can use services in a lower layer, but not
the other way around.
Tiers are physically separated, running on separate machines. A tier can call to
another tier directly, or use asynchronous messaging. Although each layer might be
hosted in its own tier, that's not required. Several layers might be hosted on the
same tier. Physically separating the tiers improves scalability and resiliency and adds
latency from the additional network communication.
In a closed layer architecture, a layer can only call the next layer immediately
down.
In an open layer architecture, a layer can call any of the layers below it.
1
A closed-layer architecture limits the dependencies between layers. However,
it might create unnecessary network traffic, if one layer simply passes requests along
to the next layer.
3-Tier architecture
2-Tier architecture
Advantages
2
Separate tiers allow us to scale them as needed.
Improve maintenance as different people can manage different tiers.
Disadvantages
3
Message Brokers
Message brokers can validate, store, route, and deliver messages to the
appropriate destinations. They serve as intermediaries between other applications,
allowing senders to issue messages without knowing where the receivers are,
whether or not they are active, or how many of them there are. This facilitates the
decoupling of processes and services within systems.
Models
Message brokers offer two basic message distribution patterns or messaging styles:
4
Publish-Subscribe messaging: In this message distribution pattern, often
referred to as "pub/sub", the producer of each message publishes it to a topic,
and multiple message consumers subscribe to topics from which they want to
receive messages.
Event streaming platforms offer more scalability than message brokers but
fewer features that ensure fault tolerance like message resending, as well as more
limited message routing and queuing capabilities.
5
They're well-suited for use in the microservices architectures that have become
more prevalent as ESBs have fallen out of favor.
Examples
NATS
Apache Kafka
RabbitMQ
ActiveMQ
6
Message Queues
Working
Messages are stored in the queue until they are processed and deleted. Each
message is processed only once by a single consumer. Here's how it works:
A producer publishes a job to the queue, then notifies the user of the job
status.
A consumer picks up the job from the queue, processes it, then signals that
the job is complete.
Advantages
7
Decoupling: Message queues remove dependencies between components
and significantly simplify the implementation of decoupled applications.
Performance: Message queues enable asynchronous communication, which
means that the endpoints that are producing and consuming messages
interact with the queue, not each other. Producers can add requests to the
queue without waiting for them to be processed.
Reliability: Queues make our data persistent, and reduce the errors that
happen when different parts of our system go offline.
Features
Most message queues provide both push and pull options for retrieving
messages. Pull means continuously querying the queue for new messages. Push
means that a consumer is notified when a message is available. We can also use
long-polling to allow pulls to wait a specified amount of time for new messages to
arrive.
In these queues, the oldest (or first) entry, sometimes called the "head" of the
queue, is processed first.
Many message queues support setting a specific delivery time for a message.
If we need to have a common delay for all messages, we can set up a delay queue.
At-Least-Once Delivery
Message queues may store multiple copies of messages for redundancy and
high availability, and resend messages in the event of communication failures or
errors to ensure they are delivered at least once.
8
Exactly-Once Delivery
Dead-letter Queues
A dead-letter queue is a queue to which other queues can send messages that
can't be processed successfully. This makes it easy to set them aside for further
inspection without blocking the queue processing or spending CPU cycles on a
message that might never be consumed successfully.
Ordering
Poison-pill Messages
Poison pills are special messages that can be received, but not processed.
They are a mechanism used in order to signal a consumer to end its work so it is no
longer waiting for new inputs, and are similar to closing a socket in a client/server
model.
Security
Message queues will authenticate applications that try to access the queue,
this allows us to encrypt messages over the network as well as in the queue itself.
Task Queues
Tasks queues receive tasks and their related data, run them, then deliver their
results. They can support scheduling and can be used to run computationally
intensive jobs in the background.
9
Backpressure
If queues start to grow significantly, the queue size can become larger than
memory, resulting in cache misses, disk reads, and even slower performance.
Backpressure can help by limiting the queue size, thereby maintaining a high
throughput rate and good response times for jobs already in the queue. Once the
queue fills up, clients get a server busy or HTTP 503 status code to try again later.
Clients can retry the request at a later time, perhaps with exponential backoff
strategy.
Examples
Amazon SQS
RabbitMQ
ActiveMQ
ZeroMQ
Publish-Subscribe
10
The subscribers to the message topic often perform different functions, and
can each do something different with the message in parallel. The publisher doesn't
need to know who is using the information that it is broadcasting, and the
subscribers don't need to know where the message comes from. This style of
messaging is a bit different than message queues, where the component that sends
the message often knows the destination it is sending to.
Working
Unlike message queues, which batch messages until they are retrieved,
message topics transfer messages with little or no queuing and push them out
immediately to all subscribers. Here's how it works:
11
Advantages
Features
1. Push Delivery
12
2. Multiple Delivery Protocols
3. Fanout
This scenario happens when a message is sent to a topic and then replicated
and pushed to multiple endpoints. Fanout provides asynchronous event notifications
which in turn allows for parallel processing.
4. Filtering
5. Durability
Pub/Sub messaging services often provide very high durability, and at least
once delivery, by storing copies of the same message on multiple servers.
6. Security
Message topics authenticate applications that try to publish content, this allows us
to use encrypted endpoints and encrypt messages in transit over the network.
Examples
Amazon SNS
Google Pub/Sub
13
Enterprise Service Bus (ESB)
Advantages
14
Greater resilience: Failure of one component does not impact the others, and
each microservice can adhere to its own availability requirements without
risking the availability of other components in the system.
Disadvantages
Examples
Below are some widely used Enterprise Service Bus (ESB) technologies:
15
Monoliths and Microservices
Monoliths
Advantages
Disadvantages
Modular Monoliths
Microservices
17
Each service has a separate codebase, which can be managed by a small
development team. Services can be deployed independently and a team can update
an existing service without rebuilding and redeploying the entire application.
Services are responsible for persisting their own data or external state
(database per service). This differs from the traditional model, where a separate data
layer handles data persistence.
Characteristics
18
Small but focused: It's about scope and responsibilities and not size, a service
should be focused on a specific problem. Basically, "It does one thing and
does it well". Ideally, they can be independent of the underlying architecture.
Built for businesses: The microservices architecture is usually organized
around business capabilities and priorities.
Resilience & Fault tolerance: Services should be designed in such a way that
they still function in case of failure or errors. In environments with
independently deployable services, failure tolerance is of the highest
importance.
Highly maintainable: Service should be easy to maintain and test because
services that cannot be maintained will be rewritten.
Advantages
Disadvantages
19
Best practices
Pitfalls
20
Beware of the distributed monolith
Our microservices are just a distributed monolith if any of these apply to it:
21
Microservices vs. Service-oriented architecture (SOA)
You might have seen Service-oriented architecture (SOA) mentioned around
the internet, sometimes even interchangeably with microservices, but they are
different from each other and the main distinction between the two approaches
comes down to scope.
So, you might be wondering, monoliths seem like a bad idea to begin with,
why would anyone use that?
Well, it depends. While each approach has its own advantages and
disadvantages, it is advised to start with a monolith when building a new system. It is
important to understand, that microservices are not a silver bullet, instead, they
22
solve an organizational problem. Microservices architecture is about your
organizational priorities and team as much as it's about technology.
We frequently draw inspiration from companies such as Netflix and their use
of microservices, but we overlook the fact that we are not Netflix. They went
through a lot of iterations and models before they had a market-ready solution, and
this architecture became acceptable for them when they identified and solved the
problem they were trying to tackle.
That's why it's essential to understand in-depth if your business actually needs
microservices. What I'm trying to say is microservices are solutions to complex
concerns and if your business doesn't have complex issues, you don't need them.
23
Event-Driven Architecture (EDA)
What is an event?
Components
24
Patterns
Sagas
Publish-Subscribe
Event Sourcing
Command and Query Responsibility Segregation (CQRS)
Advantages
Challenges
Guaranteed delivery.
Error handling is difficult.
Event-driven systems are complex in general.
Exactly once, in-order processing of events.
Use cases
Below are some common use cases where event-driven architectures are beneficial:
25
Integrating heterogeneous systems.
Fanout and parallel processing.
Examples
NATS
Apache Kafka
Amazon EventBridge
Amazon SNS
Google PubSub
26
Event Sourcing
Instead of storing just the current state of the data in a domain, use an
append-only store to record the full series of actions taken on that data. The store
acts as the system of record and can be used to materialize the domain objects.
27
to be storing events. Also, event sourcing is one of the several patterns to implement
an event-driven architecture.
Advantages
Disadvantages
28
Command and Query Responsibility Segregation (CQRS)
The core principle of CQRS is the separation of commands and queries. They
perform fundamentally different roles within a system, and separating them means
that each can be optimized as needed, which distributed systems can really benefit
from.
The CQRS pattern is often used along with the Event Sourcing pattern.
CQRS-based systems use separate read and write data models, each tailored to
relevant tasks and often located in physically separate stores.
29
When used with the Event Sourcing pattern, the store of events is the write
model and is the official source of information. The read model of a CQRS-based
system provides materialized views of the data, typically as highly denormalized
views.
Advantages
Disadvantages
Use cases
30
Better security to ensure that only the right domain entities are performing
writes on the data.
31
API Gateway
The API Gateway is an API management tool that sits between a client and a
collection of backend services. It is a single entry point into a system that
encapsulates the internal system architecture and provides an API that is tailored to
each client. It also has other responsibilities such as authentication, monitoring, load
balancing, caching, throttling, logging, etc.
Features
32
Service discovery
Reverse Proxy
Caching
Security
Retry and Circuit breaking
Load balancing
Logging, Tracing
API composition
Rate limiting and throttling
Versioning
Routing
IP whitelisting or blacklisting
Advantages
Disadvantages
33
Backend For Frontend (BFF) pattern
Also, sometimes the output of data returned by the microservices to the front
end is not in the exact format or filtered as needed by the front end. To solve this
issue, the frontend should have some logic to reformat the data, and therefore, we
can use BFF to shift some of this logic to the intermediate layer.
The primary function of the backend for the frontend pattern is to get the
required data from the appropriate service, format the data, and sent it to the
frontend. GraphQL performs really well as a backend for frontend (BFF).
34
We want to optimize the backend for the requirements of a specific client.
Customizations are made to a general-purpose backend to accommodate
multiple interfaces.
Examples
35
REST, GraphQL, gRPC
A good API design is always a crucial part of any system. But it is also
important to pick the right API technology. So, in this tutorial, we will briefly discuss
different API technologies such as REST, GraphQL, and gRPC.
What's an API?
Before we even get into API technologies, let's first understand what is an API.
REST
Concepts
Constraints
36
Uniform Interface: There should be a uniform way of interacting with a given
server.
Client-Server: A client-server architecture managed through HTTP.
Stateless: No client context shall be stored on the server between requests.
Cacheable: Every response should include whether the response is cacheable
or not and for how much duration responses can be cached at the client-side.
Layered system: An application architecture needs to be composed of
multiple layers.
Code on demand: Return executable code to support a part of your
application. (optional)
HTTP Verbs
37
HTTP response codes
HTTP response status codes indicate whether a specific HTTP request has
been successfully completed.
For example, HTTP 200 means that the request was successful.
Advantages
Disadvantages
Over-fetching of data.
Use cases
REST APIs are pretty much used universally and are the default standard for
designing APIs. Overall REST APIs are quite flexible and can fit almost all scenarios.
38
Example
There is so much more to learn when it comes to REST APIs, I will highly
recommend looking into Hypermedia as the Engine of Application State (HATEOAS).
39
GraphQL
GraphQL is a query language and server-side runtime for APIs that prioritizes
giving clients exactly the data they request and no more. It was developed by
Facebook and later open-sourced in 2015.
Concepts
Schema
A GraphQL schema describes the functionality clients can utilize once they
connect to the GraphQL server.
Queries
A query is a request made by the client. It can consist of fields and arguments
for the query. The operation type of a query can also be a mutation which provides a
way to modify server-side data.
Resolvers
Advantages
40
Code generation support.
Payload optimization.
Disadvantages
Use cases
Example
Here's a GraphQL schema that defines a User type and a Query type.
type Query {
getUser: User
type User {
id: ID
name: String
city: String
state: String
}
41
Using the above schema, the client can request the required fields easily
without having to fetch the entire resource or guess what the API might return.
getUser {
id
name
city
}
}
{
"getUser": {
"id": 123,
"name": "Karan",
"city": "San Francisco"
}
}
gRPC
Concepts
42
Protocol buffers
Service definition
Like many RPC systems, gRPC is based on the idea of defining a service and
specifying the methods that can be called remotely with their parameters and return
types. gRPC uses protocol buffers as the Interface Definition Language (IDL) for
describing both the service interface and the structure of the payload messages.
Advantages
Disadvantages
Use cases
43
Low latency and high throughput communication.
Polyglot environments.
Example
Here's a basic example of a gRPC service defined in a *.proto file. Using this
definition, we can easily code generate the HelloService service in the programming
language of our choice.
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
44
REST vs. GraphQL vs. gRPC
Now that we know how these API designing techniques work, let's compare
them based on the following parameters:
Grea
REST Low High Good Medium Bad Good Easy
t
Mediu Cust
gRPC High Great Low Great Bad Hard
m om
Which API technology is better?
Well, the answer is none of them. There is no silver bullet as each of these
technologies has its own advantages and disadvantages. Users only care about using
our APIs in a consistent way, so make sure to focus on your domain and
requirements when designing your API.
45
Long polling, Web Sockets, Server-Sent Events (SSE)
Long polling
In Long polling, the server does not close the connection once it receives a
request from the client. Instead, the server responds only if any new message is
available or a timeout threshold is reached.
Working
46
1. The client makes an initial request and waits for a response.
2. The server receives the request and delays sending anything until an update is
available.
3. Once an update is available, the response is sent to the client.
4. The client receives the response and makes a new request immediately or
after some defined interval to establish a connection again.
Advantages
Disadvantages
A major downside of long polling is that it is usually not scalable. Below are
some of the other reasons:
Creates a new connection each time, which can be intensive on the server.
Web Sockets
47
This is made possible by providing a standardized way for the server to send
content to the client without being asked and allowing for messages to be passed
back and forth while keeping the connection open.
Working
48
Advantages
Disadvantages
It is unidirectional, meaning once the client sends the request it can only
receive the responses without the ability to send new requests over the same
connection.
49
Working
Advantages
Disadvantages
Merkel Tree
I recently hit upon the need to do check pointing in a data processing system
that has the requirement that no data event can ever be lost and no events can be
processed and streamed out of order. I wanted a way to auto-detect this in
production in real time.
There are a couple of ways to do this, but since our data events already have a
signature attached to them (a SHA1 hash), I decided that a useful way to do the
checkpoint is basically keep a hash of hashes. One could do this with a hash list,
where a chain of hashes for each data element is kept and when a checkpoint occurs
the hash of all those hashes in order is taken.
50
A disadvantage of this model is if the downstream system detects a hash
mismatch (either due to a lost message or messages that are out-of-order) it would
then have to iterate the full list to detect where the problem is.
An elegant alternative is a hash tree, aka a Merkle Tree named after its
inventor Ralph Merkle.
Merkle Trees
Merkle trees are typically implemented as binary trees where each non-leaf
node is a hash of the two nodes below it. The leaves can either be the data itself or a
hash/signature of the data.
Thus, if any difference at the root hash is detected between systems, a binary
search can be done through the tree to determine which particular subtree has the
problem. Thus typically only log(N) nodes need to be inspected rather than all N
nodes to find the problem area.
The Tree Hash EXchange format (THEX) is used in some peer-to-peer systems
for file integrity verification. In that system the internal (non-leaf) nodes are allowed
to have a different hashing algorithm than the leaf nodes. In the diagram below
IH=InternalHashFn and LH=LeafHashFn.
51
The THEX system also defines a serialization format and format for dealing
with incomplete trees. The THEX system ensures that all leaves are at the same
depth from the root node. To do that it "promotes" nodes. That is when a parent
only has one child, it cannot does not take a hash of the child hash; instead it just
"inherits" it. If that is confusing, think of the Merkle tree as being built from the
bottom up: all the leaves are present and hashes of hashes are built until a single
root is present.
52
Notation: The first token is a node label, followed by a conceptual value for the
hash/signature of the node. Note that E, H and J nodes all have the same signature,
since they only have one child node.
Before I describe the implementation, it will help to see the use case I'm targeting.
The scenario above is a data processing pipeline where messages flow in one
direction. All the messages that come out of A go into B and are processed and
transformed to some new value-added structure and sent on to C. In between are
queues to decouple the systems.
To ensure that all messages are received and in the correct order, a
checkpoint is periodically created by A, summarizing all the messages sent since the
last checkpoint. That checkpoint message is put onto the Queue between A and B; B
passes it downstream without alteration so that C can read it. Between checkpoints,
system C keeps a running list of all the events it has received so that it can compute
the signatures necessary to validate what it has received against the checkpoint
message that periodically comes in from A.
53
My Implementation of a Merkle Tree
The THEX Merkle Tree design was the inspiration for my implementation, but
for my use case I made some simplifying assumptions. For one, I start with the leaves
already having a signature. Since THEX is designed for file integrity comparisons, it
assumes that you have segmented a file into fixed size chunks. That is not the use
case I'm targeting.
The THEX algorithm "salts" the hash functions in order to ensure that there
will be no collisions between the leaf hashes and the internal node hashes. It
concatenates the byte 0x01 to the internal hash and the byte 0x00 to the leaf hash:
54
Hash/Digest Algorithm
Since the leaf nodes are being passed in, my MerkleTree does not know (or
need to know) what hashing algorithm was used on the leaves. Instead it only
concerns itself with the internal leaf node digest algorithm.
For my use case, I was not concerned with detecting malicious tampering. I
only need to detect data loss or reordering, and have as little impact on overall
throughput as possible. For that I can use a CRC rather than a full hashing algorithm.
The rest of the code is written to be agnostic of the hashing algorithm - all it
deals with are the bytes of the signature.
55
Serialization / Deserialization
I chose not to use the Java Serialization framework. Instead the serialize
method just returns an array of bytes and deserialize accepts that byte array.
(magicheader:int)(numnodes:int)
[(nodetype:byte)(siglength:int)(signature:[]byte)]
Where (foo:type) indicates the name (foo) and the type/size of the serialized
element. I use a magic header of 0xcdaace99 to allow the deserializer to be certain it
has received a valid byte array.
The next number indicates the number of nodes in the tree. Then follows an
"array" of numnodes size where the elements are the node type (0x01 for internal,
0x00 for leaf), the length of the signature and then the signature as an array of bytes
siglength long.
Usage
For the use case described above, you can imagine that system A does the following:
56
// ... process and transmit the message to the downstream Queue
sendToDownstreamQueue(hash, event);
[Link](has);
if (isTimeForCheckpoint()) {
MerkleTree mtree = new MerkleTree(eventSigs);
[Link]();
byte[] serializedTree = [Link]();
sendToDownstreamQueue(serializedTree);
}
}
if (isCheckpointMessage(event)) {
MerkleTree mytree = new MerkleTree(eventSigs);
[Link]();
57
}
else {
String hash = [Link]();
[Link](hash);
// .. do something with event
}
}
go-gossip
The point is that each node transmits data while periodically exchanging
metadata based on TCP/UDP without a broadcast master.
In general, each node periodically performs health checks of other nodes and
communicates with them, but this library relies on an externally imported discovery
layer.
The gossip protocol is divided into two main categories: Push and Pull. If
implemented as a push, it becomes inefficient if a large number of peers are already
infected. If implemented as Pull, it will propagate efficiently, but there is a point to
be concerned about because the message that needs to be propagated to the peer
needs to be managed.
This project implements the Pull-based Gossip protocol. That's why we need
to implement a way to send a new message when another node requests it. In this
library, it consists of two parts, 'filter' that checks whether a message is received and
'cache' that stores the message for propagation.
58
Take a look at the list of supported features below.
Message propagation
Secure transport
Layer
Registry layer
Registry layer serves as the managed peer table. That could be static peer
table, also could dynamic peer table (like DHT).
Gossipiers() []string
59
(It means DHT or importers covers registration to Gossip protocol)
Gossipiers returns array of raw addresses. The raw addresses will validate
when gossip layer. Even if rely on validation of externally imported methods, We
need to double-check internally here(trust will make unexpected panic).
rt health checks, the peer id is not required from a metadata required point of
[Link] addition, if you receive and use a unique ID from outside, the dependency
relationship becomes severe, so I think it is correct not to have an peer id.
Gossip layer
Gossip layer serves core features that propagating gossip messages and relay
data to application when needed.
For serve that, it's detect packet and handles them correctly to the packet types.
1. The node should be able to be a source of gossip. Provide surface interface for
application programs to push gossip messages.
2. Handles them correctly to the packet types.
3. Detects is the gossip message already exist in memory and relay the gossip
messages to the application if necessary.
60
Take a look the packet specification below.
Packet
┏--------------------------┓
| Label | Actual data |
┗--------------------------┛
Label
┏-------------------------------------------┓
| Packet type| Encrypt algorithm |
┗-------------------------------------------┛
Packet handle
61
store the history permanently (currently implemented as LevelDB). If it's not very
sensitive, store it in a large enough memory cache.
Transport/Security layer
62