Even if you don’t understand the core points and implementation principles of the microservice architecture, you still want to slap the interviewer?

The division of functional teams in microservice architecture

The traditional monolithic architecture divides the system into layers with different responsibilities, and the corresponding project management also tends to divide large teams into different functional teams, mainly including: user interaction UI team, background business logic processing team and data access ORM team, DBA team, etc.

According to Conway's Law, the communication mechanism of the team should correspond to the architectural design mechanism. Therefore, under the microservice architecture, the division method of functional teams is a core element that we must first consider.

The microservice architecture is divided according to business functions. Each single business function is called a service. Each service corresponds to an independent functional team. The team includes user interaction UI designers, background service developers, DBAs, operations and operations. maintenance personnel.

Decentralized Governance of Microservices

The microservice architecture advocates decentralized service management and governance, try not to set up centralized management services, and at worst requires alternative solutions and designs when the centralized management service goes down. In the service construction of the payment platform where the author works, the first layer of SOA service is customized using the Dubbo framework. If there is a large-scale collapse of the Dubbo service, the service system will switch to point-to-point Hessian remote call, which is It is called service-based downgrading. After downgrading, there is no centralized node for point-to-point Hessian remote calls, which is in line with the principle of microservices as a whole.

Interaction Patterns for Microservices

This section introduces general design patterns for interactions between microservices. These design patterns define contracts for interactions between microservices. Both producers and callers of services need to abide by these contracts to ensure that microservices work without problems.

1. Reader fault tolerance mode

2. Consumer-driven contract model

Service contracts are divided into: provider contracts, consumer contracts and consumer-driven contracts, which describe the linkage between service providers and service consumers from the perspective of expectations and constraints.

  • Provider contract: It is the most commonly used service contract. As the name suggests, the provider contract is provider-centric. What functions and message formats are provided by the provider, each consumer will unconditionally abide by these agreements, regardless of the consumer How many functions are actually required, when consumers accept the provider contract, they will use the service according to the rules of the service provider.
  • Consumer contract: It is a more precise description of a consumer's needs. In a specific service interaction scenario, it represents which part of the data the consumer needs from the provider to provide the function. A consumer contract can be used to identify an existing provider contract, or it can be used to discover a provider contract that has not yet been specified.
  • Consumer-Driven Contract: A constraint that the service provider promises to obey to all its current consumers. Once each consumer informs the provider of their specific expectations, the provider should not break the contract no matter what time and scenario.

In the actual service interaction design, the above three contracts exist at the same time. In the payment platform where the author is located, after completing a payment, the transaction system needs to enter the account for the merchant in the accounting system. In this process, the service contract The performance is as follows.

  • Producer Contract: The accounting system provides a Dubbo service interface. The parameters are the merchant account ID, the order number and the amount.
  • Consumer contract: The accounting system returns DTO, including the merchant account ID, the order number, the amount of the account, the time of the account, the account serial number, and the account status, etc., and the transaction system only needs to use the account order number and the account status.
  • Consumer-driven contract: In order to ensure the security of funds, the transaction system, as the initiator of the entry, makes a request to the account, and the account needs to be idempotent and re-filtered to intercept repeated entry requests; the accounting system is accepting this contract. After that, even if there are any changes in the future, this limit cannot be broken, otherwise it will cause the loss of funds, which is the most serious problem in the financial system.

The three types of service contracts that need to be used for interaction between services are shown in Figure 1-10.

As can be seen from Figure 1-10, the service provider contract is a unilateral rule set by the service provider, and a consumer contract will become a part of the provider contract. Multiple service consumers can impose constraints on the service provider. The service provider needs to abide by the contract proposed by the service consumer in the future, which is the consumer-driven contract.

3. Go to data sharing mode

In practice, the design of some solutions uses cache or database as the link between two microservices. During the processing of business processes, in order to simplify the processing, the previous service stores the intermediate results in the database or cache, and the next service stores the intermediate results in the database or cache. The service takes data from the cache or database and continues processing. The processing flow is shown in Figure 1-11.

The disadvantages of this interactive flow are as follows.

  • In addition to the interface contract, the interaction between microservices also has a data storage contract.
  • When the upstream data format changes, it may cause problems in the downstream processing logic.
  • Multiple services share a resource service, and it is difficult to define responsibilities and boundaries for the operation and maintenance of resource services.
  • In the independent deployment of dual computer rooms, the routing of services and resources needs to be considered. Service calls across computer rooms cannot use the independent resource deployment mode, so it is difficult to achieve service autonomy.

Decomposition and composition patterns of microservices

