什么是TOE技术?

什么是TOE技术?,第1张

TCP/IP协议早已是网络的标准语言。随着Internet SCSI、Remote Diret Memory Access这些网络存贮标准的问世和实用化,从某种意义上说,TCP/IP又成了一种存贮协议。

我们知道,用TCP/IP协议处理网络流量,要占用大量服务器资源。为了减轻服务器的压力,一种称为TCP减负引擎(TCP Offload Engine :TOE)的技术应运而生。TCP减负引擎一般由软硬两部分组件构成,将传统的TIP/IP协议栈的功能进行延伸,把网络数据流量的处理工作全部转到网卡上的集成硬件中进行,服务器只承担TCP/IP控制信息的处理任务。这种为服务器减轻负担的技术,得到了大多数厂商的肯定。

普通网卡用软件方式进行一系列TCP/IP相关 *** 作,因此,会在三个方面增加服务器的负担,这三个方面是:数据复制、协议处理和中断处理。

  网络上每个应用程序在收发大量数据包时,要引发大量的网络I/O中断,对这些I/O中断信号进行响应,成了服务器的沉重负担。比如,一个典型的64Kbps的应用程序在向网络发送数据时,为了将这些数据装配成以太网的数据包,并对网络接收确认信号进行响应,要在服务器和网卡间触发60多个中断事件,这么高的中断率和协议分析工作量已经是相当可观的了。虽然某些网络 *** 作系统具有中断捆绑功能,能够有效减少中断信号的产生,但却无法减少服务器和网卡间响应事件的处理总量。

TCP减负引擎网卡的工作原理与普通网卡不同。普通网卡处理每个数据包都要触发一次中断,TCP减负引擎网卡则让每个应用程序完成一次完整的数据处理进程后才触发一次中断,显著减轻服务器对中断的响应负担。还是以64Kbps的应用程序为例,应用程序向网络发送数据全部完成后,才向服务器发送一个数据通道减负事件中断,数据包的处理工作由TCP减负引擎网卡来做,而不是由服务器来做,从而消除了过于频繁的中断事件对服务器的过度干扰。网络应用程序在收发数据时,经常是同一数据要复制多份,在这种情形下,TCP减负引擎网卡发挥的效益最明显。

普通网卡通过采用支持校验功能的硬件和某些软件,能够在一定程度上减少发送数据的复制量,但却无法减少接收数据的复制量。对大量接收数据进行复制通常要占用大量的机器工作周期。普通网卡先将接收到的数据在服务器的缓冲区中复制一份,经系统处理后分配给其中一个TCP连接,然后,系统再将这些数据与使用它的应用程序相关联,并将这些数据由系统缓冲区复制到应用程序的缓冲区。TCP减负引擎网卡在接收数据时,在网卡内进行协议处理,因此,它不必将数据复制到服务器缓冲区,而是直接复制到应用程序的缓冲区,这种“零拷贝”方式避免了网卡和服务器间的不必要的数据往复拷贝。

TCP减负引擎网卡能显著减轻由数据大量移动造成的服务器过载负担。实测证明,对于文件服务器和以内容服务为主的服务器应用环境来说,如果用TCP减负引擎网卡代替普通网卡,相当于为服务器增加了一个CPU。

姓名:周肇星;学号:22011110028;学院:通信工程学院

嵌牛导读DPDK是INTEL公司开发的一款高性能的网络驱动组件,旨在为数据面应用程序提供一个简单方便的,完整的,快速的数据包处理解决方案,主要技术有用户态、轮询取代中断、零拷贝、网卡RSS、访存DirectIO等

嵌牛鼻子计算机网络,高性能网络,DPDK

嵌牛提问读完本文,对DPDK技术的helloworld程序有所认识了吗?

嵌牛正文

线程运行入口是main函数,调用了rte_eal_init入口函数,以启动基础运行环境

argc和argv参数传递给rte_eal_init函数,返回值是解析的参数个数

对于DPDK库的使用者而言,初始化 *** 作已经被EAL封装起来,其初始化主要动作包括:

DPDK面向多核设计,程序会试图独占运行在逻辑核(lcore)上

RTE_LCORE_FOREACH_WORKER(lcore_id)遍历所有EAL指定可以使用的lcore

遍历时,在每个lcore上通过rte_eal_remote_launch(lcore_hello, NULL, lcore_id)启动被指定的线程:

通过rte_lcore_id函数读取自己的逻辑核编号,并打印出来

