打破規則:使用 SQLite 演示 Web 應用程序

已發表: 2022-03-10
快速總結 ↬因此,您已經構建了下一個殺手級網絡應用程序,但現在每個新產品都必須考慮的問題是:“我如何向所有人展示我的應用程序有多棒?”

在投入任何時間和金錢之前,大多數潛在用戶都希望試用該軟件或服務。 一些產品只需為用戶提供免費試用就可以很好地發揮作用,而其他應用程序則在已有樣本數據的情況下體驗最佳。 這通常是古老的模擬賬戶發揮作用的地方。

但是,任何曾經實施過模擬賬戶的人都可以證明相關的問題。 您知道互聯網上的事情是如何運行的:任何人都可以輸入數據(無論對產品是否有意義),並且匿名用戶或機器人添加的內容很可能會冒犯他人。 當然,您可以隨時重置數據庫,但多久重置一次? 最終,這真的能解決問題嗎? 我使用 SQLite 的解決方案

為什麼不將 SQLite 用於生產版本?

眾所周知,SQLite 不處理多個線程,因為在寫入命令期間整個數據庫被鎖定,這也是您不應在正常生產環境中使用它的原因之一。 但是,在我的解決方案中,每個演示軟件的用戶使用一個單獨的 SQLite 文件。 這意味著寫入限制僅限於該一個用戶,但多個同時用戶(每個用戶都有自己的數據庫文件)將不會遇到此限制。 這為用戶測試駕駛軟件提供了可控的體驗,並使他們能夠準確地查看希望他們看到的內容。

跳躍後更多! 繼續往下看↓

本教程基於我自 2015 年以來為 SaaS 演示 Web 應用程序成功運行的真實解決方案。本教程是為 Ruby on Rails(我選擇的框架)版本 3 及更高版本編寫的,但基本概念應該是能夠適應任何其他語言或框架。 事實上,由於 Ruby on Rails 遵循“約定優於配置”的軟件範式,它甚至可能更容易在其他框架中實現,尤其是裸語言(例如直接 PHP)或在管理數據庫連接方面做得不多的框架.

話雖如此,這種技術特別適合 Ruby on Rails。 為什麼? 因為,在大多數情況下,它是“與數據庫無關的”。 這意味著您應該能夠編寫 Ruby 代碼並在數據庫之間切換而不會出現任何問題。

可以從 GitHub 下載此過程的完成版本的示例。

第一步:部署環境

我們稍後會進行部署,但 Ruby on Rails 默認分為開發、測試和生產環境。 我們將為我們的應用程序添加一個新的演示環境,該環境幾乎與生產環境相同,但允許我們使用不同的數據庫設置。

在 Rails 中,通過複製config/environments/production.rb文件並重命名為demo.rb來創建一個新環境。 由於演示環境將用於類似生產環境的設置,因此您可能不需要為這個新環境更改許多配置選項,但我建議將config.assets.compilefalse更改為true ,這樣可以更輕鬆地在本地進行測試,而無需必須預編譯。

如果您運行的是 Rails 4 或更高版本,您還需要更新config/secrets.yml secrets.yml 為演示環境添加一個secret_key_base 。 請務必使此密鑰與生產環境不同,以確保每個環境之間的會話都是唯一的,從而進一步保護您的應用程序。

接下來,您需要在config/database.yml中定義數據庫配置。 雖然演示環境將主要使用我們將在下一節中介紹的複制數據庫,但我們必須定義用於演示的默認數據庫文件和設置。 將以下內容添加到config/database.yml

 demo: adapter: sqlite3 pool: 5 timeout: 5000 database: db/demo.sqlite3

在 Rails 中,您可能還需要檢查Gemfile以確保 SQLite3 在新的演示環境中可用。 您可以通過多種方式設置它,但它可能如下所示:

 group :development, :test, :demo do gem 'sqlite3' end

配置數據庫後,您需要rake db:migrate RAILS_ENV=demo ,然後根據需要將數據播種到數據庫中(無論是來自種子文件、手動輸入新數據還是複制development.sqlite3文件)。 此時,您應該通過從命令行運行rails server -e demo來檢查以確保一切正常。 當您在新的演示環境中運行服務器時,您可以確保您的測試數據是您想要的,但您可以隨時回來編輯該內容。 將內容添加到演示數據庫時,我建議創建一組乾淨的數據,以使文件盡可能小。 但是,如果您需要從另一個數據庫遷移數據,我推薦使用 YamlDb,它創建了一種獨立於數據庫的格式,用於轉儲和恢復數據。

如果您的 Rails 應用程序按預期運行,您可以繼續下一步。

第二步:使用演示數據庫

本教程的重要部分是能夠允許每個會話使用不同的 SQLite 數據庫文件。 通常,您的應用程序將為每個用戶連接到同一個數據庫,因此此任務需要額外的代碼。

要開始允許 Ruby on Rails 切換數據庫,我們首先需要將以下四個私有方法添加到application_controller.rb中。 您還需要為方法set_demo_database定義一個 before 過濾器,以便在每次頁面加載時調用引用正確演示數據庫的邏輯。

 # app/controllers/application_controller.rb # use `before_filter` for Rails 3 before_action :set_demo_database, if: -> { Rails.env == 'demo' } private # sets the database for the demo environment def set_demo_database if session[:demo_db] # Use database set by demos_controller db_name = session[:demo_db] else # Use default 'demo' database db_name = default_demo_database end ActiveRecord::Base.establish_connection(demo_connection(db_name)) end # Returns the current database configuration hash def default_connection_config @default_config ||= ActiveRecord::Base.connection.instance_variable_get("@config").dup end # Returns the connection hash but with database name changed # The argument should be a path def demo_connection(db_path) default_connection_config.dup.update(database: db_path) end # Returns the default demo database path defined in config/database.yml def default_demo_database return YAML.load_file("#{Rails.root.to_s}/config/database.yml")['demo']['database'] end

由於每個服務器會話都有不同的數據庫,因此您將數據庫文件名存儲在會話變量中。 如您所見,我們使用session[:demo_db]來跟踪用戶的特定數據庫。 set_demo_database方法通過建立與會話變量中設置的數據庫的連接來控制要使用的數據庫。 default_demo_database方法只是加載database.yml配置文件中定義的數據庫路徑。

如果您使用的是純語言,此時您可能只需更新數據庫連接腳本以指向新數據庫,然後繼續下一部分。 在 Rails 中,事情需要更多的步驟,因為它遵循“約定優於配置”的軟件範例。

第三步:複製 SQLite 文件

現在應用程序已設置為使用新數據庫,我們需要一個新演示會話的觸發器。 為簡單起見,首先使用一個基本的“開始演示”按鈕。 您還可以將其設置為收集姓名和電子郵件地址(用於銷售團隊的跟進等)或任何數量的表格。

堅持 Rails 約定,創建一個新的 'Demo' 控制器:

 rails generate controller demos new

接下來,您應該更新路由以指向您的新控制器操作,將它們包裝在條件中以防止它在生產環境中被調用。 您可以根據需要命名路線或使用標準 Rails 約定命名它們:

 if Rails.env == 'demo' get 'demos/new', as: 'new_demo' post 'demos' => 'demos#create', as: 'demos' end

接下來,讓我們在views/demos/new.html.erb中添加一個非常基本的表單。 您可能需要添加其他表單字段來捕獲:

 <h1>Start a Demo</h1> <%= form_tag demos_path, method: :post do %> <%= submit_tag 'Start Demo' %> <% end %>

魔法發生在create動作中。 當用戶提交到該路由時,該操作將使用新的唯一文件名複製demo.sqlite3文件,設置會話變量,登錄用戶(如果適用),然後將用戶重定向到相應的頁面(我們將其稱為'儀表板')。

 class DemosController < ApplicationController def new # Optional: setting session[:demo_db] to nil will reset the demo session[:demo_db] = nil end def create # make db/demos dir if doesn't exist unless File.directory?('db/demos/') FileUtils.mkdir('db/demos/') end # copy master 'demo' database master_db = default_demo_database demo_db = "db/demos/demo-#{Time.now.to_i}.sqlite3" FileUtils::cp master_db, demo_db # set session for new db session[:demo_db] = demo_db # Optional: login code (if applicable) # add your own login code or method here login(User.first) # Redirect to wherever you want to send the user next redirect_to dashboard_path end end

現在,您應該能夠再次使用運行rails server -e demo啟動服務器來在本地試用演示代碼。

如果您的服務器已經在運行,您將需要重新啟動它以進行任何更改,因為它已配置為像生產服務器一樣緩存代碼。

一旦所有代碼按預期工作,提交您對版本控制的更改並確保提交demo.sqlite3文件,而不是db/demos目錄中的文件。 如果您使用的是 git,您只需將以下內容添加到您的.gitignore文件中:

如果您想從演示用戶那裡收集其他信息(例如姓名和/或電子郵件),您可能希望通過 API 將該信息發送到您的主應用程序或其他一些銷售渠道,因為您的演示數據庫將不可靠(每次重新部署時都會重置)。

 !/db/demo.sqlite3 db/demos/*

最後一步:部署演示服務器

現在您的演示設置在本地工作,您顯然希望部署它以便每個人都可以使用它。 雖然每個應用程序都不同,但我建議演示應用程序位於單獨的服務器上,因此域作為您的生產應用程序(例如 demo.myapp.com)。 這將確保您保持兩個環境隔離。 此外,由於 SQLite 文件存儲在服務器上,因此像 Heroku 這樣的服務將無法工作,因為它不提供對文件系統的訪問。 但是,您仍然可以使用幾乎任何 VPS 提供商(例如 AWS EC2、Microsoft Azure 等)。 如果您喜歡自動化的便利,還有其他平台即服務選項可讓您使用 VPS。

無論您的部署過程如何,您可能還需要檢查應用程序是否對您存儲演示 SQLite 文件的目錄具有適當的讀/寫權限。 這可以手動或使用部署掛鉤來處理。

SQLite 不適合我。 其他數據庫系統呢?

沒有兩個應用程序是相同的,它們的數據庫要求也沒有。 通過使用 SQLite,您具有能夠快速復制數據庫以及能夠將文件存儲在版本控制中的優勢。 雖然我相信 SQLite 將適用於大多數情況(尤其是使用 Rails),但在某些情況下 SQLite 可能不適合您的應用程序的需求。 幸運的是,仍然可以將上述相同的概念用於其他數據庫系統。 對於每個系統,複製數據庫的過程會略有不同,但我將概述 MySQL 的解決方案,PostgreSQL 和其他系統也存在類似的過程。

上面介紹的大多數方法都可以在沒有任何額外修改的情況下工作。 但是,不要在版本控制中存儲 SQLite 文件,而應使用mysqldump (或 PostgreSQL 的pg_dump )導出包含您想要用於演示體驗的內容的任何數據庫的 SQL 文件。 該文件也應該存儲在您的版本控制中。

對先前代碼的唯一更改將在demos#create操作中找到。 控制器操作不會復制 SQLite3 文件,而是創建一個新數據庫,將 sql 文件加載到該數據庫中,並在必要時為數據庫用戶授予權限。 僅當您的數據庫管理員用戶與應用程序用於連接的用戶不同時,才需要授予訪問權限的第三步。 以下代碼使用標準 MySQL 命令來處理這些步驟:

 def create # database names template_demo_db = default_demo_database new_demo_db = "demo_database_#{Time.now.to_i}" # Create database using admin credentials # In this example the database is on the same server so passing a host argument is not require `mysqladmin -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } create #{new_demo_db}` # Load template sql into new database # Update the path if it differs from where you saved the demo_template.sql file `mysql -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } #{new_demo_db} < db/demo_template.sql` # Grant access to App user (if applicable) `mysql -u#{ ENV['DB_ADMIN'] } -p#{ ENV['DB_ADMIN_PASSWORD'] } -e "GRANT ALL on #{new_demo_db}.* TO '#{ ENV['DB_USERNAME'] }'@'%';"` # set session for new db session[:demo_db] = new_demo_db # Optional: login code (if applicable) # add your own login code or method here login(User.first) redirect_to dashboard_path end

與包括 PHP 在內的許多其他語言一樣,Ruby 允許您在代碼中使用反引號來執行 shell 命令(即`ls -a` )。 但是,您必須謹慎使用它,並確保不能將面向用戶的參數或變量插入命令中,以保護您的服務器免受惡意注入的代碼的侵害。 在這個例子中,我們顯式地與 MySQL 命令行工具交互,這是創建新數據庫的唯一方法。 這與 Ruby on Rails 框架創建新數據庫的方式相同。 請務必將ENV['DB_ADMIN']ENV['DB_ADMIN_PASSWORD']替換為您自己的環境變量或任何其他設置數據庫用戶名的方式。 如果您的管理員用戶與您的應用程序的用戶不同,您將需要對ENV['DB_USERNAME']執行相同的操作。

這就是切換到 MySQL 所需的全部內容! 此解決方案最明顯的優勢是您不必擔心數據庫系統之間的不同語法可能出現的潛在問題。

最終,最終決定是基於預期的質量和服務,而不是便利性和速度,並且不一定僅受價格點的影響。

最後的想法

這只是您可以使用新演示服務器執行的操作的起點。 例如,您的營銷網站可能有一個指向“試用功能 XYZ”的鏈接。 如果您不需要姓名或電子郵件,則可以將demos#create方法與/demos/?feature=xyz之類的鏈接鏈接起來,該操作將簡單地重定向到所需的功能和/或頁面,而不是中的儀表板上面的例子。

此外,如果您將 SQLite 用於開發和演示環境,則始終在版本控制中使用此示例數據庫將使您的所有開發人員都可以訪問乾淨的數據庫,以便在本地開發、測試環境或質量保證測試中使用。 可能性是無止境。

您可以從 GitHub 下載完整的演示。