Using the microservice architecture to divide services and teams is an important step in the implementation of the microservice architecture. Good division and splitting enable the system to achieve loose coupling and high cohesion, and then flexible assembly of microservices can meet the various requirements of the upper layer. business processing needs.

In the process of demand analysis and architecture design of microservice architecture, microservices are usually divided by domain verbs and nouns. For example, for an e-commerce backend system, it can be decomposed into orders, commodities, commodity catalogs, inventory, shopping carts , transaction, payment, invoice, logistics and other subsystems, each noun and verb can be a microservice. Combining these microservices together realizes the entire business flow of e-commerce platform users purchasing goods.

After this split, the system has agility, flexibility, scalability, etc. After splitting, there are multiple highly autonomous microservices, so how to combine microservices?

1. Service proxy mode

The service proxy mode is the simplest service composition mode. It chooses to call a service on the backend according to the needs of the business. Before returning to the consumer, the proxy can process the output of the back-end service, or directly return the return result of the back-end service to the consumer.

Under the microservice architecture platform where the author works, this mode is often used. A typical case is to do smooth system migration, which usually goes through the following four stages.

  • Double write on old and new systems.
  • Migrate the historical legacy data before the double-write.
  • Switch read requests to the new system.
  • Downgrade the double write logic and only write the new system.

The service proxy mode is often applied to the third step. Generally, a switch is designed for the read request switching. When the switch is turned on, the new system is queried, and when the switch is turned off, the old system is queried.

2. Service Aggregation Mode

The service aggregation mode is the most commonly used service composition mode. It calls multiple dependent microservices in a certain order according to the needs of business process processing, combines, processes and transforms the data returned by the dependent microservices, and finally uses a certain The form is returned to the consumer.

Such design principles have the following benefits.

  • Three separate sub-services can be independently developed, agilely changed, and deployed.
  • The aggregation service encapsulates the lower-level business processing services, and three independent sub-services complete data persistence and other tasks, and the project structure is clear.
  • The three independent subservices are still reusable for other consumers.

Considering the example at the beginning of this section, when splitting microservices, the e-commerce backend system is roughly divided into microservices such as order, commodity, commodity catalog, inventory, shopping cart, transaction, payment, invoice, logistics, etc., then The front-end application of the e-commerce platform is the largest aggregation service of the back-end microservices. The front-end application displays the list of products by calling the product and product catalog, and provides the user with the function of selecting products. After the user selects the product, the product is added to the shopping cart. When the user settles from the shopping cart, the transaction system is called to complete the transaction and payment.

In addition, the aggregation service can also be a pure back-end service that outputs the combined service to the user through aggregation. For example, in the above e-commerce system case, after the user selects settlement, the system calls the transaction, and the transaction system calls the inventory system to lock the inventory. , and then create a transaction order, guide the user to pay, deduct the inventory after the payment is successful, and finally issue an electronic invoice through the invoice service.

3. Service inline mode

The service tandem mode is similar to a workflow. The first service 1 is responsible for receiving requests and responding to consumers, and then interacts with service 1 after tandem services, and then service 1 interacts with service 2. Finally, the results generated from service 2 pass through service 1. and serial services are processed one by one and returned to the consumer, as shown in Figure 1-17.

Calls between services in series mode are usually implemented using synchronous RESTful-style remote calls. Note that this mode uses a synchronous invocation method. Before the series services are completed and returned, all services will block and wait, and one request will occupy It is processed by one thread, so it is not recommended to have too many layers of services in this mode. If it can be replaced by the service aggregation mode, the service aggregation mode is preferred instead of this service series mode.

Compared with the service aggregation mode, the service tandem mode has an advantage, that is, when another node is added to the tandem link, as long as it is not added directly behind the tandem service, the tandem service is imperceptible.

In the e-commerce case mentioned above, the UI front-end application calls the transaction, and the transaction calls the commodity inventory system to lock the inventory and deduct the inventory, using the service serial mode.

4. Service branch mode

The service branch mode is the product of the combination of the service proxy mode, the service aggregation mode and the service series mode.

In the actual business platform construction, due to the complexity of the business, abstract microservices may have multiple layers of dependencies, and the dependencies are not too simple, often showing a tree-shaped branch structure.

When the payment service connects to two external payment gateways, they must go through their respective payment channel gateways and support account balance payment. This payment service is actually a branch model, and there are many such service branch models in actual projects.

One day, one of the eight machines of Basic Service 6 went down. According to common sense, everyone thought that only 1/8 of the traffic was affected, but the statistics showed that the impacted business results turned out to be greater than 1/8.

After careful consideration, the reason for this result is that there are multiple layers in the call chain that repeatedly call the basic service, resulting in the cumulative effect of the traffic affected by the failure of the basic service. The specific calculation is as follows.

