Everyone is talking about microservice architecture. What exactly is microservice architecture? What are its characteristics and design patterns? When we build microservice architecture, how do we apply these design patterns in practice? How should data consistency be guaranteed? Today I will share my thoughts on the above questions. Microservice architecture featuresWhat is microservice architecture? Look at the English paragraph below. This was proposed by Martin Fowler in 2014. Microservice architecture is an architectural pattern. Since it is an architectural pattern, it must meet certain characteristics. He mentioned that microservice architecture is a combination of a series of small microservices. So, what is a "small microservice"? Maybe everyone has a different understanding. Everyone should know SOA architecture. The granularity of SOA architecture is relatively coarse. At what granularity should we split microservices? I think that microservice architecture is essentially a business architecture. The deeper your understanding of the business, the more reasonable your microservice splitting will be. For example, we are building a second-hand trading platform (Zhuanzhuan), which includes a user system, a product system, a trading system, and a search and recommendation system. Because each system is relatively independent, we can split the microservices according to each business module. Of course, this is not enough, because your product has many functions, but the general idea is to further split it according to the internal logic of the specific product. Second, build models around specific businesses. It is foolish to talk about microservice architecture without considering the business scenarios. There are two methods: first, treat the model of a certain field as an independent business unit: such as goods, orders, and users in second-hand transactions; second, treat the business behavior as an independent business unit: such as sending emails, single sign-on verification, and push services. Third, the entire microservice can be deployed independently. Since each dimension service process is independent, it is easy to understand that each module can be deployed independently. Fourth, decentralized management. Building decentralized management means that each module of the microservice has nothing to do with the development language and operating platform. The development language can be C++, Go, or the world's most popular language, and the operating platform can be Linux, Unix, Windows, etc. The first point is lightweight communication, which is easy to understand. Communication has nothing to do with module language or platform. Try to use lightweight communication to do this, so that it is easy to implement cross-platform and cross-language. After talking about these characteristics, let's take a look at what elements a standard DEMO-level microservice architecture consists of. As shown in the following figure, it mainly includes gateway, microservice, data storage, registration center, and configuration center. Since it is at the DEMO level, it must be different from the actual situation. So, in the actual case, how should we do this? This example is also the second-hand trading platform I have been working on recently - Zhuanzhuan. There are some differences here from the DEMO. The previous *** layer is still the gateway, and there is a microservice aggregation layer below it, which is used to process various business logics; below the aggregation layer is our data atomic layer, which is mainly used as a data access agent, but it is vertically separated according to different businesses. You can see that there are gateways, data layers, registration centers, and configuration centers, but they are divided into two layers in the business processing part: one layer is the atomic layer, which is the proxy layer for the entire data access, providing the user interface; the other layer is the upper-level business aggregation layer. Architecture design patterns and practical casesAbove, I briefly talked about some characteristics of microservices, what parts of DEMO-level microservices are included, and our design architecture pattern in actual cases. So, why do we use this pattern? In addition to this architecture pattern, what other architecture patterns are there? Here, there are still many patterns, and I will focus on the following points: chain design pattern, aggregator design pattern, and asynchronous sharing pattern. First, let's talk about the chain design pattern. In this pattern, the APP front-end request must first pass through the gateway layer, and then call two microservices in succession. After calling microservice 1, microservice 2 must also be called. Why is it called chain? Because after the call comes, it first goes to microservice 1, and then calls microservice 2 synchronously. Microservice 2 will do some processing. After processing, microservice 2 will feedback to microservice 1, and microservice 1 will feedback to Gateway, and finally feedback to APP. In actual business scenarios, business scenarios involving transactions and orders will use this pattern. Next is the aggregator design pattern. A call request from the APP front end passes through the Gateway and reaches the aggregation layer, which needs to call three microservices. The aggregation layer aggregates the return results of the three microservices, such as sorting or deduplication. After aggregation, it is fed back to the Gateway and APP front end. This is a typical aggregator design pattern. The third mode is the data sharing mode, which is relatively simple. For example, the APP passes through the microservice gateway and then calls microservice 1 and microservice 2. Ideally, microservice 1 and microservice 2 have their own independent DBs. However, in some cases, the request volume and storage volume of microservice 1 and microservice 2 are small. From the perspective of resource utilization, the DBs of these two microservices are shared. Therefore, this is the data sharing mode. ***One is the asynchronous message design mode. Regardless of chain design, aggregator mode or shared data mode, the architecture mode is synchronous mode. That is to say, a request must be sent to the client after each link is processed. If the request does not need to pay attention to the processing result, it can be implemented asynchronously at this time. The APP update request passes through the microservice gateway and is persisted to MQ. After the MQ is successfully written, the response is immediately sent to the APP client. After that, the microservice subscribes to the update message from MQ for asynchronous processing as needed. We will also adopt this mode to improve throughput. In the past few years from Baidu to Zhuan Zhuan, I have experienced many business scenarios, and the data models used are nothing more than aggregators, asynchronous and data sharing, especially the first two, which are particularly used. Let's look at some examples below. Next, let's look at an example. This is a second-hand trading platform (ZhuanZhuan) we built in 2015. This second-hand trading platform includes products, category search, keyword search, product recommendation and other functions. A user request comes in and first passes through the gateway. Below the gateway is our aggregation layer, which then calls products, transactions, recommendations and search related functions. Finally, the aggregation layer aggregates the results of each microservice atomic layer and responds to the client. The details are shown in the following figure: This case of asynchronous message mode is quite early. At that time, we made a feed stream, which is similar to the current WeChat Moments. This is what I did at Baidu. At that time, the architecture mode we adopted was the asynchronous architecture mode. In front is our APP, which passes through the gateway and reaches the asynchronous submission layer, which can be considered as MQ with persistence function. The user request returns after passing through the gateway to the message asynchronous submission layer. The business processing part reads the data from MQ and then processes it asynchronously. At this time, the throughput will increase, but it will bring some confusion. For example, at this time, I sent a feed, and the user will directly check it in the database when checking it again. There may be a delay in the asynchronous submission message queue, and the user will not be able to find it. The user will be confused. How to solve this problem? We wonder if we can help us do something on the front end? For example, after submitting MQ and returning Response 200, the front end cooperates to insert this feed. When the user refreshes again, I believe it will be several seconds later. Even if there is a delay, the message has been processed by your business long ago. Of course, we have specific scenarios here. This can be done in the community, but it will definitely not be done in scenarios related to finance. Data consistency practicesMicroservice modules are relatively scattered, data is also relatively scattered, and the entire system is very complex. How to implement data consistency? Local Transaction can be done in a single module, but it does not work in a microservice system. Although it is difficult to solve, it must be solved. If it is not solved, it will be difficult to implement the microservice architecture. We know that it is very difficult to do strong consistency in microservices. Today we will share more about solving final consistency. Because local transactions are not available based on different databases under microservices. Everyone must have heard of two-phase commit and three-phase commit in distributed transactions. This scenario is actually not feasible in the microservice architecture because it is essentially a synchronous mode. The throughput of data consistency is greatly reduced under the synchronous mode. Our business scenarios are nothing more than two types: the first is asynchronous call, that is, a request is written into the message queue, this mode is relatively simple. Today, I will mainly talk about how to create eventual consistency of data under the scenario of synchronous call. Since it is a synchronous call scenario and the throughput of the business system cannot be reduced, what should be done? Establish an asynchronous distributed transaction, and compensate the business in an asynchronous way after the business call fails. Our idea is whether we can implement the distributed transaction semantic strategy in the entire business logic layer? How to implement it, nothing more than two, the first is to record the business call chain (the complete parameters of calling the normal interface) when calling normal requests, and the second is to reversely compensate along the call chain in the event of an exception. Based on this idea, there are three key points in our architecture design: the first is based on the compensation mechanism, the second is to record the call chain, and the third is to provide an idempotent compensation interface. At the architectural level, see the figure below. The right side is the aggregator architecture design pattern, and the left side is the asynchronous compensation service. First, you need to introduce a Proxy in the aggregation layer. First, based on the method, add an annotation to the method name to mark the compensation method name, such as: - @Compensable(cancelMethod="cancelRecord") In addition, before calling the atomic layer, the aggregation layer records the current call request parameters through the proxy. If the business is normal, after the call is completed, the call record of the current method is archived or deleted. If the business is abnormal, the query call chain is rolled back. What have we done at the atomic layer? There are two main aspects: first, providing a normal atomic interface, and second, providing a compensated idempotent interface. The key to distributed transactions is two tables (as shown above). The first is the transaction group table. Assuming that the three requests A->B->C are a transaction, first generate a transaction ID for ABC and write it in this table. The status of the transaction will also be recorded. By default, it is normal. After the execution fails, we change the status from 1 (normal) to 2 (abnormal); the second table is the transaction call group table, which mainly records each call and related parameters in the transaction group, so the request parameters need to be recorded before calling the atomic layer. If it fails, we need to change the status of the transaction from 1 to 2; third, once the status changes from 1 to 2, the compensation service is executed. This is our compensation logic, which is to continuously scan the table where the transaction is located, such as scanning the transaction group table once a second to see if there is a status of 2 in this table, and the compensation service needs to be executed. This idea is less invasive to the business. Let's take a look at our actual example. For example, the normal process of creating an order transaction group in a second-hand trading platform, from locking inventory to reducing red envelopes to creating orders. After the transaction group is created, the business is called. First, the Proxy records the parameters of the lock inventory call, and then starts the lock inventory service call. After success, it starts the red envelope reduction and order creation process. If all are successful, it returns directly. Let's take a look at the exception process. The first few steps are the same, but if the red envelope service fails to be called or the proxy creates a red envelope, an exception will be thrown. The business returns normally, and the aggregation layer proxy needs to change the status of the transaction group from 1 to 2. At this time, the compensation service on the left will make an asynchronous compensation call. About the author: Sun Xuan, Chairman of the 58 Group Technology Committee. This article is based on the author’s speech at CCTC 2017. |
<<: Detailed explanation of sentiment analysis based on Naive Bayes and its implementation in Python
>>: Basic concepts and implementation of genetic algorithms (with Java implementation examples)
APP operation and promotion should be carried out...
In Fuzhou, you can see the earliest ship design a...
On December 13, according to Futurism, as more an...
Catch fireflies to make lanterns, It's beauti...
In the current Internet environment, user operati...
French linguist Champollion systematically studie...
In April 2022 , affected by the epidemic, consump...
The red envelope activities for products worth hu...
When it comes to the sun, everyone is familiar wi...
As the founder, chairman and CEO of Xiaomi Techno...
In daily life, we are always used to putting fres...
With the rapid development of Internet technology...
Under the influence of new consumption concepts, ...
Currently, App distribution and delivery is an in...