买了阿里云服务器,因为装的是win版本的;虽然每次发布jar包可以直接复制和启动;但是这个涉及到网络的问题,如果网络慢一点的话复制一个jar包进行需要好多分钟;于是就萌生了写一个网页实现更新jar包和启动jar包的渣渣网页;目前测试基本完成,就差优化界面和整合逻辑,基本的功能已经具备;主要分三部分完成。
(一)java关闭指定的端口号进程
本项目主要使用springboot实现,控制层实现调用killpid8081pid方法,实现指定端口号,因为我这边需要关闭的的是8081的端口进程,大家如果有需要的自行改造控制层就行;
/**
* 关闭指定的进程
* @return
*/
@ResponseBody
@RequestMapping("killpid")
public String killpid(){
try {
return Skillservice.killpid8081pid("8081");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
}
我新建了一个skillservice类,代码如下所示:
String[] split = pids.split(",");这个是获取多个端口号,可以实现多个进程号同时关闭,目前我这里只需要关闭8081端口;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
public class skillservice {
public Set<Integer> ports;
public String killpid8081pid(String pids) throws InterruptedException {
JSONObject object=new JSONObject();
String[] split = pids.split(",");
Set<Integer> ports = new HashSet<>();
for (String spid : split) {
try{
int pid = Integer.parseInt(spid);
ports.add(pid);
}catch(Exception e){
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.exit(0);
}
}
skillservice kill = new skillservice();
kill.ports = ports;
System.out.println("need kill " + ports.size() + " num");
for (Integer pid : ports) {
kill.start(pid);
}
object.put("state","200");
object.put("msg","清理完毕,程序即将退出");
Thread.sleep(5000);
System.exit(0);
return object.toString();
}
public void start(int port){
Runtime runtime = Runtime.getRuntime();
try {
//查找进程号
Process p = runtime.exec("cmd /c netstat -ano | findstr \""+port+"\"");
InputStream inputStream = p.getInputStream();
List<String> read = read(inputStream, "UTF-8");
if(read.size() == 0){
System.out.println("找不到该端口的进程");
try {
Thread.sleep(6000);
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
for (String string : read) {
System.out.println(string);
}
System.out.println("找到"+read.size()+"个进程,正在准备清理");
kill(read);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 验证此行是否为指定的端口,因为 findstr命令会是把包含的找出来,例如查找80端口,但是会把8099查找出来
* @param str
* @return
*/
private boolean validPort(String str){
Pattern pattern = Pattern.compile("^ *[a-zA-Z]+ +\\S+");
Matcher matcher = pattern.matcher(str);
matcher.find();
String find = matcher.group();
int spstart = find.lastIndexOf(":");
find = find.substring(spstart + 1);
int port = 0;
try {
port = Integer.parseInt(find);
} catch (NumberFormatException e) {
System.out.println("查找到错误的端口:" + find);
return false;
}
if(this.ports.contains(port)){
return true;
}else{
return false;
}
}
/**
* 更换为一个Set,去掉重复的pid值
* @param data
*/
public void kill(List<String> data){
Set<Integer> pids = new HashSet<>();
for (String line : data) {
int offset = line.lastIndexOf(" ");
String spid = line.substring(offset);
spid = spid.replaceAll(" ", "");
int pid = 0;
try {
pid = Integer.parseInt(spid);
} catch (NumberFormatException e) {
System.out.println("获取的进程号错误:" + spid);
}
pids.add(pid);
}
killWithPid(pids);
}
/**
* 一次性杀除所有的端口
* @param pids
*/
public void killWithPid(Set<Integer> pids){
for (Integer pid : pids) {
try {
Process process = Runtime.getRuntime().exec("taskkill /F /pid "+pid+"");
InputStream inputStream = process.getInputStream();
String txt = readTxt(inputStream, "GBK");
System.out.println(txt);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private List<String> read(InputStream in,String charset) throws IOException{
List<String> data = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
String line;
while((line = reader.readLine()) != null){
boolean validPort = validPort(line);
if(validPort){
data.add(line);
}
}
reader.close();
return data;
}
public String readTxt(InputStream in,String charset) throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(in, charset));
StringBuffer sb = new StringBuffer();
String line;
while((line = reader.readLine()) != null){
sb.append(line);
}
reader.close();
return sb.toString();
}
}
这样就能实现关闭了指定的端口,更新jar包第一步实现;
(二)上传jar包和bat文件
这里我使用的是freemaker模板,因为只是用来自己用,所以样式啥也还没进行调整,能上传就行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传jar包</title>
</head>
<body>
<form action="review" method="POST" enctype="multipart/form-data">
上传jar包: <input type="file" name="uploadpackage"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
这里需要注意的是:form标签需要增加enctype="multipart/form-data",系统才能识别我上传的其实是文件类型,控制层代码如下:
/**
* 接受jat包
* @return
*/
@RequestMapping("/review")
@ResponseBody
public String toreview(@RequestParam("uploadpackage") MultipartFile file) {
if (file.isEmpty()) {
return "上传失败,请选择文件";
}
//获取上传的jar包名称
String filename = file.getOriginalFilename();
//判断指定的文件夹中是否存在该包
File file0 = new File("C:\\app_log\\log");
//获取文件夹下的所有的文件名称
String[] fileName = file0.list();
for (String name : fileName) {
if (name.equals(filename)) {
//说明该包已经存在,需要删除该包再提交
File file1 = new File("C:\\app_log\\log\\" + name);
// 路径为文件且不为空则进行删除
if (file1.isFile() && file1.exists()) {
file1.delete();
// return "删除成功";
}
}
}
// String filePat h="C:\\package\\后台管理\\";
String filePath = "C:\\app_log\\log\\";
File dest = new File(filePath + filename);
try {
file.transferTo(dest);
return "上传成功";
} catch (Exception e) {
return e.toString();
}
}
这样就实现了jar包的上传,如果之前有一样的jar,那么会先删除之后才上传,如果没有存在,那么就直接上传就行;
上传bat文件的也跟这个逻辑一样,代码如下,我就不讲解了:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传bat文件</title>
</head>
<body>
<form action="reviewbat" method="POST" enctype="multipart/form-data">
上传bat文件: <input type="file" name="uploadbat"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
@RequestMapping("/reviewbat")
@ResponseBody
public String toreview(@RequestParam("uploadbat") MultipartFile file) {
if (file.isEmpty()) {
return "上传失败,请选择文件";
}
//获取上传的jar包名称
String filename = file.getOriginalFilename();
//判断指定的文件夹中是否存在该包
File file0 = new File("C:\\app_log\\log");
//获取文件夹下的所有的文件名称
String[] fileName = file0.list();
for (String name : fileName) {
if (name.equals(filename)) {
//说明该包已经存在,需要删除该包再提交
File file1 = new File("C:\\app_log\\log\\" + name);
// 路径为文件且不为空则进行删除
if (file1.isFile() && file1.exists()) {
file1.delete();
// return "删除成功";
}
}
}
// String filePat h="C:\\package\\后台管理\\";
String filePath = "C:\\app_log\\log\\";
File dest = new File(filePath + filename);
try {
file.transferTo(dest);
return "上传成功";
} catch (Exception e) {
return e.toString();
}
}
}
(三)使用bat启动jar包
第三部分也是本次的重点所在,我们上传了jar包之后,需要启动该项目,那我如果上去远程用命令行启动的话,那前面的过程就基本没有作用了,所以我们这里需要用java来启动bat文件
bat文件也就是上面的上传的文件,文件的内容就是启动刚刚上传的命令行:java -jar java -jar newsale-0.0.1-SNAPSHOT.jar
代码如下所示:
/**
* 实现自动化部署
* @return
*/
@RequestMapping("/bushu")
@ResponseBody
public String tobushu() {
try {
Runtime.getRuntime().exec("cmd /k start C:\\package\\tt.bat");
return "success";
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
这样就实现了通过网页上传jar包和上传bat文件,运行bat文件之后启动更新后的jar包,实现工程启动。欢迎指导交流。