1性能和时延问题在服务化之前,业务通常都是本地API调用,本地方法调用性能损耗较小。服务化之后,服务提供者和消费者之间采用远程网络通信,增加了额外的性能损耗:1)客户端需要对消息进行序列化,主要占用CPU计算资源。2)序列化时需要创建二进制数组,耗费JVM堆内存或者堆外内存。3)客户端需要将序列化之后的二进制数组发送给服务端,占用网络带宽资源。4)服务端读取到码流之后,需要将请求数据报反序列化成请求对象,占用CPU计算资源。5)服务端通过反射的方式调用服务提供者实现类,反射本身对性能影响就比较大。6)服务端将响应结果序列化,占用CPU计算资源。7)服务端将应答码流发送给客户端,占用网络带宽资源。8)客户端读取应答码流,反序列化成响应消息,占用CPU资源。通过分析我们发现,一个简单的本地方法调用,切换成远程服务调用之后,额外增加了很多处理流程,不仅占用大量的系统资源,同时增加了时延。一些复杂的应用会拆分成多个服务,形成服务调用链,如果服务化框架的性能比较差、服务调用时延也比较大,业务服务化之后的性能和时延将无法满足业务的性能需求。11 RPC框架高性能设计影响RPC框架性能的主要因素有三个。1)I/O调度模型:同步阻塞I/O(BIO)还是非阻塞I/O(NIO)。2)序列化框架的选择:文本协议、二进制协议或压缩二进制协议。3)线程调度模型:串行调度还是并行调度,锁竞争还是无锁化算法。1I/O调度模型在I/O编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。JDK15_update10版本使用epoll替代了传统的select/poll,极大地提升了NIO通信的性能,它的工作原理如图1-1所示。图1-1 非阻塞I/O工作原理Netty是一个开源的高性能NIO通信框架:它的I/O线程NioEventLoop由于聚合了多路复用器Selector,可以同时并发处理成百上千个客户端Channel。由于读写 *** 作都是非阻塞的,这就可以充分提升I/O线程的运行效率,避免由于频繁I/O阻塞导致的线程挂起。另外,由于Netty采用了异步通信模式,一个I/O线程可以并发处理N个客户端连接和读写 *** 作,这从根本上解决了传统同步阻塞I/O一连接一线程模型,架构的性能、d性伸缩能力和可靠性都得到了极大的提升。Netty被精心设计,提供了很多独特的性能提升特性,使它做到了在各种NIO框架中性能排名第一,它的性能优化措施总结如下。1)零拷贝:(1)Netty的接收和发送ByteBuffer采用DIRECTBUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAPBUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。(2)Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像 *** 作一个Buffer那样方便地对组合Buffer进行 *** 作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。(3)Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。2)内存池:随着JVM虚拟机和JIT即时编译技术的发展,对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer,情况却稍有不同,特别是对于堆外直接内存的分配和回收,是一件耗时的 *** 作。为了尽量重用缓冲区,Netty提供了基于内存池的缓冲区重用机制。性能测试表明,采用内存池的ByteBuf相比于朝生夕灭的ByteBuf,性能高23倍左右(性能数据与使用场景强相关)。3)无锁化的串行设计:在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不当,会带来严重的锁竞争,这最终会导致性能的下降。为了尽可能地避免锁竞争带来的性能损耗,可以通过串行化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。为了尽可能提升性能,Netty采用了串行无锁化设计,在I/O线程内部进行串行 *** 作,避免多线程竞争导致的性能下降。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。4)高效的并发编程:volatile的大量、正确使用;CAS和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。2高性能序列化框架影响序列化性能的关键因素总结如下。1)序列化后的码流大小(网络带宽的占用)。2)序列化&反序列化的性能(CPU资源占用)。3)是否支持跨语言(异构系统的对接和开发语言切换)。4)并发调用的性能表现:稳定性、线性增长、偶现的时延毛刺等。相比于JSON等文本协议,二进制序列化框架性能更优异,以Java原生序列化和Protobuf二进制序列化为例进行性能测试对比,结果如图1-2所示。图1-2 序列化性能测试对比数据在序列化框架的技术选型中,如无特殊要求,尽量选择性能更优的二进制序列化框架,码流是否压缩,则需要根据通信内容做灵活选择,对于、音频、有大量重复内容的文本文件(例如小说)可以采用码流压缩,常用的压缩算法包括GZip、Zig-Zag等。3高性能的Reactor线程模型该模型的特点总结如下。1)有专门一个NIO线程:Acceptor线程用于监听服务端,接收客户端的TCP连接请求。2)网络I/O *** 作:读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送。3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止产生并发 *** 作。由于Reactor模式使用的是异步非阻塞I/O,所有的I/O *** 作都不会导致阻塞,理论上一个线程可以独立处理所有I/O相关的 *** 作,因此在绝大多数场景下,Reactor多线程模型都可以完全满足业务性能需求。Reactor线程调度模型的工作原理示意如图1-3所示。图1-3 高性能的Reactor线程调度模型12 业务最佳实践要保证高性能,单依靠分布式服务框架是不够的,还需要应用的配合,应用服务化高性能实践总结如下:1)能异步的尽可能使用异步或者并行服务调用,提升服务的吞吐量,有效降低服务调用时延。2)无论是NIO通信框架的线程池还是后端业务线程池,线程参数的配置必须合理。如果采用JDK默认的线程池,最大线程数建议不超过20个。因为JDK的线程池默认采用N个线程争用1个同步阻塞队列方式,当线程数过大时,会导致激烈的锁竞争,此时性能不仅不会提升,反而会下降。3)尽量减小要传输的码流大小,提升性能。本地调用时,由于在同一块堆内存中访问,参数大小对性能没有任何影响。跨进程通信时,往往传递的是个复杂对象,如果明确对方只使用其中的某几个字段或者某个对象引用,则不要把整个复杂对象都传递过去。举例,对象A持有8个基本类型的字段,2个复杂对象B和C。如果明确服务提供者只需要用到A聚合的C对象,则请求参数应该是C,而不是整个对象A。4)设置合适的客户端超时时间,防止业务高峰期因为服务端响应慢导致业务线程等应答时被阻塞,进而引起后续其他服务的消息在队列中排队,造成故障扩散。5)对于重要的服务,可以单独部署到独立的服务线程池中,与其他非核心服务做隔离,保障核心服务的高效运行。6)利用Docker等轻量级OS容器部署服务,对服务做物理资源层隔离,避免虚拟化之后导致的超过20%的性能损耗。7)设置合理的服务调度优先级,并根据线上性能监控数据做实时调整。2事务一致性问题服务化之前,业务采用本地事务,多个本地SQL调用可以用一个大的事务块封装起来,如果某一个数据库 *** 作发生异常,就可以将之前的SQL *** 作进行回滚,只有所有SQL *** 作全部成功,才最终提交,这就保证了事务强一致性,如图2-1所示。服务化之后,三个数据库 *** 作可能被拆分到独立的三个数据库访问服务中,此时原来的本地SQL调用演变成了远程服务调用,事务一致性无法得到保证,如图2-2所示。图2-2 服务化之后引入分布式事务问题假如服务A和服务B调用成功,则A和B的SQL将会被提交,最后执行服务C,它的SQL *** 作失败,对于应用1消费者而言,服务A和服务B的相关SQL *** 作已经提交,服务C发生了回滚,这就导致事务不一致。从图2-2可以得知,服务化之后事务不一致主要是由服务分布式部署导致的,因此也被称为分布式事务问题。21 分布式事务设计方案通常,分布式事务基于两阶段提交实现,它的工作原理示意图如图2-3所示。图2-3 两阶段提交原理图阶段1:全局事务管理器向所有事务参与者发送准备请求;事务参与者向全局事务管理器回复自己是否准备就绪。阶段2:全局事务管理器接收到所有事务参与者的回复之后做判断,如果所有事务参与者都可以提交,则向所有事务提交者发送提交申请,否则进行回滚。事务参与者根据全局事务管理器的指令进行提交或者回滚 *** 作。分布式事务回滚原理图如图2-4所示。图2-4 分布式事务回滚原理图两阶段提交采用的是悲观锁策略,由于各个事务参与者需要等待响应最慢的参与者,因此性能比较差。第一个问题是协议本身的成本:整个协议过程是需要加锁的,比如锁住数据库的某条记录,且需要持久化大量事务状态相关的 *** 作日志。更为麻烦的是,两阶段锁在出现故障时表现出来的脆弱性,比如两阶段锁的致命缺陷:当协调者出现故障,整个事务需要等到协调者恢复后才能继续执行,如果协调者出现类似磁盘故障等错误,该事务将被永久遗弃。对于分布式服务框架而言,从功能特性上需要支持分布式事务。在实际业务使用过程中,如果能够通过最终一致性解决问题,则不需要做强一致性;如果能够避免分布式事务,则尽量在业务层避免使用分布式事务。22 分布式事务优化既然分布式事务有诸多缺点,那么为什么我们还在使用呢?有没有更好的解决方案来改进或者替换呢?如果我们只是针对分布式事务去优化的话,发现其实能改进的空间很小,毕竟瓶颈在分布式事务模型本身。那我们回到问题的根源:为什么我们需要分布式事务?因为我们需要各个资源数据保持一致性,但是对于分布式事务提供的强一致性,所有业务场景真的都需要吗?大多数业务场景都能容忍短暂的不一致,不同的业务对不一致的容忍时间不同。像银行转账业务,中间有几分钟的不一致时间,用户通常都是可以理解和容忍的。在大多数的业务场景中,我们可以使用最终一致性替代传统的强一致性,尽量避免使用分布式事务。在实践中常用的最终一致性方案就是使用带有事务功能的MQ做中间人角色,它的工作原理如下:在做本地事务之前,先向MQ发送一个prepare消息,然后执行本地事务,本地事务提交成功的话,向MQ发送一个commit消息,否则发送一个rollback消息,取消之前的消息。MQ只会在收到commit确认才会将消息投递出去,所以这样的形式可以保证在一切正常的情况下,本地事务和MQ可以达到一致性。但是分布式调用存在很多异常场景,诸如网络超时、VM宕机等。假如系统执行了local_tx()成功之后,还没来得及将commit消息发送给MQ,或者说发送出去由于网络超时等原因,MQ没有收到commit,发生了commit消息丢失,那么MQ就不会把prepare消息投递出去。MQ会根据策略去尝试询问(回调)发消息的系统(checkCommit)进行检查该消息是否应该投递出去或者丢弃,得到系统的确认之后,MQ会做投递还是丢弃,这样就完全保证了MQ和发消息的系统的一致性,从而保证了接收消息系统的一致性。3研发团队协作问题服务化之后,特别是采用微服务架构以后。研发团队会被拆分成多个服务化小组,例如AWS的TwoPizzaTeam,每个团队由2~3名研发负责服务的开发、测试、部署上线、运维和运营等。随着服务数的膨胀,研发团队的增多,跨团队的协同配合将会成为一个制约研发效率提升的因素。31 共用服务注册中心为了方便开发测试,经常会在线下共用一个所有服务共享的服务注册中心,这时,一个正在开发中的服务发布到服务注册中心,可能会导致一些消费者不可用。解决方案:可以让服务提供者开发方,只订阅服务(开发的服务可能依赖其他服务),而不注册正在开发的服务,通过直连测试正在开发的服务。它的工作原理如图3-1所示。图3-1 只订阅,不发布32 直连提供者在开发和测试环境下,如果公共的服务注册中心没有搭建,消费者将无法获取服务提供者的地址列表,只能做本地单元测试或使用模拟桩测试。还有一种场景就是在实际测试中,服务提供者往往多实例部署,如果服务提供者存在Bug,就需要做远程断点调试,这会带来两个问题:1)服务提供者多实例部署,远程调试地址无法确定,调试效率低下。2)多个消费者可能共用一套测试联调环境,断点调试过程中可能被其他消费者意外打断。解决策略:绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式将以服务接口为单位,忽略注册中心的提供者列表。33 多团队进度协同假如前端Web门户依赖后台A、B、C和D4个服务,分别由4个不同的研发团队负责,门户要求新特性2周内上线。A和B内部需求优先级排序将门户的优先级排的比较高,可以满足交付时间点。但是C和D服务所在团队由于同时需要开发其他优先级更高的服务,因此把优先级排的相对较低,无法满足2周交付。在C和D提供版本之前,门户只能先通过打测试桩的方式完成Mock测试,但是由于并没有真实的测试过C和D服务,因此需求无法按期交付。应用依赖的服务越多,特性交付效率就越低下,交付的速度取决于依赖的最迟交付的那个服务。假如Web门户依赖后台的100个服务,只要1个核心服务没有按期交付,则整个进度就会延迟。解决方案:调用链可以将应用、服务和中间件之间的依赖关系串接并展示出来,基于调用链首入口的交付日期作为输入,利用依赖管理工具,可以自动计算出调用链上各个服务的最迟交付时间点。通过调用链分析和标准化的依赖计算工具,可以避免人为需求排序失误导致的需求延期。34 服务降级和Mock测试在实际项目开发中,由于小组之间、个人开发者之间开发节奏不一致,经常会出现消费者等待依赖的服务提供者提供联调版本的情况,相互等待会降低项目的研发进度。解决方案:服务提供者首先将接口定下来并提供给消费者,消费者可以将服务降级同Mock测试结合起来,在Mock测试代码中实现容错降级的业务逻辑(业务放通),这样既完成了Mock测试,又实现了服务降级的业务逻辑开发,一举两得。35 协同调试问题在实际项目开发过程中,各研发团队进度不一致很正常。如果消费者坐等服务提供者按时提供版本,往往会造成人力资源浪费,影响项目进度。解决方案:分布式服务框架提供Mock桩管理框架,当周边服务提供者尚未完成开发时,将路由切换到模拟测试模式,自动调用Mock桩;业务集成测试和上线时,则要能够自动切换到真实的服务提供者上,可以结合服务降级功能实现。36 接口前向兼容性由于线上的Bug修复、内部重构和需求变更,服务提供者会经常修改内部实现,包括但不限于:接口参数变化、参数字段变化、业务逻辑变化和数据表结构变化。在实际项目中经常会发生服务提供者修改了接口或者数据结构,但是并没有及时知会到所有消费者,导致服务调用失败。解决方案:1)制定并严格执行《服务前向兼容性规范》,避免发生不兼容修改或者私自修改不通知周边的情况。2)接口兼容性技术保障:例如Thrift的IDL,支持新增、修改和删除字段,字段定义位置无关性,码流支持乱序等。4总结服务化之后,无论是服务化框架,还是业务服务,都面临诸多挑战,本章摘取了其中一些比较重要的问题,并给出解决方案和最佳实践。对于本章节没有列出的问题,则需要服务框架开发者和使用者在实践中探索,找出一条适合自己产品的服务化最佳实践。

  第一大优势:技术先进

  专有实时 *** 作内核 自主开发

  采用基于底层网络协议,紧凑实时

  保证系统的安全性、高效性和集成性

  专用的驱动和实时内存管理

  专用的嵌入式安全中间件 自主开发的web中间件和数据库中间件

  具备体积小、高效率、高开放性的特点

  考虑了性能、开放性和空间的关系

  充分利用多进程、共享内存技术和多种缓冲技术保证速度

  各种专利算法 触发式七级分配缓冲算法

  三维动态协议匹配算法

  分层的令牌桶过滤器算法

  数据包过滤索引算法

  文件HASH存储算法

  IP数据包快速解析和缓存算法

  各种独特技术 反向触发式远程协助技术

  基于DMA的零拷贝技术

  动态协议识别技术

  第二大优势:国内最全的非法网址库

  70多个分类,最全的暴力、色情、间谍、病毒网站库,并可自动学习,不断更新,有效保护网络

  ·最实用的协议特征库

  15大分类100种小分类全面的p2p、IM、流媒体策略

  ·强大的QOS管理特征

  人员、部门、策略、文件类型、网站、ip、服务任何特征都可流控

  第三大优势:最高可部署性,安装方便

  ·旁路、网桥、NAT、代理

  ·可分布\集中\堆叠\热备、多路

  ·高可扩展性

  第四大优势:最全的认证授权方式

  ·近20种绑定方式: Ip mac hostname ldap local radius AD httpauth Usb-key 客户端程序

  ·最好完全无缝的一次验证技术支持:AD httpauth pop3

  第五大优势:强大的数据中心和多样的数据存储备份方式

  ·强大的数据冗于能力:1) 审记中心方便使用 2) 唯一同时具备内置和外置的数据中心,满足任何机构长时间海量数据存储的要求

  ·强大的搜索和导出能力:1) 任何元素的准确搜索 2) 支持搜索引擎式的模糊搜索

  3)支持包括支持doc\ppt\txt\excel等的内容全文检索 4)支持ftp、文件共享、pop3接口等多种备份方式

  第六大优势:资质最全

  ·“金盾网络行为管理系统”软件著作权

  ·“金盾网络行为管理系统”软件产品认证

  ·上海市计算机软件重点实验室的测评报告

  ·公安部三所的计算机安全产品测评报告

  ·公安部计算机安全产品销售许可证

  ·信息产业部软件企业认证

  ·2007年度网管员世界编辑选择奖

  ·2007年度中国安全信息网编辑推荐奖

  ·公安部互联网审计管理系统认证和销售许可证

  ·国家保密局涉密信息系统产品检测证书

  ·2008年度国家中小企业科技创新基金支持

  ·2009网管员世界最值得推荐产品

  ·2008年计算机安全防护优秀解决方案

  第七大特点:久经考验

  数千家优质用户选择金盾,多种复杂网络环境的考验,使宝创占据了网络行为管理中、高端市场的第一把交椅!!

