目录
训练
我们最后一次结束该操作时,放置了一个静态网页假人,该假人是使用Flutter开发的。该页面显示了我们服务开发的进度,但是开发和发布开始日期的数据必须在应用程序中进行硬编码。因此,我们失去了更改页面信息的能力。现在该开发数据服务器应用程序了。“ Dart语言中的服务:简介,后端基础结构”一文中列出了所有服务应用程序。
在本文中,我们将使用Aqueduct框架编写一个应用程序,我们将评估其在不同模式下的性能和资源消耗,编写用于编译为Windows和Linux的本机应用程序的工具包,处理域应用程序类的数据库模式的迁移,甚至将我们的工具docker映像发布到公共DockerHub寄存器。
用处
安装渡槽
让我们从安装Dart开发套件dart-sdk开始。您可以按照此处的建议使用操作系统的软件包管理器进行安装。但是,对于Windows,默认情况下,系统上未安装任何程序包管理器。所以就:
- 下载档案并将其解压缩到C驱动器中:
- , , , . . « »
- Path . dart , , C:\dart-sdk\bin
- , dart pub ( dart)
dart --version
pub -v
- , ,
- aqueduct CLI (command line interface)
pub global activate aqueduct
aqueduct
从理论上讲,也可以在本地安装PostgreSQL数据库服务器。但是,Docker将允许我们避免这种需求,并使开发环境类似于服务器上的运行时。
应用程序生成
因此,让我们在VsCode中打开服务器文件夹
code c:/docs/dart_server
对于那些没有看过第一篇和第二篇文章的人,可以从guthub存储库中克隆源代码:
git clone https://github.com/AndX2/dart_server.git
让我们创建一个应用程序模板:
aqueduct create data_app
让我们熟悉项目模板的内容:
- README.md-说明如何使用渡槽项目,运行测试,生成API文档等的说明。支持文件。
- pubspec.yaml — pub. , , , .
- config.yaml config.src.yaml — . .
- analysis_options.yaml — ( ). .
- .travis.yml — (continuous Integration). .
- pubspec.lock .packages — pub. — , , — ().
- .dart_tool/package_config.json — , aqueduct CLI. .
- bin/main.dart — (, ). ( ).
- lib/channel.dart — ApplicationChannel — . Aqueduct CPU RAM. ( Dart isolate) () .
- lib/data_app.dart — . (library) dart_app
- test/ — . -, . Postman.
组态
首先要解决的任务是在启动时设置应用程序。Aqueduct具有用于从配置文件中提取参数的内置机制,但是在Docker容器中运行时,这种机制并不是很方便。我们将采取不同的行动:
- 让我们将变量列表传递给容器操作系统。
- 在容器内启动应用程序时,我们将读取操作系统环境变量并将其用于初始配置。
- 让我们创建一条路由,以通过网络查看正在运行的应用程序的所有环境变量(当从管理面板查看应用程序状态时,这将非常有用)。
在/ lib文件夹中,创建几个文件夹,并创建第一个用于访问环境变量的存储库:构造函数中的
EnvironmentRepository以Map <String,String>字典的形式从操作系统中读取环境变量,并将它们保存在私有变量_env中。让我们添加一个以字典形式获取所有参数的方法:lib / service / EnvironmentService-用于访问EnvironmentRepository数据的逻辑组件:
依赖注入
在这里,您需要停止并处理组件依赖性:
- 网络控制器将需要变量服务的实例,
- 该服务对于整个应用程序必须是唯一的,
- 要创建服务,必须首先创建变量存储库的实例。
我们将使用GetIt库解决这些问题。将所需的程序包连接到pubspec.yaml:
创建注入器容器lib / di / di_container.dart的实例,并编写一个注册存储库和服务的 方法:在应用程序准备方法中调用DI容器初始化方法:
网络层
lib / controller / ActuatorController -http网络组件。它包含用于访问应用程序服务数据的方法: 让我们在lib / controller / Routes中声明控制器的路由处理程序:
第一次开始
要运行,您需要:
- 将应用程序打包到Docker映像中,
- 将容器添加到docker-compose脚本中,
- 配置NGINX代理请求。
在应用程序文件夹中创建一个Dockerfile。这是用于为Docker构建和运行映像的脚本: 将应用程序容器添加到docker-compose.yaml脚本: 使用应用程序的配置变量创建data_app.env文件: 向NGINX调试配置conf.dev.d / default.conf添加新位置: 运行调试带有预构建标志的脚本:
docker-compose -f docker-compose.yaml -f docker-compose.dev.yaml up --build
该脚本已成功运行,但有几个警告点:
- Google的官方Dart图片已归档290MB 。打开包装后,它将占用更多空间-754MB。查看图像列表及其尺寸:
docker images
- 构建和JIT编译时间为100+秒。太多的应用无法运行
- 启动后立即在docker仪表板中消耗300 MB
- 在压力测试(仅网络GET / API /执行器/请求)中,对于一个隔离运行的应用程序,内存消耗在350-390 MB范围内
据推测,我们预算VPS的资源不足以运行这种资源密集型应用程序。让我们检查:
- 在服务器上为新版本的应用程序创建一个文件夹,然后复制项目的内容
ssh root@dartservice.ru "mkdir -p /opt/srv_2" && scp -r ./* root@91.230.60.120:/opt/srv_2/
- 现在,您需要将/ opt / srv_1 / public /中的网页项目以及/ opt / srv_1 / sertbot /文件夹的全部内容传输到此文件夹(该文件包含NGINX的SSL证书,并让我们加密机器人日志),然后从/ opt复制密钥/ srv_1 / dhparam /
- 在单独的控制台中启动服务器资源监视器
htop
- 让我们在/ opt / srv_2 /文件夹中运行docker -compose脚本
docker-compose up --build -d
- 这是应用程序组装在启动之前的外观:
- 如此-在工作中:
在可用的1GB RAM中,我们的应用程序消耗了1.5GB“占用”了丢失的页面文件。是的,该应用程序已启动,但是我们没有在谈论任何负载能力。 - 让我们停止脚本:
docker-compose down
AOT
我们有三个任务要解决:
- 减少飞镖应用程序对RAM的消耗,
- 减少启动时间,
- 减少应用程序docker容器的大小。
解决方案是在运行时放弃飞镖。从2.6版开始,dart应用程序支持将其编译为本机可执行代码。Aqueduct支持从4.0.0-b1版本开始的编译。
让我们从本地删除渡槽CLI开始:
pub global deactivate aqueduct
安装新版本:
pub global activate aqueduct 4.0.0-b1
让我们提高pubspec.yaml中的依赖性:让我们构建本
机应用程序:
aqueduct build
结果将是单个文件data_app.aot程序集,大小约为6MB。您可以使用参数立即启动此应用程序,例如:
data_app.aot --port 8080 --isolates 2
启动后立即消耗的内存少于10 MB。
让我们看看负载。测试参数:网络GET /执行器请求,最大可用速度的100个线程,10分钟。结果:
总计:平均速度-对于1.4kV JSON响应正文,每秒13k个请求,平均响应时间-7 ms,内存消耗(两个实例)为42 MB。没有错误。
当我们对六个应用程序实例重复测试时,平均速度当然会提高到19k /秒,但是当内存消耗为64 MB时,处理器的利用率将达到45%。
这是一个极好的结果。
集装箱包装
在这里,我们将面临另一个困难:我们只能将dart应用程序编译为当前OS的本机。就我而言,这是Windows10 x64。在Docker容器中,我当然更喜欢Linux发行版之一-例如Ubuntu 20.10。
这里的解决方案是一个中间的docker-stand,仅用于为Ubuntu构建本机应用程序。让我们编写它/ dart2native / Dockerfile: 现在,将其构建到名为aqueduct_builder:4.0.0-b1的Docker映像中,覆盖旧版本(如果有):
docker build --pull --rm -f "dart2native\Dockerfile" -t aqueduct_builder:4.0.0-b1 "dart2native"
让我们检查:
docker images
让我们为本地应用程序docker-compose.dev.build.yaml编写一个构建脚本: 运行该构建脚本:
docker-compose -f docker-compose.dev.build.yaml up
为Ubuntu编译的data_app.aot文件已占用9 MB。启动时,使用19 MB的RAM(用于两个实例)。让我们在相同条件下进行本地负载测试,但在具有NGINX代理(GET,100个线程)的容器中:
平均每秒5.3k请求。同时,RAM的消耗不超过55 MB。与已安装的飞镖和渡槽相比,磁盘上的图像大小已从840 MB减少到74 MB。
让我们编写一个新脚本docker-compose.aot.yaml来启动应用程序。为此,我们将通过安装基本映像“空” Ubuntu:20.10来替换data_app description块。让我们挂载程序集文件并更改启动命令:
让我们再解决一个服务问题:实际上,安装了dart和aqueduct的docker build映像是一个非常可重用的工具。将其上传到公共寄存器并作为现成的可下载映像进行连接是有意义的。这要求:
- 向公共注册器(例如DockerHub)进行注册,
- 使用相同的登录名本地登录
docker login
- 使用登录名/标题:标记方案重命名上传的图像
docker image tag a365ac7f5bbb andx2/aqueduct:4.0.0-b1
- 将图像卸载到寄存器
docker push andx2/aqueduct:4.0.0-b1
https://hub.docker.com/repository/docker/andx2/aqueduct/general
现在我们可以修改构建脚本以使用公共映像
数据库连接
Aqueduct已经具有内置的ORM,可用于PostgreSQL数据库。要使用它,您需要:
- 创建描述数据库中记录的域对象。
- . : , , . Aqueduct , , ManagedObject ( ), , . .
- . , , .
- , aqueduct, , seed() — - .
- aqueduct CLI.
首先,在docker-compose.aot.yaml脚本中将一个新的Docker容器连接到PostgreSQL数据库。基于Linux Alpine(嵌入式应用程序的Linux的“紧凑”版本)的就绪映像: 在这里,您需要注意环境变量文件data_db.env。事实是该映像已预先配置为使用这些变量作为用户名,主机,端口和访问密码。让我们将这些变量添加到文件中: 值是有条件给出的。 我们还将主机文件夹./data_db/安装到用于存储数据库数据的容器中。 接下来,在data_app应用程序中,添加/ service / DbHelper类以使用环境变量连接到数据库:
让我们创建一个由ORM管理的域对象以获取客户端应用程序的设置: 添加存储库和服务以添加设置并获取当前版本: 网络控制器: 在DI容器中注册新组件: 向路由器添加新的控制器和端点: 现在我们将生成数据库迁移文件。让我们执行:
aqueduct db generate
结果将是在项目文件夹中创建迁移文件:
现在您需要解决服务问题:迁移必须从安装了渡槽(和dart)的系统应用于容器中运行的数据库,并且必须在本地开发期间和在服务器上都进行。对于这种情况,我们将使用先前构建和发布的AOT组件图像。让我们编写相应的docker-compose数据库迁移脚本:
一个有趣的细节是数据库连接字符串。运行脚本时,可以传递带有环境变量作为参数的文件,然后在脚本中使用以下变量进行替换:
docker-compose -f docker-compose.migrations.yaml --env-file=./data_app.env --compatibility up --abort-on-container-exit
我们还要注意启动标志:
- --compatibility-与2.x脚本的docker -compose版本兼容。这将允许将部署选项用于限制容器的资源使用,在版本3.x中将忽略这些选项。我们将RAM消耗限制为200MB,并将CPU使用率限制为50%
- --abort-on-container-exit-此标志设置脚本执行模式,以便当其中一个脚本容器停止时,所有其他脚本容器将终止。因此,当执行迁移数据库模式的命令并且停止带有渡槽的容器时,docker-compose也将终止数据库容器。
出版物
要准备发布应用程序,您必须:
- data_app.env data_db.env. , POSTGRES_PASSWORD=postgres_password
- docker-compose.aot.yaml docker-compose.yaml.
- /api/actuator. .
将 应用程序文件夹./data_app/复制到服务器。此处重要的一点是copy命令中的-p开关(在保留文件属性的同时进行复制)。让我提醒您,在构建本机应用程序时,我们将执行权限设置为data_app.aot文件:
scp -rp ./data_app root@dartservice.ru:/opt/srv_1
让我们也复制:
- 更改了NGINX配置./conf.d/default.conf
- 启动和迁移脚本docker-compose.yaml,docker-compose.migrations.yaml
- 具有环境变量data_app.env和data_db.env的文件
在服务器上添加/ opt / srv_1 / data_db文件夹。这是要在数据库容器中挂载的主机文件系统卷。所有PostgreSQL数据都将保存在这里。
mkdir /opt/srv_2/data_db
让我们执行用于迁移数据库模式的脚本:
docker-compose -f docker-compose.migrations.yaml --env-file=./data_app.env up --abort-on-container-exit
让我们运行应用程序脚本:
docker-compose up -d
->源代码github
而不是结论
后端应用程序的框架已准备就绪。在下一篇文章的基础上,我们将编写一个新的应用程序来授权服务用户。为此,我们将使用oAuth2规范并与VK和Github集成。