Assuming that the traffic entering the system is n, the call chain starts from service 3 to call service 6, and service 3 has 1/8 traffic failure, then the remaining successful traffic is 7/8 × n, and the remaining successful traffic continues Going to service 5, service 5 calls service 6 again, and another 1/8 of the traffic fails, leaving 7/8 × 7/8 × n.

Assuming that the number of machines in the basic service resource pool is i, the number of machines that hang up at one time is j, and the basic service is called x times in a call chain, the calculation formula of the correctly processed traffic is:

Assuming the allowable availability volatility is a, the minimum number of machines that should be configured when the underlying service is down one at a time is:

Convert the formula:

Since only one machine is allowed to go down at a time:

Therefore, the number i of machines that need to be set is obtained as:

For the above case, a maximum of one basic service 6 is allowed to go down at a time. In this case, the volatility of the availability needs to be kept less than 25%. There are two layers of services that depend on the basic service 6, which is calculated by the above formula:

i > 7.5

As a result, deploy at least 9 machines for service 6, so that when 1 machine goes down, the volatility impact on availability is controlled within 25%.

5. Service Asynchronous Message Pattern

All the previous service composition modes are implemented using synchronous RESTful synchronous calls. The synchronous call mode will block the thread during the calling process. If the service provider does not return, the service consumer will be blocked all the time. In severe cases The thread pool of the service will be full, and an avalanche effect will occur.

A typical case is that in the e-commerce system, after the transaction is completed, a message notification is sent to the logistics system to notify the logistics system to deliver goods, as shown in Figure 1-24.

6. Service Shared Data Mode

The service sharing data mode is actually an anti-pattern. In Section 1.3.3, the data sharing mode is proposed. Since data sharing is removed, interaction and communication are only performed through well-defined interfaces between services, so that each service is autonomous. Yes, the service itself and the team of the service include full-role stack technical and operational staff, these people are professional people doing professional things, so that communication is resolved within the team, so efficiency can be maximized.

However, in the following two scenarios, we still need the data sharing mode.

  • Unitized Architecture

Some platforms have high performance requirements, so microservices are used to split services and communicate through network services. Although the bandwidth of network communication is already wide, there will be performance losses. In this scenario In this way, different microservices can share some resources, such as cache, database, etc., and even the cache and data can be deployed in a physical machine with the microservices in the physical topology to minimize the performance loss caused by network communication. , we refer to this approach as a "unitized architecture".

  • Legacy overall service

For traditional monolithic services left over from history, in the process of reconstructing microservices, we found that the database tables that monolithic services depend on are coupled together, and their splitting needs to be denormalized, which may cause data consistency problems. Without a complete understanding and certainty of it, it will choose to maintain the status quo and let different microservices temporarily share data storage.

Fault Tolerance Patterns for Microservices

1. Bulkhead isolation mode

Here, the design of the ship is used as an analogy to the bulkhead isolation mode. If a ship encounters an accident and one of the cabins is flooded, we hope that this cabin and other cabins are isolated, and we hope that other cabins can not enter the water and not be affected. . In the microservice architecture, this is mainly reflected in the following two aspects.

1) Microservice container grouping

The payment platform where the author is located applies microservices, and divides the service pool of each node of the microservice into three groups: quasi-production environment, grayscale environment and production environment. The quasi-production environment is for internal use; the grayscale environment will run the traffic of some ordinary merchants; most of the production traffic and the traffic of VIP merchants will run in the production environment. In this way, in a relatively large reconstruction process, we can make full use of the isolation of the grayscale environment for pre-verification, and use the traffic of ordinary merchants to verify that there is no problem in reconstruction, and then go to the production environment.

2) Thread pool isolation

In the process of implementing the microservice architecture, we do not necessarily split each service into small strengths, depending on the functional team and financial conditions. We generally divide the same type of functions into one microservice, and try to avoid microservices. The service is too meticulous and the cost is increased, and it is enough.

2. Fusing mode

The circuit fuse switch at home can be used as an analogy to the fuse mode. If the electricity consumption at home is too large, the circuit fuse switch will automatically trip. At this time, it is necessary to manually find the electrical appliance that uses too much electricity to solve the problem, and then turn on the circuit fuse. switch. In this process, the circuit safety switch plays the role of protecting the entire home circuit system.

3. Current limiting mode

The capacity and performance of services are limited. In Chapter 3, we will introduce how to evaluate the maximum performance and capacity of services during the architectural design process. However, even if we consider the performance pressure in the design stage, and from the design and deployment These problems are solved, but the volume of business grows over time, and a sudden increase in volume is a common thing for a rapidly growing platform.

