在Rust web服务中使用Redis的方法

Redis一直是网络生态系统的重要组成部分,它经常用作缓存、消息代理或简单地用作数据存储。

在这篇文章中,我们将演示如何在一个Rust web应用程序中使用Redis。

我们将探索两种种使用Redis的方法:

  • 使用同步连接池
  • 使用异步连接池

对于同步池,我们使用基于r2d2库的r2d2-redis。我们在异步解决方案中使用mobc,还有许多其他异步连接池,如deadpool和bb8,它们都以类似的方式工作。

话不多说,让我们开始吧!

新建一个项目:

cargo new rust-redis-web-example

在Cargo.toml中加入依赖:

[dependencies]</code>
<code>tokio = { version = "1.19", features = ["full"] }</code>
<code>warp = "0.3.2"</code>
<code>redis = "0.21"</code>
<code>r2d2_redis = "0.14"</code>
<code>mobc-redis = "0.7"</code>
<code>mobc = "0.7"</code>
<code>thiserror = "1.0"

首先,让我们设置一些共享类型,在main.rs中:

type WebResult= std::result::Result;</code>
<code>type Result= std::result::Result;</code>
<code>const REDIS_CON_STRING: &str = "redis://127.0.0.1/";

定义这两个Result类型是为了节省一些输入,并表示内部Errors (Result)和外部Errors (WebResult)。

接下来,定义这个内部error类型并为其实现Reject,以便它可以处理从程序返回的HTTP错误。

