SpringBoot+Vue+MySQL+Redis在Windows机器上一键安装部署
整体安装部署流程:
- 初始化依赖服务和程序目录;
- 编写安装及卸载bat脚本;
- 通过Inno Setup将程序目录打包成exe;
Inno Setup下载地址:https://jrsoftware.org/isdl.php
Inno Setup文档:https://jrsoftware.org/ishelp/
1、初始化目录
将所需依赖和程序放入install目录,所需依赖尽量选择解压版,这样注册服务即可直接使用。如下图示,JDK、Nginx、MySQL、Redis等均为解压版。
2、安装及卸载bat脚本
安装脚本install.bat内容:
(1)将依赖和程序注册成Windows服务;
(2)执行数据库脚本初始化;
(3)创建Windows任务计划;
2.1 注册服务
JDK
JDK可以选择配置环境变量,也可以通过相对路径执行java运行命令。本文使用相对路径方式,避免注册环境变量影响机器的其他java服务。
SpringBoot打完的jar包,使用winsw方式注册成Windows服务,具体命令如下:
cd app
"%cd%\business_service.exe" install
sc config business start=auto
net start business
Nginx
Nginx注册服务同样使用winsw方式,命令如下:
cd nginx-1.21.6
"%cd%\nginx_service.exe" install
sc config nginx start=auto
net start nginx
具体可参考:Windows服务器上Nginx注册服务及设置开机启动
Vue打包后的静态资源默认放在Nginx下的html目录。
MySQL
mysql.exe支持注册Windows服务,直接使用install命令即可,具体命令如下:
::安装vcredist_x64依赖
bin\vcredist_x64.exe
::注册mysql服务
bin\mysqld.exe -install mysql --defaults-file="%cd%\my.ini"
bin\mysqld.exe --initialize-insecure --user=mysql
::配置服务开机自启动
sc config mysql start=auto
::启动mysql服务
net start mysql
Redis
redis-server.exe也提供了直接注册服务的方式, 具体命令如下:
cd Redis-x64-5.0.14
set inipath=%cd%\redis.windows.conf
"%cd%\redis-server.exe" --service-install "%inipath%" --service-name redis --loglevel verbose
sc config redis start=auto
net start redis
2.2 数据库初始化
::创建数据库
"bin\mysql.exe" -uroot < "%cd%\sql\create_db.sql"
::数据库初始化
"bin\mysql.exe" -ubusiness < "%cd%\sql\business-admin.sql"
注意:需交互式输入对应用户的密码;
2.3 创建Windows任务计划
每天定时备份数据或文件,具体bat命令如下:
::添加MySQL自动备份任务计划
schtasks /create /F /tn "mysql-backup" /tr %cd%\bin\mysql_backup.bat /sc daily /st 02:00
卸载uninstall.bat脚本内容:
(1)删除服务和任务计划。
@echo off
::关闭服务
net stop business
net stop redis
net stop nginx
::删除服务
sc delete redis
sc delete nginx
sc delete business
::删除任务计划
schtasks /delete /tn "mysql-backup" /f
3、Inno Setup打包
从Inno Setup下载最新版本,本文使用的版本为:innosetup-6.2.1.exe
下载安装完毕后,在安装目录\Inno Setup 6\Examples下,有官方提供的一些Example示例,可以参考并编写自己的打包脚本。如下图示:
可以根据AllPagesExample.iss示例,了解所有安装页面,并组织自己的安装流程页面。
Inno Setup打包配置可参考以下Demo:
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppId "business-app-release"
#define MyAppName "商务云平台"
#define MyAppVersion "V1.1.5"
#define MyAppPublisher "AppBusinessCloud,Inc."
#define MyAppURL ""
#define MyAppExeName "商务云平台.exe"
[Setup]
AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName}{#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName=business
DisableProgramGroupPage=yes
WizardStyle=modern
OutputBaseFilename=business
Compression=lzma
SolidCompression=yes
SetupLogging=yes
;OutputDir=
DisableWelcomePage=no
LicenseFile=install/License.txt
SetupIconFile=D:\business\business-install\install\favicon.ico
AppCopyright=Copyright (C) 2022 AppBusinessCloud, Inc.
[Languages]
Name: "cn"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"
[Files]
Source: "D:\business\business-install\install\*"; DestDir:"{app}";Flags: ignoreversion recursesubdirs createallsubdirs
[Icons]
Name: "{commondesktop}\{#MyAppName}"; IconFilename: "{app}\favicon.ico"
[INI]
Filename: "{app}\business.ini"; Section:"default"; Key: "app"; String: "{app}"
Filename: "{app}\business.ini"; Section:"default"; Key: "tenantName"; String: "{code:GetBusinessInfo|name}"
Filename: "{app}\business.ini"; Section:"default"; Key: "tenantCode"; String: "{code:GetBusinessInfo|code}"
[Code]
var
CustomInputPage: TInputQueryWizardPage;
tenantName, tenantCode, serialNumber: String;
procedure WriteFixmedinsInfoFile;
var
fileName,tempStr:String;
svArray: TArrayOfString;
nLines,i:Integer;
begin
Log('WriteFixmedinsInfoFile...');
fileName := ExpandConstant('{app}\sql\org.sql');
LoadStringsFromFile(fileName, svArray);
nLines := GetArrayLength(svArray);
for i := 0 to nLines - 1 do
begin
tempStr := svArray[i];
Log(tempStr);
if (Pos('#{tenantName}', tempStr) > 0) then
begin
StringChangeEx(svArray[i], '#{tenantName}', tenantName, True); //将[tenantName]占位符替换为tenantName对应值
end;
if (Pos('#{tenantCode}', tempStr) > 0) then
begin
StringChangeEx(svArray[i], '#{tenantCode}', tenantCode, True); //将[tenantCode]占位符替换为tenantCode对应值
end;
if (Pos('#{serialNumber}', tempStr) > 0) then
begin
StringChangeEx(svArray[i], '#{serialNumber}', serialNumber, True); //将[serialNumber]占位符替换为serialNumber对应值
end;
end;
SaveStringsToUTF8File(fileName, svArray, false);
end;
var SerialNumberMemo: TNewMemo;
procedure InitializeWizard;
var
Index: Integer;
CustomEdit: TCustomEdit;
begin
Log('InitializeWizard...');
// Create the page
CustomInputPage := CreateInputQueryPage(wpLicense,
'租户信息', '提示信息:租户名称和租户编码请联系商务获取!',
'请填写您的租户名称和租户编码信息');
// Add items (False means it's not a password edit)
CustomInputPage.Add('&租户名称:', False);
CustomInputPage.Add('&租户编码:', False);
Index := CustomInputPage.Add('&序列号:', False);
CustomEdit := CustomInputPage.Edits[Index];
SerialNumberMemo := TNewMemo.Create(WizardForm);
SerialNumberMemo.Parent := CustomEdit.Parent;
SerialNumberMemo.SetBounds(CustomEdit.Left, CustomEdit.Top, CustomEdit.Width, ScaleY(70));
CustomEdit.Visible := False;
CustomInputPage.PromptLabels[Index].FocusControl := SerialNumberMemo;
// Set initial values (optional)
CustomInputPage.Values[0] := ExpandConstant('');
CustomInputPage.Values[1] := ExpandConstant('');
CustomInputPage.Values[3] := ExpandConstant('');
end;
function GetBusinessInfo(Param: String): String;
begin
Result := tenantName;
if Param = 'name' then begin
Result := tenantName;
WriteFixmedinsInfoFile;
end else if Param = 'code' then begin
Result := tenantCode;
end;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
{ Validate certain pages before allowing the user to proceed }
Result :=True;
if CurPageID = CustomInputPage.ID then begin
if CustomInputPage.Values[0] = '' then begin
MsgBox('租户名称不能为空!', mbError, MB_OK);
Result := False;
end else if CustomInputPage.Values[1] = '' then begin
MsgBox('租户编码不能为空!', mbError, MB_OK);
Result := False;
end else if SerialNumberMemo.Text = '' then begin
MsgBox('序列号不能为空!', mbError, MB_OK);
Result := False;
end
// 非空格式校验
else if CustomInputPage.Values[1] <> '' then begin
if (Length(CustomInputPage.Values[1]) < 12) then
begin
MsgBox('租户格式编码错误!', mbError, MB_OK);
Result := False;
end;
end;
// Read values into variables
tenantName := CustomInputPage.Values[0];
tenantCode := CustomInputPage.Values[1];
serialNumber := SerialNumberMemo.Text;
end;
end;
[Run]
Filename: "{app}\install.bat"
[UninstallRun]
RunOnceId: "SmapleUnInstallService"; Filename: "{app}\uninstall.bat"
更多Inno Setup使用参见官方帮助文档:Inno Setup Help