Microservices in Practice (1): Advantages and Disadvantages of Microservice Architecture

[Editor's note] This article is from the official Nginx blog and is the first of a series of articles on microservices. It mainly discusses the shortcomings of traditional monolithic applications, as well as the advantages and challenges of microservice architecture. As the author said, the microservice architecture is more suitable for building complex applications, although it also has its own shortcomings.

 

This article was written by Chris Richardson, the founder of the early Java-based Amazonite EC2 PaaS platform CloudFoundry.com. Now he advises enterprises on how to develop and deploy applications. He also writes frequently about microservices at http://microservices.io.

 

Microservices are gaining traction in blogs, social media discussion groups, and conference presentations, and they are ranked very high on Gartner's 2014 Hype Cycle. At the same time, there are many skeptics in the software community who think microservices are nothing new. Naysayers sees this as a repackaging of the SOA architecture. However, despite the differing debates, the microservices architectural pattern is providing tremendous assistance for agile deployments and implementation of complex enterprise applications.

 

This blog is the first in a series of seven articles on how to design, develop, and deploy microservices. The reader will learn the method and compare it with the monolithic architecture pattern. This series of articles will describe the different elements of a microservices architecture. You will learn about the pros and cons of the microservices architecture pattern in order to decide whether it is better to apply the microservices architecture to your own projects, and how to apply this pattern.

 

Let's first look at why you should consider using microservices.

 

Develop monolithic applications

 

Suppose you are preparing to develop a taxi scheduling software to compete with Uber and Hailo. After preliminary meetings and requirements analysis, you may start this new project manually or using a generator based on Rails, Spring Boot, Play or Maven, its The hexagonal architecture is modular, and the architecture diagram is as follows:

1.png

 

The core of the application is the business logic, which is done by modules that define services, domain objects, and events. Around the core are adapters that deal with the outside world. Adapters include database access components, message components that produce and process messages, and web modules that provide API or UI access support.

 

Although it is also modular logic, it will eventually be packaged and deployed as a monolithic application. The exact format depends on the application language and framework. For example, many Java applications are packaged in WAR format and deployed on Tomcat or Jetty, while other Java applications are packaged in self-contained JAR format. Likewise, Rails and Node.js are packaged in hierarchical directories.

 

This style of application development is common because IDEs and other tools are good at developing a simple application that is easy to debug, simply run the application, and link the UI with Selenium to complete end-to-end testing. Monolithic applications are also easy to deploy. You only need to copy the packaged application to the server side, and you can easily scale the application by running multiple copies in the backend of the load balancer. In the early days this type of application worked fine.

 

Disadvantages of Monolithic Applications

 

Unfortunately, this simple approach has significant limitations. A simple app grows larger over time. In each sprint, the development team faces a new "story" and then develops a lot of new code. In a few years, this small and simple app can turn into a giant monster. Here's an example I recently discussed with a developer who was writing a tool to analyze dependencies between JAR files in one of their millions of lines of code applications. I'm pretty sure this code is a monster that many developers have worked on over the years.

 

Once your app becomes a big, complex monster, it must be painful for the development team. The main problem with agile development and deployment is that the application is so complex that it is impossible for any single developer to understand it. Therefore, fixing bugs and properly adding new features becomes very difficult and time-consuming. Plus, team morale goes downhill. If the code is difficult to understand, it cannot be modified correctly. In the end, it will lead to a huge, incomprehensible quagmire.

 

Monolithic applications also slow down development. The larger the app, the longer the startup time. For example, a recent survey showed that sometimes apps took longer than 12 minutes to start. I've also heard that some apps take 40 minutes to start. If the developer needs to restart the application frequently, most of the time will be spent waiting, and the production efficiency will be greatly affected.

 

In addition, complex and huge monolithic applications are not conducive to continuous development. Today, the norm for SaaS applications is to change many times a day, which is very difficult for a monolithic application model. Also, the impact of this change was not well understood, so a lot of manual testing had to be done. Then, continuous deployment will also be difficult.

 

Monolithic applications will be very difficult to expand when resources conflict between different modules. For example, a module that implements a CPU-sensitive logic should be deployed in AWS EC2 Compute Optimized instances, while another in-memory database module is more suitable for EC2 Memory-optimized instances. However, since these modules are deployed together, a compromise has to be made in hardware selection.

 

Another problem with monolithic applications is reliability. Because all modules run in one process, a bug in any one module, such as a memory leak, will likely bring down the entire process. In addition, since all application instances are unique, this bug will affect the reliability of the entire application.

 

Finally, monolithic applications make it difficult to adopt new architectures and languages. For example, imagine you have 2 million lines of code written in the XYZ framework. If you want to change to the ABC framework, both time and cost are very expensive, even if the ABC framework is better. Therefore, it is an insurmountable chasm. You have to bow your head to the first choice.

 

To sum it up: You start off with a successful business-critical application, and then turn into a giant, incomprehensible monster. Hiring potential developers is difficult because of outdated, inefficient technologies. Applications cannot scale, reliability is low, and ultimately, agile development and deployment becomes impossible.

 

So how to deal with it?

 

Microprocessing Architecture - Dealing with Complexity

 

Many companies, such as Amazon, eBay, and NetFlix, solve the above problems by adopting the microprocessing architecture pattern. The idea is not to develop one giant monolithic application, but to decompose the application into small, interconnected microservices.

 

A microservice generally completes a specific function, such as order management, customer management, and so on. Each microservice is a tiny hexagonal application with its own business logic and adapters. Some microservices also publish APIs for use by other microservices and application clients. Other microservices complete a web UI. At runtime, each instance may be a cloud VM or a Docker container.

 

For example, a possible decomposition of the system described earlier is as follows:

2.png

 

Each application functional area is done using microservices, and the web application is split into a series of simple web applications (eg one for passengers and one for taxi drivers). Such a split is easier to deploy for different users, devices and special application scenarios.

 

Each background service exposes a REST API, and many services themselves also use APIs provided by other services. For example, driver management uses a notification service that informs the driver of a potential need. UI services activate other services to update web pages. All services use asynchronous, message-based communication. Microservice internals will be discussed in subsequent series.

 

Some REST APIs are also open to mobile applications used by passengers and drivers. These applications do not directly access background services, but pass intermediate messages through API Gateway. API Gateway is responsible for tasks such as load balancing, caching, access control, and API billing monitoring, which can be easily implemented through NGINX. API Gateway will be introduced in subsequent articles.

3.png

 

The microservices architectural pattern in the diagram above corresponds to the Y-axis representing the Scalable Scale Cube, a three-dimensional scaling model described in The Art of Scalability book. The other two scalable axes, the X axis consists of multiple copies of the application running on the backend of the load balancer, and the Z axis is the routing of demand to related services.

 

The application can basically be represented by the above three dimensions, and the Y-axis represents the decomposition of the application into microservices. At runtime, the X-axis represents running multiple instances behind a load balancer, providing throughput. Some applications may still use the Z-axis to partition services. The diagram below shows how the trip management service is deployed on Docker running on AWS EC2.

4.png

 

At runtime, the trip management service consists of multiple service instances. Each service instance is a Docker container. To ensure high availability, these containers generally run on multiple cloud VMs. In front of the service instances is a layer of load balancers such as NGINX, which are responsible for distributing requests among the various instances. The load balancer also handles other requests, such as caching, permission control, API statistics and monitoring.

 

This microservice architecture model profoundly affects the relationship between applications and databases. Unlike traditional multiple services sharing a database, each service in the microservice architecture has its own database. In addition, this line of thinking also affects enterprise-level data models. At the same time, this model means multiple copies of data, but if you want to get the benefits of microservices, a database unique to each service is necessary, because this kind of architecture requires this kind of loose coupling. The following diagram shows the sample application database schema.

5.png

 

Each service has its own database, and each service can use a database type that is more suitable for it, also known as a polyglot consistent architecture. For example, driver management (discovering which driver is closer to the passenger) must use a database that supports geographic information queries.

 

On the surface, the microservices architecture pattern is a bit like SOA in that they are composed of multiple services. However, to look at this from another angle, the microservices architecture pattern is an SOA without Web Services (WS-) and ESB services. Microservice applications are happy to use simple and lightweight protocols like REST instead of WS- and avoid ESB and ESB-like functionality within microservices. The microservices architecture pattern also rejects SOA concepts like canonical schema.

 

Benefits of Microservice Architecture

 

The microservices architecture pattern has many benefits. First, the complexity problem is solved by decomposing a huge monolithic application into multiple service methods. The application is decomposed into multiple manageable branches or services with the same functionality. Each service has a well-defined boundary with an RPC- or message-driven API. The microservices architecture pattern provides a modular solution to functionality that is difficult to implement with monolithic coding, whereby individual services are easy to develop, understand, and maintain.

 

Second, this architecture allows each service to be developed by a dedicated development team. Developers can freely choose development technologies and provide API services. Of course, many companies try to avoid confusion and only offer certain technology options. This freedom, then, means that developers aren't forced to use outdated technology that a project started with, they can choose the technology they have today. Even, because the services are relatively simple, it is not very difficult to rewrite the previous code with current technology.

 

Third, the microservice architecture pattern is that each microservice is deployed independently. Developers no longer need to coordinate the impact of other service deployments on this service. This change can speed up deployment. UI teams can use AB testing to quickly deploy changes. The microservice architecture pattern makes continuous deployment possible.

 

Finally, the microservices architectural pattern enables each service to scale independently. You can deploy the scale that meets your needs based on the scale of each service. Even more, you can use hardware that is more suitable for serving resource needs. For example, you can deploy CPU-intensive services on EC2 Compute Optimized instances and in-memory databases on EC2 memory-optimized instances.

 

Weaknesses of Microservice Architecture

 

Fred Brooks wrote 30 years ago, "there are no silver bullets" and like any other technology, microservices architecture has its downsides. One of them is similar to his name, "microservices" emphasizes the size of the service, in fact, some developers advocate the establishment of slightly larger, 10-100 LOC service groups. Although small services are more willing to be adopted, don't forget that this is only the choice of the end and not the end. The purpose of microservices is to effectively split applications for agile development and deployment.

 

Another major disadvantage is that microservice applications are distributed systems, which introduce inherent complexity. Developers need to choose between RPC or message passing and complete the inter-process communication mechanism. What's more, they must write code to deal with local failures in message passing that are too slow or unavailable. Of course, this is not difficult, but compared to the method or process invocation through the language level in the monolithic application, this technology under microservices is more complicated.

 

Another challenge with microservices comes from partitioned database architectures. In commercial transactions, it is common to update news for multiple business sub-subjects at the same time. This kind of transaction is easy for monolithic applications because there is only one database. In a microservice architecture application, different databases used by different services need to be updated. Using distributed transactions is not necessarily a good choice, not only because of CAP theory, but also because today's highly scalable NoSQL databases and messaging middleware do not support this requirement. In the end you have to use an eventual consistency approach, which puts more demands and challenges on the developer.

 

Testing an application based on a microservice architecture is also a complex task. For example, using the popular Spring Boot architecture, it is easy to test the REST API of a monolithic web application. In turn, the same service test needs to start all services related to it (at least the stubs for those services). Again, the complexity that comes with adopting a microservices architecture cannot be underestimated.

 

Another challenge is that changes to the application of the microservice architecture pattern will ripple across multiple services. For example, suppose you are working on a case where you need to modify services A, B, and C, and A depends on B, and B depends on C. In a monolithic application, you only need to change the relevant modules, integrate the changes, and deploy. In contrast, the microservice architecture pattern needs to consider the impact of related changes on different services. For example, you need to update service C, then B, and finally A. Fortunately, many changes generally affect only one service, and changes that require coordination across multiple services are rare.

 

Deploying a microservice application is also very complicated. A distributed application only needs to simply deploy its own servers behind a complex balancer. Each application instance is required to configure basic services such as database and message middleware. In contrast, a microservice application generally consists of a large number of services. For example, according to Adrian Cockcroft, Hailo is made up of 160 different services and Netflix has about 600. Each service has multiple instances. This creates a lot of parts that need to be configured, deployed, scaled, and monitored. In addition to this, you also need to implement a service discovery mechanism (published in a follow-up article) to discover the address of the service it communicates with (including the server address and port). Traditional problem-solving methods cannot be used to solve such complex problems. Next, successfully deploying a microservice application requires developers to have sufficient control over the deployment method and a high degree of automation.

 

One way to automate this is to use a PaaS service such as Cloud Foundry. PaaS provides developers with an easy way to deploy and manage microservices, and it solves all these problems in a package. At the same time, systems and network specialists configuring PaaS can adopt best practices and strategies to simplify these issues. Another way to automate the deployment of microservices applications is to develop the most basic PaaS system for you. A typical starting point is to use a clustering solution such as Mesos or Kubernetes with Docker. In the following series, we will look at how to easily provide caching, permission control, API statistics and monitoring at the microservice level based on software deployment methods such as NGINX.

 

Summarize

 

Building complex applications is really hard. Monolithic architectures are more suitable for lightweight and simple applications. It really sucks if you use it to develop complex applications. The microservices architecture pattern can be used to build complex applications, of course, this architecture model has its own shortcomings and challenges.

 

In a follow-up blog, I'll explore microservices architectural patterns in depth and discuss strategies such as service discovery, service deployment choices, and how to decompose a distributed application into multiple services.

 

This article is reproduced from: http://dockone.io/article/394

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326380971&siteId=291194637