6个版本
0.3.5 | 2020年5月15日 |
---|---|
0.3.4 | 2020年3月24日 |
0.3.2 | 2020年2月6日 |
0.3.1 | 2020年1月15日 |
#18 in #解析器
33 每月下载量
用于 5 crates
79KB
1.5K SLoC
cueball
关于
多节点服务连接池
Cueball是一个库,用于“玩球桌”——管理到多节点服务的连接池。Cueball的此实现受到了Joyent许多服务和软件组件使用的原始Node.js实现cueball的启发。Rust实现依赖于两个主要特性来管理一组节点提供的服务的连接。这些是Resolver
特性和Connection
特性。
解析器
解析器负责定位逻辑服务中可用的所有节点或后端,获取它们的IP地址和端口信息(或连接所需的内容),并跟踪它们。这通常是一种服务发现客户端。一个例子是使用DNS SRV记录作为服务发现机制来查找后端的基于DNS的解析器实现。
连接
在cueball中,连接不一定是TCP套接字。它可以是有某种逻辑连接到服务的任何东西,只要它遵守与套接字相似的接口。
这旨在允许API用户将“连接”表示为应用或会话层概念。例如,构建一个连接到LDAP服务器的连接池,这些连接在被认为是“连接”之前执行绑定操作(认证)可能很有用。
除了Resolver
和Connection
实现外,cueball用户还为cueball连接池提供一个函数来建立到所需服务的连接。cueball连接池为该函数建立的特性界限如下
FnMut(&Backend) -> C + Send + 'static
where C: Connection
需要的是一个函数,它接收解析器中的Backend
引用并返回Connection
的某个实例。
此函数的目的是提供一种方法来捕获建立服务连接所需的应用程序级配置信息。例如,数据库连接可能需要特定的数据库名称或用户名才能建立连接。
重新平衡
随着服务的后端(Backend
)的增减,连接池会重新平衡配置的连接数(max_connections
)到可用的后端集合中。当解析器(Resolver
)通知连接池已添加新的后端或已删除现有的后端时,将发生重新平衡。连接池会响应这些事件之一以保持连接在可用后端之间的均匀分布。
在执行实际重新平衡之前,连接池在收到解析器(Resolver
)的消息时使用可配置的延迟。这种延迟是为了处理在很短的时间内解析器可能会发送多个消息的情况,使得连接池在重新平衡连接时更加高效。默认的重新平衡延迟时间为100毫秒。
重新平衡可能会导致连接池暂时超出池配置的最大连接数。如果解析器通知连接池删除后端,但该后端的连接仍在使用中,连接计数可能会超过最大值,直到这些连接返回连接池并被丢弃。
去相干性
在cueball中,去相干性用来表示连接池中连接顺序的周期性随机洗牌。去相干性的目标是避免在连接池的生命周期中出现不希望的模式。例如,假设一个服务有三个后端,分别为A
、B
和C,连接池的最大连接数为九。初始连接分布可能如下所示
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_interval
。decoherence_interval
表示去相干性洗牌周期的长度,以秒为单位。如果在ConnectionPoolOptions
结构中没有指定此值,则默认值为300秒。
示例
使用cueball进行连接管理需要实现Resolver
特性和Connection
特性。Resolver
特性的实现者向连接池提供有关提供特定服务的节点信息。Connection
特性定义了建立和关闭特定服务连接的行为。
以下是一个使用假设的Resolver
和Connection
实现来创建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.
})
}
}
存在多种实现Resolver
和Connection
特性的方案,这些方案对想要开始使用cueball
的人来说可能很有用。
Resolver
特性实现者
Connection
特性实现者
最低支持的Rust版本
当前最低支持的Rust版本是1.39。
依赖项
约5.5MB
约102K SLoC