页缺失(英语:Page fault,又名硬错误、硬中断、分页错误、寻页缺失、缺页中断、页故障等),指的是当软件试图访问已映射在虚拟地址空间中,但是并未被加载在物理内存中的一个分页时,由中央处理器的内存管理单元所发出的中断。

通常情况下,用于处理此中断的程序是 *** 作系统的一部分。如果 *** 作系统判断此次访问是有效的,那么 *** 作系统会尝试将相关的分页从硬盘上的虚拟内存文件中调入内存。而如果访问是不被允许的,那么 *** 作系统通常会结束相关的进程。

虽然其名为“页缺失”错误,但实际上这并不一定是一种错误。而且这一机制对于利用虚拟内存来增加程序可用内存空间的 *** 作系统(比如Microsoft Windows和各种类Unix系统)中都是常见且有必要的。

VA:Virtual Address 虚拟地址

PA:Physical Address 物理地址

MMU:Memory Manage Unit 内存管理单元

TLB:Translation Lookaside Buffer 旁路快表缓存/地址变换高速缓存

PTE:Page Table Entry 分页表项

CPU通过地址总线可以访问连接在地址总线上的所有外设,包括物理内存、IO设备等等,但从CPU发出的访问地址并非是这些外设在地址总线上的物理地址,而是一个虚拟地址,由MMU将虚拟地址转换成物理地址再从地址总线上发出,MMU上的这种虚拟地址和物理地址的转换关系是需要创建的,并且MMU还可以设置这个物理页是否可以进行写 *** 作,当没有创建一个虚拟地址到物理地址的映射,或者创建了这样的映射,但那个物理页不可写的时候,MMU将会通知CPU产生了一个缺页异常。

只有程序运行时用到了才去内存中寻找虚拟地址对应的页帧,找不到才可能进行分配,这就是内存的惰性(延时)分配机制。

对于一个运行中的进程来说,不是所有的虚拟地址在物理内存中都有对应的页。虚拟地址空间根据固定大小一般是4KB进行划分,物理内存可以设置不同的页面大小,通常物理页大小和虚拟页大小是一样的,都是 4KB。

CPU并不直接和物理内存打交道,而是把地址转换的活外包给了MMU,MMU是一种硬件电路,其速度很快,主要工作是进行内存管理,地址转换只是它承接的业务之一。

一起看看MMU是如何搞定地址转换的。

每个进程都会有自己的页表Page Table,页表存储了进程中虚拟地址到物理地址的映射关系,所以就相当于一张地图,MMU收到CPU的虚拟地址之后开始查询页表,确定是否存在映射以及读写权限是否正常,如图:

对于4GB的虚拟地址且大小为4KB页,一级页表将有2^20个表项,页表占有连续内存并且存储空间大,多级页表可以有效降低页表的存储空间以及内存连续性要求,但是多级页表同时也带来了查询效率问题。

我们以2级页表为例,MMU要先进行两次页表查询确定物理地址,在确认了权限等问题后,MMU再将这个物理地址发送到总线,内存收到之后开始读取对应地址的数据并返回。

MMU在2级页表的情况下进行了2次检索和1次读写,那么当页表变为N级时,就变成了N次检索+1次读写。

可见,页表级数越多查询的步骤越多,对于CPU来说等待时间越长,效率越低,这个问题还需要优化才行。

CPU觉得MMU干活虽然卖力气,但是效率有点低。有没有提升效率的办法呢?

我们知道 CPU 用的数据经常是一小搓,但是每次MMU都还要重复之前的步骤来检索,害,就知道埋头干活了,也得讲究方式方法呀!

找到瓶颈之后,MMU引入了新武器,江湖人称快表的TLB(其实,就是缓存),别看TLB容量小,但是正式上岗之后干活还真是不含糊。

当CPU给MMU传新虚拟地址之后,MMU先去问TLB那边有没有,如果有就直接拿到物理地址发到总线给内存,齐活。

TLB容量比较小,难免发生Cache Miss,这时候MMU还有保底的老武器页表 Page Table,在页表中找到之后MMU除了把地址发到总线传给内存,还把这条映射关系给到TLB,让它记录一下刷新缓存。

TLB容量不满的时候就直接把新记录存储了,当满了的时候就开启了淘汰大法把旧记录清除掉,来保存新记录,彷佛完美解决了问题。

TLB的容量毕竟有限,为此必须依靠Page Table一起完成TLB Miss情况的查询,并且更新到TLB建立新映射关系。