#[derive(Error, Debug)]</code>
<code>pub enum Error {</code>
<code>    #[error("mobc error: {0}")]</code>
<code>    MobcError(#[from] MobcError),</code>
<code>    #[error("r2d2 error: {0}")]</code>
<code>    R2D2Error(#[from] R2D2Error),</code>
<code>}</code>
<code>#[derive(Error, Debug)]</code>
<code>pub enum MobcError {</code>
<code>    #[error("could not get redis connection from pool : {0}")]</code>
<code>    RedisPoolError(mobc::Error),</code>
<code>    #[error("error parsing string from redis result: {0}")]</code>
<code>    RedisTypeError(mobc_redis::redis::RedisError),</code>
<code>    #[error("error executing redis command: {0}")]</code>
<code>    RedisCMDError(mobc_redis::redis::RedisError),</code>
<code>    #[error("error creating Redis client: {0}")]</code>
<code>    RedisClientError(mobc_redis::redis::RedisError),</code>
<code>}</code>
<code>#[derive(Error, Debug)]</code>
<code>pub enum R2D2Error {</code>
<code>    #[error("could not get redis connection from pool : {0}")]</code>
<code>    RedisPoolError(r2d2_redis::r2d2::Error),</code>
<code>    #[error("error parsing string from redis result: {0}")]</code>
<code>    RedisTypeError(r2d2_redis::redis::RedisError),</code>
<code>    #[error("error executing redis command: {0}")]</code>
<code>    RedisCMDError(r2d2_redis::redis::RedisError),</code>
<code>    #[error("error creating Redis client: {0}")]</code>
<code>    RedisClientError(r2d2_redis::redis::RedisError),</code>
<code>}</code>
<code>impl warp::reject::Reject for Error {}

上面定义了通用的错误类型和我们将实现的每一种使用Redis方法的错误类型。错误本身只是处理连接、池的创建和命令执行等 错误。

使用r2d2(同步)

r2d2 crate是第一个被广泛使用的连接池,它现在仍然被广泛使用。Redis的连接池是r2d2-redis crate。

在src目录下创建r2d2_pool.rs文件,因为我们现在使用的是连接池,所以这个池的创建也需要在r2d2模块中处理。

use crate::{R2D2Error::*, Result, REDIS_CON_STRING};</code>
<code>use r2d2_redis::redis::{Commands, FromRedisValue};</code>
<code>use r2d2_redis::{r2d2, RedisConnectionManager};</code>
<code>use std::time::Duration;</code>
<code>pub type R2D2Pool = r2d2::Pool;</code>
<code>pub type R2D2Con = r2d2::PooledConnection;</code>
<code>const CACHE_POOL_MAX_OPEN: u32 = 16;</code>
<code>const CACHE_POOL_MIN_IDLE: u32 = 8;</code>
<code>const CACHE_POOL_TIMEOUT_SECONDS: u64 = 1;</code>
<code>const CACHE_POOL_EXPIRE_SECONDS: u64 = 60;</code>
<code>pub fn connect() -> Result> {</code>
<code>    let manager = RedisConnectionManager::new(REDIS_CON_STRING).map_err(RedisClientError)?;</code>
<code>    r2d2::Pool::builder()</code>
<code>        .max_size(CACHE_POOL_MAX_OPEN)</code>
<code>        .max_lifetime(Some(Duration::from_secs(CACHE_POOL_EXPIRE_SECONDS)))</code>
<code>        .min_idle(Some(CACHE_POOL_MIN_IDLE))</code>
<code>        .build(manager)</code>
<code>        .map_err(|e| RedisPoolError(e).into())</code>
<code>}

定义一些常量来配置池,如打开和空闲连接,连接超时和连接的生命周期,池本身是使用RedisConnectionManager创建的,传递给它的参数是redis连接字符串。

不要太担心配置值,大多数连接池都有一些缺省值,这些缺省值将适用于基本应用程序。

我们需要一种方法来获得连接池,然后向Redis设置和获取值。

pub fn get_con(pool: &R2D2Pool) -> Result{</code>
<code>    pool.get_timeout(Duration::from_secs(CACHE_POOL_TIMEOUT_SECONDS))</code>
<code>        .map_err(|e| {</code>
<code>            eprintln!("error connecting to redis: {}", e);</code>
<code>            RedisPoolError(e).into()</code>
<code>        })</code>
<code>}</code>
<code>pub fn set_str(pool: &R2D2Pool, key: &str, value: &str, ttl_seconds: usize) -> Result<()> {</code>
<code>    let mut con = get_con(&pool)?;</code>
<code>    con.set(key, value).map_err(RedisCMDError)?;</code>
<code>    if ttl_seconds > 0 {</code>
<code>        con.expire(key, ttl_seconds).map_err(RedisCMDError)?;</code>
<code>    }</code>
<code>    Ok(())</code>
<code>}</code>
<code>pub fn get_str(pool: &R2D2Pool, key: &str) -> Result{</code>
<code>    let mut con = get_con(&pool)?;</code>
<code>    let value = con.get(key).map_err(RedisCMDError)?;</code>
<code>    FromRedisValue::from_redis_value(&value).map_err(|e| RedisTypeError(e).into())</code>
<code>}

我们尝试从池中获取连接,并配置超时时间。在set_str和get_str中,每次调用这些函数时都会调用get_con。

使用mobc(异步)

在src目录下创建r2d2_pool.rs文件,让我们定义配置并创建连接池。

use crate::{MobcError::*, Result, REDIS_CON_STRING};</code>
<code>use mobc::{Connection, Pool};</code>
<code>use mobc_redis::redis::{AsyncCommands, FromRedisValue};</code>
<code>use mobc_redis::{redis, RedisConnectionManager};</code>
<code>use std::time::Duration;</code>
<code>pub type MobcPool = Pool;</code>
<code>pub type MobcCon = Connection;</code>
<code>const CACHE_POOL_MAX_OPEN: u64 = 16;</code>
<code>const CACHE_POOL_MAX_IDLE: u64 = 8;</code>
<code>const CACHE_POOL_TIMEOUT_SECONDS: u64 = 1;</code>
<code>const CACHE_POOL_EXPIRE_SECONDS: u64 = 60;</code>
<code>pub async fn connect() -> Result{</code>
<code>    let client = redis::Client::open(REDIS_CON_STRING).map_err(RedisClientError)?;</code>
<code>    let manager = RedisConnectionManager::new(client);</code>
<code>    Ok(Pool::builder()</code>
<code>        .get_timeout(Some(Duration::from_secs(CACHE_POOL_TIMEOUT_SECONDS)))</code>
<code>        .max_open(CACHE_POOL_MAX_OPEN)</code>
<code>        .max_idle(CACHE_POOL_MAX_IDLE)</code>
<code>        .max_lifetime(Some(Duration::from_secs(CACHE_POOL_EXPIRE_SECONDS)))</code>
<code>        .build(manager))</code>
<code>}

这和r2d2非常相似,这不是巧合;许多连接池库都从r2d2出色的API中获得了灵感。

async fn get_con(pool: &MobcPool) -> Result{</code>
<code>    pool.get().await.map_err(|e| {</code>
<code>        eprintln!("error connecting to redis: {}", e);</code>
<code>        RedisPoolError(e).into()</code>
<code>    })</code>
<code>}</code>
<code>pub async fn set_str(pool: &MobcPool, key: &str, value: &str, ttl_seconds: usize) -> Result<()> {</code>
<code>    let mut con = get_con(&pool).await?;</code>
<code>    con.set(key, value).await.map_err(RedisCMDError)?;</code>
<code>    if ttl_seconds > 0 {</code>
<code>        con.expire(key, ttl_seconds).await.map_err(RedisCMDError)?;</code>
<code>    }</code>
<code>    Ok(())</code>
<code>}</code>
<code>pub async fn get_str(pool: &MobcPool, key: &str) -> Result{</code>
<code>    let mut con = get_con(&pool).await?;</code>
<code>    let value = con.get(key).await.map_err(RedisCMDError)?;</code>
<code>    FromRedisValue::from_redis_value(&value).map_err(|e| RedisTypeError(e).into())</code>
<code>}

现在看起来应该很熟悉了,传入池并在开始时获取连接,但这一次采用异步方式,使用async和await。

下一步就是把它们结合到一个warp web应用中,修改main.rs:

use std::convert::Infallible;</code>
<code>use mobc_pool::MobcPool;</code>
<code>use r2d2_pool::R2D2Pool;</code>
<code>use thiserror::Error;</code>
<code>use warp::{Rejection, Filter, Reply};</code>
<code>mod r2d2_pool;</code>
<code>mod mobc_pool;</code>
<code>type WebResult= std::result::Result;</code>
<code>type Result= std::result::Result;</code>
<code>const REDIS_CON_STRING: &str = "redis://127.0.0.1/";</code>
<code>#[tokio::main]</code>
<code>async fn main() {</code>
<code>    let mobc_pool = mobc_pool::connect().await.expect("can create mobc pool");</code>
<code>    let r2d2_pool = r2d2_pool::connect().expect("can create r2d2 pool");</code>
<code>    let mobc_route = warp::path!("mobc")</code>
<code>        .and(with_mobc_pool(mobc_pool.clone()))</code>
<code>        .and_then(mobc_handler);</code>
<code>    let r2d2_route = warp::path!("r2d2")</code>
<code>        .and(with_r2d2_pool(r2d2_pool.clone()))</code>
<code>        .and_then(r2d2_handler);</code>
<code>    let routes = mobc_route.or(r2d2_route);</code>
<code>    warp::serve(routes).run(([0, 0, 0, 0], 8080)).await;</code>
<code>}</code>
<code>fn with_mobc_pool(</code>
<code>    pool: MobcPool,</code>
<code>) -> impl Filter+ Clone {</code>
<code>    warp::any().map(move || pool.clone())</code>
<code>}</code>
<code>fn with_r2d2_pool(</code>
<code>    pool: R2D2Pool,</code>
<code>) -> impl Filter+ Clone {</code>
<code>    warp::any().map(move || pool.clone())</code>
<code>}</code>
<code>async fn mobc_handler(pool: MobcPool) -> WebResult{</code>
<code>    mobc_pool::set_str(&pool, "mobc_hello", "mobc_world", 60)</code>
<code>        .await</code>
<code>        .map_err(|e| warp::reject::custom(e))?;</code>
<code>    let value = mobc_pool::get_str(&pool, "mobc_hello")</code>
<code>        .await</code>
<code>        .map_err(|e| warp::reject::custom(e))?;</code>
<code>    Ok(value)</code>
<code>}</code>
<code>async fn r2d2_handler(pool: R2D2Pool) -> WebResult{</code>
<code>    r2d2_pool::set_str(&pool, "r2d2_hello", "r2d2_world", 60)</code>
<code>        .map_err(|e| warp::reject::custom(e))?;</code>
<code>    let value = r2d2_pool::get_str(&pool, "r2d2_hello").map_err(|e| warp::reject::custom(e))?;</code>
<code>    Ok(value)</code>
<code>}

用Docker启动一个本地Redis实例:

docker run -p 6379:6379 redis

接下来,运行 cargo run。

使用curl测试:

curl http://localhost:8080/r2d2</code>
<code>curl http://localhost:8080/mobc

到此这篇关于在Rust web服务中使用Redis的文章就介绍到这了,更多相关Rust 使用Redis内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 教你使用RustDesk 搭建一个自己的远程桌面中继服务器

    目录 应用背景 干货下载链接 搭建自己的rustdesk中转服务器 服务端安装 客户端配置 愉快使用 应用背景 现在很多商业远程软件要么收费,要么有或多或少的问题.因此急需一个可以自定义且方便快捷的远程桌面软件代替他们,rustdesk就是这样一个开源项目 干货下载链接 github地址:https://github.com/rustdesk/rustdesk 可以自行下载编译,或者按照自己的需求修改. release 可直接下载发布二进制 server端下载:https://rustdesk.

  • 在Rust web服务中使用Redis的方法

    Redis一直是网络生态系统的重要组成部分,它经常用作缓存.消息代理或简单地用作数据存储. 在这篇文章中,我们将演示如何在一个Rust web应用程序中使用Redis. 我们将探索两种种使用Redis的方法: 使用同步连接池 使用异步连接池 对于同步池,我们使用基于r2d2库的r2d2-redis.我们在异步解决方案中使用mobc,还有许多其他异步连接池,如deadpool和bb8,它们都以类似的方式工作. 话不多说,让我们开始吧! 新建一个项目: cargo new rust-redis-we

  • Web服务中的异常处理(2)

    Web服务的实现 为了达到这个示例的目的,我们创建一个名为CategoriesService的Web服务,选择一个可视化的C#ASP.NETWeb服务作为项目的模版.一旦创建项目,我们就添加一个名为AddCategories的方法,并且给这个方法添加下列代码: [WebMethod] publicboolAddCategories(stringxml) { try { using(SqlConnectionconn=newSqlConnection()) { if(ValidateXml(xml

  • Web服务中的异常处理(4)

    客户端的异常处理 这个部分,我们将看看在客户端怎样处理从Web服务中所抛出的异常.为了说明这个做法,我们来创建一个新项目CategoriesServiceClient.一旦项目被创建,就在默认的表单上添加一个命令按钮,并命名为btnInvoke.因为需要在客户端引用Web服务,所以在项目CategoriesService中添加一个WebReference.可以通过Project->AddReference菜单选项来完成添加.然后修改命令按钮的Click事件,如下所示. privatevoidbt

  • 修改Tomcat服务中的端口配置方法

    1.修改Tomcat服务中的端口配置: 分别修改安装目录下的conf子目录中的server.xml文件(注意:两个文件中对应的端口号要不一样),修改如下 : a.修改Shutdown端口(默认为8005端口),将8005修改为没有在使用的端口号,例如8055. <Server port="8005" shutdown="SHUTDOWN" debug="0"> b.修改http访问端口(默认为8080端口),将8080修改为tomca

  • python实现在windows服务中新建进程的方法

    本文实例讲述了python实现在windows服务中新建进程的方法.分享给大家供大家参考.具体实现方法如下: 需要安装的软件:python和pywin32,我这里装的分别是python-2.6.amd64.pywin32-217.win-amd64-py2.6 文件名:dma_ws.py #!python import win32serviceutil import win32service import win32event import os from subprocess import P

  • C语言实现在windows服务中新建进程的方法

    本文实例讲述了C语言实现在windows服务中新建进程的方法.分享给大家供大家参考.具体如下: 运行环境:visual stdio 2008 文件名:testService.c #include <windows.h> #include <stdio.h> #include <time.h> #include <tchar.h> HANDLE hMutex; SERVICE_STATUS ServiceStatus; SERVICE_STATUS_HANDL

  • springboot中使用redis的方法代码详解

    特别说明: 本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ​redis 作为一个高性能的内存数据库,如果不会用就太落伍了,之前在 node.js 中用过 redis,本篇记录如何将 redis 集成到 spring boot 中.提供 redis 操作类,和注解使用 redis 两种方式.主要内容如下: •docker 安装 redis •springboo

  • Bottle部署web服务及postman接口的方法

    Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Python的标准库外,其不依赖任何其他模块. from bottle import route, request, run import requests import cv2 import numpy as np @route('/testimg',method='POST')# def testimg(): try: #获取对应params值 result = {} result["nam

  • Docke中安装redis的方法

    一.搜索redis镜像 docker search redis 二.下载redis镜像 docker pull redis:latest 三.启动redis容器 --name 启动后的容器名称 --restart=always 自启动 -p 端口映射 -v 目录挂载 --requirepass 登陆密码 docker run -itd --name redis --restart=always -p 6379:6379 -v /data/docker/redis/config:/etc/redi

  • Java Web项目中解决中文乱码方法总结(三种最新方法)

    目录 前言 问题背景 下面我说三种方法供大家参考 方法一: 方法二: 第三种方法: Tomcat结构与介绍 bin conf lib logs temp webapps work 前言 JavaEE(Java Enterprise Edition),Java企业版,是一个用于企业级web开发平台.最早由Sun公司定制并发布,后由Oracle负责维护.JavaEE平台规范了在开发企业级web应用中的技术标准.在JavaEE平台共包含了13个技术规范(随着JavaEE版本的变化所包含的技术点的数量会

随机推荐