环境搭建
rust安装
编辑器 rust插件安装
环境变量配置
国内源配置
demo
需求
1 搭建服务,完成一个可以接受任何参数的post的方法
2 接入opensearch,将post收到的参数上传的opensearch
3 将该demo做成docker运行
步骤
1.使用cargo new xx --bin 创建新项目
2.编辑器打开新项目 在Cargo.toml引入需要的包
[dependencies]
# rocket 接口相关
rocket = { version = "0.5.0", features = ["json"] }
# serde json序列化反序列化
serde = { version = "1.0", features = ["derive"] }
# opensearch 操作包
opensearch = { version = "2.0.0" }
# tokio 异步线程包
tokio = { version = "1.24.2", features = ["full"] }
# chrono 时间包
chrono = {version="0.4.23"}
3.编写代码
#[launch]
fn rocket() -> Rocket<Build> {
let rt = Runtime::new().unwrap();
let client = rt.block_on(init_open_search()).expect("Failed to initialize OpenSearch");
rocket::build()
.manage(client)
.mount("/", routes![index])
.mount("/data", routes![create])
}
async fn init_open_search() -> Result<OpenSearch, Box<dyn std::error::Error>> {
let url = Url::parse("url")?;
let conn_pool = SingleNodeConnectionPool::new(url);
let transport = TransportBuilder::new(conn_pool)
.auth(Credentials::Basic("username".parse().unwrap(), "passwrod.".parse().unwrap()))
.build()?;
let client = OpenSearch::new(transport);
Ok(client)
}
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
#[post("/", format = "application/json", data = "<data>")]
async fn create(data: rocket::serde::json::Json<HashMap<String, Value>>, client: &State<OpenSearch>) -> String {
let params = data.0;
let _ = send_data(client, params.clone()).await;
format!("Received data: {:?}", params)
}
async fn send_data(client: &State<OpenSearch>, data: HashMap<String, Value>) -> Result<(), Box<dyn std::error::Error>> {
let log_data = LogData {
timestamp: chrono::offset::Utc::now().timestamp(),
data: data,
};
let response = client
.index(IndexParts::Index("test_stream_rust"))
.body(json!(log_data))
.send().await.expect("Failed to add OpenSearch");
println!("Successfully indexed a document {}", response.status_code());
Ok(())
}
#[derive(Serialize, Deserialize)]
struct LogData {
timestamp: i64,
data: HashMap<String, Value>,
}
4.docker build
ARG RUST_VERSION=1.76
ARG APP_NAME=opensearch-service
################################################################################
# Create a stage for building the application.
FROM rust:${RUST_VERSION} AS build
ARG APP_NAME
RUN USER=root cargo new --bin ${APP_NAME}
WORKDIR /app
COPY ./src ./src
COPY ./Cargo.toml ./Cargo.toml
RUN cargo build --release
RUN cp /app/target/release/${APP_NAME} /app/target/release/server
FROM debian:stable-slim
RUN apt-get update && apt-get install -y libssl3 && rm -rf /var/lib/apt/lists/*
# Copy the executable from the "build" stage.
COPY --from=build /app/target/release/server /usr/local/bin/server
# Configure rocket to listen on all interfaces.
ENV ROCKET_ADDRESS=0.0.0.0
# Expose the port that the application listens on.
EXPOSE 8000
# What the container should run when it is started.
CMD ["/usr/local/bin/server"]
5.测试
5.1 启动服务
docker build -t opensearch-service .
winpty docker run -p 8000:8000 -it --rm opensearch-service
5.2 调用接口
postman测试
5.3 验证数据
opensearch查看对应indexs,看到内容成功插入
6.总结
6.1 关于cargo中的features
你引用的包里还引用了别人的包,但是默认是不自动引入这些配置了features的包(即可选引用包), 通过features可以把对应的包及功能引入
6.2 rocket中#[launch]可以在代码中不加入main方法
6.3 tikio异步转同步
opensearch的demo全是异步的,但是rocket使用同步,为了引入客户端,将异步转为同步
let rt = Runtime::new().unwrap();
let client = rt.block_on(init_open_search()).expect("Failed to initialize OpenSearch");
6.4 如何在rocket引入一个全局变量
rocket::build() .manage(client)
此处manage引入后,所有的api方法中都可以通过参数拿到这个client
6.5 关于json序列化
#[derive(Serialize, Deserialize)]
struct LogData {
timestamp: i64,
data: HashMap<String, Value>,
}
此处的log data 中不要直接引入别人的包中的对象,可能无法直接序列化,尽量用基础类。
6.6 docker打包问题
一开始用用docker init 构建项目后 docker run直接报错找不到包。
排查方法
6.6.1 先注释掉 dockerfile中最后一句 "CMD ["/usr/local/bin/server"]",再次运行,找到对应文件位置后发现打好的原生包是存在的,但是命令行运行还是报错找不到包
6.6.2 内核缺少依赖包
ldd filename 通过该命令发现内核缺少依赖包
为了解决这个问题,将运行环境从alpine切换为debian:stable-slim,发现还是缺少rocket依赖的openssl3的包
加入命令RUN apt-get update && apt-get install -y libssl3 && rm -rf /var/lib/apt/lists/*
成功打包运行