设想,CPU给MMU的虚拟地址,在TLB和 Page Table都没有找到对应的物理页帧,该怎么办呢?

没错,这就是缺页异常Page Fault,它是一个由硬件中断触发的可以由软件逻辑纠正的错误。

假如目标内存页在物理内存中没有对应的页帧或者存在但无对应权限,CPU 就无法获取数据,这种情况下CPU就会报告一个缺页错误。

由于CPU没有数据就无法进行计算,CPU罢工了用户进程也就出现了缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的 Page Fault Handler 处理。

缺页异常并不可怕,只要CPU要的虚拟地址经过MMU的一番寻址之后没有找到或者找到后无权限,就会出现缺页异常,因此触发异常后的处理流程将是重点内容。

缺页中断会交给PageFaultHandler处理,其根据缺页中断的不同类型会进行不同的处理:

page cache用于缓存文件的页数据,buffer cache用于缓存块设备(如磁盘)的块数据。页是逻辑上的概念,因此page cache是与文件系统同级的;块是物理上的概念,因此buffer cache是与块设备驱动程序同级的。

page cache与buffer cache的共同目的都是加速数据I/O:写数据时首先写到缓存,将写入的页标记为dirty,然后向外部存储flush,也就是缓存写机制中的write-back(另一种是write-through,Linux未采用);读数据时首先读取缓存,如果未命中,再去外部存储读取,并且将读取来的数据也加入缓存。 *** 作系统总是积极地将所有空闲内存都用作page cache和buffer cache,当内存不够用时也会用LRU等算法淘汰缓存页。

page cache中的每个文件都是一棵基数树(radix tree,本质上是多叉搜索树),树的每个节点都是一个页。根据文件内的偏移量就可以快速定位到所在的页,如下图所示。

IO 会产生page cache ,具体过程参考下图:

首先往用户缓冲区 buffer(这是 Userspace Page) 写入数据,然后 buffer 中的数据拷贝到内核缓冲区(这是 Pagecache Page),如果内核缓冲区中还没有这个 Page,就会发生 Page Fault 会去分配一个 Page,拷贝结束后该 Pagecache Page 是一个 Dirty Page(脏页),然后该 Dirty Page 中的内容会同步到磁盘,同步到磁盘后,该 PageCache Page 变为 Clean Page 并且继续存在系统中。

Kafka为什么不自己管理缓存,而非要用page cache?原因有如下三点:

Kafka三大件(broker、producer、consumer)与page cache的关系可以用下面的简图来表示。

producer生产消息时,会使用 pwrite() 系统调用(对应到Java NIO中是FileChannelwrite() API),按偏移量写入数据,并且都会先写入page cache里。consumer消费消息时,会使用sendfile()系统调用(对应FileChanneltransferTo() API),零拷贝地将数据从page cache传输到broker的Socket buffer,再通过网络传输。

https://blogcsdnnet/sinat_22338935/article/details/114320664

https://wwwjianshucom/p/92f33aa0ff52

  Kafka是一个由Scala和Java编写的企业级的消息发布和订阅系统,最早是由Linkedin公司开发,最终开源到Apache软件基金会的项目。Kafka是一个分布式的,支持分区的,多副本的和多订阅者的高吞吐量的消息系统,被广泛应用在应用解耦、异步处理、限流削峰和消息驱动等场景。本文将针对Kafka的架构和相关组件进行简单的介绍。在介绍Kafka的架构之前,我们先了解一下Kafk的核心概念。

  在详细介绍Kafka的架构和基本组件之前,需要先了解一下Kafka的一些核心概念。

Producer: 消息的生产者,负责往Kafka集群中发送消息;

Consumer: 消息的消费者,主动从Kafka集群中拉取消息。

Consumer Group: 每个Consumer属于一个特定的Consumer Group,新建Consumer的时候需要指定对应的Consumer Group ID。

Broker: Kafka集群中的服务实例,也称之为节点,每个Kafka集群包含一个或者多个Broker(一个Broker就是一个服务器或节点)。

Message: 通过Kafka集群进行传递的对象实体,存储需要传送的信息。

Topic: 消息的类别,主要用于对消息进行逻辑上的区分,每条发送到Kafka集群的消息都需要有一个指定的Topic,消费者根据Topic对指定的消息进行消费。

Partition: 消息的分区,Partition是一个物理上的概念,相当于一个文件夹,Kafka会为每个topic的每个分区创建一个文件夹,一个Topic的消息会存储在一个或者多个Partition中。

