What is the microkernel architecture design?

The micro-kernel architecture design is relatively hot now, and it sounds like it is related to the operating system kernel. As a Java programmer, the operating system kernel seems to have nothing to do with us. But if I say that the microkernel is actually a plug-in architecture, you must have a face of confusion, "You actually explain to Java programmers what a plug-in architecture is? I use it every day, Eclipse, IntelliJ IDEA, OSGi, Spring Plugin, SPI, etc., which is not a plug-in architecture. Some of my projects also use plug-in design, such as using plug-ins to achieve process control customization, etc.". But don’t worry, even the technology we use every day, and most people know that if we can explain it more clearly and find some problems from it, make some optimizations to help future architecture design , Then most people can benefit from daily design and development, wouldn’t it be better. Now let's talk about the microkernel architecture design.

Operating system kernel designed by a microkernel

Microkernel design is actually a plug-in system. We all know that the operating system kernel was born relatively early, so plug-inization was first used in the kernel design, so the term microkernel design came into being.

A microkernel is a kind of kernel: it only completes the functions that the kernel has to complete, including clock interruption, process creation and destruction, process scheduling, inter-process communication, and other things such as file system, memory management, device drivers, etc. The system process is placed in the user space. To put it bluntly, the microkernel is relative to the macrokernel. Linux is a typical macrokernel. In addition to clock interruption, process creation and destruction, process scheduling, inter-process communication, other file systems, memory management, input and output , Device driver management needs to be completed by the kernel.

In other words, the micro-kernel is relative to the macro-kernel, which is a low-level program that contains very many functions, which is what we are talking about now. It does a lot of things and is not pluggable. Modifying some small functions will involve the recompilation of the entire program. For example, a small bug in a function may cause problems with the entire kernel. This is why many people call Linux monolithic OS. The micro-kernel is only responsible for the most core functions, and other functions are added in the form of plug-ins through user-mode independent processes, and then the micro-kernel is responsible for process management, scheduling, and communication between processes, thereby completing the functions required by the entire kernel. There is a problem with a basic function, but the function exists as an independent process and will not have any impact on other processes and cause the kernel to be unavailable. At most, a certain function of the kernel is unavailable now.

A microkernel is a program segment running at the highest level, which can perform some functions that a user mode program cannot. The microkernel uses inter-process communication to coordinate the cooperation between the various system processes, which requires system calls, and system calls need to switch the stack and protect the process scene, which is time-consuming; while the macro kernel completes each module through simple function calls Therefore, in theory, the efficiency of the macro kernel is higher than that of the micro kernel. This is the same as the microservice architecture design. After we divide the Monolith application into multiple small applications, the system design becomes more complicated. Before, it was all internal function calls of the application. Now it involves network communication, timeout and other issues. At the same time The response time will be lengthened.

Having talked here, I believe that everyone has a general understanding of the micro-kernel and the macro-kernel. But one of the biggest problems with the macro kernel is to customize and maintain the original copy. There are more and more mobile devices and IoT devices. If you want to adapt a huge and complex kernel to a device, it is a very complicated thing. If it is very simple, then adapt the Linux kernel to the Android kernel. , Even to Tesla and other in-vehicle systems, basically everyone can do it.

Therefore, we need a micro-kernel architecture design, which is convenient for customization, and is very small, which can realize hot replacement of functions or online updates, etc. This is the core requirement of the micro-kernel. But the micro-kernel has a problem of operating efficiency, so between the micro-kernel and the macro-kernel, there is a Hybrid kernel, mainly to have the flexibility of the micro-kernel, and at the same time, to have the performance of the macro-kernel at key points. Microkernel design does have efficiency problems in theory, but with chip design and hardware performance improvements, this aspect may have been greatly improved, and it is no longer the most critical issue.

In general, the kernel design has three forms, as follows:

Plug-in architecture design

I talked about the role of the microkernel in the design of the operating system kernel. Next, we will discuss more general plug-in architecture design. After all, everyone understands the term.

The plug-in architecture is very simple, with two core components: the core system and the plug-in component. Core System is responsible for managing various plug-ins. Of course, Core System will also include some important functions, such as plug-in registration management, plug-in life cycle management, communication between plug-ins, dynamic plug-in replacement, etc. The overall structure is as follows:

