#连接池 # #服务 #特质 #解析器 #cueball #nodejs

cueball-static-resolver

这是 cueball 解析器特质的实现,它提供了一个静态后端列表供连接池使用

2 个版本

0.3.1 2020 年 5 月 19 日
0.3.0 2020 年 1 月 8 日

#27 in #解析器

26 每月下载次数
用于 cueball-postgres-connecti…

MPL-2.0 许可证

82KB
1.5K SLoC

cueball

关于

多节点服务连接池

Cueball 是一个用于“打台球”的库——管理到多节点服务的连接池。此 cueball 实现受到 Joyent 服务的许多软件组件使用的原始 Node.js 实现 cueball 的启发。Rust 实现依赖于两个主要特质来管理节点集合的连接,这些节点提供一项服务。这些是 Resolver 特质和 Connection 特质。

解析器

解析器负责定位逻辑服务中所有可用的节点或后端,获取它们的 IP 地址和端口信息(或连接所需的其他信息),并跟踪它们。这通常是某种形式的服务发现客户端。一个例子是使用 DNS SRV 记录作为服务发现机制以查找后端的基于 DNS 的解析器实现。

连接

在 cueball 中,连接不一定只是一个 TCP 套接字。它可以是有某种逻辑连接到服务的任何东西,只要它遵循与套接字相似的接口。

这旨在允许 API 的用户将“连接”表示为应用或会话层概念。例如,构建连接到 LDAP 服务器的连接池可能很有用,在它们被视为“连接”之前执行绑定操作(身份验证)。

除了 ResolverConnection 的实现外,cueball 用户还向 cueball 连接池提供一个函数来建立到所需服务的 连接。cueball 连接池为此函数建立的特质界限如下

FnMut(&Backend) -> C + Send + 'static
where C: Connection

要求是一个函数,它接受解析器中一个 Backend 引用并返回一个 Connection 实例。

此函数的目的是提供一种方式来捕获建立到服务 连接 所需的应用级配置信息。例如,数据库连接可能需要应用程序特定的配置,例如数据库名称或用户名,以便建立连接。

重新平衡

随着服务后端(Backend)的增减,连接池会在可用的后端集合之间重新平衡配置的连接数(max_connections)。当解析器(Resolver)通知连接池添加了新的后端或删除了现有后端时,就会发生重新平衡。连接池会根据这些事件之一来重新平衡连接,以保持连接在可用的后端之间均匀分布。

连接池在执行实际重新平衡之前,会使用一个可配置的延迟来处理从解析器(Resolver)收到的消息。这种延迟是为了考虑到解析器在很短的时间内可能会发送多条消息的情况,从而使连接池在重新平衡连接时更加高效。默认的重新平衡延迟时间为100毫秒。

重新平衡可能会导致连接池临时超过为池配置的最大连接数。如果解析器(Resolver)通知连接池删除了后端,但该后端的连接仍在使用中,那么连接计数可能会超过最大值,直到这些连接返回到连接池并丢弃。

不协调性

在cueball中,不协调性被用来指代连接池中连接顺序的周期性随机洗牌。不协调性的目标是避免在连接池的生命周期中出现的不理想模式。例如,假设有一个服务有三个后端,ABC,并且连接池的最大连接数为九。初始连接分布可能如下所示

1 2 3 4 5 6 7 8 9
A B C A B C A B C

cueball连接池使用内部队列来存储连接。由于连接池中的连接可能被占用不均匀的时间段,队列可能从初始状态变为以下状态

1 4 7 2 5 8 3 6 9
A A A B B B C C C

这种情况并不理想,因为同一个后端必须处理多个连续请求,而其他后端处于空闲状态。对于cueball来说,理想的分布是在后端之间均匀分配工作,不仅限于连接数,还要考虑到随时间推移的请求分布。诚然,上述例子是一个极端情况,模式可能会根据工作负载迅速解决,但无法保证这种情况一定会发生。cueball周期性不协调洗牌的目标是破坏这些可能持续一段时间的模式。

与不协调性相关的配置选项之一是:decoherence_intervaldecoherence_interval表示不协调性洗牌周期的长度(以秒为单位)。如果在ConnectionPoolOptions结构中未指定此值,则默认值为300秒。

示例

使用cueball进行连接管理需要实现Resolver特性和Connection特性。Resolver特性的实现者向连接池提供有关可用于提供特定服务的节点信息。Connection特性定义了与特定服务建立和关闭连接的行为。

以下是一个使用假设的ResolverConnection实现来创建cueball连接池的示例。

use std::thread;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::{Arc, Barrier, Mutex};
use std::sync::mpsc::Sender;
use std::{thread, time};

use slog::{Drain, Logger, info, o};

use cueball::backend;
use cueball::backend::{Backend, BackendAddress, BackendPort};
use cueball::connection::Connection;
use cueball::connection_pool::ConnectionPool;
use cueball::connection_pool::types::ConnectionPoolOptions;
use cueball::error::Error;
use cueball::resolver::{BackendAddedMsg, BackendMsg, Resolver};

fn main() {
    let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
    let log = Logger::root(
        Mutex::new(
            slog_term::FullFormat::new(plain).build()
        ).fuse(),
        o!("build-id" => "0.1.0")
    );

    let be1 = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55555);
    let be2 = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55556);
    let be3 = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55557);

    let resolver = FakeResolver::new(vec![be1, be2, be3]);

    let pool_opts = ConnectionPoolOptions::<FakeResolver> {
        max_connections: 15,
        claim_timeout: Some(1000)
        resolver: resolver,
        log: log.clone(),
        decoherence_interval: None,
    };

    let pool = ConnectionPool::<DummyConnection, FakeResolver>::new(pool_opts);

    for _ in 0..10 {
        let pool = pool.clone();
        thread::spawn(move || {
            let conn = pool.claim()?;
            // Do stuff here
            // The connection is returned to the pool when it falls out of scope.
        })
    }
}

存在几个关于ResolverConnection特性的实现,这些实现可能对想要开始使用cueball的任何人都有用。

Resolver特性实现者

Connection特性实现者

最低支持的 Rust 版本

当前最低支持的 Rust 版本是 1.39。

依赖项

~5.5MB
~102K SLoC