Segment: 一个partition当中存在多个segment文件段(分段存储),每个Segment分为两部分,log文件和 index 文件,其中 index 文件是索引文件,主要用于快速查询log 文件当中数据的偏移量位置;

log文件: 存放Message的数据文件,在Kafka中把数据文件就叫做日志文件。一个分区下面默认有n多个log文件(分段存储)。一个log文件大默认1G,消息会不断追加在log文件中,当log文件的大小超过1G的时候,会自动新建一个新的log文件。

index文件: 存放log文件的索引数据,每个index文件有一个对应同名的log文件。

  后面我们会对上面的一些核心概念进行更深入的介绍。在介绍完Kafka的核心概念之后,我们来看一下Kafka的对外提供的基本功能,组件及架构设计。

  如上图所示,Kafka主要包含四个主要的API组件:

1 Producer API

  应用程序通过Producer API向Kafka集群发送一个或多个Topic的消息。

2 Consumer API

  应用程序通过Consumer API,向Kafka集群订阅一个或多个Topic的消息,并处理这些Topic下接收到的消息。

3 Streams API

  应用程序通过使用Streams API充当流处理器(Stream Processor),从一个或者多个Topic获取输入流,并生产一个输出流到一个或者多个Topic,能够有效地将输入流进行转变后变成输出流输出到Kafka集群。

4 Connect API

  允许应用程序通过Connect API构建和运行可重用的生产者或者消费者,能够把kafka主题连接到现有的应用程序或数据系统。Connect实际上就做了两件事情:使用Source Connector从数据源(如:DB)中读取数据写入到Topic中,然后再通过Sink Connector读取Topic中的数据输出到另一端(如:DB),以实现消息数据在外部存储和Kafka集群之间的传输。

  接下来我们将从Kafka的架构出发,重点介绍Kafka的主要组件及实现原理。Kafka支持消息持久化,消费端是通过主动拉取消息进行消息消费的,订阅状态和订阅关系由客户端负责维护,消息消费完后不会立刻删除,会保留历史消息,一般默认保留7天,因此可以通过在支持多订阅者时,消息无需复制多分,只需要存储一份就可以。下面将详细介绍每个组件的实现原理。

1 Producer

  Producer是Kafka中的消息生产者,主要用于生产带有特定Topic的消息,生产者生产的消息通过Topic进行归类,保存在Kafka 集群的Broker上,具体的是保存在指定的partition 的目录下,以Segment的方式(log文件和index文件)进行存储。

2 Consumer

  Consumer是Kafka中的消费者,主要用于消费指定Topic的消息,Consumer是通过主动拉取的方式从Kafka集群中消费消息,消费者一定属于某一个特定的消费组。

3 Topic

  Kafka中的消息是根据Topic进行分类的,Topic是支持多订阅的,一个Topic可以有多个不同的订阅消息的消费者。Kafka集群Topic的数量没有限制,同一个Topic的数据会被划分在同一个目录下,一个Topic可以包含1至多个分区,所有分区的消息加在一起就是一个Topic的所有消息。

4 Partition

  在Kafka中,为了提升消息的消费速度,可以为每个Topic分配多个Partition,这也是就之前我们说到的,Kafka是支持多分区的。默认情况下,一个Topic的消息只存放在一个分区中。Topic的所有分区的消息合并起来,就是一个Topic下的所有消息。每个分区都有一个从0开始的编号,每个分区内的数据都是有序的,但是不同分区直接的数据是不能保证有序的,因为不同的分区需要不同的Consumer去消费,每个Partition只能分配一个Consumer,但是一个Consumer可以同时一个Topic的多个Partition。

5 Consumer Group

  Kafka中的每一个Consumer都归属于一个特定的Consumer Group,如果不指定,那么所有的Consumer都属于同一个默认的Consumer Group。Consumer Group由一个或多个Consumer组成,同一个Consumer Group中的Consumer对同一条消息只消费一次。每个Consumer Group都有一个唯一的ID,即Group ID,也称之为Group Name。Consumer Group内的所有Consumer协调在一起订阅一个Topic的所有Partition,且每个Partition只能由一个Consuemr Group中的一个Consumer进行消费,但是可以由不同的Consumer Group中的一个Consumer进行消费。如下图所示:

在层级关系上来说Consumer Group好比是跟Topic对应的,而Consumer就对应于Topic下的Partition。Consumer Group中的Consumer数量和Topic下的Partition数量共同决定了消息消费的并发量,且Partition数量决定了最终并发量,因为一个Partition只能由一个Consumer进行消费。当一个Consumer Group中Consumer数量超过订阅的Topic下的Partition数量时,Kafka会为每个Partition分配一个Consumer,多出来的Consumer会处于空闲状态。当Consumer Group中Consumer数量少于当前定于的Topic中的Partition数量是,单个Consumer将承担多个Partition的消费工作。如上图所示,Consumer Group B中的每个Consumer需要消费两个Partition中的数据,而Consumer Group C中会多出来一个空闲的Consumer4。总结下来就是:同一个Topic下的Partition数量越多,同一时间可以有越多的Consumer进行消费,消费的速度就会越快,吞吐量就越高。同时,Consumer Group中的Consumer数量需要控制为小于等于Partition数量,且最好是整数倍:如1,2,4等。