Plug-in architecture is very helpful to the design of microservice architecture. Considering isolation, plug-ins may run as independent processes. If these processes are extended to the network and distributed on many servers, this is the prototype of the microservice architecture. , So the students who know microkernel will not bother to discuss the microservice architecture with you. I believe you also understand that in addition to the traditional IT contempt chain factor, this is indeed the principle in principle.

Back to the microservice architecture design scenario, we renamed the Plug-in component to Service. This is similar to the service in the microkernel design. At this time, the microservice and the microkernel are almost the same, and both involve service registration, Communication between management and service, etc. Then let's take a look at how the microkernel solves the communication problem between services? The following is taken from Wikipedia:

Because all service processes run in different address spaces, under the micro-core architecture, function calls cannot be made directly like the macro-core. Under the micro-core architecture, an inter-process communication mechanism must be created to allow service processes to exchange messages with each other, call each other's services, and complete synchronization through a message passing mechanism. The use of a master-slave architecture makes it particularly advantageous in a distributed system, because the same set of inter-process communication mechanisms can be used between the remote system and the local process.

In other words, the message-based inter-process communication mechanism is adopted. Messages are the simplest. There are two interfaces: send and receive. The message is sent out, and then waits for the message to be received, and then the message is sent after processing. Everyone here should also know that this is asynchronous. Going back to the plug-in architecture design, the Plug-in component design includes interaction specifications, that is, the interface for communicating with the outside world. If it is based on message communication, it is the send and receive interfaces, which can be said to be very simple.

But there is another problem here, and that is inter-process communication. You may ask, what is the good question about this is that the two processes send messages to each other. But here is the biggest question, that is, is there any third party involved in the inter-process communication? As shown below:

Of course, in the kernel design of the operating system, it must be forwarded through the kernel, which is the bus architecture we understand. The kernel is responsible for coordinating the communication between various processes. Everyone can understand this. If process A is sent directly to another process B, it must know the corresponding memory address. The service in the microkernel can be replaced at any time. If the service is unavailable or replaced, notify and Is the other communication process too complicated? As mentioned earlier, there are only send and receive interfaces, and no other interfaces to notify offline or unavailable services. In the design of the microkernel, the process must send messages to the Kernel through the bus structure, and then the kernel sends the messages to the corresponding process. This is a bus design. In fact, many applications will use the EventBus structure when doing Plug-in component decoupling, which is actually the design mechanism of the bus.

Why do mothers-in-laws say this? Because it is very critical. Distributed process communication is the core of microservices. The service-to-service communication we understand is that service A starts the listening port, service B will establish a connection with service A, and then the two communicate. This method is different from the bus architecture design in which the kernel is responsible for message reception and forwarding in the microkernel design. For example, when using HTTP, HSF and other communication protocols, it is equivalent to the kernel telling the respective addresses of both parties in the communication, and then they can communicate. Then there is nothing Kernel, and no bus structure design is used. This is the traditional service discovery mechanism.

But there is another mode, which is a completely transparent plug-in communication mechanism, as shown in the following figure:

Plug-in components, that is, services in the microservice architecture, cannot communicate directly, but need to be forwarded by the Core System. The benefits of this are the same as the microkernel architecture. Plug-ins have no direct contact with each other, and they are very transparent. For example, after service A goes offline, there is no need to notify other services at all; service A is replaced, and there is no need to notify other services; Service A goes from data center 1 to data center 2, without notifying other services; even if the network between service N and service A is not interconnected, the two can communicate.

There is a problem here: performance issues. We all know that between two points, the straight line is the shortest. Why do we need to go around to Core System more? This is the contention between the microkernel and the macrokernel. The use of function calls is very fast, while the message communication between processes is very slow, but the benefits of this communication mechanism through an intermediary are also very obvious. So how to improve the performance of this bus-based communication? Of course, if you choose a high-performance binary protocol, you don’t need a text protocol like HTTP 1.1; using the Zero Copy mechanism, you can quickly forward network packets; good network hardware, such as RDMA; good protocols, such as UDP-based QUIC etc. In summary, just like the microkernel, the performance of this kind of microservice communication can be improved. Of course, if you really can't stand this kind of performance, in key scenarios, you can use Hybrid mode to mix in the design of direct communication between some services, but it can only be used in scenarios with extreme performance.

In addition, there are various plug-in components in the plug-in architecture, and the communication mechanisms are also different. Some are RPC, some are Pub/Sub, some do not require ACK (such as the Beacon interface), and some It is two-way communication and so on. Of course, you can choose different communication protocols, but there is a problem here, that is, Core System needs to understand this protocol before it can perform message routing. At this time, the Core System needs to write a large number of Adapters to parse these protocols. For example, Envoy contains various filters to support different protocols, such as HTTP, MySQL, ZooKeeper, etc., but the Core System will become very complicated and unstable.

In addition, you can choose a general protocol. Core System only supports this protocol. Each plug-in communicates based on this protocol. As for how the service communicates with other external systems, such as database and github integration, these Core Systems don’t care. , That's just something inside the Service. At present, the more common protocol is gRPC. For example, K8s will use this protocol internally. In addition, Dapr also uses gRPC protocol for service integration, because the communication model provided by gRPC can basically meet most communication scenarios. Of course, the other is RSocket, which provides a richer communication model and is also suitable for communication scenarios between services such as Core System. Compared with gRPC, RSocket can run on various transport layers, such as TCP, UDP, WebSocket, RDMA, etc. On the contrary, gRPC can only run on HTTP 2 at present.

Extension of three service communications

As mentioned earlier, it is best to use the Core System designed by the plug-in architecture as a route for message communication between services. If this is the case, a Broker mode will be produced, and of course it may also be an Agent. Everyone here will definitely think of Service Mesh, that's right. Of course, you can choose the Agent Sidecar mode or the centralized Broker mode. The functions of the two are the same, but the processing methods are different. Agent is based on the service registration and discovery mechanism, and then finds the agent serving the other party, and then communicates between the two agents, which just saves the cost of calling between services. But the Broker is centralized. Everyone sends and receives messages to the Broker. It does not involve the service registration discovery mechanism or the push of service meta-information, which is the bus structure.

What I am doing now is based on the architecture design of this Broker bus. In RSocket Broker, the micro-kernel architecture is also used. Of course, it may not be the best. The core of RSocket Broker is to manage registered services, routing management, data collection, etc., without adding too many functions. Like the design concept of Core System, only necessary functions are added. If you want to expand more functions of the entire system, such as sending text messages, sending emails, connecting to cloud storage services, etc., you need to write a Service, then connect with the broker, then receive the message from the broker, and send it after processing (Send) Just give it to Broker. The overall structure is as follows:

Many students will ask, when the load of the service instance is too high, how does Broker achieve dynamic expansion? Broker will provide you with data, such as a service instance QPS. As for whether to expand, you only need to write a service, collect data from Broker, and after analysis, call K8s API for expansion. Broker does not load these business functions. Only very necessary functions will be added, which is the same as the Core System design.

Going back to the flexibility of the plug-in architecture, if there is a KV storage plug-in in the system, you can save KV data as long as you follow the message format or communication interface. But you don’t care too much about whether it’s stored in Redis, stored in Tair, or in the KV service in the cloud. This provides a good foundation for service standardization and replaceability, which is very helpful for application to the cloud or cloud native. Big, the whole system has great flexibility.

Four summary

In fact, there are a lot of books about microkernels. Needless to say, books about operating systems. The other two books are also very good. They are also very helpful for general architecture design, especially for microservice scenarios. I also refer to this Two books for this article.

Microkernel architecture design has a very good reference for microservice design, but a very big problem of microservices is the division of service boundaries. Compared with operating systems, it has been developed for decades, and it is very stable, and the function division is very easy. The microservice architecture serves the business. Although the business faced may have existed for hundreds of years, software, digitization, and process have not been for many years. In addition to the complexity of the actual business, there are various compromises. I personally think The microservice architecture will be more complicated.

 

Original link

This article is the original content of Alibaba Cloud and may not be reproduced without permission.

Guess you like

Origin blog.csdn.net/weixin_43970890/article/details/113994268