diesel rust_在Rust和Diesel中创建便捷的微服务

diesel rust

Rust是目前最受关注的语言之一。 虽然该语言在去年五月刚刚达到1.0,但它在稳定性和功能方面Swift发展。 由于这种快速而稳定的增长,Rust确实引起了编程界许多人的关注。 但是,是什么使Rust如此吸引人? 这值得我们花费时间和精力吗?

使Rust真正令我兴奋的是,它开始被公认为C / C ++替代品。 我个人从来没有真正关心过C和C ++的设计,但是Rust似乎解决了许多我不喜欢这些语言的问题。 更重要的是,它使我再次对低级系统编程感到兴奋!

作为Rubyist,我对Helix等项目非常感兴趣,这些项目允许开发人员使用Rust作为扩展Ruby库(而不是C)功能的手段。 随着Helix的孕育和成熟,Rust很可能在不久的将来成为基于Ruby的堆栈的重要组成部分。

Rust社区中发生了很多值得一提的事情。 但是,尽管我认为Rust的未来令人兴奋,但重要的是要了解Rust在当今的应用方式

我之前写过这样的想法,即在您的堆栈中添加相当新的语言或框架可能会带来相当大的风险。 在较年轻的语言中,突破性变化往往发生得更多,Rust也不例外。 尽管Dropbox和Mozilla等公司在生产中都有大量基于Rust的项目,但我仍然相信,与他们相比,让我们承担较小的风险是明智的。

这就是微服务在您的体系结构中具有强大优势的地方。 它们使您能够冒着Rust等新事物的风险,并以相当低的成本将其实施到您的生态系统中。 同样,这并不能消除使用较年轻语言的风险和风险。 但是,它无疑使您可以承担较小剂量的风险。

我想探索的想法是在Rust中创建一个小型微服务的感觉。 有多难? 可能吗 我需要什么工具? 这个想法伴随着健康的问题清单。

因此,我们将要做的是:我们将假设我们有一个具有基本API的博客应用程序。 尽管听起来有些愚蠢,但我们将创建一个使用此API的Rust服务,并将每个博客帖子存储到数据库中。 假设我们有一个看起来像这样的Blog API项目,让我们利用Rust来创建一些很棒的东西!

培养锈

在开始任何这些之前,我们需要找出将Rust引入我们的编程环境的最佳方法。

有两种非常简单的方法来设置Rust。 但是,我个人使用RustUp来处理Rust版本。 这很像Ruby的RVM或Node的NVM。

RustUp的出色之处在于它能够轻松在Rust的两个发行渠道之间切换。 对于那些不知道的人,Rust本质上有两种语言实例。 一个实例(或通道)被称为“稳定”,其中包含稳定的功能。 另一个称为Nightly,它包含边缘功能。

对于我们今天要构建的内容,我将使用Nightly。 这似乎是一个奇怪的选择,因为我们正在寻求创建更“稳定”的东西。 但是,对于Rust来说,对未来进行计划会比较明智,因为我们知道许多这些功能最终都将进入稳定的Rust。

要在RustUp中进行设置:

rustup install stable
rustup install nightly
rustup default nightly

这应该将Nightly安装在我们的钥匙串中,并使其成为我们创建的任何新项目的默认Rust源。 如果您想使用Stable,请不要担心。 您随时可以随意切换回它!

现在通过Cargo(Rust的本地包管理器)创建Rust项目:

cargo new rustweb

这应该在当前目录中创建一个非常简单的Rust项目。 如果您打开该项目,则可以看到它包含一个名为Cargo.toml的文件。 这是我们对文件的依赖关系存储的地方。

Cargo.toml与我们在NPM的package.json或Rails的Gemfile看到的非常相似。 该文件列出了项目依赖项,而我们出色的工具可帮助我们找出其余部分。

用柴油生Rust

一群很棒的软件开发人员最近创建了一个称为Diesel的Rust框架。 柴油很有趣,因为它从Rails的Active Record中汲取了许多ORM设计灵感。 基于我在Rails中的经验,我发现Diesel确实很有吸引力并且易于使用。

