"Microservice Design" Xpress Summary Memo

Chapter 1 Microservices

1. With the popularity of domain-driven design, continuous delivery (CD), virtualization, basic design automation, small autonomous teams, and large-scale cluster systems, the application of microservices was born. It wasn't invented, but a trend or pattern drawn from the real world.

 

2. What are microservices? Microservices are small, autonomous services that work together.

    Be small and focus on doing one thing well: the Single Responsibility Principle, "Bring together things that change for the same reason and separate things that change for different reasons."

    Microservices apply this idea to independent services. Determining the boundaries of the service according to the boundaries of the business makes it easy to determine where a function code should be placed. And because the service focuses on a certain boundary, it can avoid many related problems that arise from a large code base.

    Autonomy: A microservice is an independent entity that can be independently deployed on PAAS or exist as an operating system process. Services expose APIs, and services communicate through these APIs. API implementation technology should avoid coupling with consumers, which means that API implementation methods that are not related to specific technologies should be selected to ensure that the choice of technology is not limited.

 

3. The benefits of microservices:

    Technology Heterogeneity: In a system with multiple services cooperating with each other, the technology best suited for that service can be used in different services.

 

    Elasticity: A key change in elastic engineering is the "bulkhead"; the boundary of a service is a prominent bulkhead, and for a system with a single service, it is possible to reduce full functionality by running the same instance on a different machine The probability of unavailability, however, the microservice system itself can handle the problem of service unavailability and functional degradation very well.

 

    Scaling: A large monolithic service function scales as a whole. Even if only a small part of the system has performance issues, the entire service needs to be scaled. If you use smaller services, you can scale only the services that need to be scaled, so that those services that don't need to be scaled can be run on smaller, less-performing hardware.

 

    Simplified deployment: For a monolithic system, changing even just one line of code requires redeploying the entire application. This kind of deployment has high impact and high risk, so the relevant stakeholders are afraid to deploy it lightly. Deployments are less frequent, which means that there are large differences in functionality between releases and a higher chance of error. In a microservices architecture, each service is deployed independently, which allows for faster deployment of specific parts of the code. If something goes wrong, it will also affect a service, and it is easy to roll back quickly, which also means that new features we develop can be used faster.

 

    SOA Service Oriented Architecture is a design that consists of multiple services that work together to provide a set of capabilities. A service usually exists as a separate form in the operating system process. Services communicate through network calls instead of in-process calls.

    You can think of microservices architecture as a specific approach to SOA.

 

    Microservices are not a free lunch, they are not a silver bullet, and if you want a general rule, microservices are the wrong choice. You have to deal with all the complexities that a distributed system has to deal with. Every company, organization, and system is different, and whether or not microservices are right for you, or how well you can adopt them, depends on many factors.

 

 

 

Chapter 2 Evolutionary Architecture

 

    An important responsibility of the architect is to ensure that the team has a shared technical vision to help us deliver the systems our customers want. In some cases, architects only need to work with one team, when they are equivalent to technical leaders. In other cases, they are responsible for the technical vision of the entire project, often coordinating work across multiple teams, or even within an organization. Regardless of the level, the role of the architect is delicate. In the average organization, it takes very good developers to become architects, but usually attracts more criticism than other roles. Compared to other roles, architects have a more direct impact on aspects such as the quality of the systems built, the working conditions of colleagues, and the organization's ability to respond to change. This role is difficult to do well, why?

 

    We found that there is a role that can be better compared to the IT architect, an idea that Erik Doenenburg told me, and he thinks the better analogy is an urban planner, not an architect. If you've played SimCity, you should be familiar with the role of the city planner. The role of urban planners is to optimize the layout of towns and make them more accessible to current residents, and colleagues will also consider some future factors. To achieve this, he needs to collect all kinds of information. The way the planner affects the evolution of the city is interesting, he does not directly say "build a building like this in that place", instead it partitions the city. Like in SimCity, you might plan one part of the city as an industrial area, another as a residential area, and then everyone else decides what buildings to build. Of course, this decision will be subject to certain constraints. For example, the factory must be built in an industrial area. Urban planners think more about how any public facility moves from one area to another than what happens in each area specifically.

    Urban planners should try to anticipate possible changes, but also need to be aware of the fact that trying to control aspects directly often doesn't work.

 

    Future changes are hard to foresee, so rather than predicting all the possibilities of change, make a plan to allow for it. To that end, overly detailed design of everything should be avoided. A city is a system that should make the people who live in it happy. To borrow a quote from Frank Buschmann: One of the responsibilities of an architect is to ensure that the system is suitable for developers to work on.

 

    Therefore, our architects should focus on the general direction like urban planners, and only participate in very specific details in very limited cases. They need to ensure that the system can not only meet current needs, but also adapt to future changes.

 

    As an architect, you shouldn't focus too much on what's going on within each area, but more on what's going on between areas. This means that we should consider how different services interact with each other, or ensure that we can monitor the health of the entire system.

 

    Each service can allow the team to choose a different technology stack or data storage technology, of course, other issues need to be considered. But in fact, there is no limit to allowing teams to choose any technology stack. For example, if you need to support 10 different technology stacks, you may encounter difficulties in recruiting, or it is difficult to exchange people between different teams.

 

    "Worry about the interactions between services without paying too much attention to what's going on inside each service".

 

    Sometimes we can't fully adhere to the technical vision, for example in order to release some urgent features, you may ignore some constraints, in fact, this is just another trade-off that needs to be done. Our technology vision has its own reasons, so deviation from this vision can bring benefits in the short-term, but in the long-term there is a cost. The concept of technical Zhao Wu can be used to help us understand this trade-off, just as the Zhao Wu owed in the real world needs to be repaid, so does the accumulated technical debt.

 

    Not only does taking shortcuts introduce technical debt, but sometimes the goals of the system change and it doesn't fit with the existing implementation, which can also introduce technical debt.

 

    The architect's job is to start from a higher level and understand how to make tradeoffs. It is important to understand the layers of debt and how it affects the system. For some organizations, the architect should be able to provide some gentle guidance and then let the team decide how to pay off this technical debt. Others require a more structured approach, such as maintaining a debt list and reviewing it regularly.

 

    

    The reality is varied, but I'm a big proponent of using microservices teams with better autonomy and more freedom to solve problems. If you're in an organization with too many restrictions on developers, then microservices may not be for you.

 

    "Governance ensures the achievement of corporate goals by assessing stakeholder needs, current conditions and possible next steps, setting direction by prioritizing and making decisions. Overseeing agreed directions and goals." --COBIT 5

 

     I firmly believe that great software comes from great people. So if you're only worried about technical issues, I'm afraid you're seeing far less than half of the problems.

 

