(Star ImportNew to improve Java skills)
Let’s clarify the question before we talk about architectural style, what is architecture? Why should we choose architecture? What problems are used to solve?
What is a schema
Book definition: “The architecture of software is an abstract structure that consists of the various components of the software and the dependencies between them.”
My understanding is that architecture is to select the right technology and middleware according to the business, and assemble these modules according to the appropriate design pattern to meet the needs of business characteristics.
Select the purpose of the architectural style
The original intention of our choice of architectural style is the “three principles of change” (our own understanding): better cost reduction and efficiency improvement, faster release and launch, and better maintenance of system stability.
Any architectural style can achieve functional requirements, but a good architectural style can improve non-functional requirements on top of functional requirements. So you might ask, what are the non-functional requirements? Examples: extensibility, stability, etc.
Here I will use the pit I have stepped on with the combination of cognition and I will tell you in detail how we have evolved from a monolithic architecture to a distributed architecture. On the road to the evolution to a distributed monolithic architecture, how to make a choice, and why did you choose a microservices architecture + a distributed architecture at the same time. Next, we will combine a system as a case study and explain it through the main line.
First, let’s talk about the experience and transformation of the original monolithic architecture.
At the beginning of the creation of the system, we often focused on the business, a single point of deployment system, all services hit a package, quickly online. It meets the rapid release of the initial stage of the business, and is suitable for small and medium-sized companies that do not have their own PaaS platform, and to cope with the rapid iteration of the business in the early stage, the development, iteration, testing, and release are very convenient. So what are the types of monolithic architectures?
The monolithic architecture type
Monomer architecture is also divided into large mud mass architecture, hierarchical monomer architecture, modular monomer architecture, what is the difference between them?
Large mud monolithic architecture: No layering, all modules are focused together, interspersed with each other (unless you take over and need to renovate, don’t create such an architectural style. This kind of large mud mass structure is difficult to split, and the final end is often rebuilt);
Hierarchical monolithic architectures: a popular choice. Architectures are simply layered, such as the traditional MVC three-tier architecture;
Modular monolithic architecture: Generally with the development of business, it evolves from a hierarchical monolithic architecture, characterized by the introduction of multiple business modules and the provision of corresponding service capabilities.
Advantages and disadvantages of monolithic architectures
App development is simple
Easy to make large-scale changes to applications
Testing is relatively simple and intuitive
Deployment is simple and straightforward
Scale-out is effortless
In the early stage of the business, the advantages of the single architecture, in any way, are better than other architectural styles, but with the increase and coupling of the business, the shortcomings of the single architecture are gradually exposed, which is also in line with the “Conway’s Law”. So what problems will be exposed in the “later stage” of the monolithic architecture?
The code base is bloated
Excessive complexity can scare off developers
The time from code submission to actual deployment is long and prone to problems
Difficult to scale
The stability of the system is not guaranteed
There is a long-term dependence on a potentially obsolete technology stack
These shortcomings of the monolithic architecture actually affect the “three more principles” I mentioned above. After the above preparation, I believe that everyone has a simple understanding of the monolithic architecture style.
It’s not enough to have a methodology, we have to combine the project and the code snippet to deepen the understanding and achieve real application. Next, I will use an inventory system to explain in series. First use this chart to understand what the inventory system is used for?
At the beginning of creation, 1 service provider product inventory maintenance, inventory inquiry, inventory deduction capability.
With the development of business, inventory is oriented to multiple services: B-side business, internal business system of the platform, and external middle office of the platform. C-side business, order goods deduct inventory, gateway query inventory quantity.
The case of monolithic architecture – the inventory system
The initial inventory code is layered as follows:
api: A Dubbo service provided to the outside world
common: Encapsulates public methods
dao: Encapsulates the database DHCP interaction
domain: An entity class
inner-api: API interaction within the system
rpc: Upstream and downstream RCP interactions
service: The business logic layer
web: Web services layer
worker: The task scheduling layer
For an initial long time, we deployed two monolithic services. One is the API interface to ensure upstream inventory queries and calls, and the other is the background management platform of the Web service. These two single services fit well with the initial business iteration and release speed, but later with the increase of additional calls to the business, the performance and stability of the single service have fluctuated greatly.
Unexpected, reasonable accident tragedy
On the evening of June 26, 2015, on the eve of a promotion, the web management platform for inventory was hung up, due to the large number of inventory imports, and the lack of memory of the server caused the machine to go down. Merchants and operators cannot maintain the amount of inventory through the way of guide tables, and have experienced many horizontal expansions before this. There are still unexpected traffic and stability issues.
Moreover, in the next big promotion process, the single service API interface of the inventory is also under great pressure.
On the one hand, there are many upstream callers, such as the store gateway in the home page of the app, to check whether the goods are in stock and whether they are displayed. The shopping cart plus cart will also query the quantity of the inventory of the goods, the bill of lading will deduct the inventory quantity, and even the subsequent order cancellation will also call the inventory interface.
On the other hand, large KA merchants operate on inventory through the middle office docking, in order to keep the inventory of the merchant’s store and the inventory of the online platform consistent as much as possible, and reduce the oversold and undersold caused by inconsistent online and offline inventory. The interval between middle-office synchronization is very short, and it takes 5 minutes to 10 minutes to synchronize all of them once. Subsequently, with the increase of merchants settled, this order of magnitude also grew very rapidly. So we opened the door to the evolution of monolithic services to distributed services.
Advantages and disadvantages of distributed architectures
The system is highly fault-tolerant
The business code is highly readable
These advantages are exactly what our inventory system lacked at that time, especially the availability and system fault tolerance, which were the primary goals of our system evolution iteration.
As described in “Distributed Architecture System”, the core concept of distributed architecture is also to split the system according to (function, business, field, etc.), through a reasonable split structure, to achieve the decoupling of each business module, and at the same time, through the system-level fault-tolerant design, to build a highly available, scalable open technology system on the cheap hardware infrastructure.
So what exactly should we split the inventory system according to, function? Business? Field? Before splitting, we must clarify the design goals and avoid the waste of manpower and cost resources caused by the wrong direction of the target. Before figuring out the goals, let’s take a look at the shortcomings of a distributed architecture and understand them to measure what trade-offs need to be made to meet our goals, which, like the CAP principle, can only satisfy two of them, the AP or the CP.
2) Disadvantages of distributed architecture
There are many services, and it costs some personnel to understand the business modules after the split
Upgrading the technology stack consumes manpower
Persistence of distributed transactions
RPC interaction loss between business modules
The inventory system is characterized by high availability, high concurrency, and strong data consistency. Next, let’s talk about how inventory is transformed from a monolithic architecture to a distributed architecture.
How monolithic architectures transition to distributed architectures
Because the biggest problem with inventory is stability, we started by splitting the features.
1) Function splitting
This step is relatively simple, we sort out the inventory service-oriented business side for service division. This part does not require much code modification, a set of interfaces can be deployed to different clusters by changing different group aliases.
After the split, different services should deal with different business parties, and the isolation of system errors is good, which will not say that there is a situation of loss and loss, and stability is also guaranteed. After solving the stability problem, we are left with some breathing interval to have time to optimize the code. As mentioned earlier, we only solve the problem of fault tolerance through distributed cluster deployment, but the code is still a set, and the bloated code will slow down our development and launch speed. Then the next thing to be carried out is to decouple the business code, which is also the most difficult. How do we do it?
2) Business splitting
What is the idea of business splitting?
Guided by the business itself, fully understand the business model of the system, and divide the business boundaries;
The scope of business dependencies, segmentation of functions, minimization of duplicate dependencies between functions;
Evaluate according to the impact of the split function, and dismantle the small to ensure the large;
Do not modify the business logic during the split, and do not perform any optimization actions other than the split (unless it is a bug).
Based on the above splitting ideas, how is the inventory system divided into business modules? What code moved?
3) How to divide the business modules
Regarding business division, there are many methodologies on the Internet, such as the event storm method, the four-color modeling method, etc., but the ten thousand laws are inseparable from their origins, that is, around the event. Take inventory system examples: inventory initialization (store + sku inventory creation), inventory quantity maintenance (modify the spot quantity, modify the sellable status), deduction business (shopping cart deduction, bill of lading deduction, order cancellation deduction), reminder business (out-of-stock reminder), etc. Each event has its own link axis, and the timeline can form a closed loop.
4) How to split on the original module
Most monolithic architectures are process-oriented design, and the domain layer is flooded with various DTO, VO, BO, so in the process of layer-to-layer data interaction, most of them have experienced multiple POJOs. In addition, the service layer is full of data interaction with the DAO layer and mixed business, and seriously violates the principle of dependency inversion, and the entire layer becomes very heavy. Here is an example:
References to each other between the same levels
The service layer contains too much business logic to guarantee atomicity
Here are some code snippets as examples to describe what we need to do in the process of splitting the business.
Splits the service tier CQS
Extract the business logic from the original service layer and ensure that the service method follows the SRP principle
Add a new way to aggregate service layers by aggregating the service layer (or to the adapter referred to in the hexagonal architecture).
The original code
Retrofit of CQS and SRP to dismantle GOD Classes
After pulling away to the Business layer Business tier
Build a good business layer
Split the summary
Split up here, the division of the business layer is basically clearer. Moreover, in the process of incrementally integrating the underlying code, the process-oriented business lines are also sorted out more clearly, and the underlying methods are also extracted to the business layer and provide services through the interface. Then the next problem we have to face is how to split the specific reading and writing.
Build distributed services based on CQRS
As we mentioned above, the overall function was split, and there was no split of specific read and write services. In the service-oriented scenario, the function is also a read service and a write service. So what principles do we have to guide the separation of read and write services? That’s the idea of CQRS: command responsibility query separation, not just code, but also applies to services.
Split reads or writes first
It is recommended to start with split read, because the read service is simpler than the write service, and it is easier to improve the stability of the system’s external services, the process of writing the service is relatively large compared to the underlying change, and the test cycle will be longer. In the early stage, the probability of problems with the verb service system will be relatively large, so in terms of comprehensive stability and scalability, the priority split read service is a better choice.
Does the idea of CQRS fit all business scenarios?
Taking the inventory system as an example, we will reproduce a version according to the CQRS idea to see what problems will arise.
Each modification synchronizes the inventory to the task table
The schedule task reads the task table
Synchronize the task table’s modification data to Redis in the Read service
In this process, there are two problems:
The problem with big data task synchronization: This is the data synchronization speed problem of Event Bus synchronization Redis.
Latency issues: Inventory requirements are very real-time, and inventory can be bogged down if delays caused by task backlogs. Oversold and oversold caused by a large number of incorrect inventory quantities will instantly crush the business.
Therefore, every architecture, every idea must be analyzed in conjunction with the business. We can learn from CQRS’s command query separation of duties, in the face of business system deployment, do not rigidly follow the inherent pattern, to make certain trade-offs on the existing style. Therefore, when we dealt with the inventory business, we created a CQRS-StockCenter based on the style of CQRS.
Practical use of CQRS: CQRS-StockCenter
The business layer writes commands
writeService Service writes read service Redis
MQ messages are written to MySQL backups and write flows as asynchronous data completion
Inventory relies heavily on Redis as middleware for inventory inquiry and modification through this design. Strong consistency of data is guaranteed. The inventory is separated from the reading and writing on the original service, ensuring the separation of CQRS command responsibility queries of the system.
Everybody knows the business. In simple terms, a transaction consists of a set of associative operations, A-> B->C, and if execution to C reports errors, then B-> A is rolled back.
For local transactions, this is relatively simple, if you use a transactional database such as MySQL, and do not involve multiple data sources, it is very easy to guarantee the ACID of the transaction. But what we’re going to mention here is distributed transactions.
After the system is split, since each service is an independent module and is responsible for a piece of business, then under the process of the entire business axis, the cross-system transaction rollback of each service node becomes a problem.
There are also some solutions in the industry, such as JTA (Java Transaction API or Java Transaction API) and JTS (Java Transaction Service as Java Transaction Service), which provide distributed transaction services for the J2EE platform.
However, this need to meet the XA (two-phase commit) standard is very heavy. And now the diversity of the business, many databases, such as MongoDB, do not support XA’s standard distributed transactions, and some popular middleware, such as RabbitMQ and Kafka, do not support distributed transactions.
Transferred from: Shudong Jun,
– EOF –
1. The evolution of high-concurrency distributed architecture on the server side
2, do not pass the Request to the asynchronous thread! There are pits!
3. The new technical director has introduced a new business structure to the company’s projects, which is a best practice!
Got a harvest after reading this article? Please forward and share it with more people
Follow “ImportNew” to improve your Java skills
Likes and looks are the biggest support ❤️