1 为什么选择官方 Rust 客户端?
- 语义化兼容 :客户端 主版本 与 ES 主版本 严格对应,8.x 客户端可对接任何 8.x 服务器;不存在跨主版本兼容承诺 (docs.rs)
- 100% API 覆盖:稳定 API 全量映射,Beta/实验特性可按需开启
- 异步高效 :内建
reqwest
+tokio
,零额外胶水 - Cloud Ready:支持 Cloud ID,几行代码直连 Elastic Cloud
2 快速开始
2.1 依赖配置
toml
[dependencies]
elasticsearch = "9.0.0-alpha.1" # 与 ES 9.x 对应
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
可选 Cargo Feature
rustls-tls
:纯 Rust TLSbeta-apis
/experimental-apis
:启用 Beta / 实验端点(自动包含 Beta) (docs.rs)
2.2 初始化客户端
rust
use elasticsearch::{Elasticsearch, http::transport::Transport};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1) 默认本地 https://localhost:9200
let client = Elasticsearch::default();
// 2) 指定单节点 URL
let transport = Transport::single_node("https://es.local:9200")?;
let client = Elasticsearch::new(transport);
// 3) Cloud ID + Basic 认证
use elasticsearch::auth::Credentials;
let cloud_id = "cluster_name:.....";
let creds = Credentials::Basic("elastic".into(), "pass".into());
let transport = Transport::cloud(cloud_id, creds)?;
let client = Elasticsearch::new(transport);
Ok(())
}
3 核心操作全景
3.1 单条写入
rust
use elasticsearch::IndexParts;
use serde_json::json;
client
.index(IndexParts::IndexId("tweets", "1"))
.body(json!({
"user": "kimchy",
"message": "Hello, Elasticsearch!"
}))
.send()
.await?;
3.2 Bulk 批量写入
rust
use elasticsearch::{BulkParts, http::request::JsonBody};
use serde_json::json;
let mut body: Vec<JsonBody<_>> = Vec::with_capacity(4);
body.push(json!({"index": {"_id": "1"}}).into());
body.push(json!({"user": "kimchy", "msg": "Bulk 1"}).into());
body.push(json!({"index": {"_id": "2"}}).into());
body.push(json!({"user": "forloop", "msg": "Bulk 2"}).into());
let resp = client
.bulk(BulkParts::Index("tweets"))
.body(body)
.send()
.await?;
assert!(!resp.json::<serde_json::Value>().await?["errors"].as_bool().unwrap());
3.3 搜索
rust
use elasticsearch::SearchParts;
use serde_json::json;
let resp = client
.search(SearchParts::Index(&["tweets"]))
.from(0)
.size(10)
.body(json!({
"query": { "match": { "message": "Elasticsearch rust" } }
}))
.send()
.await?;
let hits = resp.json::<serde_json::Value>().await?;
for h in hits["hits"]["hits"].as_array().unwrap() {
println!("{:?}", h["_source"]);
}
3.4 Cat API ------ 索引概览
rust
use elasticsearch::cat::CatIndicesParts;
use serde_json::Value;
let body = client
.cat()
.indices(CatIndicesParts::Index(&["*"]))
.format("json")
.send()
.await?
.json::<Value>()
.await?;
for idx in body.as_array().unwrap() {
println!("Index: {}", idx["index"]);
}
4 传输层高级定制
rust
use elasticsearch::{
http::transport::{SingleNodeConnectionPool, TransportBuilder},
};
use url::Url;
let url = Url::parse("https://es.secure.local:9243")?;
let pool = SingleNodeConnectionPool::new(url);
let transport = TransportBuilder::new(pool)
.cert_validation(false) // 忽略证书验证(测试用)
.disable_proxy() // 跳过系统代理
.build()?;
let client = Elasticsearch::new(transport);
5 TLS 与 Feature 切换
方案 | 配置 | 适用场景 |
---|---|---|
native-tls(默认) | 无需显式声明 | 调用系统 OpenSSL / SChannel |
rustls-tls | default-features = false , features = ["rustls-tls"] |
纯 Rust,可发往 Wasm、musl |
6 版本兼容策略
- 主版本必须一致 ;如
elasticsearch = "8"
必配合 ES 8.x - 小版本向前兼容 :8.5 客户端可连 8.0 集群,但新增 API 不可调用;反之亦然 (docs.rs)
7 常见问题 FAQ
问题 | 解决方案 |
---|---|
invalid certificate |
使用 rustls-tls , 或 TransportBuilder::cert_validation(false) 暂时跳过 |
method not allowed |
核对 *_Parts 枚举是否匹配正确 URL 变体 |
如何同步调用? | 官方仅提供 异步 接口,需在 tokio::runtime 中 block_on |
Beta/实验 API 404 | 在 Cargo.toml 启用 beta-apis / experimental-apis |
8 最佳实践
- 连接池复用 :单实例
Elasticsearch
放全局,内部自动管理 HTTP 连接。 - Bulk 分片:控制单批 5--15 MB 或 5k 条,权衡内存与吞吐。
- 日志链路 :启用
RUST_LOG=elasticsearch=trace
抓包定位慢查询。 - Cloud 部署:使用 Cloud ID + API Key,免维护证书、负载均衡。
- Typed Source :业务模型
#[derive(Serialize, Deserialize)]
,配合serde_json::from_value
获取强类型文档。
9 结语
官方 elasticsearch
crate 让 Rust 后端 也能享受与 Java/Python 同级的 ES 支持:全 API、强类型、异步高效 。
无论是打造实时日志管线、还是为 RAG 系统提供检索服务,都能凭借 Rust 的安全与性能优势,跑出新高度。
现在,就把老旧的 REST wrapper 替换掉,体验全新的 Rust × Elasticsearch 吧!