6 Segment

  考虑到消息消费的性能,Kafka中的消息在每个Partition中是以分段的形式进行存储的,即每1G消息新建一个Segment,每个Segment包含两个文件:log文件和index文件。之前我们已经说过,log文件就是Kafka实际存储Producer生产的消息,而index文件采用稀疏索引的方式存储log文件中对应消息的逻辑编号和物理偏移地址(offset),以便于加快数据的查询速度。log文件和index文件是一一对应,成对出现的。下图展示了log文件和index文件在Partition中的存在方式。

  Kafka里面每一条消息都有自己的逻辑offset(相对偏移量)以及存在物理磁盘上面实际的物理地址便宜量Position,也就是说在Kafka中一条消息有两个位置:offset(相对偏移量)和position(磁盘物理偏移地址)。在kafka的设计中,将消息的offset作为了Segment文件名的一部分。Segment文件命名规则为:Partition全局的第一个Segment从0开始,后续每个segment文件名为上一个Partition的最大offset(Message的offset,非实际物理地偏移地址,实际物理地址需映射到log中,后面会详细介绍在log文件中查询消息的原理)。数值最大为64位long大小,由20位数字表示,前置用0填充。

  上图展示了index文件和log文件直接的映射关系,通过上图,我们可以简单介绍一下Kafka在Segment中查找Message的过程:

   1 根据需要消费的下一个消息的offset,这里假设是7,使用二分查找在Partition中查找到文件名小于(一定要小于,因为文件名编号等于当前offset的文件里存的都是大于当前offset的消息)当前offset的最大编号的index文件,这里自然是查找到了00000000000000000000index。

   2 在index文件中,使用二分查找,找到offset小于或者等于指定offset(这里假设是7)的最大的offset,这里查到的是6,然后获取到index文件中offset为6指向的Position(物理偏移地址)为258。

   3 在log文件中,从磁盘位置258开始顺序扫描,直到找到offset为7的Message。

至此,我们就简单介绍完了Segment的基本组件index文件和log文件的存储和查询原理。但是我们会发现一个问题:index文件中的offset并不是按顺序连续存储的,为什么Kafka要将索引文件设计成这种不连续的样子?这种不连续的索引设计方式称之为稀疏索引,Kafka中采用了稀疏索引的方式读取索引,kafka每当log中写入了4k大小的数据,就往index里以追加的写入一条索引记录。使用稀疏索引主要有以下原因:

   (1) 索引稀疏存储,可以大幅降低index文件占用存储空间大小。

   (2) 稀疏索引文件较小,可以全部读取到内存中,可以避免读取索引的时候进行频繁的IO磁盘 *** 作,以便通过索引快速地定位到log文件中的Message。

7 Message

  Message是实际发送和订阅的信息是实际载体,Producer发送到Kafka集群中的每条消息,都被Kafka包装成了一个Message对象,之后再存储在磁盘中,而不是直接存储的。Message在磁盘中的物理结构如下所示。

  其中 key 和 value 存储的是实际的Message内容,长度不固定,而其他都是对Message内容的统计和描述,长度固定。因此在查找实际Message过程中,磁盘指针会根据Message的 offset 和 message length 计算移动位数,以加速Message的查找过程。之所以可以这样加速,因为Kafka的log文件都是顺序写的,往磁盘上写数据时,就是追加数据,没有随机写的 *** 作。

8Partition Replicas

  最后我们简单聊一下Kafka中的Partition Replicas(分区副本)机制,08版本以前的Kafka是没有副本机制的。创建Topic时,可以为Topic指定分区,也可以指定副本个数。kafka 中的分区副本如下图所示:

  Kafka通过副本因子(replication-factor)控制消息副本保存在几个Broker(服务器)上,一般情况下副本数等于Broker的个数,且同一个副本因子不能放在同一个Broker中。副本因子是以分区为单位且区分角色;主副本称之为Leader(任何时刻只有一个),从副本称之为 Follower(可以有多个),处于同步状态的副本叫做in-sync-replicas(ISR)。Leader负责读写数据,Follower不负责对外提供数据读写,只从Leader同步数据,消费者和生产者都是从leader读写数据,不与follower交互,因此Kafka并不是读写分离的。同时使用Leader进行读写的好处是,降低了数据同步带来的数据读取延迟,因为Follower只能从Leader同步完数据之后才能对外提供读取服务。

  如果一个分区有三个副本因子,就算其中一个挂掉,那么只会剩下的两个中,选择一个leader,如下图所示。但不会在其他的broker中,另启动一个副本(因为在另一台启动的话,必然存在数据拷贝和传输,会长时间占用网络IO,Kafka是一个高吞吐量的消息系统,这个情况不允许发生)。如果指定分区的所有副本都挂了,Consumer如果发送数据到指定分区的话,将写入不成功。Consumer发送到指定Partition的消息,会首先写入到Leader Partition中,写完后还需要把消息写入到ISR列表里面的其它分区副本中,写完之后这个消息才能提交offset。

  到这里,差不多把Kafka的架构和基本原理简单介绍完了。Kafka为了实现高吞吐量和容错,还引入了很多优秀的设计思路,如零拷贝,高并发网络设计,顺序存储,以后有时间再说。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/zaji/13496137.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2025-09-01
下一篇2025-09-01

发表评论

登录后才能评论

评论列表(0条)

    保存