Many companies, such as Amazon, eBay, and NetFlix, have solved this problem by adopting a micro-architecture pattern. The idea is not to develop a single, monolithic application, but to break it up into small, interconnected microservices.
Microservices are gaining traction on blogs, social media discussion groups, and conference presentations, and are ranked very high on Gartner’s 2014 Hype Cycle. At the same time, there are plenty of skeptics in the software community who think microservices are nothing new. Naysayers sees this as a repackaging of SOA architecture. However, despite the different debates, the microservices architecture pattern is providing a significant contribution to agile deployment and the implementation of complex enterprise applications.
First let’s look at why we should use microservices.
Develop monolithic applications
Let’s say you’re developing taxi scheduling software to compete with Uber and Hailo. After initial meetings and requirements analysis, you might start the new project either manually or using a generator based on Rails, Spring Boot, Play, or Maven. Its hexagonal architecture is modular, as shown below:
The core of the application is the business logic, which is accomplished by modules that define services, domain objects, and events. Around the core are the adapters that interact 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 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 will be packaged as WAR and deployed on Tomcat or Jetty, while other Java applications will be packaged as self-contained JAR formats, and Rails and Node.js will be packaged as 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 running the application and using Selenium linked UI to do end-to-end testing. Standalone applications are also easy to deploy, simply copy packaged applications to the server and scale applications easily by running multiple copies on the back end of the load balancer. In the early days such applications worked well.
The deficiency of monomer application
Unfortunately, this simple approach has major limitations. A simple application gets bigger over time. In each sprint, the development team is faced with a new “story” and then develops a lot of new code. In a few years, this small, simple app will grow into a giant monster. Here’s an example. I was recently talking to a developer who was writing a tool to analyze the dependencies between JAR files in their application with millions of lines of code. I’m pretty sure this code is a monster that many developers have been working on for years.
Once your app becomes a big, complex monster, it’s a pain in the neck 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. As a result, fixing bugs and adding new features correctly becomes very difficult and time-consuming. Plus, team morale goes down. If the code is difficult to understand, it can’t be changed properly. It will eventually lead to a great, incomprehensible quagmire.
Singleton applications also slow down development. The larger the application, the longer the startup time. For example, a recent survey showed that apps sometimes took longer than 12 minutes to launch. I’ve also heard that some apps take 40 minutes to start up. If the developer has to restart the app frequently, then most of the time will be spent waiting and productivity will suffer.
In addition, complex and large monolithic applications are not conducive to sustainable development. Today, the norm for SaaS applications is to change many times a day, which is very difficult for a monolithic application model. In addition, the impact of this change is not well understood, so a lot of manual testing has to be done. Then it’s going to be tough to keep deploying.
Singleton applications can be difficult to scale when resources conflict between different modules. For example, a module that completes 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, because these modules are deployed together, a hardware choice has to be compromised.
Another problem with monolithic applications is reliability. Because all modules run in a process, a bug in any module, such as a memory leak, can bring down the entire process. In addition, because all application instances are unique, this bug affects the reliability of the entire application.
Finally, monolithic applications make it difficult to adopt new architectures and languages. For example, imagine that you have two million lines of code written in the XYZ framework. If you want to change to ABC framework, both time and cost are very expensive, even if ABC framework is better. So it’s an unbridgeable gap. You have to bow to your first choice.
To recap: You start with a successful business-critical application and then it becomes a giant, incomprehensible monster. Hiring potential developers is difficult because of outdated, inefficient technology. Applications cannot scale, reliability is low, and ultimately, agile development and deployment becomes impossible.
So what to do about it?
Microprocessing architecture — Handling complex things
Many companies, such as Amazon, eBay, and NetFlix, have solved this problem by adopting a microprocessing-structured model. The idea is not to develop a single, monolithic application, but to break it up into small, interconnected microservices.
A microservice typically performs a specific function, such as order management, customer management, and so on. Each microservice is a miniature hexagonal application with its own business logic and adapter. Some microservices also publish apis for other microservices and application clients to use. Other microservices complete a Web UI, and at runtime, each instance may be a cloud VM or Docker container.
For example, a system described above might be decomposed as follows:
Each application area is done using microservices, and the Web application is broken down into a series of simple Web applications (one for passengers, one for taxi drivers, for example). This split is easier to deploy for different users, devices, and specific application scenarios.
Each backend service exposes a REST API, and many services themselves use apis provided by other services. For example, driver management uses notification services that inform drivers of a potential need. UI services activate other services to update Web pages. All services employ asynchronous, message-based communication. The internal mechanics of microservices will be discussed in a future series.
Some REST apis are also open to mobile applications used by passengers and drivers. Instead of directly accessing backend services, these applications pass intermediate messages through the API Gateway. The API Gateway is responsible for load balancing, caching, access control, API billing monitoring, and other tasks. It can be easily implemented through NGINX, and will be covered in subsequent articles.
· The Microservices architecture pattern in The figure above corresponds to The Y-axis representing The Scaleable Cube, a three-dimensional scaleable model described in The Art of Scalability. The other two scalable axes are the X-axis, which consists of multiple copies of applications running on the back end of the load balancer, and the Z-axis, which routes requirements to related services.
Applications can be basically represented by the above three dimensions, with the Y-axis representing the decomposition of applications into microservices. At runtime, the X-axis represents running multiple instances hidden behind the load balancer, providing throughput capability. Some applications may still partition services along the Z-axis. The figure below illustrates how the trip management service is deployed on a Docker running on AWS EC2.
At run time, the trip management service consists of multiple service instances. Each service instance is a Docker container. To ensure high availability, these containers typically run on multiple cloud VMS. Service instances are preceded by a layer of load balancers such as NGINX, which distribute requests between instances. The load balancer also handles other requests, such as caching, permission control, API statistics, and monitoring.
This microservice architecture pattern has a profound impact on the relationship between applications and databases. Unlike traditional services that share a single database, each microservice architecture has its own database. In addition, this thinking also affects enterprise data patterns. At the same time, this pattern implies multiple pieces of data, but one database per service is necessary if you want to reap the benefits of microservices because the architecture requires this kind of loose coupling. The figure below illustrates the sample application database architecture.
Each service has its own database, and each service can use a database type that is more appropriate for it, also known as multilingual consistency 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, microservices architecture patterns are a bit like SOA in that they are composed of multiple services. Another way to look at this, however, is that the microservices architecture pattern is an SOA without Web services (WS-) and ESB services. Microservice applications prefer simple lightweight protocols, such as REST, rather than WS-, and avoid using ESB and ESB-like capabilities within microservices. The micro architecture pattern also rejects SOA concepts such as Canonical Schema.
Benefits of microservices architecture
There are many benefits to the microservices architecture pattern. First, the complexity problem is solved for multiple service methods by breaking down large monolithic applications. Without changing functionality, the application is decomposed into manageable branches or services. Each service has a boundary that is clearly defined using RPC- or message-driven apis. The microservices architecture pattern provides a modular solution to functionality that would be difficult to implement using singleton coding, so individual services are easy to develop, understand, and maintain.
Second, this architecture allows each service to be developed by a dedicated development team. Developers are free to choose their own development technologies and provide API services. Of course, many companies try to avoid confusion by offering only a few technology options. This freedom, however, means that developers aren’t forced to use outdated technology from the start of a project, they can opt for current technology. Even so, because the services are relatively simple, it is not too difficult to rewrite the old code using today’s 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 adopt AB testing to quickly deploy changes. The microservice architecture pattern enables continuous deployment.
Finally, the microservice architecture pattern allows each service to scale independently. You can scale to meet the requirements based on the scale of each service. Even more, you can use hardware that is better suited to service resource requirements. For example, you can deploy CPU-sensitive services on EC2 Compute Optimized Instances and in-memory databases on EC2 Memory-Optimized Instances.
Deficiencies in microservices architecture
Fred Brooks wrote 30 years ago, “There are no silver Bullets,” and like any other technology, the microservices architecture has its limitations. One similar to his name, “microservices,” emphasizes service size, and in fact, some developers advocate building slightly larger, 10-100 LOC service groups. Although small services are more likely to be adopted, don’t forget that this is an end of choice and not an end in itself. The purpose of microservices is to effectively split applications for agile development and deployment.
Another major disadvantage is that microservice applications are distributed systems with inherent complexity. Developers need to choose between RPC or messaging and implement the interprocess communication mechanism. What’s more, they have to write code to handle local failures such as slow or unusable message delivery. It’s not that difficult, of course, but it’s a little more complicated with microservices than it is with singleton applications with language-level method or process invocation.
Another challenge with microservices comes from partitioned database architectures. It is common in business transactions to update messages to multiple business subparties at the same time. This transaction is easy for singleton applications because there is only one database. In microservices architecture applications, different databases used by different services need to be updated. Using distributed transactions is not necessarily a good choice, not just because of CAP theory, but because today’s highly scalable NoSQL databases and messaging middleware do not support this requirement. In the end you have to use an ultimate consistency approach, which puts more demands and challenges on developers.
Testing an application based on microservices architecture is also a complex task. For example, using the popular Spring Boot architecture, it is easy to test a single Web application’s REST API. Conversely, the same service test needs to start all the services associated with it (at least the STUBS for those services). Again, don’t underestimate the complexity that comes with adopting a microservice architecture.
Another challenge is that changes in the application of the microservices architecture pattern will affect multiple services. For example, suppose you are completing A case where you need to modify services A, B, and C, and A depends on B, and B depends on C. In a singleton application, you just change the relevant modules, integrate the changes, and deploy. In contrast, the microservices 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 few changes require coordination of multiple services.
Deploying a microservice application is also complex, and a distributed application simply needs to deploy its own server behind a complex equalizer. Each application instance requires configuration of basic services such as databases and message-oriented middleware. In contrast, a microservice application typically consists of a large number of services. NetFlix, for example, has about 600 services, according to Adrian Cockcroft. There are multiple instances of each service. This leaves a lot to configure, deploy, expand, and monitor, in addition to implementing a service discovery mechanism (table in a future article) to discover the addresses (including server addresses and ports) of the services that communicate with it. Traditional problem-solving methods cannot be applied to such a complex problem. In turn, the successful deployment of a microservice application requires the developer to have sufficient control over the deployment method and a high degree of automation.
One automated approach is to use PaaS services, such as Cloud Foundry. PaaS provides developers with an easy way to deploy and manage microservices, and it solves all of these problems in a package. At the same time, system and network experts who configure PaaS can adopt best practices and policies to simplify these issues. Another way to automate the deployment of microservice applications is to develop a PaaS system that is basic to you. A typical starting point is to use a clustering solution, such as Mesos or Kubernetes with Docker. Later in the series we will look at how software deployment methods such as NGINX can easily provide caching, permission control, API accounting, and monitoring at the microservices level.
conclusion
Building complex applications is really hard. Monolithic architectures are better suited for lightweight, simple applications. If you use it for complex applications, it’s really bad. The microservices architecture model can be used to build complex applications, but of course it has its drawbacks and challenges.
In future blogs, I will delve deeper into microservice architecture patterns and discuss strategies such as service discovery, service deployment choices, and how to decompose a distributed application into multiple services.
Complete project source code welcome to study together with the relevant technology, source code access please add :2670716182