Diesel需要一些额外的设置,但是它使我们能够相对轻松地根据自己选择的数据库查询数据。 它还将帮助我们稳定地构建微服务。 让我们来看看。

设置柴油

我们将密切关注Diesel已发布的教程 。 但是,我们将在中途偏离它的路径来创建我们的服务。 如果我说的话让您感到困惑,请参阅其指南以获取清晰的信息。

要将Diesel和其他依赖项引入我们的Rust项目,我们需要更新Cargo.toml以显示以下内容:

Cargo.toml

[dependencies]
diesel = "0.8.0"
diesel_codegen = { version = "0.8.0", features = ["postgres"] }
dotenv = "0.8.0"
hyper = { version = "0.9", default-features = false, features = ["security-framework"] }
rustc-serialize = "0.3.19"

要将这些库纳入我们的项目,请运行cargo build 。 这不仅会获取库,还会针对它们构建我们的应用程序实例。 这对于确定哪些依赖项对您的代码库有效,而对您的代码库无效则特别有用。

现在我们已经将Diesel的库集成到我们的Rust应用程序中,但我们仍然需要一种通过命令行来控制它的方法。 为此,我们要运行以下命令来安装diesel_clicargo install diesel_cli

接下来,我们将告诉Diesel在哪里寻找我们的数据库。 在我们的案例中,我们正在使用自己的新数据库创建微服务。 对于我们的应用程序,我建议使用Postgres 。 但是,如果您需要其他内容,Diesel可以处理各种数据库选项。

我们将其数据库命名为: rustweb

echo DATABASE_URL=postgres://username:password@localhost/rustweb > .env

为了完成设置,我们将运行diesel setup以完成工作。 这应该将我们的项目和环境配置为与Diesel框架一起正常工作。 在执行一些简单的命令之后,我们就可以开始绘制数据库的基础了。

制作我们的数据库

由于我们的应用程序是微服务,因此我们假设我们的项目依赖于其他类型的应用程序。 无论该应用程序是巨型独石还是一系列较小的微服务,我们都需要设计该微服务,以便它可以转换和存储其他来源的数据。 有效的微服务就像可互换的部分,我们的应用程序需要反映这种模块化。

我们将通过构建一个posts表来开始此过程,该表将存储我们通过外部API消费的博客文章。 要在Diesel中进行构建,我们将输入diesel migration generate create_posts

生成器将创建一个up.sqldown.sql文件,这将帮助我们构建数据库。 我不确定您编写纯SQL的舒适程度如何,但是Diesel是开始学习的好地方。

up.sql迁移中,我们将编写逻辑以在数据库中创建实际的posts表。 给定一个抽象的Post模型,它应该看起来像这样。

Post
- id (int)
- title (string)
- body (text / string)

我们将按照以下方式编写SQL迁移:

up.sql

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR NOT NULL,
    body TEXT NOT NULL
)

在继续之前,让我们谈谈Rust的原始数据结构。

Rust使用的底层数据类型非常类似于C和C ++。 对于那些处理Ruby或JavaScript的人来说,这似乎有些陌生。 但是,学习如何处理不同的数据原语和结构当然不会造成伤害。 Rust使您可以更好地控制要处理的数据。 但是,它确实需要更多步骤才能正确处理所有内容。

我们的down.sql看起来没有那么吓人,因为它的工作非常简单:删除posts表。

down.sql

DROP TABLE posts

为了在数据库中实际创建表,我们需要运行diesel migration run

而已! 现在,我们有了所需的数据库。 现在,我们有了基本的数据库结构,让我们处理Diesel应用程序的内部逻辑。

建立库逻辑

还记得Cargo生成的其余那些文件吗? 我们将从src/lib.rs开始。 这是我们的库文件,我们将使用它来将所有单个部分连接在一起。 将其视为整个应用程序的主要方法。

首先,我们需要编写一些内容以将我们连接到刚刚创建的数据库。 我们还将要构建我们的模型并导入我们的模式。 我们将一口气完成所有这些工作:

src/lib.rs

#![feature(proc_macro)]

#[macro_use] extern crate diesel;
#[macro_use] extern crate diesel_codegen;
extern crate dotenv;

