- TypeScript Microservices
- Parth Ghiya
- 870字
- 2021-06-25 21:48:39
Gateway aggregation and offloading
Dump or move specialized, common services and functionalities to a gateway. This pattern can introduce simplicity by moving shared functionality into a single part. Shared functionality can include things such as the use of SSL certificates, authentication, and authorization. A gateway can further be used to join multiple requests into a single request. This pattern simplifies needs where a client has to make multiple calls to different microservices for some operation:
Problem: Often, to perform a simple task, a client may need to make multiple HTTP calls to various different microservices. Too many calls to a server requires an increase in resources, memory, and threads, which adversely affects performance and scalability. Many features are commonly used across multiple services; an authentication service and a product checkout service are both going to use the log in the same way. This service requires configuration and maintenance. Also, these type of services need an extra set of eyes as they are essential. For example, token validation, HTTPS certificate, encryption, authorization, and authentication. With each deployment, it is difficult to manage that as it has to span across the whole system.
Solution: The two major components in this design pattern are the gateway and gateway aggregator. The gateway aggregator should always be placed behind the gateway. Hence, single responsibility is achieved, with each component doing the operation they are meant to do.
Gateway: It offloads some of the common operations such as certificate management, authentication, SSL termination, cache, protocol translation, and so on to one single place. It simplifies the development and abstracts all this logic in one place and speeds up development in a huge organization where not everyone has access to the gateway, only specialized teams work on it. It maintains consistency throughout the application. The gateway can ensure a minimum amount of logging and thus help out to find the faulty microservice. It's much like the facade pattern in object-oriented programming. It acts as the following:
- Filter
- Single entry point that exposes various microservices
- Solution to a common operation such as authorization, authentication, central configuration, and so on, abstracting this logic into a single place
- Router for traffic management and monitoring
Netflix uses a similar approach and they are able to handle more than 50,000 requests per hour and they open sourced ZuuL:
- Gateway aggregator: It receives the client request, then it decides to which different systems it has to dispatch the client request, gets the results, and then aggregates and sends them back to the client. For the client, it is just one request. Overall round trips between client and server are reduced.
Here is an example for aggregator: https://gist.github.com/parthghiya/3f1c3428b1cf3cc6d76ddd18b4521e03.js
- Take care of: The following pitfalls should be properly handled in order to successfully implement this design pattern in microservices:
- Do not introduce service coupling, that is, the gateway can exist independently, without other service consumers or service implementers.
- Here, every microservice will be dependent on the gateway. Hence, the network latency should be as low as possible.
- Make sure to have multiple instances of the gateway, as only a single instance of the gateway may introduce it as a single point of failure.
- Each of the requests goes through the gateway. Hence, it should be ensured that gateway has efficient memory and adequate performance, and can be easily scaled to handle the load. Have one round of load testing to make sure that it is able to handle bulk load.
- Introduce other design patterns such as bulkheads, retry, throttle, and timeout for efficient design.
- The gateway should handle logic such as the number of retries, waiting for service until.
- The cache layer should be handled, which can improve performance.
- The gateway aggregator should be behind the gateway, as the request aggregator will have another. Combining them in a gateway will likely impact the gateway and its functionalities.
- While using the asynchronous approach, you will find yourself smacked by too many promises of callback hell. Go with the reactive approach, a more declarative style. Reactive programming is prevalent from Java to Node.js to Android. You can check out this link for reactive extensions across different links: https://github.com/reactivex.
- Business logic should not be there in the gateway.
- When to use: This pattern should be used in the following scenarios:
- There are multiple microservices across and a client needs to communicate with multiple microservices.
- Want to reduce the frequent network calls when the client is in lesser range network or cellular network. Breaking it in one request is efficient as then the frontend or the gateway will only have to cache one request.
- When you want to encapsulate the internal structure or introduce an abstract layer to a large team present in your organization.
- When not to use: The following scenarios are when this pattern won't be a good fit:
- When you just want to reduce the network calls. You cannot introduce a whole level of complexity for just that need.
- The latency by the gateway is too much.
- You don't have asynchronous options in the gateway. Your system makes too many synchronous calls for operations in the gateway. That would result in a blocking system.
- Your application can't get rid of coupled services.