Last month, we talked about the increasing popularity of adopting microservices in application development. Microservices architecture involves breaking down a software application into it’s smaller components, rather than just having one large software application. Typically, this involves dividing a software application into smaller, distinct business capabilities. These can then communicate with each other via an interface. Microservices are an architectural style used to build an application which is best suited for a large, complex system. A microservice-based application contains loosely coupled, small, independently-deployed projects. Below is a breakdown of the 9 components within a microservice
1) Service Fabric
- Azure Service Fabric is Microsoft’s microservice platform and includes container orchestration as well as developer programming models to build highly scalable microservice applications.
- We can deploy a service fabric platform to Azure, AWS, or on premise (on a Windows or Linux machine).
- As of now, we can deploy stateless containerized applications to the service fabric.
- A service fabric platform provides features like Naming Service (Service Discovery and Service Registry), Monitoring, Logging, fault tolerance capability, etc.
- We can add host/virtual machines from different geographic and network locations to the service fabric cluster through Powershell CLI or through a service fabric manifest file.
- The service fabric then automatically balances the nodes; if any node goes down, it will up another node and host the required application instance on the newly added node.
2) Service Programming Model
Stateless Services
- When we need to persist data to external storage, we will build a stateless service.
- Most of the time, we need to store data in external storage (MS SQL, NoSQL storage) so stateless services are used most of the time.
- We can containerize this type of service; as of now, we can host this type of service to Docker in the service fabric.
Stateful Service
- When we need to persist data to the service itself, in that case, we can build a stateful service.
- We can reduce latency between business logic and data.
- Stateful services store through reliable collections to the node itself (in main memory, hard disk).
- Data will be stored across different partitions to scale and replicate to several nodes, to ensure data availability 24/7.
- As of now, we cannot host a service to a container, but in the future, this feature might be included.
3) Communication between Services
- The client application will communicate with services through REST API (HTTP Request/Response) protocols. This type of communication is synchronous in nature; the caller will wait until the response gets back.
- We can follow different techniques to communicate between services (communication between services within a cluster).
- We can communicate between services asynchronously (using an Event Bus, a service can publish/subscribe to specifics event by another service) wherever possible; in case we are somehow not able to communicate asynchronously, then we will synchronously communicate between services over REST APIs.
- This way, we can reduce the tight coupling within the system, and in case we need to implement other behavior for specific events, that service just needs to subscribe to that specific event. That way we can choreograph the system and make the system more loosely coupled.
- We can use an Azure service bus to perform asynchronous message-based communication between services. There are other tools also available, e.g. RabbitMQ, ActiveMQ, etc.
4) Event Bus
- Event buses allow the publish-subscribe style of communication between services.
- Using an event bus, we can establish asynchronous style of communication between services.
- Each service can subscribe to a particular event, and whenever the event occurs, the specified method will be invoked in the service.
- For instance, if Service A publishes an event named “OrderCreated” and this event is subscribed to by Service B, then whenever “OrderCreated” is published by Service A this event will be pushed to the queue, and later it will be processed by “Service B.”
- We can further extend the event bus to implement event sourcing and CQRS (Command Query Responsibility Segregation) patterns.
- The event bus enables us to implement an asynchronous messaging pattern; thus the system can be choreographed properly and we can reduce coupling between services.
- Azure Service Bus can be used to implement this.
5) Circuit Breaker
- Many times, due to the fault of a downstream service, the caller system might collapse due to all resources being exhausted. Because of this, we might end up with the whole system down because of cascade fall down; to prevent this, we use the Circuit Breaker pattern, which works like a switch between the caller service and the downstream service. If the downstream service times out or returns any exceptions occurred (i.e. HTTP 5XX errors), then the switch will be set off.
- To apply the Circuit Breaker pattern, we can use the POLLY
- POLLY provides features like Circuit Breaker and Bulkhead isolation.
6) Load Balancer
- The load balancer will route traffic to different nodes to distribute traffic over available nodes.
- The load balancer first queries the Service Registry (Naming Service) to get the details of available online nodes so that it can route traffic to these nodes.
- We can use the Azure Load Balancer service to implement this.
7) Service Discovery and Service Registry (Naming Service)
- Both these techniques are responsible for finding the physical location of a particular service in the cluster.
- Whenever any service is in the cluster, it will register its IP address with the service registry. After a particular timespan, a heartbeat signal will be sent to know whether the service is still alive or not.
- Whenever any service request comes to the network, it will first send a request to the service registry to find out the location of the service; this mechanism is implemented on top of the load balancer. Once the load balancer gets the physical location of that node, it will redirect the request to that specific node.
- If we use a Service Fabric as our microservice platform, then Service Discovery and Service Registry mechanisms will be built-in (Naming Service in Service Fabric).
8) API Gateway
- The API Gateway consists of several small projects which act like an aggregator or a proxy to the inner service layer.
- The API Gateway acts like an entry point to the application.
- There are several benefits of API Gateways: we can implement Authorization and HTTPS termination, and we can aggregate responses from various services and send them back to the caller so that they do not need to call multiple services. This will be very helpful for mobile clients.
- We can use the API Gateway as a back end for the front end. We do have separate gateways for different types of consumer, e.g. web client, mobile client, etc.
9) Authentication and Authorization
- We can use IdentityServer4, Auth0, or Octa for authentication and authorization.
- IdentityServer4 is a community driven project which offers features like Single Sign-on, Refresh tokens, tokens based security, revoking a generated token, Support External Identity provider (Google, Facebook), and Admin UI (in beta version) for user management.
- It is built on top of .NET Core, so we can deploy it on either Windows or Linux machines.
Pros: Advantages of Using Microservices
- Each service is relatively small, so it is easier to maintain code and reduce build time.
- We can add more features in less time compared to monolith applications.
- Each service can be deployed independently, and we can do releases more aggressively.
- Easy to scale (horizontal scaling).
- Different technology stacks can be used in different services.
Cons: Disadvantages of Using Microservices
- Limited tooling support available to deal with complexity in microservice-based applications.
- Difficult to create a testing environment.
- Memory/bandwidth consumption will be increased.
- There is no support for transactions.
- One should have domain knowledge to break functionality into individual services.
- Must deal with the problems of a distributed system.
- Performance might be reduced.
- Not suitable for small applications.