項目背景 在java項目部署過程中,由於內外部各種因素,可能會遇到一些感覺操作不便捷的場景,例如 jar包未隨系統自動啟動需要每次手動重啟 系統vpn堡壘機多重防禦更新繁瑣 系統無圖形化界面命令行操作複雜 等等...... 在工作中之前也總結了windows的Jar包部署工具與linux下的jar包 ...
項目背景
在java項目部署過程中,由於內外部各種因素,可能會遇到一些感覺操作不便捷的場景,例如
- jar包未隨系統自動啟動需要每次手動重啟
- 系統vpn堡壘機多重防禦更新繁瑣
- 系統無圖形化界面命令行操作複雜
- 等等......
在工作中之前也總結了windows的Jar包部署工具與linux下的jar包自動化部署腳本,這次就想著否能將二者統一結合,本著簡單/高效/功能專一的原則,做出一
個可視化jar包部署平臺,JarManage應運而生
功能介紹
項目地址:https://gitee.com/code2roc/jar-manage
支持線上創建項目,上傳Jar包,自動備份,配置啟動參數,註冊系統服務,查看啟動日誌等功能,具有以下優點
- 基於servlet開發,依賴簡潔,部署包10MB左右
- 結合嵌入式tomcat一鍵部署,無外部容器依賴
- 使用h2db存儲數據,無外部資料庫依賴
- 適配windows/linux平臺,滿足多種環境
- 具體項目經平臺部署後自動註冊系統服務,無需擔心伺服器重啟
系統架構圖如下
系統截圖展示
技術分析
平臺識別
首先通過系統os識別是windows平臺還是linux平臺
String os = System.getProperty("os.name").toLowerCase();
if (os.startsWith("win")) {
platform = DepolyPlatform.Windows;
}
通過system-release文件識別部分基於CentOS開發的Linux系統
String command = "cat /etc/system-release";
String result = CMDUtil.executeLinuxCommand(command);
if (result.startsWith("Red Hat")) {
platform = DepolyPlatform.LinuxRedHat;
} else if (result.startsWith("CentOS")) {
platform = DepolyPlatform.LinuxCentOS;
} else if (result.startsWith("openEuler")) {
platform = DepolyPlatform.LinuxOpenEuler;
}
通過issue文件識別部分基於Ubuntu/Debian開發的Linux系統
command = "cat /etc/issue";
result = CMDUtil.executeLinuxCommand(command);
if (!StringUtil.isEmpty(result)) {
if (result.startsWith("Ubuntu")) {
platform = DepolyPlatform.LinuxUbuntu;
} else if (result.startsWith("Debian")) {
platform = DepolyPlatform.LinuxDebian;
}
}
windows註冊服務
通過sc query命令判斷服務狀態
public String getStatus(String serviceName) {
String status = DepolyStatus.UnInstall;
try {
String command = "sc query " + serviceName;
String commandResultFilePath = CMDUtil.executeWindowCommandStoreFile(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(commandResultFilePath)));
String line = reader.readLine();
while (line != null) {
if (line.trim().startsWith("STATE")) {
if (line.trim().substring(line.trim().indexOf(":") + 1, line.trim().indexOf(":") + 4).trim().equals("1"))
status = DepolyStatus.Stopped;
else if (line.trim().substring(line.trim().indexOf(":") + 1, line.trim().indexOf(":") + 4).trim().equals("2"))
status = DepolyStatus.Startting;
else if (line.trim().substring(line.trim().indexOf(":") + 1, line.trim().indexOf(":") + 4).trim().equals("3"))
status = DepolyStatus.Stopping;
else if (line.trim().substring(line.trim().indexOf(":") + 1, line.trim().indexOf(":") + 4).trim().equals("4"))
status = DepolyStatus.Running;
}
line = reader.readLine();
}
} catch (IOException e) {
LogUtil.error(e);
}
return status;
}
通過winsw這個開源項目配置exe和xml文件將jar包註冊為windows服務,項目地址:https://github.com/winsw/winsw/
linux註冊服務
通過systemctl status命令判斷服務狀態
public String getStatus(String serviceName) {
String status = DepolyStatus.UnInstall;
try {
String command = "systemctl status " + serviceName;
String commandResultFilePath = CMDUtil.executeLinuxCommandWithStore(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(commandResultFilePath)));
String line = reader.readLine();
while (line != null) {
if (line.trim().startsWith("Active")) {
if (line.trim().indexOf("inactive (dead)") > 0)
status = DepolyStatus.Stopped;
else if (line.trim().indexOf("active (running)") > 0)
status = DepolyStatus.Running;
else if (line.trim().indexOf("failed") > 0)
status = DepolyStatus.Stopped;
}
line = reader.readLine();
}
} catch (IOException e) {
LogUtil.error(e);
}
return status;
}
通過拷貝service文件到systemd/system目錄下註冊linux服務
yml配置文件識別
- maven配置
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.26</version>
</dependency>
- 配置文件
jarmanage:
port: 8555
username: admin
password: abcd@1234
backupcount: 5
- 工具類
public static String getConfigValue(String configName){
String configValue = "";
try{
Yaml yaml = new Yaml();
InputStream resourceAsStream = new FileInputStream(new File("resources"+File.separator+"application.yml"));
Map obj = yaml.load(resourceAsStream);
Map<String,Object> param = (Map) obj.get("jarmanage");
configValue = ConvertUtil.convert2String(param.get(configName));
}catch (Exception e){
LogUtil.error(e);
}
return configValue;
}
h2database使用
- maven引用
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
</dependency>
- 工具類
public static Connection getConnection() throws Exception {
File file = new File("database");
Connection conn = DriverManager.getConnection("jdbc:h2:file:" + file.getAbsolutePath() + File.separator + "manage", "root", "abcd@1234");
return conn;
}
public static void executeSQL(String sql) {
try {
Connection conn = getConnection();
Statement stmt = conn.createStatement();
stmt.execute(sql);
stmt.close();
conn.close();
} catch (Exception e) {
LogUtil.error(e);
}
}
servelt內置tomcat打包
- maven引用
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.35</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>9.0.35</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.35</version>
</dependency>
- 手動啟動
//啟動tomcat服務
// 1.創建一個內嵌的Tomcat
Tomcat tomcat = new Tomcat();
// 2.設置Tomcat埠
tomcat.setPort(8555);
// 3.設置工作目錄,tomcat需要使用這個目錄進行寫一些東西
final String baseDir = "workspace" + File.separator;
tomcat.setBaseDir(baseDir);
tomcat.getHost().setAutoDeploy(false);
// 4. 設置webapp資源路徑
String webappDirLocation = "webapp" + File.separator;
StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
// 5. 設置上下文路每徑
String contextPath = "";
ctx.setPath(contextPath);
ctx.addLifecycleListener(new Tomcat.FixContextListener());
ctx.setName("jar-manage");
tomcat.getHost().addChild(ctx);
//6.啟動
tomcat.getConnector();
tomcat.start();
tomcat.getServer().await();
- 打包包含引用類庫,自定義配置xml,指定運行class
<plugins>
<plugin>
<!-- 打包包含引用 -->
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<!-- 自定義配置 -->
<descriptor>package.xml</descriptor>
</descriptors>
<archive>
<manifest>
<!-- 運行類 -->
<mainClass>com.code2roc.jarmanage.Application</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 https://maven.apache.org/xsd/assembly-2.1.1.xsd">
<!-- TODO: a jarjar format would be better -->
<id>depoly</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>true</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>src/main/webapp/</directory>
<outputDirectory>/webapp</outputDirectory>
<includes>
<include>**/**</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources/</directory>
<outputDirectory>/resources</outputDirectory>
<includes>
<include>**/**</include>
</includes>
</fileSet>
</fileSets>
</assembly>