Rust Rocket: 构建Restful服务项目实战
前言
这几天我的笔记系统开发工作进入了搬砖期,前端基于Yew,后端基于Rocket。关于Rocket搭建Restful服务,官方也有介绍,感觉很多细节不到位。因此我打算花2到3天的时间来整理一下,也算是对自己的一个交代。
对于有一定经验的开发者来说,他们可能已经熟悉了 Restful 开发中的基本 HTTP 方法,如 GET、POST、PUT 和 DELETE。然而,从项目实战的角度来说,这些方法的细节处理是不容忽视的。在项目开发中,我们必须关注文件夹结构的组织、参数的获取、返回值的处理和日志处理等方面的问题。这些问题的解决对于一个项目的质量至关重要,任何偷工减料或者不恰当的行为都会对项目造成不良影响。
在本篇文章中,我将会从实战的角度出发,帮助大家全面了解如何处理这些细节问题。
介绍
Rocket 是一个强大的 Rust 网络框架,可帮助开发者快速、安全地构建灵活易用的 web 应用程序,并提供类型安全性。在本实战文章中,我将使用 Rocket 框架搭建 Restful 服务,实现对笔记数据的 GET、POST、PUT 和 DELETE 操作,并使用 Postgres 数据库进行数据持久化。
准备工作
安装Rust
Rust安装非常简单,命令行如下:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
需要注意的是,安装 Rust 的命令中有一个与人机交互的环节,需要选择安装模式。通常情况下,直接按回车键选择默认选项即可。
但是,如果您希望在Dockerfile中来安装rust,比如,创建一个基于 CentOS 的 Rust 环境的 Docker 镜像,就必须跳过人机交互的环节,让命令行自动执行默认选项。
如何通过命令行来自动执行默认选项呢,可能会对不熟悉 Linux 命令的人来说有些困难。不过,不用担心,这个坑我已经踩过去了,在 Dockerfile 中,您可以使用下面的命令来实现。
run curl -s --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y
(您可以在Docker hub上搜到官方制作的Rust的镜像,但是这个镜像是基于Ubuntu的。我曾经就遇到一个Rust项目运行于Centos7,依赖glibc 2.17。但是Rust的 Ubuntu镜像中的glibc的版本是2.35,为此做了一个Centos7的Docker镜像来编译Rust项目)
创建项目
创建项目很简单,就下面一行命令。但是从实战的角度,要多说两句。
cargo new my-app
我们是先创建git仓库,再初始化项目代码呢,还是先初始化项目代码,再来设置git仓库?这里可能会小纠结一下。
两种情况都可以,在我们分析这两种情况之前,我们来仔细看一下这个命令行到底为我们创建了什么?
my-app
是文件夹名称,也是项目的名称,它会自动初始化到my-app/Cargo.toml
中,代码如下:
[package]
name = "my-app"
version = "0.1.0"
edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
创建的文件结构如下:
bash-3.2$ cargo new my-app
bash-3.2$ tree ./my-app
./my-app
├── Cargo.toml
└── src└── main.rs1 directory, 2 files
情况1. 先创建git,再创建项目
因此,如果我们先创建git,再初始化项目代码,那么我么创建项目的真正命令行如下:
git clone git@gitee.com:xxx/my-app
cd my-app
cargo new my-app
git add .
git commit . -m "init commit"
git push
得到的项目文件夹结构如下:
bash-3.2$ tree ./my-app
./my-app
└── my-app├── Cargo.toml└── src└── main.rs2 directories, 2 files
从上面的目录结构来看,我们的开发目录是./my-app/my-app
,比项目的目录多了一层。这样的好处是,可以把项目中和开发无关的文件放在项目的根目录./my-app
中,比如,部署脚本,需求文档,项目相关的会议记录等;把和Rust开发相关的文件放在./my-app/my-app
中。
情况2. 先创建项目,再创建git
如果我们先创建git,再初始化项目代码,那么我们创建项目的真正命令行如下:
cargo new my-app
cd my-app
git init
git add .
git commit . -m "init commit"
git remote add origin git@gitee.com:xxx/my-app.git
git push -u origin "master"
得到的项目文件夹结构如下:
bash-3.2$ tree ./my-app
./my-app
├── Cargo.toml
└── src└── main.rs1 directory, 2 files
从上面的目录结构看,我们的项目目录和开发目录是同一个目录。这样做的好处在于整个项目的目录结构比较直观,方便理解。
上面从实战的角度介绍了项目的初始化方式,大家可以根据自己项目的特点和诉求进行调整。
Restful基础
项目创建好了,从实战的角度,按理说我们应该讲实现了。但具体实现什么呢?面对这个问题,我想还是先稍微介绍一下Restful。
RESTful(Representational State Transfer)是一种软件架构风格,它基于 HTTP 协议的设计理念,旨在提供一种简单、轻量级、可扩展和易于理解的方式来构建和访问 Web 服务。说Restful基于HTTP协议的设计理念,主要是因为它基于HTTP的方法: GET(获取资源)、POST(创建资源)、PUT(更新资源)、DELETE(删除资源)来完成服务接口的构建。
在Restful服务中,资源(Resources)是一个很重要的设计概念。RESTful 服务将数据和功能封装为资源,并通过统一的资源标识符(URI)来访问这些资源。我们上面提到的GET,POST,PUT和DELETE都是围绕资源来设计的。在项目实战中,对资源的识别是很重要的一个设计环节。
Restful服务通过Json和Xml数据格式来和客户端交换数据,这一点,在Javascript应用横行天下的今天尤为重要。从项目实战的角度,这个特点意味着它可以和几乎所有的客户端进行数据交互。我认为这是Restful成为现代 Web 开发的常用的架构风格的重要原因。
实战开始
实战是各种概念和经验交汇后的结果,因此,这里的实战多少带了我的一些个人色彩在里面。我尽量将“为什么要这么做”的原因也分享给大家,大家可以结合自己的项目实际情况来进行裁剪。(之所以说是尽量
,也是因为这里面的水太深,有的地方我也是“知其然”不知其“所以然”。)
实战的内容包括:项目的目录结构,路由参数处理,路由返回值处理,日志。
项目的目录结构
笔记系统的后端api目录结构如下:
bash-3.2$ tree ./note_book_api/
./note_book_api/
├── LICENSE
├── app
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── error.log
│ ├── src
│ │ ├── fairings.rs
│ │ ├── main.rs
│ │ ├── models.rs
│ │ ├── routes.rs
│ │ ├── services
│ │ │ ├── mod.rs
│ │ │ └── note_book.rs
│ │ └── utils.rs
│ └── test.sh
└── db_scripts├── create_container.sh└── init└── init_db.sh5 directories, 14 files
从这个目录结构,大家可以看出这个结构这里采用的是第一种情况的项目管理方式,即git仓库目录中包含开发目录。在git仓库目录中,包含开发目录app
,和数据库相关的脚本目录db_scripts
。
项目的开发目录app
,从代码设计上分为
- routes:用于定义路由;
- fairings:用于定义rocket的中间件,之所以命名为fairings,是因为Rocket的中间件都要实现
rocket::fairing::Fairing
trait; - models:用于定义与业务相关的数据模型;
- services:用于实现业务逻辑的服务模块;
- utils:用于定义各种功能代码,例如与IO相关的功能;
项目的数据库相关的脚本目录db_scripts
init_db.sh
用于初始化数据库结构。这个脚本应该具有幂等性,即保证项目在多次业务迭代中,数据库相关的结构能够持续更新。create_container.sh
用于初始化数据库的docker容器。这个脚本应该具有幂等性,用于持续更新数据库容器。
路由参数处理
路由参数的处理可以归纳为以下几种情况
- 从post方法中获取数据
- 从url上获取数据
- 从query上获取数据
路由参数解析是Rocket框架的重要功能之一,它充分展现了Rocket框架的强大和灵活性。通过Rocket框架,我们能够轻松地解析和提取路由中的参数,无论是路径参数还是查询参数。Rocket框架的参数解析功能,为我们构建功能丰富的Web应用提供了强有力的支持。
从post方法中获取数据
#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<String, MyStatus> {...
}
上面这段代码,实际上包含了两个概念,路由和handler。第一个参数"/api/nores"
必须是字符串常量,format是序列化的数据格式,包括"application/json",data对应的参数名称。Json<Note>
用于将http中data中的数据反序列化为对应的对象。
就上面的代码而言,可以考虑
让Note
结构体通过#[derive(Default)]
来实现default trait,以确保当客户端传过来的数据在反序列化时,如果出现了字段不匹配的情况,系统不会报错。
这里我说考虑
,是因为这个报错不是一个单纯的错误。它关系到数据逻辑的严谨性和逻辑容错性问题。严谨性越高,容错性就越低,所以,从实战的角度来说,这是一个架构设计的权衡点。
format除了"json",还有下面几种类型:
“any” - MediaType::Any
“binary” - MediaType::Binary
“bytes” - MediaType::Bytes
“html” - MediaType::HTML
“plain” - MediaType::Plain
“text” - MediaType::Text
“json” - MediaType::JSON
“msgpack” - MediaType::MsgPack
“form” - MediaType::Form
“js” - MediaType::JavaScript
“css” - MediaType::CSS
“multipart” - MediaType::FormData
“xml” - MediaType::XML
“pdf” - MediaType::PDF
“markdown” - MediaType::Markdown
“md” - MediaType::Markdown
从url上获取数据
#[get("/api/note/<id>")]
pub async fn get_note(id: String) -> Result<Json<Note>, MyStatus> {...
}
通过<id>
,将url上的参数映射到参数上。
从query上获取数据
#[get("/api/note?<id>")]
pub async fn get_note1(id: String) -> Result<Json<Note>, MyStatus> {...
}
通过<id>
,将query上的参数映射到参数上。
路由返回值(Response)处理
Rocket支持路由返回值包括: String, str, File, Option和Result。但是从项目的实战的角度来说,我个人偏向于使用Result来作为路由的返回值。
#[post("/api/notes", format = "application/json", data = "<note>")]
pub async fn post_notes(note: Json<Note>) -> Result<Json<Res<String>>, MyStatus> {Ok(Json(Res::new(services::insert_or_update_note(¬e.into_inner()).await?)))
}
我个人认为使用Result有以下两个好处
- 统一错误类型,有利于让内部代码的错误处理变得更加简洁
- 统一返回值的基本结构,有利于客户端的调用
首先说一下让内部代码的错误处理变得更加简洁。
Result<Json<Res<String>>, MyStatus>
意味着,在这个项目的代码中,我们把任何函数的返回值都定义成Result<T, MyStatus>
,或者实现impl From<E> for MyStatus
。这种处理方式使得代码的错误处理变得异常的简洁。例如下面的代码:
pub async fn insert_or_update_note(note: &Note) -> Result<String, MyStatus> {...if note.has_id() {let _ = client.execute("update notes set title=$2, content=$3 where id=$1",&[&id, ¬e.title, ¬e.content],).await.map_err(MyError::from)?;} else {...}Ok(id)
}
在上面的代码中,直接通过map_err(MyError::from)?
完成了execute
函数在执行时的错误处理,从而省去了if ... else ...
或者match
的冗繁写法。我在已经感受到了Rust类型的一等公民地位中专门讨论了类型转的问题。
关于统一返回值的结构,有利于客户端的数据读取设计。
这里我定义了范型Res<T>
,这个结构使Javascript客户端能够以统一的方式来解析数据,从而简化客户端的代码。
#[derive(Debug, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Res<T>{pub data: T
}
日志处理
对于后端的api来说,日志功能尤其重要,其重要程度怎么形容都不过分。目前,对于笔记系统来说,部署规模都还很低,因此,我才用的是simplelog
框架。
在Rocket中,日志通过Faring trait 来实现。Fairing trait 允许我们在应用程序启动过程中插入全局的中间件。
在我们的笔记系统设计中,我将日志中间件放在fairings.rs
文件中。如果项目更加复杂可以将fairings.rs
文件扩展成fairings
文件夹。
farings.rs
#[rocket::async_trait]
impl Fairing for LoggingMiddleWare {fn info(&self) -> Info {Info {name: "Error",kind: Kind::Response,}}async fn on_response<'r>(&self, _req: &'r Request<'_>, res: &mut Response<'r>) {info!("response:{:?}", res);}
}
在main.rs文件中,初始化logger,并将logger添加到rocket管道中。
main.rs
#[launch]
fn rocket() -> _ {CombinedLogger::init(vec![TermLogger::new(LevelFilter::Info,Config::default(),TerminalMode::Mixed,ColorChoice::Auto,),WriteLogger::new(LevelFilter::Info,Config::default(),File::create("error.log").unwrap(),),]).unwrap();...rocket::build().attach(LoggingMiddleWare).mount("/", all_routes)
}
总结
好了,关于构建Restful服务的项目实战暂时就到这里了。从实战的角度,我们讨论了项目的初始化,项目目录的创建,路由参数的处理,路由返回值处理和日志处理。但是从实战的角度,要讨论的内容却远不止于此。后面,我会结合项目的进展和个人的经验,再和大家分享更多的内容。希望能够得到大家的反馈和支持。
相关文章:
Rust Rocket: 构建Restful服务项目实战
前言 这几天我的笔记系统开发工作进入了搬砖期,前端基于Yew,后端基于Rocket。关于Rocket搭建Restful服务,官方也有介绍,感觉很多细节不到位。因此我打算花2到3天的时间来整理一下,也算是对自己的一个交代。 对于有一…...
苹果签名有多少种类之TF签名(TestFlight签名)是什么?优势是什么?什么场合需要应用到?
(一)TestFlight 能够让您:邀请内部和外部的测试人员为应用程序提供反馈。 跟踪应用程序在测试过程中发现的 bug 和用户体验问题。 收集 Crash 报告,了解应用程序在真实设备上的运行状况。 要使用 TestFlight,您可以按照…...
如何将图片存到数据库(以mysql为例), 使用ORM Bee更加简单
如何将图片存到数据库 1. 创建数据库: 2. 生成Javabean public class ImageExam implements Serializable {private static final long serialVersionUID 1596686274309L;private Integer id;private String name; // private Blob image;private InputStream image; //将In…...
【“栈、队列”的应用】408数据结构代码
王道数据结构强化课——【“栈、队列”的应用】代码,持续更新 链式存储栈(单链表实现),并基于上述定义,栈顶在链头,实现“出栈、入栈、判空、判满”四个基本操作 #include <stdio.h> #include <…...
es的nested查询
一、一层嵌套 mapping: PUT /nested_example {"mappings": {"properties": {"name": {"type": "text"},"books": {"type": "nested","properties": {"title": {"t…...
<一>Qt斗地主游戏开发:开发环境搭建--VS2019+Qt5.15.2
1. 开发环境概述 对于Qt的开发环境来说,主流编码IDE界面一般有两种:Qt Creator或VSQt。为了简单起见,这里的操作系统限定为windows,编译器也通用VS了。Qt版本的话自己选择就可以了,当然VS的版本也是依据Qt版本来选定的…...
python:进度条的使用(tqdm)
摘要:为python程序进度条,可以知道程序运行进度。 python中,常用的进度条模块是tqdm,将介绍tqdm的安装和使用 1、安装tqdm: pip install tqdm2、tqdm的使用: (1)在for循环中的使用࿱…...
Java类型转换和类型提升
目录 一、类型转换 1.1 自动类型转换(隐式) 1.1.1 int 与 long 之间 1.1.2 float 与 double 之间 1.1.3 int 与 byte 之间 1.2 强制类型转换(显示) 1.2.1 int 与 long 之间 1.2.2 float 与 double 之间 1.2.3 int 与 d…...
C# 读取 Excel xlsx 文件,显示在 DataGridView 中
编写 read_excel.cs 如下 using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Data; using System.Linq; using System.Text; using System.Data.OleDb;namespace ReadExcel {public partial class Program{static…...
Docker02基本管理
目录 1、Docker 网络 1.1 Docker 网络实现原理 1.2 Docker 的网络模式 1.3 网络模式详解 1.4 资源控制 1.5 进行CPU压力测试 1.6 清理docker占用的磁盘空间 1.7 生产扩展 1、Docker 网络 1.1 Docker 网络实现原理 Docker使用Linux桥接,在宿主机虚拟一个Docke…...
Scala第十章
Scala第十章 章节目标 1.数组 2.元组 3.列表 4.集 5.映射 6.迭代器 7.函数式编程 8.案例:学生成绩单 scala总目录 文档资料下载...
10.4 校招 实习 内推 面经
绿泡*泡: neituijunsir 交流裙 ,内推/实习/校招汇总表格 1、校招 | 集度2024届秋招正式启动(内推) 校招 | 集度2024届秋招正式启动(内推) 2、校招 | 道通科技2024秋季校园招聘正式启动啦! …...
从0开始深入理解并发、线程与等待通知机制(中)
一,深入学习 Java 的线程 线程的状态/生命周期 Java 中线程的状态分为 6 种: 1. 初始(NEW):新创建了一个线程对象,但还没有调用 start()方法。 2. 运行(RUNNABLE):Java 线程中将就绪(ready)和…...
UE5报错及解决办法
1、编译报错,内容如下: Unable to build while Live Coding is active. Exit the editor and game, or press CtrlAltF11 if iterating on code in the editor or game 解决办法 取消Enable Live Coding勾选...
怎么通过docker/portainer部署vue项目
这篇文章分享一下如何通过docker将vue项目打包成镜像文件,并使用打包的镜像在docker/portainer上部署运行,写这篇文章参考了vue-cli和docker的官方文档。 首先,阅读vue-cli关于docker部署的说明,上面提供了关键的几个步骤。 从上面…...
【面试经典150 | 矩阵】旋转图像
文章目录 写在前面Tag题目来源题目解读解题思路方法一:原地旋转方法二:翻转代替旋转 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主,并附带…...
机器人制作开源方案 | 家庭清扫拾物机器人
作者:罗诚、李旭洋、胡旭、符粒楷 单位:南昌交通学院 人工智能学院 指导老师:揭吁菡 在家庭中我们有时无法到一些低矮阴暗的地方进行探索,比如茶几下或者床底下,特别是在部分家庭中,如果没有及时对这些阴…...
C++算法 —— 动态规划(8)01背包问题
文章目录 1、动规思路简介2、模版题:01背包第一问第二问优化 3、分割等和子集4、目标和5、最后一块石头的重量Ⅱ 背包问题需要读者先明白动态规划是什么,理解动规的思路,并不能给刚接触动规的人学习。所以最好是看了之前的动规博客࿰…...
ASUS华硕天选4笔记本FA507NU7735H_4050原装出厂Win11系统
下载链接:https://pan.baidu.com/s/1puxQOxk4Rbno1DqxhkvzXQ?pwdhkzz 系统自带网卡、显卡、声卡等所有驱动、出厂主题壁纸、Office办公软件、MyASUS华硕电脑管家、奥创控制中心等预装程序...
金蝶OA server_file 目录遍历漏洞
漏洞描述 金蝶OA server_file 存在目录遍历漏洞,攻击者通过目录遍历可以获取服务器敏感信息 漏洞影响 金蝶OA 漏洞复现 访问漏洞url: 漏洞POC Windows服务器: appmonitor/protected/selector/server_file/files?folderC://&suffi…...
read_image错误
File is no BMP-File(Halcon 错误代码5560)类似的错误一般都是图片内部封装的格式与外部扩展名不一致导致(也就是扩展名并不是真实图片的格式扩展)。 通过软件“UltraEdit”(http://www.onlinedown.net/soft/7752.htm)使用16进制查看&#x…...
文本分词排序
文本分词 在这个代码的基础上 把英语单词作为一类汉语,作为一类然后列出选项 1. 大小排序 2. 小大排序 3. 不排序打印保存代码 import jieba# 输入文本,让我陪你聊天吧~ lines [] print("请输入多行文本,以\"2333.3\"结束&am…...
SQL与关系数据库基本操作
SQL与关系数据库基本操作 文章目录 第一节 SQL概述一、SQL的发展二、SQL的特点三、SQL的组成 第二节 MySQL预备知识一、MySQL使用基础二、MySQL中的SQL1、常量(1)字符串常量(2)数值常量(3)十六进制常量&…...
【2023年11月第四版教材】第18章《项目绩效域》(第一部分)
第18章《项目绩效域》(第一部分) 1 章节内容2 干系人绩效域2.1 绩效要点2.2 执行效果检查2.3 与其他绩效域的相互作用 3 团队绩效域3.1 绩效要点3.2 与其他绩效域的相互作用3.3 执行效果检查3.4 开发方法和生命周期绩效域 4 绩效要点4.1 与其他绩效域的相…...
Docker启动Mysql
如果docker里面没有mysql需要先pull一个mysql镜像 docker pull mysql其中123456是mysql的密码 docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -d mysql可以使用如下命令进入Mysql的命令行界面 docker exec -it mysql bash登录mysql使用如下命令,root是…...
QScrollArea样式
简介 QScrollBar垂直滚动条分为sub-line、add-line、add-page、sub-page、up-arrow、down-arrow和handle几个部分。 QScrollBar水平滚动条分为sub-line、add-line、add-page、sub-page、left-arrow、right-arrow和handle几个部分。 部件如下图所示: 样式详…...
【gitlab】git push -u origin master 报403
问题描述 gitlab版本:14.0.5 虚拟机版本:centos7 项目:renren-fast 原因分析 .git -> config目录下 url配错 但这个url不是手动配置的,还不知道怎么生成。 解决方法 把配置错误的url改成gitlab的project的url 这样&#…...
第二篇:矩阵的翻转JavaScript
一维数组的翻转 // 一维矩阵翻转 // 实例: arr [1,2,3,4,5] > [5,4,3,2,1] let n readline() let arr readline().split( ).map(Number) // console.log(n,arr) let temp 0 for(let i 0; i < n/2;i){temp arr[i]arr[i] arr[n-i-1]arr[n-i-1] temp }…...
代码随想录算法训练营第五十七天 | 动态规划 part 15 | 392.判断子序列、115.不同的子序列
目录 392.判断子序列思路代码 115.不同的子序列思路代码 392.判断子序列 Leetcode 思路 dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]递推公式: 初始化:为0遍历顺序ÿ…...
【国漫逆袭】人气榜,小医仙首次上榜,霍雨浩排名飙升,不良人热度下降
Hello,小伙伴们,我是小郑继续为大家深度解析国漫资讯。 为了提升作品和角色的讨论度,增加平台的用户活跃度,小企鹅推出了动漫角色榜,该榜单以【年】【周】【日】为单位,通过角色的点赞量和互动量进行排名 上周的动漫角…...
企业宣传网站在哪里做/seo从入门到精通
在IE7的开发中,据说新增加了一个Native对象——XMLHttpRequest。怎么难道开发IE7的"新警察"不知道IE6们都用ActiveX对象XmlHttp吗?XmlHttp出了什么问题,IE7为什么要这么做?原来一切就为了一个简单的兼容而已,…...
吉林建设工程信息网站/2022最新永久地域网名
转载http://www.cnblogs.com/scrat/archive/2012/06/25/2560904.html 什么是僵尸进程 僵尸进程是指它的父进程已经退出(父进程没有等待(调用wait/waitpid)它),而该进程dead之后没有进程接受,就成为僵尸进程,也就是(zombie)进程。 僵尸进程是怎…...
wordpress怎么添加论坛/谷歌搜索引擎363
版权声明]:版权归作者所有,转载时请以超链接形式标明文章原始出处和作者信息及本声明:http://www.open-lib.com/Forum/Read_69_1.action 前言: 当jquery ajax在utf-8编码下(页面utf-8,接收utf-8),无任何问题…...
建行网站网址是多少/免费推广引流平台
>>回到总目录<< 为了不辜负已经订阅了专栏的同学们的信任,所以本专栏不会有任何的优惠活动。 另外,当订阅人数每次达到 2 n ( n > 2 ) 2^n(n>2) 2...
网站上的logo怎么做/必应搜索引擎国际版
我们将使用Postman来进行日志写入操作。Postman的下载地址,你可以Google一下。 1. 在上一节中,我们启动完成ELK的Docker后,可以在浏览器中打开:http://192.168.10.109:9200/(IP是Docker容器所在的服务器IP)…...
做微信的网站叫什么软件/给网站做seo的价格
算法思路: 简单来说归并排序就是将两个有序的序列合并成一个完整的有序序列。具体步骤如下: 1. 选取序列1的第一个元素和序列2的第一个元素,较小的存放到新序列的第一位 2. 选序列1的第二个元素再和序列2的第一个元素比…...