pub mod schema;
pub mod models;

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;

use self::models::{Post, NewPost};

pub fn establish_connection() -> PgConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url)
        .expect(&format!("Error connecting to {}", database_url))
}

pub fn create_post(conn: &PgConnection, title: &str, body: &str) -> Post {
    use schema::posts;

    let new_post = NewPost {
        title: title,
        body: body,
    };

    diesel::insert(≠w_post).into(posts::table)
        .get_result(conn)
        .expect("Error saving new post")
}

到目前为止,我们拥有的是一大堆标题内容和两个函数。 似乎仅需几种方法即可完成许多设置。

但是,将此视为我们应用程序的基础。 我们正在做的所有事情都将贯穿于此。 这很重要,因为它将整个事情联系在一起。

它的值得指出的是怎么establish_connection方法只需要在数据库URL通过.env我们创建并试图与它连接的Postgres。 很简单!

如果您仔细查看具有以下内容的部分:

pub mod schema;
pub mod models;

您开始怀疑架构和模型部分的实际位置。 好吧,我们将要编写它们!

src/models.rs

use schema::posts;

#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String
}

#[derive(Insertable)]
#[table_name="posts"]
pub struct NewPost<'a> {
    pub title: &'a str,
    pub body: &'a str,
}

这应该创建一个可查询的Post对象和一个可插入的NewPost对象。 我们将串联使用这些数据,以针对我们的数据库插入和查询信息。 总体来说,这是一个非常简单的文件。 对schema.rs文件也可以这样说:

src/schema.rs

infer_schema!("dotenv:DATABASE_URL");

该文件所做的只是从您先前创建的.env文件中获取数据库架构,其中包含数据库URL。 这为我们与数据库交互提供了至关重要的联系。 这是Rust和Diesel多么简单的简短示例!

回到我们的lib.rs文件,我们有create_post函数。

pub fn create_post(conn: &PgConnection, title: &str, body: &str) -> Post {
    use schema::posts;

    let new_post = NewPost {
        title: title,
        body: body,
    };

    diesel::insert(≠w_post).into(posts::table)
        .get_result(conn)
        .expect("Error saving new post")
}

create_post很有趣,因为它接受Postgres连接,title属性和body属性,并NewPost创建一个NewPost对象。 我们在这里使用架构来定义在何处插入NewPost对象。

编写可执行脚本

现在,我们已经创建了一个坚实的基础,让我们编写直接与我们的API交互的Rust代码。

Rust库项目的工作方式是在/bin/文件夹中创建可执行文件。 然后,我们从库中获取代码片段,并使用它们来执行任务。 为了演示,我们将创建以下内容:

get_posts_from_source.rs

extern crate rustweb;
extern crate diesel;
extern crate hyper;
extern crate rustc_serialize;

use self::rustweb::*;

use hyper::{Client};
use std::io::Read;
use rustc_serialize::json;
use std::env::args;

// Automatically generates traits to the struct
#[derive(RustcDecodable, RustcEncodable)]
pub struct PostSerializer {
    id: u8,
    title: String,
    body: String,
}

fn main() {
    let start_s = args().nth(1).expect("Please provide a min id");
    let start : i32 = match start_s.parse() {
        Ok(n) => {
           n
       },
       Err(_) => {
           println!("error: first argument not an integer");
           return;
       },
   };
    let stop_s = args().nth(2).expect("Please provide a max id");
    let stop : i32 = match stop_s.parse() {
        Ok(n) => {
           n
       },
       Err(_) => {
           println!("error: second argument not an integer");
           return;
       },
   };
    for x in start..stop {
        let url = format!("http://localhost:3000/api/v1/posts/{}", x);
        let response = get_content(&url).unwrap();
        let decoded: PostSerializer = json::decode(&response).unwrap();
        create_post_from_object(&decoded);
    }
}

fn get_content(url: &str) -> hyper::Result {
    let client = Client::new();
    let mut response = try!(client.get(url).send());
    let mut buffer = String::new();
    try!(response.read_to_string(μt buffer));
    Ok(buffer)
}

