如何使用托管数据库和对象存储建立可扩展的 Laravel 6 应用程序

介绍

在横向扩展 Web 应用程序时,您通常会遇到的第一个困难是处理文件存储和数据持久性,这主要是因为很难在多个应用程序节点之间保持可变数据的一致性;必须采取适当的策略来确保在一个节点中创建的数据立即可用于群集中的其他节点。

解决一致性问题的一种实际方法是使用 管理数据库对象存储系统. 第一种将数据持久性向管理数据库外包,后者将提供远程存储服务,您可以保留静态文件和可变内容,例如用户上传的图像。

以下图表展示了如何在PHP应用程序中使用这种设置来实现水平可扩展性:

Laravel at scale diagram

在本指南中,我们将更新现有的 Laravel 6 应用程序,通过将其连接到管理的 MySQL 数据库并设置 S3 兼容的对象存储器来为其准备水平可扩展性,以便保存用户生成的文件。

Travellist v1.0

<$>[注] :本指南使用 DigitalOcean Managed MySQLSpaces来展示可扩展的应用程序设置,使用受管理的数据库和对象存储。

前提条件

要开始这个教程,你首先需要以下前提条件:

  • 作为拥有sudo权限的非root用户访问Ubuntu 18.04服务器,以及安装在服务器上的活性防火墙. 要设置这些功能,请参考我们的Ubuntu 18.04初始服务器设置指南
  • Nginx和PHP-FPM在您的服务器上安装和配置,详见[如何在Ubuntu 18.04上安装LEMP] (https://andsky.com/tech/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04)中的步骤13。 您应该跳过安装 MySQL 的步骤 。
  • [composer] (https://getcomposer.org/) 安装在您的服务器上, 如步骤所解释 12[如何在Ubuntu 18.04上安装和使用作曲器(https://andsky.com/tech/tutorials/how-to-install-and-use-composer-on-ubuntu-18-04)。
  • 管理管理MySQL 8数据库的资质. 对于这个指南,我们将使用一个数字海洋管理MySQL集群,但这里的指令对于其他管理的数据库服务应同样有效.
  • S3相容对象存储服务的读写权限的一组API密钥. 在本指南中,我们将使用数字海洋空间,但您可以自由使用自己选择的提供者.
  • 安装和配置的`s3cmd' 工具可连接到对象存储驱动器。 关于如何为 " 数字海洋空间 " 设置这一平台的说明,请参见我们的产品文档。 (英语)

步骤1:安装MySQL 8客户端

默认的 Ubuntu apt 存储库配有 MySQL 5 客户端,这与我们将在本指南中使用的 MySQL 8 服务器不兼容. 要安装兼容的 MySQL 客户端,我们需要使用 Oracle 提供的 MySQL APT 存储库。

首先,在您的 Web 浏览器中导航到 MySQL APT Repository page。 查找右下角的 Download 按钮,然后点击到下一页。 此页面将提示您登录或注册 Oracle Web 帐户。 您可以跳过它,而不是寻找说 No thanks, just start my download 的链接。 复制链接地址并返回您的终端窗口。

此链接应该指向一个 .deb 包,将设置 MySQL APT 仓库在您的服务器上. 安装后,您将能够使用 apt 来安装 MySQL 的最新版本. 我们将使用 curl 将此文件下载到临时位置。

进入您的服务器的tmp文件夹:

1cd /tmp

现在下载带有弯曲的包,并使用您从MySQL APT仓库页面复制的URL:

1curl -OL https://dev.mysql.com/get/mysql-apt-config_0.8.13-1_all.deb

在下载完成后,您可以使用dpkg来安装该包:

1sudo dpkg -i mysql-apt-config_0.8.13-1_all.deb

您将收到一个屏幕,您可以选择您想选择的默认MySQL版本,以及您感兴趣的MySQL组件:

MySQL APT Repository Install

你不需要在这里改变任何东西,因为默认选项将安装我们需要的存储库。

接下来,您需要更新您的apt缓存:

1sudo apt update

现在我们终于可以安装 MySQL 8 客户端:

1sudo apt install mysql-client

一旦该命令完成,请检查软件版本号,以确保您有最新版本:

1mysql --version

你会看到这样的输出:

1[secondary_label Output]
2mysql Ver 8.0.18 for Linux on x86_64 (MySQL Community Server - GPL)

在下一步,我们将使用MySQL客户端连接到您的管理MySQL服务器并为应用程序准备数据库。

步骤 2:创建一个新的MySQL用户和数据库

在本文写作时,原生MySQL PHP库mysqlnd不支持(https://www.php.net/manual/en/ref.pdo-mysql.php)`caching_sha2_authentication`,这是MySQL 8的默认身份验证方法,我们需要创建一个使用mysql_native_password身份验证方法的新用户,才能将我们的Laravel应用连接到MySQL 8服务器。

要开始,请使用管理员帐户登录您的服务器,以自己的 MySQL 用户、主机和端口替换所突出的值:

1mysql -u MYSQL_USER -p -h MYSQL_HOST -P MYSQL_PORT

当被提示时,请提供管理员用户的密码. 登录后,您将可以访问MySQL 8服务器命令行界面。

首先,我们将为应用程序创建一个新的数据库. 运行以下命令创建一个名为travellist的新数据库:

1CREATE DATABASE travellist;

接下来,我们将创建一个新用户并设置密码,使用mysql_native_password作为该用户的默认身份验证方法。

1CREATE USER 'travellist-user'@'%' IDENTIFIED WITH mysql_native_password BY 'MYSQL_PASSWORD';

现在我们需要在我们的应用程序数据库上授予此用户权限:

1GRANT ALL ON travellist.* TO 'travellist-user'@'%';

您现在可以退出MySQL提示:

1exit;

您现在有一个专用数据库和一个兼容的用户可以连接到您的 Laravel 应用程序. 在下一步,我们将获得应用程序代码和配置详细信息,以便您的应用程序可以连接到您的管理 MySQL 数据库。

<$>[注] 在本指南中,我们将使用 Laravel Migrationsdatabase seeds来设置我们的应用表. 如果您需要将现有本地数据库迁移到DigitalOcean Managed MySQL数据库,请参阅我们关于 如何将MySQL数据库导入DigitalOcean Managed Databases的文档。

步骤 3 – 设置 demo 应用程序

要开始,我们将从其 Github 存储库中获取 Laravel 演示应用程序。

该演示应用程序是一个旅行桶列表应用程序,最初是在我们在Ubuntu 18.04上如何安装和配置Laravel的指南中开发的(https://andsky.com/tech/tutorials/how-to-install-and-configure-laravel-with-lemp-on-ubuntu-18-04)。更新后的应用程序现在包含可由访问者上传的旅行照片和世界地图等视觉改进,还引入了数据库迁移脚本和数据库种子,以创建应用程序表,并使用工匠命令填充样本数据。

要获得与本教程兼容的应用程序代码,我们将从Github的项目存储库下载 1.1版本

1cd ~
2curl -L https://github.com/do-community/travellist-laravel-demo/archive/1.1.zip -o travellist.zip

现在,解开应用程序的内容,并以以下方式更名其目录:

1unzip travellist.zip
2mv travellist-laravel-demo-1.1 travellist

导航到旅行者目录:

1cd travellist

在继续前,我们需要安装一些Laravel框架所需的PHP模块,即:php-xml,php-mbstring,php-xml和php-bcmath。

1sudo apt install unzip php-xml php-mbstring php-xml php-bcmath

要安装应用程序依赖,运行:

1composer install

您将看到类似于此的输出:

 1[secondary_label Output]
 2Loading composer repositories with package information
 3Installing dependencies (including require-dev) from lock file
 4Package operations: 80 installs, 0 updates, 0 removals
 5  - Installing doctrine/inflector (v1.3.0): Downloading (100%)         
 6  - Installing doctrine/lexer (1.1.0): Downloading (100%)         
 7  - Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%)         
 8  - Installing erusev/parsedown (1.7.3): Downloading (100%)      
 9
10...
11
12Generating optimized autoload files
13> Illuminate\Foundation\ComposerScripts::postAutoloadDump
14> @php artisan package:discover --ansi
15Discovered Package: beyondcode/laravel-dump-server
16Discovered Package: fideloper/proxy
17Discovered Package: laravel/tinker
18Discovered Package: nesbot/carbon
19Discovered Package: nunomaduro/collision
20Package manifest generated successfully.

应用程序依赖性现在安装了,接下来,我们将配置应用程序连接到管理的MySQL数据库。

创建.env 配置文件并设置 App 密钥

现在我们将创建一个包含变量的 .env 文件,用于配置 Laravel 应用程序以每个环境为基础. 该应用程序包含一个示例文件,我们可以复制并修改其值以反映我们的环境设置。

.env.example 文件复制到名为 .env 的新文件:

1cp .env.example .env

现在我们需要设置应用程序密钥. 此密钥用于加密会话数据,应该设置为一个独特的32个字符长的字符串。

1php artisan key:generate

让我们编辑环境配置文件来设置数据库细节。 使用您所选择的命令行编辑器打开 .env 文件. 在这里,我们将使用 nano:

1nano .env

查找数据库凭证部分,以下变量需要您的注意:

DB_HOST:您管理的MySQL服务器主机。 DB_PORT:您管理的MySQL服务器端口。 DB_DATABASE:我们在 [步骤 2]创建的应用程序数据库的名称(#step-2%E2%80%94-creating-a-new-mysql-user-and-database)。 DB_USERNAME:我们在 [步骤 2]创建的数据库用户(#step-2%E2%80%94-creating-a-new-mysql-user-and-database)。 DB_PASSWORD:我们在 [步骤 2]定义的用户数据库的密码(步骤-2%E2%80%94-creating-a

使用您自己的管理的 MySQL 信息和凭证更新突出的值:

1...
2DB_CONNECTION=mysql
3DB_HOST=MANAGED_MYSQL_HOST
4DB_PORT=MANAGED_MYSQL_PORT
5DB_DATABASE=MANAGED_MYSQL_DB
6DB_USERNAME=MANAGED_MYSQL_USER
7DB_PASSWORD=MANAGED_MYSQL_PASSWORD
8...

保存并关闭文件,键入CTRL+X,然后在完成编辑时键入YENTER

现在该应用程序已配置为连接到MySQL数据库,我们可以使用Laravel的命令行工具手工创建数据库表并填充样本数据。

设置存储链接

在执行工匠命令提供的数据库工具之前,我们需要创建一个象征链接到公共存储文件夹,该文件夹将托管我们在应用程序中使用的旅行照片。

以下命令将创建一个在公共目录内部的符号链接,该目录通过Web服务器被公开曝光,指向应用程序的内部存储目录存储/应用程序/公共:

1php artisan storage:link
1[secondary_label Output]
2The [public/storage] directory has been linked.

要检查链接是否创建并指向何处,您可以运行:

1ls -la public/

你会看到这样的输出:

 1[secondary_label Output]
 2total 36
 3drwxrwxr-x 5 sammy sammy 4096 Oct 25 14:59 .
 4drwxrwxr-x 12 sammy sammy 4096 Oct 25 14:58 ..
 5-rw-rw-r--  1 sammy sammy 593 Oct 25 06:29 .htaccess
 6drwxrwxr-x 2 sammy sammy 4096 Oct 25 06:29 css
 7-rw-rw-r--  1 sammy sammy 0 Oct 25 06:29 favicon.ico
 8drwxrwxr-x 2 sammy sammy 4096 Oct 25 06:29 img
 9-rw-rw-r--  1 sammy sammy 1823 Oct 25 06:29 index.php
10drwxrwxr-x 2 sammy sammy 4096 Oct 25 06:29 js
11-rw-rw-r--  1 sammy sammy 24 Oct 25 06:29 robots.txt
12lrwxrwxrwx 1 sammy sammy 41 Oct 25 14:59 storage -> /home/sammy/travellist/storage/app/public
13-rw-rw-r--  1 sammy sammy 1194 Oct 25 06:29 web.config

迁移和人口化数据库

现在我们将使用 Laravel Migrationsdatabase seeds来设置应用程序表,这将有助于我们确定我们的数据库配置是否按预期运行。

要执行将创建应用程序使用的表的迁移脚本,运行:

1php artisan migrate

您将看到类似于此的输出:

1[secondary_label Output]
2Migration table created successfully.
3Migrating: 2019_09_19_123737_create_places_table
4Migrated:  2019_09_19_123737_create_places_table (0.26 seconds)
5Migrating: 2019_10_14_124700_create_photos_table
6Migrated:  2019_10_14_124700_create_photos_table (0.42 seconds)

若要用样本数据填充数据库,请运行:

1php artisan db:seed

你会看到这样的输出:

1[secondary_label Output]
2Seeding: PlacesTableSeeder
3Seeded:  PlacesTableSeeder (0.86 seconds)
4Database seeding completed successfully.

应用表现在创建并填充了样本数据。

运行测试服务器(可选)

您可以使用手工服务命令快速验证应用程序内部的所有设置都是正确的,然后再配置一个像 Nginx 这样的全功能 Web 服务器来长期服务应用程序。

我们将使用端口8000暂时为应用程序提供测试服务. 如果您在服务器上启用了 UFW 防火墙,您应先允许访问该端口:

1sudo ufw allow 8000

现在,要运行Laravel通过工匠工具暴露的PHP内置服务器,运行:

1php artisan serve --host=0.0.0.0 --port=8000

此命令将阻止您的终端,直到通过CTRL+C中断,它将使用内置的PHPWeb服务器在所有网络接口上测试应用程序,使用端口8000

现在,进入您的浏览器并使用服务器的域名或IP地址在端口8000访问应用程序:

1http://server_domain_or_IP:8000

你会看到下面的页面:

Travellist v1.0

如果您看到此页面,这意味着应用程序正在成功从配置的管理数据库中提取位置和照片的数据,图像文件仍然存储在本地磁盘中,但我们将在本指南的下一步中改变这一点。

当您完成测试应用程序时,您可以通过点击CTRL+C来停止服务命令。

不要忘了再次关闭端口8000,如果您在服务器上运行 UFW:

1sudo ufw deny 8000

步骤 4 — 配置 Nginx 以服务应用程序

虽然内置的PHP Web 服务器非常有用用于开发和测试目的,但它不打算被用作服务PHP应用程序的长期解决方案。

要开始,我们将应用程序文件夹移动到 /var/www,这是在 Nginx 上运行 Web 应用程序的常见位置. 首先,使用 mv 命令将应用程序文件夹及其所有内容移动到 /var/www/travellist:

1sudo mv ~/travellist /var/www/travellist

现在我们需要给网页服务器用户写入存储bootstrap/cache文件夹,其中Laravel存储应用程序生成的文件。

若要将阅读、写入和执行(rwx)权限包含在所需目录上,请运行:

1sudo setfacl -R -m g:www-data:rwx /var/www/travellist/storage
2sudo setfacl -R -m g:www-data:rwx /var/www/travellist/bootstrap/cache

应用程序文件现在有序,但我们仍然需要配置 Nginx 来服务内容. 为此,我们将创建一个新的虚拟主机配置文件在 /etc/nginx/sites-available:

1sudo nano /etc/nginx/sites-available/travellist

以下配置文件包含 Nginx 上 Laravel 应用程序的 推荐设置:

 1[label /etc/nginx/sites-available/travellist]
 2server {
 3    listen 80;
 4    server_name server_domain_or_IP;
 5    root /var/www/travellist/public;
 6
 7    add_header X-Frame-Options "SAMEORIGIN";
 8    add_header X-XSS-Protection "1; mode=block";
 9    add_header X-Content-Type-Options "nosniff";
10
11    index index.html index.htm index.php;
12
13    charset utf-8;
14
15    location / {
16        try_files $uri $uri/ /index.php?$query_string;
17    }
18
19    location = /favicon.ico { access_log off; log_not_found off; }
20    location = /robots.txt  { access_log off; log_not_found off; }
21
22    error_page 404 /index.php;
23
24    location ~ \.php$ {
25        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
26        fastcgi_index index.php;
27        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
28        include fastcgi_params;
29    }
30
31    location ~ /\.(?!well-known).* {
32        deny all;
33    }
34}

将此内容复制到您的 /etc/nginx/sites-available/travellist 文件中,并调整所突出的值以匹配您的配置。

要激活新的虚拟主机配置文件,在网站启用中创建一个符号链接到旅行者:

1sudo ln -s /etc/nginx/sites-available/travellist /etc/nginx/sites-enabled/

<$>[注意] 注意:如果您有另一个虚拟主机文件,此前已配置为同一服务器_名称用于旅行者虚拟主机,您可能需要通过在/etc/nginx/sites-enabled/中删除相应的符号链接来禁用旧配置。

要确认配置不包含任何语法错误,您可以使用:

1sudo nginx -t

你应该看到这样的输出:

1[secondary_label Output]
2nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
3nginx: configuration file /etc/nginx/nginx.conf test is successful

若要应用这些更改,请重新加载 Nginx:

1sudo systemctl reload nginx

如果您现在重新加载您的浏览器,应用程序图像将被打破,这是因为我们将应用程序目录移动到服务器内部的新位置,因此我们需要重新创建到应用程序存储文件夹的象征链接。

删除旧链接:

1cd /var/www/travellist
2rm -f public/storage

现在再次运行工匠命令来生成存储链接:

1php artisan storage:link

现在,进入您的浏览器并使用服务器的域名或IP地址访问应用程序,如您的配置文件中的server_name指令所定义:

1http://server_domain_or_IP

Travellist v1.0

在下一步中,我们将集成一个对象存储服务到应用程序中,这将取代当前用于旅行照片的本地磁盘存储。

步骤 5 — 将 S3 兼容的对象存储集成到应用程序中

现在我们将设置应用程序以使用 S3 兼容的对象存储服务来存储在索引页面上显示的旅行照片. 由于应用程序已经在本地磁盘中存储了几个样本照片,我们还将使用 s3cmd 工具将现有的本地图像文件上传到远程对象存储中。

如何为 Laravel 设置 S3 驱动程序

Laravel 使用联盟/flysystem,这是一个文件系统抽象库,允许一个 Laravel 应用程序使用和组合多个存储解决方案,包括本地磁盘和云服务。

访问应用程序目录:

1cd /var/www/travellist
1composer require league/flysystem-aws-s3-v3

您将看到类似于此的输出:

 1[secondary_label Output]
 2Using version ^1.0 for league/flysystem-aws-s3-v3
 3./composer.json has been updated
 4Loading composer repositories with package information
 5Updating dependencies (including require-dev)
 6Package operations: 8 installs, 0 updates, 0 removals
 7  - Installing mtdowling/jmespath.php (2.4.0): Loading from cache
 8  - Installing ralouphie/getallheaders (3.0.3): Loading from cache
 9  - Installing psr/http-message (1.0.1): Loading from cache
10  - Installing guzzlehttp/psr7 (1.6.1): Loading from cache
11  - Installing guzzlehttp/promises (v1.3.1): Loading from cache
12  - Installing guzzlehttp/guzzle (6.4.1): Downloading (100%)         
13  - Installing aws/aws-sdk-php (3.112.28): Downloading (100%)         
14  - Installing league/flysystem-aws-s3-v3 (1.0.23): Loading from cache
15...

现在安装了所需的包,我们可以更新应用程序以连接到对象存储器. 首先,我们将再次打开 .env 文件,以设置配置细节,如对象存储服务的密钥,桶名称和区域。

打开.env 文件:

1nano .env

包括以下环境变量,以对象存储配置详细信息代替突出值:

1[label /var/www/travellist/.env]
2DO_SPACES_KEY=EXAMPLE7UQOTHDTF3GK4
3DO_SPACES_SECRET=exampleb8e1ec97b97bff326955375c5
4DO_SPACES_ENDPOINT=https://ams3.digitaloceanspaces.com
5DO_SPACES_REGION=ams3
6DO_SPACES_BUCKET=sammy-travellist

保存并关闭文件完成后. 现在打开 config/filesystems.php 文件:

1nano config/filesystems.php

我们将这个磁盘命名为空间,我们将使用我们在.env文件中设置的环境变量来配置新的磁盘。

 1[label config/filesystems.php]
 2
 3'spaces' => [
 4   'driver' => 's3',
 5   'key' => env('DO_SPACES_KEY'),
 6   'secret' => env('DO_SPACES_SECRET'),
 7   'endpoint' => env('DO_SPACES_ENDPOINT'),
 8   'region' => env('DO_SPACES_REGION'),
 9   'bucket' => env('DO_SPACES_BUCKET'),
10],

仍然在同一文件中,找到条目,并更改它以将新的空间磁盘设置为默认的云文件系统磁盘:

1[label config/filesystems.php]
2'cloud' => env('FILESYSTEM_CLOUD', 'spaces'),

完成编辑后保存和关闭文件. 从您的控制器中,您现在可以使用Storage::cloud()方法作为访问默认的cloud磁盘的捷径。

该应用程序现在已配置为使用对象存储,但我们仍然需要更新将新照片上传到应用程序的代码。

首先,让我们看看当前的上传Photo路径,位于PhotoController类中。

1nano app/Http/Controllers/PhotoController.php
 1[label app/Http/Controllers/PhotoController.php]
 2
 3
 4public function uploadPhoto(Request $request)
 5{
 6   $photo = new Photo();
 7   $place = Place::find($request->input('place'));
 8
 9   if (!$place) {
10       //add new place
11       $place = new Place();
12       $place->name = $request->input('place_name');
13       $place->lat = $request->input('place_lat');
14       $place->lng = $request->input('place_lng');
15   }
16
17   $place->visited = 1;
18   $place->save();
19
20   $photo->place()->associate($place);
21   $photo->image = $request->image->store('/', 'public');
22   $photo->save();
23
24   return redirect()->route('Main');
25}

此方法接受了POST请求,并在照片表中创建了一个新照片条目。它开始通过检查照片上传表中是否已选择一个现有位置,如果不是这样,它将使用所提供的信息创建一个新位置。该位置随后设置为访问并保存到数据库。随后,创建了一个关联,以便新照片链接到指定位置。图像文件然后存储在公共磁盘的根文件夹中。最后,照片被保存到数据库。

在这个行中,图像文件被保存到磁盘中使用商店方法。商店方法用于将文件保存到filesystem.php配置文件中定义的任何磁盘中。

为了做到这一点,我们需要在商店方法调用中用空间磁盘取代公共磁盘,我们还需要确保上传文件的可见性设置为 _public_而不是 private

下面的代码包含完整的PhotoController类,包括更新的UploadPhoto方法:

 1[label app/Http/Controllers/PhotoController.php]
 2<?php
 3
 4namespace App\Http\Controllers;
 5
 6use Illuminate\Http\Request;
 7use App\Photo;
 8use App\Place;
 9use Illuminate\Support\Facades\Storage;
10
11class PhotoController extends Controller
12{
13   public function uploadForm()
14   {
15       $places = Place::all();
16
17       return view('upload_photo', [
18           'places' => $places
19       ]);
20   }
21
22   public function uploadPhoto(Request $request)
23   {
24       $photo = new Photo();
25       $place = Place::find($request->input('place'));
26
27       if (!$place) {
28           //add new place
29           $place = new Place();
30           $place->name = $request->input('place_name');
31           $place->lat = $request->input('place_lat');
32           $place->lng = $request->input('place_lng');
33       }
34
35       $place->visited = 1;
36       $place->save();
37
38       $photo->place()->associate($place);
39       $photo->image = $request->image->store('/', 'spaces');
40       Storage::setVisibility($photo->image, 'public');
41       $photo->save();
42
43       return redirect()->route('Main');
44   }
45}

将更新的代码复制到自己的PhotoController,以反映所突出的更改。

我们仍然需要修改应用程序的主要视图,以便它使用对象存储文件URL来渲染图像。

1nano resources/views/travel_list.blade.php

现在找到页面的脚部部分,目前它看起来像这样:

 1[label resources/views/travel_list.blade.php]
 2@section('footer')
 3   <h2>Travel Photos <small>[ <a href="{{ route('Upload.form') }}">Upload Photo</a> ]</small></h2>
 4   @foreach ($photos as $photo)
 5       <div class="photo">
 6          <img src="{{ asset('storage') . '/' . $photo->image }}" />
 7           <p>{{ $photo->place->name }}</p>
 8       </div>
 9   @endforeach
10
11@endsection

更换当前的图像 src 属性以使用来自 spaces 存储磁盘的文件 URL:

1<img src="{{ Storage::disk('spaces')->url($photo->image) }}" />

如果你现在去浏览器并重新加载应用程序页面,它只会显示破碎的图像. 这是因为这些旅行照片的图像文件仍然只有在本地磁盘上. 我们需要上传现有的图像文件到对象存储,这样已经存储在数据库中的照片可以在应用程序页面上成功显示。

使用s3cmd同步本地图像

s3cmd工具可以用来同步本地文件与S3兼容的对象存储服务. 我们将运行一个同步命令,将存储/app/public/photos内的所有文件上传到对象存储服务。

访问公共应用程序存储目录:

1cd /var/www/travellist/storage/app/public

要查看已存储在远程磁盘上的文件,您可以使用 s3cmd ls 命令:

1s3cmd ls s3://your_bucket_name

现在运行同步命令,将公共存储文件夹中的现有文件上传到对象存储:

1s3cmd sync ./ s3://your_bucket_name --acl-public --exclude=.gitignore

这将同步当前文件夹( storage/app/public)与远程对象存储的 root dir. 你将得到类似的输出:

1[secondary_label Output]
2upload: './bermudas.jpg' -> 's3://sammy-travellist/bermudas.jpg'  [1 of 3]
3 2538230 of 2538230 100% in 7s 329.12 kB/s done
4upload: './grindavik.jpg' -> 's3://sammy-travellist/grindavik.jpg'  [2 of 3]
5 1295260 of 1295260 100% in 5s 230.45 kB/s done
6upload: './japan.jpg' -> 's3://sammy-travellist/japan.jpg'  [3 of 3]
7 8940470 of 8940470 100% in 24s 363.61 kB/s done
8Done. Uploaded 12773960 bytes in 37.1 seconds, 336.68 kB/s.

现在,如果你再次运行s3cmd ls,你会看到三个新文件被添加到你的对象存储库的根文件夹:

1s3cmd ls s3://your_bucket_name
1[secondary_label Output]
22019-10-25 11:49 2538230 s3://sammy-travellist/bermudas.jpg
32019-10-25 11:49 1295260 s3://sammy-travellist/grindavik.jpg
42019-10-25 11:49 8940470 s3://sammy-travellist/japan.jpg

所有图像应该现在可见,如果您使用浏览器调试工具检查它们,您将注意到它们都使用来自您的对象存储中的URL。

测试整合

演示应用程序现在完全功能,存储文件在远程对象存储服务中,并将数据保存到管理的MySQL数据库中。

从您的浏览器访问/upload应用程序路径:

1http://server_domain_or_IP/upload

您将看到以下形式:

Travellist Photo Upload Form

您现在可以上传几张照片来测试对象存储集成.从计算机中选择一个图像后,您可以从下滑菜单中选择一个现有的位置,或者您可以通过提供其名称和地理坐标(https://www.latlong.net/)来添加一个新的位置,这样它就可以加载到应用地图中。

步骤 6 — 用只读节点扩展数字海洋管理的MySQL数据库(可选)

由于只读操作通常比在数据库服务器上编写操作更频繁,因此通过设置多个只读节点来扩展数据库集群是一个常见的做法。

为了展示此设置,我们首先将添加2个只读节点到我们的DigitalOcean Managed MySQL集群中,然后,我们将配置Laravel应用程序以使用这些节点。

点击 DigitalOcean Cloud Panel并遵循以下指示:

  1. 进入 Databases 并选择您的 MySQL 集群。 2 点击行动并从下滑菜单中选择添加只读节点。 3 配置节点选项并点击创建按钮。 请注意,新节点可能需要几分钟才能准备好。 4 重复步骤 1-4 再一次,以便您有 2 个只读节点。 5 请注意两节节点的主机,因为我们需要它们用于我们的 Laravel 配置。

一旦你的只读节点准备好了,回到您的终端。

我们现在将配置我们的Laravel应用程序以与多个数据库节点工作. 当我们完成时,如INSERTUPDATE等查询将被转发到您的主要集群节点,而所有SELECT查询将被重定向到您的只读节点。

首先,前往服务器上的应用程序目录,并使用您所选择的文本编辑器打开您的 .env 文件:

1cd /var/www/travellist
2nano .env

查找MySQL数据库配置并评论DB_HOST行:

1[label /var/www/travellist/.env]
2DB_CONNECTION=mysql
3#DB_HOST=MANAGED_MYSQL_HOST
4DB_PORT=MANAGED_MYSQL_PORT
5DB_DATABASE=MANAGED_MYSQL_DB
6DB_USERNAME=MANAGED_MYSQL_USER
7DB_PASSWORD=MANAGED_MYSQL_PASSWORD

保存并关闭文件完成后. 现在在文本编辑器中打开config/database.php:

1nano config/database.php

请在连接阵列中寻找mysql条目. 您应该在这个配置阵列中包含 三个新项目:粘贴条目将设置群集节点,而粘贴选项设置为将重复使用条目连接,以便在同一请求周期内立即可用到数据库中。

 1[label /var/www/travel_list/config/database.php]
 2...
 3      'mysql' => [
 4         'read' => [
 5           'host' => [
 6              'READONLY_NODE1_HOST',
 7              'READONLY_NODE2_HOST',
 8           ],
 9         ],
10         'write' => [
11           'host' => [
12             'MANAGED_MYSQL_HOST',
13           ],
14         ],
15       'sticky' => true,
16...

保存和关闭文件,当你完成编辑时. 要测试一切按预期运行,我们可以创建一个临时路线在routes/web.php 中,从数据库中提取一些数据,并显示有关正在使用的连接的细节。

打开routes/web.php文件:

1nano routes/web.php

包括下列路线:

1[label /var/www/travel_list/routes/web.php]
2...
3
4Route::get('/mysql-test', function () {
5  $places = App\Place::all();
6  $results = DB::select( DB::raw("SHOW VARIABLES LIKE 'server_id'") );
7
8  return "Server ID: " . $results[0]->Value;
9});

现在,进入您的浏览器并访问/mysql-test应用路径:

1http://server_domain_or_IP/mysql-test

你会看到这样的页面:

mysql node test page

重新加载页面几次,你会注意到服务器ID值发生变化,这表明请求正在随机分布到两个只读节点之间。

结论

在本指南中,我们为一个高度可用和可扩展的环境准备了Laravel 6应用程序,我们将数据库系统外包给了外部管理的MySQL服务,并将S3兼容的对象存储服务集成到应用程序中,以存储用户上传的文件。

包含本指南所做的所有修改的最新演示应用程序代码可在 Github 上的应用程序存储库中找到 2.1 标签

从这里,您可以设置一个 负载平衡器来分配负载并将应用程序扩展到多个节点之间,您也可以利用此设置创建一个 容器化环境来在 Docker 上运行应用程序。

Published At
Categories with 技术
comments powered by Disqus