编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

文盘Rust —安全连接 TiDB/Mysql(rust安全性)

gowuye 2024-04-29 16:10 13 浏览 0 评论

#科技之巅#

作者:京东科技 贾世闻

最近在折腾rust与数据库集成,选了Tidb Cloud Serverless Tier 作为数据源。Tidb 无疑是近五年来最优秀的国产开源分布式数据库,Tidb Cloud Serverless Tier作为pingcap旗下的云产品方便又经济,这次使用还有一些小惊喜,这个后文再说。

Tidb Cloud Serverless Tier 的使用文档还是很全面的,详细情况请参考:使用 TiDB Cloud (Serverless Tier) 构建 TiDB 集群(https://docs.pingcap.com/zh/tidb/stable/dev-guide-build-cluster-in-cloud#%E7%AC%AC-1-%E6%AD%A5%E5%88%9B%E5%BB%BA%E5%85%8D%E8%B4%B9%E9%9B%86%E7%BE%A4)

集群建立完成后,Tidb Cloud Serverless Tier 有个小功能是可以显示主流客户端以及流行编程语言的连接代码。包括: MysqlCli、MyCli、JDBC、Python、golang以及Nodejs。

嗯?rust 的代码在哪儿?很遗憾没有rust的代码。而且为了安全起见,Tidb Cloud Serverless Tier 貌似只支持安全连接。在查找文档过程中rust 的 数据库驱动和很多orm文档中也没有关于安全详细的描述,不少思路是在issues里面给出的。索性把rust 连接 mysql 主流方式的安全连接代码都记录下来,一来给自己留个备忘,二来给需要的同学做个提示。

以下实例所使用的的标的建表语句如下

CREATE TABLE IF NOT EXISTS sample (
        id BIGINT NOT NULL ,
        name VARCHAR(128) NOT NULL,
        gender TINYINT NOT NULL,
        mobile VARCHAR(11) NOT NULL,
        create_time DATETIME NOT NULL, 
        update_time DATETIME NOT NULL, 
        PRIMARY KEY(id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

mysql rust driver

rust-mysql-simple(https://github.com/blackbeam/rust-mysql-simple),纯 rust 实现的 mysql 驱动。

依赖

[dependencies]
# mysql origin
mysql = "*"

代码

use chrono::Local;
use mysql::prelude::*;
use mysql::*;
use rbatis::snowflake::new_snowflake_id;
use serde::Deserialize;
use serde::Serialize;


pub const TABLE_NAME: &str = "sample";


#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BizOrigin {
    pub id: i64,
    pub name: String,
    pub gender: u8,
    pub mobile: String,
    pub create_time: Option,
    pub update_time: Option,
}


fn main() -> std::result::Result<(), Box> {
    let fmt = "%Y-%m-%d %H:%M:%S";
    // 原生方式连接
    let cert_path = std::path::Path::new("/etc/ssl/cert.pem");
    let ssl_opts = SslOpts::default().with_root_cert_path(Some(cert_path));
    let opts = OptsBuilder::new()
        .ip_or_hostname(Some("gateway01.us-east-19.prod.aws.tidbcloud.com"))
        .tcp_port(4000)
        .user(Some("tidbcloudtier.root"))
        .pass(Some("xxxxxxxxxxxx"))
        .ssl_opts(ssl_opts)
        .db_name(Some("test"));


    let mut conn_origin = Conn::new(opts)?;
    let (_, cipher_origin): (Value, String) = "SHOW STATUS LIKE 'Ssl_cipher'"
        .first(&mut conn_origin)?
        .unwrap();
    println!(">>>>> Cipher in use from origin: {}", cipher_origin);


    let create_statment = format!(
        "
    CREATE TABLE IF NOT EXISTS {} (
        id BIGINT NOT NULL ,
        name VARCHAR(128) NOT NULL,
        gender TINYINT NOT NULL,
        mobile VARCHAR(11) NOT NULL,
        create_time DATETIME NOT NULL, 
        update_time DATETIME NOT NULL, 
        PRIMARY KEY(id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;",
        TABLE_NAME
    );
    conn_origin.query_drop(create_statment)?;


    let bizes = vec![
        BizOrigin {
            id: new_snowflake_id(),
            name: "Bob".to_string(),
            gender: 1,
            mobile: "13037777876".to_string(),
            create_time: Some(Local::now().format(fmt).to_string()),
            update_time: Some(Local::now().format(fmt).to_string()),
        },
        BizOrigin {
            id: new_snowflake_id(),
            name: "Jecika".to_string(),
            gender: 0,
            mobile: "13033457876".to_string(),
            create_time: Some(Local::now().format(fmt).to_string()),
            update_time: Some(Local::now().format(fmt).to_string()),
        },
    ];


    conn_origin.exec_batch(
        r"insert into sample (id,name,gender,mobile,create_time,update_time) 
    values (:id,:name,:gender,:mobile,:create,:update)",
        bizes.iter().map(|p| -> Params {
            params! {
                "id"=>p.id,
                "name"=>p.name.to_owned(),
                "gender"=>p.gender.to_owned(),
                "mobile"=>p.mobile.to_owned(),
                "create"=>p.create_time.as_ref(),
                "update"=>p.update_time.as_ref()
            }
        }),
    )?;


    // Let's select payments from database. Type inference should do the trick here.
    let selected_bizs = conn_origin.query_map(
        "SELECT id,name,gender,mobile,create_time,update_time from sample",
        |(id, name, gender, mobile, create_time, update_time)| BizOrigin {
            id,
            name,
            gender,
            mobile,
            create_time,
            update_time,
        },
    )?;
    println!("selected result {:?}", selected_bizs);


    Ok(())
}

代码并不复杂,首先创建SslOpts,指定CA文件的位置;然后使用OptsBuilder 生成链接配置信息;最后创建Connection。后面是执行表创建以及验证链接,最后是对标的 insert 和 select 操作。

sqlx

sqlx(https://github.com/launchbadge/sqlx)是纯 Rust 编写的异步 SQL Crate。

依赖

[dependencies]
# sqlx
sqlx = "0.6.2"

代码

use futures::TryStreamExt;
use sqlx::mysql::MySqlPoolOptions;


#[tokio::main]
async fn main() {
    let sqlx_opts = sqlx::mysql::MySqlConnectOptions::new()
        .host("gateway01.us-east-19.prod.aws.tidbcloud.com")
        .port(4000)
        .database("test")
        .username("tidbcloudtier.root")
        .password("xxxxxxxxxxxx")
        .ssl_ca("/etc/ssl/cert.pem");


    let pool = MySqlPoolOptions::new()
        .connect_with(sqlx_opts)
        .await
        .unwrap();  


    let mut rows = sqlx::query("select * from sample").fetch(&pool);
    while let Some(row) = rows.try_next().await.unwrap() {
        println!("row is {:?}", row);
    }
}

SeaORM

SeaORM(https://github.com/SeaQL/sea-orm)是在 sqlx 之上构建的 orm 框架。

依赖

[dependencies]
# SeaORM
sqlx = "0.6.2"
sea-orm = { version = "0.10.6", features = [ "sqlx-mysql", "runtime-async-std-native-tls", "macros" ] }

代码

use sea_orm::ConnectionTrait;
use sea_orm::DbBackend;
use sea_orm::SqlxMySqlConnector;
use sea_orm::{FromQueryResult, Statement as sea_statment};
use sqlx::MySqlPool;


#[derive(Debug, FromQueryResult)]
pub struct SeaOrmBiz {
    pub id: i64,
    pub name: String,
    pub gender: Option,
    pub mobile: String,
    pub create_time: chrono::NaiveDateTime,
    pub update_time: chrono::NaiveDateTime,
}


#[tokio::main]
async fn main() {
    let sqlx_opts = sqlx::mysql::MySqlConnectOptions::new()
        .host("gateway01.us-east-19.prod.aws.tidbcloud.com")
        .port(4000)
        .database("test")
        .username("tidbcloudtier.root")
        .password("xxxxxxxxx")
        .ssl_ca("/etc/ssl/cert.pem");


    let pool = MySqlPool::connect_with(sqlx_opts).await.unwrap();
    let db = SqlxMySqlConnector::from_sqlx_mysql_pool(pool);


    let rs = db
        .execute(sea_statment::from_string(
            db.get_database_backend(),
            "select 1 from dual;".to_string(),
        ))
        .await;
    println!(">>>>> Cipher in use from sea_orm:{:?}", rs);


    let biz: Vec = SeaOrmBiz::find_by_statement(sea_statment::from_sql_and_values(
        DbBackend::MySql,
        r#"SELECT * FROM sample;"#,
        vec![],
    ))
    .all(&db)
    .await
    .unwrap();
    println!(">>>>> selet rs is {:?}", biz);
}

SeaOrm 依赖 sqlx。首先构建 sqlx::MySqlConnectOptions 然后根据 MySqlConnectOptions 构建 sqlx::MySqlPool 最后构建 sea_orm::SqlxMySqlConnector 用于与 mysql 通信。

Rbatis

rbatis:https://github.com/rbatis/rbatis

依赖

[dependencies]
# rbatis integration
rbs = "0.1.13"
rbatis = "4.0.44"
rbdc-mysql = "0.1.18"

代码

use rbatis::rbdc::datetime::FastDateTime;
use rbatis::Rbatis;
use rbdc_mysql::options::MySqlConnectOptions;
use rbdc_mysql::{driver::MysqlDriver, options::MySqlSslMode as rbdc_MysqlSslMode};
use rbs::to_value;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;


pub const TABLE_NAME: &str = "sample";


#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BizRbatis {
    pub id: Option,
    pub name: Option,
    pub gender: Option,
    pub mobile: Option,
    pub create_time: Option,
    pub update_time: Option,
}
rbatis::crud!(BizRbatis {}, TABLE_NAME);


#[tokio::main]
async fn main() -> std::result::Result<(), Box> {
    // rbatis 连接
    let rb = Rbatis::new();
    let opt = MySqlConnectOptions::new()
        .host("gateway01.us-east-19.prod.aws.tidbcloud.com")
        .port(4000)
        .database("test")
        .username("tidbcloudtier.root")
        .password("xxxxxxxxxx")
        .ssl_mode(rbdc_MysqlSslMode::VerifyIdentity)
        .ssl_ca("/etc/ssl/cert.pem");
    rb.init_opt(MysqlDriver {}, opt).unwrap();
    rb.get_pool().unwrap().resize(3);


    let sql_show_ssl_cipher = "SHOW STATUS LIKE 'Ssl_cipher'";


    let cipher_rbatis = rb
        .fetch_decode::>>(sql_show_ssl_cipher, vec![])
        .await;


    println!(">>>>> Cipher in use from rbatis: {:?}", cipher_rbatis);


    let sql_select_one = format!("select * from {} limit ?;", TABLE_NAME);
    let row = rb
        .fetch_decode::(&sql_select_one, vec![to_value!(1)])
        .await;
    println!(">>>>> rbatsis select result={:?}", row);


    Ok(())
}

首先,新建一个Rbatis struct;构建 rbdc_mysql::options::MySqlConnectOptions (rbdc 相当于java体系里的jdbc,是rbatis的衍生项目);最后通过配置好的 rbdc_mysql::options::MySqlConnectOptions 初始化 Rbatis。

后记

在这次实验中笔者也试图使用 Diesel(https://github.com/diesel-rs/diesel) 建立 mysql 安全连接,不过在编译的时候失败,未入门先放弃。Diesel 由于开发时间久远,彼时各个数据库的 rust 原生驱动缺失,所以大量采用 c/c++ driver进行构建,这次编译失败也是因为在macos上找不到 mysqlclient 导致。有对 Diesel 强依赖的同学可以继续探索。

再来说说对 SeaOrm 和 Rbatis 的直观感受。SeaOrm 构建实体比较麻烦,如果不是通过工具手工构建实体比较烧脑;实体中包含各种与其他实体的关系;动态sql 可以通过 sea_query 工具包来构建。Rbatis 构建实体心智负担就小很多,一张表一个实体;动态 sql 可以通过 HtmlSql 和 PySql 实现,sql 与代码充分解耦。rbdc 作为 Rbatis 的衍生项目,显然是要做 rust 生态的JDBC。从感觉上来讲 SeaOrm 更像 hibernate;而 Rbatis 是复刻 Mybatis。

数据库是应用程序打交道最多的外部资源,相关话题也很多,有机会再和大家聊聊 rust 与 数据库打交道的更多细节。

咱们下期见。

相关推荐

Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优
Nginx 响应提速10倍,你需要知道的缓存性能优化——FastCGI调优

Nginx缓存优化是帮助大家提升网站性能的重要操作之一,proxy_cache主要用于反向代理时,对后端内容源服务器进行缓存;fastcgi_cache主要用于...

2024-05-20 14:44 gowuye

王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动
王者荣耀天魔缭乱和逐梦之音返场活动地址 3月22日开启返场活动

王者荣耀官方终于确定了天魔缭乱和逐梦之音的返场活动,这让不少小伙伴乐开了花,返场活动将会在3月22日开启,下面就带来王者荣耀天魔缭乱和逐梦之音返场活动地址!王者...

2024-05-20 14:44 gowuye

常见的嵌入式web服务器有哪些?

嵌入式WEB服务器常见的有:Lighttpd,Shttpd,Thttpd,Boa,Mini_httpd,Appweb,Goahead。Lighttpd地址:http://www.light...

简述几款常见的嵌入式web服务器
简述几款常见的嵌入式web服务器

嵌入式web服务器,是web服务器当中的一种,是基于嵌入式系统而实现的web服务器。指的是在嵌入式系统(通俗点就是单片机系统)上实现的一个web服务器,可以通过...

2024-05-20 14:44 gowuye

教你如何利用fastcgi_cache缓存加速WordPress

在使用nginx缓存之前,必须在nginx里面加载专门的模块,这个模块叫做ngx_cache_purge。添加ngx_cache_purge模块下载ngx_cache_purge模块ngx_cache...

扫描WordPress漏洞

检测已知漏洞WPScan是一款广泛使用的WordPress安全扫描工具,它的一项重要功能是检测已知漏洞。在这篇文章中,我们将深入探讨WPScan如何检测已知漏洞,并结合实际示例,帮助读者更好地理解和应...

消灭 Bug!推荐几个给力的开源 Bug 跟踪工具
消灭 Bug!推荐几个给力的开源 Bug 跟踪工具

在这个充满bug的世界里,最遥远的距离不是生与死,而是你亲手制造的bug就在你眼前,你却怎么都找不到它。因此本文准备了7款优秀的开源bug跟踪系...

2024-05-20 14:43 gowuye

生物信息分析入门全攻略

生物信息学是生命科学研究的重大前沿领域,未来将占据生命科学研究的半壁江山。已经有越来越多的小伙伴投入到生物信息的学习中,但是入门难、深入慢、摸不到方向等都成为持续学习的拦路虎。本文根据生物信息技术大牛...

elkb实践经验,再赠送一套复杂的配置文件
elkb实践经验,再赠送一套复杂的配置文件

原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。宝剑锋从磨砺出,梅花香自苦寒来。诗人白居易,三月下江南,看到沿路开放的桃花,心潮澎湃...

2024-05-20 14:43 gowuye

超详细从0到1 搭建ELK监控
超详细从0到1 搭建ELK监控

监控分类?Metrics用于记录可聚合的数据。例如,1、队列的当前深度可被定义为一个度量值,在元素入队或出队时被更新;HTTP请求个数可被定义为一个计数器,...

2024-05-20 14:42 gowuye

嵌入式开发 之Web配置页面开发
嵌入式开发 之Web配置页面开发

1.PHP是最好的语言??开发动态页面首选的语言是PHP,村村不能在这里忽悠人,如果你的硬件性能允许切略懂PHP,看到这里就可以退出了。本文面向的受众是Linu...

2024-05-20 14:42 gowuye

Python开发一个网站目录扫描工具用来检测网站是否有漏洞?
Python开发一个网站目录扫描工具用来检测网站是否有漏洞?

开发一个网站目录扫描工具是用来检测网站是否有非法目录请求的一个常见需求之一,我们要通过这个扫描工具来找到通过某个域名可以访问到的网站路径,可能对于有些系统来讲,...

2024-05-20 14:42 gowuye

创建一个类似Youtube的Id——使用PHP/Python/JS/Java/SQL

id通常都是用数字,不巧的是只有10个数字来使用,所以如果你有很多的记录,id往往变得非常冗长。当然对于计算机来说无所谓,但我们更希望id尽可能短。所以我们如何能使id变短?我们可以利用字母让它们附加...

快速云:有助于移动应用安全开发的五条妙计
快速云:有助于移动应用安全开发的五条妙计

许多企业不断地向其开发团队提供培训。但是某些漏洞,如早在十多年前就发现的SQL注入,如今仍广泛存在于各种应用中。因而,安全培训永不过时。在开发移动应用时,开发者...

2024-05-20 14:41 gowuye

洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映
洛杉矶国际电影节最佳动画短片奖影片《G’DAY》正式全网上映

7月2日,由M&CSaatchi创作,由深受好评的澳大利亚导演迈克尔·格雷西执导的动画短片《G’day》,正式在全网上映。该影片因其出色的创意赢得了洛...

2024-05-20 14:41 gowuye

取消回复欢迎 发表评论: