从内存池到连接池 老码农眼中的资源池

如果将互联网应用比喻成冲浪的话, 可能需要先学会在池中游泳吧。

引子

AI赋能万物,老码农的伙伴们也曾经开发了一个基于图数据库的知识问答系统,在压力测试的时候发现随着并发数的增加,响应的时延明显变长,看时延分布,是应用程序与图数据库之间的交互时延过长。结构不做调整,优化图数据库后,发现在并发量上来之后,效果仍不明显。 看代码,观察ELK中的日志,发现了问题所在————高并发时连接的创建时间较长。时间所限,替换为httpclient的连接池,post 和 get都采用池中的连接,性能问题迎刃而解。

在编程的世界里,经常会遇到连接池,那连接池到底是什么呢/span>

什么是池

池,一种资源抽象的形象化说法。编程世界中的池是一组资源, 可以随时使用, 但不随时地创建和释放。资源池(resource pool)被认为是一种设计模式,这里的资源主要是指系统资源, 这些资源不专属于某个进程或内部资源。客户端向池请求资源, 并使用返回的资源进行指定的操作。当客户端使用完资源后, 会把资源放回池中而不是释放或丢弃掉。

任何技术都有自己的应用边界,池作为一种资源使用技术,典型的使用情形是:

  1. 当获取资源的成本较高的时候

  2. 当请求资源的频率很高且使用资源总数较低的时候

  3. 当面对性能问题,涉及到处理时间延迟的时候

池中的资源主要有两类:需要系统调用(system call) 的系统资源,或主演需要网络通信的远程资源, 如数据库连接、套接字连接、线程和内存分配等等。池中的资源一般不包括像字体库或图片等大的数据对象, 那些资源的存储一般是通过是数据缓存或数据库技术实现的。由于资源池的存在, 从池中获取资源所需的时间变成了可预知的,从而在一定程度上解决性能的问题。

根据资源的类型,资源池一般包括连接池、线程池和内存池。

连接池

连接池是创建和管理一个网络连接资源池的技术,这些连接一般预先准备好被任何需要它们的线程或者进程使用。网络连接根据连接的生命周期可以粗略的分为两种:长链接和短链接。就web应用而言,短连接就是一般的http请求,长连接如websocket。

短链接适合大部分应用。对于远程方法的执行时间远大于连接创建时间(看网络情况大约为数毫秒)的时候,其连接创建时间可以被忽略,此时短连接策略基本不会有较大性能损失。另外,对于非频繁调用火灾对延迟时间不敏感的服务也适合使用短连接策略。 

对于高并发或者高吞吐量的应用,网络连接的创建消耗是很大的,对于这种应用应该使用长连接策略的连接池实现。

从内存池到连接池 老码农眼中的资源池

为每个用户打开和维护数据库连接需要消耗大量的资源,而数据库连接池用于提高数据库中执行命令的性能,减少了用户必须等待的时间。在数据库连接池中, 创建连接后将其放入池中, 再次使用, 不必重新建立新的连接。如果所有的连接都被使用, 则创建新的连接并被添加到池中。

基于 web 的应用程序和企业应用程序一般都使用应用服务器来处理连接池。当页面需要访问数据库时, 只需使用池中的现有连接, 并且只在池中没有空闲连接的情况下建立新连接。这减少了连接到数据库响应单个请求的开销,需要频繁访问数据库的本地应用程序也可以从数据库连接池中受益。一些库不仅实现了数据库连接池还实现了相关的 SQL 查询池, 简化了数据库操作密集型应中连接池的实现。Java中常用的数据库连接池有:DBCP 、C3P0、BoneCP、Proxool、DBPool、XAPool、Primrose、SmartPool、MiniConnectionPoolManager及Druid等。

通过对连接池进行配置, 对最小连接、最大连接和空闲连接的数量加以限制, 可以优化在特定场景和特定环境中数据库连接池的性能。

端上的连接池

由于互联网尤其是广域网中的速度非可控性,特别是移动互联网(基于3G/4G)的速度的不确定性,在端上的应用也将连接池作为一种重要的技术手段。

以Chrome浏览器为例,其网络库采取连接池的方式管理连接的建立、分配以及释放,当请求可以直接从连接池中获取复用连接时,可以减少建立连接的时间消耗。除了websoket连接池之外,包含三种类型的连接池:

  • TransportClientSocketPool

  • SSLClientSocketPool

  • SOCKSClientSocketPool

其中TransportClientSocketPool为低层连接池,SSLClientSocketPool和SOCKSClientSocketPool为高层连接池,高层连接池包含低层连接池或其他高层连接池的对象,这三种连接池类可以组合出多种连接池对象。打开chrome://net-internals/#sockets 可以看到浏览器当前的连接状态。

在app中,连接池同样被广泛采用,主流的网络通信库都支持连接池,例如Okhttp。平台层也是如此,例如Android 平台中的binder 连接池。

从内存池到连接池 老码农眼中的资源池

对于内存池的应用而言,可以通过以下方式分配、访问和释放内存:

  • 从池中分配内存时,函数将确定所需块的池。如果该池的所有区块已被保留,则该函数试图在下一个较大的池中找到一个。分配的内存块用句柄表示。

  • 获取分配内存的访问指针

  • 释放以前分配的内存块

内存池将句柄划分为池索引、内存块索引以及版本, 从而在内部解释句柄。池和内存块索引允许使用句柄快速访问对应的块, 而在每个新分配中增量的版本允许检测已经释放内存块的句柄。

内存池允许使用恒定的执行时间来分配内存。数千个对象在池中的内存释放只是一个操作, 而不是一个一个的Free。内存池也可以采用树状结构, 应用于特殊的编程行为, 如循环,递归等。固定大小的块内存池不需要为每个块分配元数据存储, 不需要描述分配块的大小等特性。

内存池还可用于对象, 在这种情况下,对象本身没有外部资源, 只占用内存, 已经创建了的对象避免了对象创建时的内存分配。当对象创建成本较高时, 对象池是有用的, 但在某些情况下, 这种简单的对象池可能并不有效, 实际上还可能会降低性能。

小结

池是一种资源共享和复用的技术,把管理的理念引入到编程世界中。从基础的内存池,到线程池,再到各种连接池,根据应用场景还可以继续细分,如句柄池,缓存池…..几乎涵盖了互联网应用的大部分角落。如果将互联网成冲浪的话, 可能需要先学会在池中游泳吧。




来源:半吊子全栈工匠

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2018年1月13日
下一篇 2018年1月13日

相关推荐