fn create_post_from_object(post: &PostSerializer) {
    let connection = establish_connection();
    println!("==========================================================");
    println!("Title: {}", post.title);
    println!("==========================================================\n");
    println!("{}\n", post.body);
    create_post(&connection, &post.title, &post.body);
}

ew。 这要花很多钱。让我们从标头和序列化器模型开始将其分解为几部分:

extern crate rustweb;
extern crate diesel;
extern crate hyper;
extern crate rustc_serialize;

use self::rutweb::*;

use hyper::{Client};
use std::io::Read;
use rustc_serialize::json;
use std::env::args;

// Automatically generates traits to the struct
#[derive(RustcDecodable, RustcEncodable)]
pub struct PostSerializer {
    id: u8,
    title: String,
    body: String,
}

这段代码的有趣之处在于,我们正在做很多以前见过的事情。 我们正在导入我们的项目和Diesel。 但是,现在还有其他一些人加入了该党。 该文件正在使用Rust的Hyper库和Rust的序列化器功能。

这些额外的库使我们可以从服务器请求并序列化JSON响应。 Hyper完成请求。 序列化程序执行序列化。 很简单。

接下来,我们将看一下文件get_contentcreate_post_from_object

fn get_content(url: &str) -> hyper::Result {
    let client = Client::new();
    let mut response = try!(client.get(url).send());
    let mut buffer = String::new();
    try!(response.read_to_string(μt buffer));
    Ok(buffer)
}

get_content使用Hyper库从我们的本地主机URL中提取数据。 它读取响应并将其转换为Rust可以理解的字符串。 如果所有这些都成功,它将返回API Post对象的String JSON表示形式。

fn create_post_from_object(post: &PostSerializer) {
    let connection = establish_connection();
    println!("==========================================================");
    println!("Title: {}", post.title);
    println!("==========================================================\n");
    println!("{}\n", post.body);
    create_post(&connection, &post.title, &post.body);
}

create_post_from_object使用序列化的Post对象并建立与我们数据库的连接。 然后,它利用我们的create_post函数在数据库中创建Post对象!

最后,我们将看到main()方法如何将所有这些功能联系在一起。

fn main() {
    let start_s = args().nth(1).expect("Please provide a min id");
    let start : i32 = match start_s.parse() {
        Ok(n) => {
           n
       },
       Err(_) => {
           println!("error: first argument not an integer");
           return;
       },
   };
    let stop_s = args().nth(2).expect("Please provide a max id");
    let stop : i32 = match stop_s.parse() {
        Ok(n) => {
           n
       },
       Err(_) => {
           println!("error: second argument not an integer");
           return;
       },
   };
    for x in start..stop {
        let url = format!("http://localhost:3000/api/v1/posts/{}", x);
        let response = get_content(&url).unwrap();
        let decoded: PostSerializer = json::decode(&response).unwrap();
        create_post_from_object(&decoded);
    }
}

我们将迭代X次,并在API的一系列记录中创建Post对象。 我们将利用上面刚刚创建的PostSerializer将数据组织成Diesel可以使用的东西。

假设我们的API数据库中有10个帖子,我们可以通过在控制台中键入以下内容来运行脚本:

cargo run --bin get_posts_from_source 1 10

您应该看到我们的代码正在运行,输出API发布数据并将其存储到我们的rustweb数据库中!

让我们从这里经历的所有事情解压缩。

结语

我们已经阅读了许多材料,可能会感到不知所措或过于复杂。 但是,我鼓励您使用它来帮助确定编写Rust微服务是否适合您。 可以将其视为草绘一个想法,希望它会变得非常有用。

如果您对将来我将如何应用这个想法感兴趣,请跟随GitHub上的项目! 我很乐意看到一些有关我的表现的意见和建议。

Rust可能需要一段时间才能掌握。 但是,这是一种探索和学习的有益语言。 也许在您的学习中,您可能会发现一些对您和您的组织有益的东西!

翻译自: https://www.javacodegeeks.com/2016/12/creating-expedient-microservices-rust-diesel.html

diesel rust

你可能感兴趣的:(数据库,c++,python,java,编程语言)