通常,我们的应用程序不需要并行处理成千上万的用户,也不需要在一秒钟内处理成千上万的消息。我们只需要应付数十或数百个并发连接的用户,就可以在内部应用程序或某些微服务应用程序中承受如此大的负担。
在这种情况下,我们可以使用某些高级框架/库,这些框架/库在线程模型/已用内存方面没有得到优化,并且仍然可以承受一些合理的资源和相当快的交付时间。
但是,有时会遇到这样的情况,即我们的系统部分需要比其他应用程序更好地扩展。用传统的方式或框架编写系统的这一部分可能会导致巨大的资源消耗,并且需要启动同一服务的许多实例来处理负载。导致能够处理数万个连接的能力的算法和方法也称为 C10K问题。
在本文中,我将主要关注可以通过TCP Connections / Traffic进行的优化,以优化您的(微)服务实例以浪费尽可能少的资源,更深入地研究OS如何与TCP和套接字,以及最后但并非最不重要的一点,是如何深入了解所有这些内容。让我们开始吧。
I / O编程策略
让我们描述一下我们目前拥有什么样的I / O编程模型,以及在设计应用程序时需要选择哪些选项。首先,没有好的或坏的方法,只有更适合我们当前用例的方法。选择错误的方法可能在将来带来非常不便的后果。这可能会浪费资源,甚至可能从头开始重写应用程序。
通过阻塞处理来阻塞I / O
每连接线程服务器
这种方法背后的思想是,没有任何专用/空闲线程,则不接受套接字连接(我们将在后面显示其含义)。在这种情况下,阻塞意味着该特定线程已绑定到该连接, 并且总是在从该连接读取或写入该连接时阻塞。
套接字服务器的最简单版本, 它从端口5050开始 ,以阻塞方式进行读取 InputStream
和写入 OutputStream
。当我们需要通过单个连接传输少量对象,然后在需要时关闭它时,启动一个新对象很有用。
-
即使没有任何高级库也可以实现它。
-
使用阻塞流进行读取/写入(等待阻塞
InputStream
读取操作,该操作由当时TCP接收缓冲区中可用的字节填充提供的字节数组, 并返回字节数或-1-流的末尾)并消耗字节,直到我们有足够的数据来构建请求。 -
当我们开始以无限方式为传入连接创建线程时,可能会出现一个大问题和效率低下。我们将为非常昂贵的线程创建和内存影响付出代价,这是将一个Java线程映射到一个内核线程时产生的。
-
除非我们确实需要一个内存不足的应用程序并且不想加载很多属于某些框架的类,否则它不适合“实际”生产。
具有阻塞处理功能的非阻塞I / O
基于线程池的服务器
这是大多数知名企业HTTP服务器所属的类别。通常,此模型使用多个线程池 来使处理在多CPU环境中更有效,并且更适合企业应用程序。有多种方法可以配置线程池,但是基本概念在所有HTTP服务器中都完全相同。请参阅 HTTP Grizzly I / O策略 ,以了解通常可以基于基于线程池的非阻塞服务器配置的所有可能策略。
优点
缺点
如何设置线程池
好的,我们有一个或多个线程池来处理阻塞的业务操作。但是,线程池的最佳大小是多少?我们会遇到两个问题:
我会参考Brian Goetz的一本很棒的书《Java Concurrency in Practice》 ,其中说确定线程池的大小并不是一门精确的科学,它更多地是关于了解环境和任务性质的。
如果我们的程序包含I / O或其他阻止操作,则您需要一个更大的池,因为不允许您的线程一直一直放置在CPU上。您需要估计等待时间的比率,以便使用一些探查器或基准测试来计算任务的时间,并观察生产工作负载不同阶段(高峰时间与非高峰时间)的CPU利用率。
-
接受新连接的第一个线程池。如果一个线程能够管理传入连接的速度,它甚至可以是单线程池。通常有两个待办事项列表可以填写,而下一个传入连接则被拒绝。检查是否可以正确使用持久连接。
-
第二个线程池,用于通过非阻塞方式(选择器线程或IO线程)从套接字读取/写入套接字。每个选择器线程都处理多个客户端(通道)。
-
第三个线程池分隔请求处理的非阻塞和阻塞部分(通常称为工作线程)。该选择线程不能被一些阻塞操作被阻止,因为所有其他通道将无法取得任何进展(只有一个信道组线程,该线程将被阻止)。
-
非阻塞读取/写入是使用缓冲区实现的,选择器线程会从套接字读取新字节并写入专用缓冲区(缓冲的缓冲区),只要不满足处理请求的特定线程(因为它们没有足够的数据来构造)例如HTTP请求)。
-
我们需要澄清术语“ 非阻塞”:
-
如果我们在Socket Server的上下文中进行讨论,则非阻塞意味着线程不与打开的连接绑定,并且不等待输入数据(甚至在TCP发送缓冲区已满时甚至写入数据),只要尝试读取并检查是否没有字节,则没有字节被添加到缓冲区中以进行进一步处理(构造请求),并且给定的选择器线程继续从另一个打开的连接中进行读取。
-
但是,就处理请求而言,代码在大多数情况下是 BLOCKING,这意味着我们执行一些代码来阻塞当前线程,并且该线程等待I / O绑定处理的完成(数据库查询,HTTP调用) ,从磁盘中读取..)或某些持久的CPU绑定处理(计算散列/阶乘,加密挖掘等)。如果执行完成,则线程将被唤醒并继续执行某些业务逻辑。
-
-
该BLOCKING 我们的业务逻辑的自然是最主要的原因,工人池如此巨大,我们只需要得到大量的线程发挥作用来提高吞吐量。 否则,在更高的负载(例如,更多的HTTP请求)的情况下,我们可能会导致所有线程处于阻塞状态,并且没有可用的线程来处理请求(没有处于Runnable状态的线程准备在CPU上执行)。
-
即使请求的数量很高并且许多我们的工作线程在某些阻止操作中被阻止,我们也能够接受新的连接,即使我们可能无法立即处理它们的请求并且数据必须在TCP接收中等待缓冲区。
-
许多框架/库(Spring Controllers,Jersey等)和HTTP Server(Jetty,Tomcat,Grizzly等)都使用此编程模型,因为这样很容易编写业务代码,并且如果确实使线程阻塞,需要。
-
并行性通常不是由CPU的数量决定的,而是由阻塞操作的性质和工作线程的数量所限制。通常,这意味着如果阻塞操作(I / O)与进一步执行(在请求过程中)的时间比例太高,那么我们可以得出以下结论:
-
在阻塞操作(数据库查询,..)上有很多阻塞线程,并且
-
很多请求等待工作线程被处理,
-
由于没有线程而无法充分利用的CPU可以继续执行
-
-
更大的线程池导致上下文切换和CPU缓存使用效率低下
-
线程池太小,我们没有足够的线程来覆盖所有线程被阻塞的时间,比如说等待I / O操作,而您的CPU没有得到有效利用。
-
线程池太大,我们为许多实际上空闲的线程付出了代价(请参见下面,当我们运行很多线程时的价格)。
-
您的环境有多少CPU和多少内存?
-
任务主要执行计算,I / O还是某种组合?
-
它们是否需要稀缺资源(JDBC连接)?线程池和连接池会相互影响,当我们充分利用连接池时,增加线程池以获得更好的吞吐量可能没有意义。
具有非阻塞处理的非阻塞I / O
基于与CPU核心相同数量的线程的服务器
如果我们能够以非阻塞方式管理大部分工作负载,则此策略是最有效的。这意味着使用非阻塞算法来实现对套接字的处理(接受连接,读取,写入),但是即使业务处理也不包含任何阻塞操作。
此策略的典型代表是Netty Framework,因此让我们深入了解如何实现此框架的体系结构基础,以了解为什么它最适合解决C10K问题。如果您想详细了解其实际工作原理,那么我可以推荐以下资源:
Netty in Action- 由Netty Framework Norman Mauer的作者撰写。这是了解如何使用具有各种协议的处理程序基于Netty实现客户端或服务器的宝贵资源。
用户指南4.x的 - Netty中,显示所有基本原则,一个示例应用程序。
I / O处理,协议的实现以及所有其他处理程序都应该使用非阻塞操作来永不停止当前线程。我们总是可以使用其他线程池来阻止操作。然而,如果我们需要把我们的每一个要求,以阻止操作,那么我们几乎利用了Netty的电力专用线程池的开关处理,因为我们很有可能在相同的情况下结束了与非阻塞IO用封闭处理-一个大线程池只是在我们应用程序的不同部分中。
具有异步编程模型的I / O库
Netty是一个I / O库和框架,它简化了非阻塞IO编程,并为服务器和传入连接的生命周期中发生的事件提供了异步编程模型。我们只需要使用lambda连接到回调即可,然后免费获得所有内容。
许多协议都可以使用,而无需依赖某些庞大的库
开始使用纯JDK NIO构建应用程序是非常令人沮丧的,但是Netty包含使程序员处于较低级别的功能,并提供了使许多事情变得更加高效的可能性。Netty已经包含了大多数众所周知的协议,这意味着我们可以比带有许多样板的高级库(例如,用于HTTP / REST的Jersey / Spring MVC)中的协议更加高效。
确定正确的非阻塞用例,以充分利用Netty的功能
在上面的图片中,我们可以看到Netty Architecture的主要组件
-
EventLoopGroup —收集事件循环并提供要注册到事件循环之一的通道。
-
EventLoop —处理给定事件循环的已注册通道的所有I / O操作。EventLoop仅在一个线程上运行。因此,一个事件循环的最佳数量
EventLoopGroup
是CPU的数量(发生Page Fault时,某些框架使用CPU数量 + 1 来具有附加线程)。 -
管道-保留处理程序的执行顺序(在发生某些输入或输出事件时被排序和执行的组件,包含实际的业务逻辑)。管道和处理程序在所属的线程上执行,因此,处理程序中的阻塞操作将阻塞给定上的所有其他处理/通道 。
EventLoop
EventLoop
-
摘要
感谢你阅读我的文章,请在下面发表评论。
-
有什么问题可以加下qq:2062583349。也可添加vx:admindesire,有java、python、web等习资料和视频课程干货”。欢迎交流!