当前位置: 首页 > 科技观察

什么是微内核架构设计?

时间:2023-03-12 18:40:01 科技观察

作为一名Java程序员,相信同学们都听说过微内核架构设计,也都有自己的理解。那么微内核是怎么提出来的呢?微内核在操作系统内核的设计中扮演什么角色?本文从插件架构的角度阐述微内核架构设计,通过微内核架构与微服务架构的对比,分享其对微服务设计的参考意义。微内核架构的设计现在比较火。听起来好像跟操作系统内核有关。作为Java程序员,远到操作系统内核的事情似乎与我们无关。但如果我说微内核其实是插件(Plug-in)架构,你肯定会一脸疑惑,“你居然给Java程序员解释什么是插件架构?我天天用,Eclipse,IntelliJIDEA、OSGi、SpringPlugin、SPI等,哪个不是插件架构,我的一些项目也是用插件设计的,比如用插件实现流程控制定制等。”不过不用担心,即使是我们日常使用的技术,而且大多数人都知道,如果我们能把它解释得更清楚,并能从中发现一些问题,做一些优化,对以后的架构设计有帮助,那么大多数人都能从中受益日常的设计开发,岂不是更好。现在让我们谈谈微内核架构设计。1.微内核设计操作系统内核微内核设计实际上是一个插件系统。我们都知道操作系统内核诞生的时间比较早,所以插件化最早用于内核设计,所以才有了微内核设计之名。微内核就是这样一个内核:它只完成内核必须完成的功能,包括时钟中断、进程创建和销毁、进程调度、进程间通信,其他如文件系统、内存管理、设备驱动等.被用作系统进程被放置在用户态空间。说白了,微内核是相对于宏内核而言的。例如,Linux就是一个典型的宏内核。除了时钟中断、进程创建和销毁、进程调度、进程间通信外,其他的文件系统、内存管理、输入输出、设备驱动管理都需要内核来完成。也就是说,微内核是相对于宏内核而言的。宏内核是一个底层程序,包含了很多功能,也就是我们现在说的Monolith。它做了很多事情,而且它是不可插拔的。修改一些小的功能会涉及到整个程序的重新编译。例如,函数中的一个小错误可能会导致整个内核出现问题。这就是为什么许多人称Linux为单体操作系统。微内核只负责最核心的功能,其他功能通过独立的用户态进程作为插件添加,再由微内核负责进程管理、调度以及进程间的通信,从而完成需要的功能整个内核。基本上是某个函数有问题,但是这个函数是作为一个独立的进程存在的,不会影响其他进程导致内核不可用。顶多现在内核的某个功能不可用了。微内核是运行在最高层的程序片段,可以完成一些用户态程序无法完成的功能。微内核通过进程间通信协调各个系统进程之间的协作,需要系统调用,系统调用需要切换栈和保护进程站点,耗时长;而宏内核通过简单的函数调用完成各个模块之间的协作,所以理论上宏内核的效率要高于微内核。这和微服务的架构设计是一样的。当我们将Monolith应用拆分成多个小应用后,系统的设计就变得更加复杂了。之前是应用程序内部的函数调用,现在涉及到网络通信、超时等问题。同时响应时间将增加。说到这里,相信大家对微内核和宏内核都有了一个大概的了解,看起来各有千秋。但是宏内核最大的问题之一是定制和维护。如今,移动设备和物联网设备越来越多。将庞大而复杂的内核适配到某个设备上是非常复杂的。如果很简单,那就把Linux内核适配到Android内核,甚至像Tesla这样的车载系统,基本上人人都能做到。因此,我们需要一种易于定制、体积很小、能够实现功能的热替换或在线更新的微内核架构设计。这是微内核的核心要求。但是微内核存在运行效率问题,所以在微内核和宏内核之间,有一个Hybridkernel,主要是有微内核的灵活性,同时在关键点上有宏内核的性能。理论上,微内核设计确实存在效率问题,但随着芯片设计和硬件性能的提升,这方面可能已经有了很大的改善,不再是最关键的问题。总的来说,内核设计有以下三种形式,如下:2.插件架构设计上面我们讲了微内核在操作系统内核设计中的作用,接下来我们将讨论更通用的插件架构设计。毕竟,每个人都明白这个词。插件架构非常简单,由两个核心组件组成:核心系统和插件组件。核心系统负责管理各种插件。当然,CoreSystem还包括一些重要的功能,如插件注册管理、插件生命周期管理、插件间通信、插件动态替换等。总体结构如下:inarchitecture对微服务架构的设计很有帮助。考虑到隔离性,插件可能作为一个独立的进程运行。如果把这些进程延伸到网络上,分布在很多服务器上,这就是微服务架构。服务架构的原型,让懂微内核的同学懒得跟大家讨论微服务架构。相信你明白了,除去IT传统的鄙视链因素,原则上确实是这样。回到微服务架构设计场景,我们将Plug-in组件重命名为Service(Service),类似于微内核设计中的服务。这时候微服务和微内核就差不多了,都涉及到服务注册、管理和服务之间的通信等等。那我们来看看微内核是怎么解决服务之间的通信问题的?以下摘自维基百科:由于所有服务进程运行在不同的地址空间,在微内核架构下,不能像宏内核那样直接进行函数调用。在微内核架构下,需要建立进程间通信机制,服务进程可以通过消息传递机制交换消息,调用对方的服务,完成同步。主从架构的使用使其在分布式系统中具有特殊的优势,因为远程系统和本地进程之间可以使用同一套进程间通信机制。也就是说,采用了基于消息的进程间通信机制。消息最简单,就两个接口:send和receive,发送消息,等待接收到消息,处理完再发送消息。这里大家应该知道这是异步的。回到插件架构设计,Plug-in组件设计包括交互规范,即与外界通信的接口。如果是基于消息通信,就是发送和接收接口,可以说是非常简单了。但是这里还有一个问题,就是进程间通信。你可能会问,这有什么值得怀疑的,因为这两个进程互相发送消息。但是这里有个最大的问题,就是是否有第三方参与进程间通信?如下图所示:当然,在操作系统的内核设计中,必须通过内核进行转发,也就是我们理解的总线架构,内核负责进程间的协调通信。每个人都可以理解这一点。如果进程A直接发送给另一个进程B,你必须知道对应的内存地址。微内核中的服务可以随时更换。如果服务不可用或被替换,你必须通知和它通信的其他进程是否太复杂?前面说了只有send和receive接口,没有其他通知下线或者服务不可用的接口。在微内核的设计中,进程向Kernel发送消息必须是通过总线结构,再由Kernel发送给相应的进程。这样的总线设计。其实很多应用在对Plug-in组件进行解耦的时候都会用到EventBus的结构,这其实就是总线的设计机制。为什么婆婆妈妈会这样说呢?因为它非常重要。分布式进程通信是微服务的核心。我们理解的服务到服务通信就是服务A开启监听端口,服务B会和服务A建立连接,然后两者就可以通信了。这种方式不同于微内核设计中内核负责接收和转发消息的总线架构设计。比如在使用HTTP、HSF等通信协议时,相当于内核通知了通信双方的地址,然后他们就可以相互通信了。然后就没有Kernel了,也没有采用总线结构设计。这是传统的服务发现机制。但是还有一种模式,就是完全透明的插件通信机制,如下图所示:插件组件,即微服务架构中的服务,不能直接通信,需要通过Core转发系统。这样做的好处和微内核架构一样。插件之间没有直接联系,彼此之间非常透明。比如服务A下线后,根本不需要通知其他服务;服务A被替换,不需要通知其他服务;服务A从数据中心1到数据中心2,没有通知其他服务;即使服务N和服务A之间的网络不可互操作,两者仍然可以通信。这里有一个问题:性能问题。我们都知道,两点之间,直线最短。为什么要绕过核心系统?这是微内核和宏内核之间的争论。使用函数调用速度非常快,而进程间的消息通信速度非常慢,但是这种通过中介的通信机制带来的好处也是非常明显的。那么如何提高这种基于总线的通信的性能呢?当然,比如选择高性能的二进制协议,就不需要HTTP1.1文本协议;零拷贝机制可用于快速转发网络数据包;好的网络硬件,比如RDMA;好的协议,比如基于UDP的QUIC等。总结一下,跟微内核一样,可以提高这种微服务通信的性能。当然,如果实在受不了这种性能,在关键场景下,可以使用Hybrid模式混入一些服务间直接通信的设计,但只能在性能极致的场景下使用。另外,插件架构中有各种插件组件,通信机制也各不相同。有的是RPC,有的是Pub/Sub,有的不需要ACK(比如Beacon接口),有的是双向通信等等。当然,你可以选择不同的通信协议,但这里有个问题,就是CoreSystem需要理解这个协议,才能进行消息路由。这时候CoreSystem就需要编写大量的Adapter来解析这些协议。例如,Envoy包含各种过滤器以支持不同的协议,如HTTP、MySQL、ZooKeeper等,但CoreSystem会变得非常复杂和不稳定。此外,您还可以选择通用协议。核心系统只支持该协议,所有插件都基于该协议进行通信。至于Service如何与其他外部系统通信,比如数据库、github集成等,这些CoreSystems并不关心,那只是Service内部的事情。目前比较常见的协议是gRPC。比如K8s内部就使用了这个协议。此外,Dapr还使用gRPC协议进行服务集成,因为gRPC提供的通信模型基本可以满足大部分通信场景。当然还有一个就是RSocket,它提供了更丰富的通信模型,同样适用于CoreSystem等服务间通信的场景。与gRPC相比,RSocket可以运行在各种传输层上,如TCP、UDP、WebSocket、RDMA等。相反,gRPC目前只运行在HTTP2上。3.服务通信的扩展前面说了,最好使用插件架构设计的CoreSystem作为服务间消息通信的路由。如果是这种情况,就会产生Broker模式,当然也有可能是Agent。在座各位肯定会想到ServiceMesh,没错。当然你可以选择AgentSidecar模式或者中心化Broker模式。两者的功能相同,只是处理方法不同。代理是基于服务注册和发现机制,然后找到为对方服务的代理,然后在两个代理之间进行通信,只是节省了服务之间调用的开销。但是,Broker是中心化的。每个人都向Broker发送和接收消息。不涉及服务注册发现机制,也不涉及服务元信息的推送。是总线结构。我现在做的就是基于这个Broker总线的架构设计。在RSocketBroker中,也是采用微内核架构设计的。当然,它可能不是最好的。RSocketBroker的核心是管理注册服务、路由管理、数据采集等,没有增加太多的功能。与CoreSystem的设计理念相同,只是增加了必要的功能。如果想扩展整个系统更多的功能,比如发送短信,发送邮件,连接云存储服务等,需要写一个Service,然后连接Broker,接收Broker的消息,以及处理后将它们发送(发送)给Broker就可以了。整体结构如下:很多同学会问,当服务实例负载过高时,Broker如何实现动态扩容?Broker会为你提供数据,比如一个服务实例的QPS。至于是否扩容,只需要写一个Service,从Broker收集数据,分析后,调用K8sAPI扩容,Broker不加载这些业务功能,只增加非常必要的功能,这点和Core一样系统设计。回到插件架构的灵活性上,如果系统中有KV存储插件,只要按照报文格式或者通信接口,就可以保存KV数据。但是你不用太在意是存储在云端的Redis,Tair,还是KV服务中。这为服务的标准化和可替换性提供了良好的基础,这对云上或云原生的应用程序非常有帮助。大,整个系统具有很大的灵活性。4.总结其实介绍微内核的书籍有很多。操作系统的书就不用说了,另外两本书也很不错,对于一般的架构设计,尤其是微服务场景,也很有帮助。这篇文章也是参考这两本书写的。微内核架构设计对于微服务设计有很好的借鉴意义,但是微服务一个非常大的问题就是服务边界的划分。相对于操作系统,已经发展了几十年,非常稳定,功能划分也很容易。微服务架构服务于业务。虽然它所面对的业务可能已经存在了数百年,但它已经很多年没有软件化、数字化和流程化了。除了真实业务的复杂性和各种妥协之外,我个人认为微服务架构更复杂一些。