Chapter 3 How to Model Services

 

    What kind of service is good service? Design principles: loose coupling, high cohesion.

    

    When you're thinking about bounded contexts within your organization, you shouldn't think in terms of shared data, but in terms of the capabilities those contexts can provide.

    If the system can be decomposed into bounded contexts to represent the domain, then the modification of a function is more likely to be limited to a single microservice boundary, which reduces the scope of modification and can be more Deploy quickly.

 

 Chapter 4 Integration

 

    Because I think it's very important to keep the technology agnostic of the way microservices communicate. This means that you should not choose an integration method that has limitations on the specific implementation technology of microservices.

 

    Don't over-abstract remote calls so that networking factors are completely hidden; make sure you can upgrade the server-side interface independently without forcing the client to upgrade, so pay attention to this balance when writing client-side code.

 

    DRY is not violated within microservices, but DRY can be violated appropriately in cross-service situations. Introducing a lot of coupling between services can be a worse problem than duplicating code.

 

    The fact that the client consumes the service response as flexibly as possible is in line with Postel's law (also called the law of robustness), which holds that each module in the system should be "strict in and out", that is, be strict with what it sends, and be strict with what it receives. things to be forgiving. The original context of this principle is the interaction between network devices, because in this scenario, all kinds of strange things can happen. In the request/response scenario, this principle can help us reduce the modification of the consumer when the service changes.

 

    1) If you have done everything you can to avoid modification to the interface (but it is unavoidable), the next task is to limit its impact. We don't want to force the client to follow the server to upgrade, because we want the microservices to be released independently of each other. One of the more successful methods I've used is to have both the new interface and the old interface coexist on the same service. So when releasing a breaking change, you can deploy a colleague's version that includes the old and new interfaces. (When the old interface is no longer used, delete the interface and related code)

 

    2) Another method of version management that is often mentioned is to run different versions of the service at the same time, and then route old users to the old version of the service, and new users can see the new version of the service. It is reasonable to use both versions of the service in the short term, especially if you are doing blue-green deployments or canary releases. In these cases, the different versions of the service may only coexist for a few minutes or hours, and there are usually only two versions. The longer it takes to upgrade consumers to a new version, the more likely you should consider the practice of keeping two sets of APIs in the same microservice.    

 

Chapter 5 Decomposing Monolithic Systems

 

    A common algorithm for handling distributed transactions is two-phase commit. In this way, the first is voting. At this stage, each participant will (cohort) tell the transaction manager whether it should continue. If all the shards received by the transaction manager are successful, they are told to commit. As soon as a negative vote is received, the transaction manager will let all participants roll back. This method will make all participants tentatively and wait for the instructions of the central coordination process, which will easily lead to the interruption of the system. If the transaction manager goes down, the pending transaction will never complete. If a cohort fails to send a message during the voting phase, all other participants will block, and commits after voting have ended may also fail. The algorithm implicitly believes that the above situation cannot happen, i.e. if a cohort votes yes during the voting phase, it must be submitted successfully. Cohort needs a mechanism to ensure this happens. This means that this algorithm is not foolproof and just tries to catch most of the failure scenarios.

 

    All of these scenarios add complexity. As you can see, distributed transactions are error-prone and bad for scaling. This approach to eventual consistency through retries and compensation makes problem localization more difficult, and may require additional compensation measures to fix potential data inconsistencies.

    If there is now a business operation that occurs in a single transaction across systems, ask yourself if this is really necessary. Is it possible to simply put them into different local transactions and then rely on the concept of eventual consistency? Such a system would be easier to build and extend. If you come across a scene that does require consistency, try to avoid putting them in different places, do try to do so. If that doesn't work, avoid thinking in purely technical terms (such as database transactions) and explicitly create a concept to represent the transaction. You can think of this concept as a handle or hook on top of which operations like compensating transactions can be performed relatively easily, which is also a way to monitor these complex concepts in the system.

 

 

Chapter 11 Microservices at Scale

 

    If you wait too long to decide that the call fails, the whole system will be slowed down. If the timeout is too short, you will mistake a call that might still be working as a failure. If there are no timeouts at all, a down downstream system can hang the entire system. (circuit breaker, bulkhead isolation scheme)

    Bulkhead: We should use a different connection pool for each downstream service's connections. We can think of a circuit breaker as an automatic mechanism that seals a bulkhead that not only protects consumers from problems with downstream services, but also keeps downstream services from making more calls to prevent possible adverse effects. For example, Hystrix allows you to implement bulkheads that deny requests under certain conditions to avoid resource saturation, which is called load shedding. Sometimes denying requests is the best way to avoid critical systems becoming overwhelmed or known as bottlenecks on multiple upstream services.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326717786&siteId=291194637