There are several mainstream methods as follows to achieve current limiting.

1) Counter

The number of visits per unit time is calculated by atomic variables. If it exceeds a certain threshold, subsequent requests will be rejected, and the count will be restarted after the next unit time.

In the implementation method of the counter, a circular array is usually defined (see Figure 1-30). For example, a circular array with 5 elements is defined, the counting period is 1s, and the number of visits within 4s can be recorded, of which 1 element is the current The sign of the time point. Generally speaking, every second the program will print the access volume of the previous 3s to the log for statistical analysis.

We divide the number of seconds in the time by the number of array elements 5, and then take the modulo and map it to the data element in the ring array. If the current time is 1 000 000 002s, then the third element in the ring array corresponding to the current time , the subscript is 2.

The current time is 1 000 000 002s, the corresponding counter is in the third element, and the subscript is 2. The current request is the first access request in this time period. That is, the element with the subscript 3 is cleared; within 1 000 000 002s, if any request finds that the element with the subscript 3 is not 0, the atomic variable 3 will be cleared and the time of clearing will be recorded.

At this time, the program can accumulate the third element, that is, the element with the subscript 2, and determine whether the threshold is reached. If the threshold is reached, the request will be rejected, otherwise the request will pass; at the same time, the data access volume of this time and the previous 3 seconds will be printed. , the print result is as follows.

Current: 1 time, last 1s: 302 times, last 2s: 201 times, last 3s: 518 times

However, if there is no request volume in the current second, and the counter for the next second cannot be cleared, it must be cleared first and then used after the request of the next second arrives, and the clearing time will be updated.

2) Token barrel

The token bucket is a popular technical solution to implement current limiting. It produces a fixed number of tokens per unit time through a thread, and then puts the tokens into the queue. Each request call needs to take a token from the bucket. , after getting the token, you are eligible to execute the request call, otherwise you can only wait for the token to be executed, or just discard it.

3) Semaphore

Current limiting is similar to the loopholes in life. No matter how much oil is poured, the flow of the leaking pipe below is limited. In fact, the semaphore we use at the application layer can also achieve current limiting. 
An example of using a semaphore is as follows:

public class SemaphoreExample {
    private ExecutorService exec = Executors.newCachedThreadPool();
    public static void main(String[] args) {
        final Semaphore sem = new Semaphore(5);
        for (int index = 0; index < 20; index++) {
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        // 获得许可
                        sem.acquire();
                        // 同时只有5个请求可以到达这里
Thread.sleep((long) (Math.random()));
                        // 释放许可
                        sem.release();

                        System.out.println("剩余许可:" + sem.availablePermits());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            exec.execute(run);
        }
        exec.shutdown();
}
}

4. Failover Mode

If circuit breakers and current throttling occur in the microservice architecture, how to deal with rejected requests? The mode to solve this problem is called failover mode, which is usually divided into the following types.

  • Adopt a fail-fast strategy, and directly return the user error, so that the user knows that a problem has occurred and decides the follow-up treatment.
  • Whether there is a backup service, if there is a backup service, quickly switch to the backup service.
  • The failed service may be a problem with a certain machine, not all machines, such as OOM problem. In this case, it is suitable to use the failover strategy and use the retry method to solve the problem, but this method requires the service provider's Services implement idempotency.

Granularity of Microservices

In a service-based system or a microservice architecture, how do we split services is the most reasonable? What kind of granularity is the most suitable for services to be split into?

According to the original intention of microservices, services should be divided according to the functions of the business, until the functions and responsibilities of each service are single, or even can no longer be divided, so that each service can be deployed independently, and it is convenient to expand and shrink. Effectively improve utilization. The finer the disassembly, the smaller the coupling of the service, the better the cohesion, and the more suitable for agile release and launch.

However, dismantling too much will lead to a large number of services in the system, and the interdependent relationship is more complex. More importantly, according to Conway's law, the team must respond to the system architecture, and each microservice must have corresponding independence and autonomy. This is also an unrealistic idea.

Therefore, it is advisable to split the microservices here. The principle is that the splitting can allow the user to freely arrange the underlying sub-services to obtain the corresponding combined services. At the same time, the construction of the team and the number and distribution of personnel should be considered. .

Some companies package each interface into a project, or package each JDBC call into a project, and then claim to be "microservices", and finally have hundreds or thousands of microservice projects, which is unreasonable. Of course, some companies couple a coarse-grained process completed by a set of interfaces into a project, so that when the upper-layer service wants to use a separate service in this set of interfaces, because this service is coupled with other logic, so It is also unreasonable to customize the process to meet the needs of the user to use some services, because the service granularity is too coarse.

Related Posts