对现有Spring MVC + JSP项目部署方案的改造

对现有Spring MVC + JSP项目部署方案的改造

Scroll Down

对现有Spring MVC + JSP项目部署方案的改造

head

吐槽

虽然我以前见过手动替换war包的,但是当这边原来的人员离职,项目交由我,居然教我手动copy classes文件和 JSP文件的时候,我是崩溃的。。

0202年了,为什么还会有人这么做?而且,他们竟然能忍个一两年都是一直手动,这点我还是有、服气的。

总结一下他们的操作步骤:

1、使用Eclipse本地点击clean

2、至少2、3分钟,等待classes和JSP更新到对应存放的目录

3、远程一台windows 2012服务器、登上之后再远程另一台windows 2012服务器

4、将这两个文件夹里的文件copy到远程服务器对应的目录,完成替换

5、手动重启服务器上的 tomcat

整个步骤下来十几二十分钟就没了,遇到一些其他的情况,走个神啥的,部署一下半小时起步。


V1.0 本地push + 服务器.bat

这群玩意真的配称之为程序员吗。。对不起我实在是忍不了,感觉思维完全不是一个路数,虽然我也很菜,起码我也知道做程序的目的就是为了简化啊。

我和他们绝不同的一点是,他们只会抱怨,却从不想办法解决,或者不去付出行动,而我骂完之后,我会想办法动手去做。

于是我想了想有何解法,一开始想的是直接用个Jenkins得了,然后顺便把版本管理工具换成Git,我也尝试了把项目改换成 Spring Boot + Maven,但是折腾了一天,由于各种奇奇怪怪的问题,暂时失败了。

甚至激进的想过要不要给它重构掉,但是权衡了一波,考虑到种种原因,只好暂时放弃。

于是我申请要一个端口,说弄自动部署。。结果竟然不了了之,行,不给就不给,不给我也能想办法。

我的思路是,既然暂时没办法将编译源码的步骤放到服务器执行,那我就来想办法将他们手动执行的这些给自动化,分析一下这5个操作其实只做了两件事

1、本地代码编译

2、将编译之后的代码放到服务器,并使其运行

我的本地编译有热部署的插件帮我搞定,那么就只要保证到服务器上能直接获得到我本地的编译之后的文件就行了,于是事情变得简单:

将本地编译之后的代码上传到在线的代码仓库,然后在服务器上拉下来就好了。

其实按照规范来讲,这些操作都是要报备的,但是emmmm一言难尽,连测试服务器都不给。

考虑到网络原因,在线代码仓库我选了码云,然后项目根目录底下建了个 git ignore文件:

README.md
README.en.md
config/
lib/
META-INF/
tld/
web.xml
classes/*.properties
classes/*.xml
classes/*.zip
classes/templates
classes/ROOT
classes/com/package/common/util/Const.class
.idea
.settings

把乱七八糟的有些不能删,还有些暂时不敢删的的文件排除出去,使最后提交上去的只有classes/pages/ 这两个文件夹里的东西。

然后服务器上建了个文件夹从码云把编译之后的文件拉下来,又写了个bat,replace-classes-pages.bat:

@echo off
d:
cd /project-name-resources\project
git pull 
xcopy   classes\com\package\name  D:\web\WebRoot\WEB-INF\classes\com\package\name /s /y && xcopy   pages  D:\web\WebRoot\WEB-INF\pages /s  /y
net stop tomcat 
ping -n 20 127.1 >nul
net start tomcat 

这个写得很简陋,而且有改进的空间,写的时候也遇到了各种问题,比如重启tomcat一开始写的是net stop tomcat && net start tomcat

直接双击执行bat,一切执行正常,可是当我在程序里调用此bat的时候,只能关闭掉tomcat服务,死活不能给我启动。。巨坑,暂时没想到是为何。

但是还是很麻烦,我每次得在本地从SVN拉下来他们更新的代码,然后切换窗口使IDEA失去焦点触发热部署,接着到命令行执行git push那一套,再远程到服务器上双击bat。

由于远程服务器还不止一个组在用,所以连接还经常被人给挤掉。。。也不知是谁,我太难了。

呼~ 听起来就让人喘不过气来,不过那天晚上再搞就很太晚了,于是我就回去了,起码1.0版本上了,再也不用我手动压缩文件夹,再拷贝过去解压替换了。


V2.0 WebHook + Node · JS

由于是老项目,还没办法重构,所以整个过程十分的曲折。

一开始我直接在项目里用Java写了个接口,接收到请求就本地调用replace-classes-pages.bat,但是老是各种原因失败。我就拉着我们前端用Node起了个服务,然后用shelljs去调用bat。

java代码就非常的简单,但是好用:

@Controller
@NoAuth
public class CI {
    private final Logger logger = LoggerFactory.getLogger(CI.class);
    @RequestMapping("/ci")
    @ResponseBody
    public String ci(HttpServletRequest request) {
        String password = "this is a secret...";
        String auth = request.getHeader("X-Gitee-Token");
        if (StringUtils.isNotEmpty(auth)) {
            if (password.equals(auth)) {
                String result = HttpUtil.get("http://127.0.0.1:3000");
                logger.info("请求ci,result:{}", result);
                return "okkkkk";
            }
        }
        return "wtf!";
    }
}

js代码如下:

var _path = _interopRequireDefault(require("path"));

var _fsExtra = _interopRequireDefault(require("fs-extra"));

var _express = _interopRequireDefault(require("express"));

var _shelljs = _interopRequireDefault(require("shelljs"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const app = (0, _express.default)();
app.get('/', (req, res) => {
  const common_resources_path = "D:/project-name-resources/nongwei";
  const common_target_path = "D:/web/WebRoot/WEB-INF";

  const class_resources = _path.default.join(common_resources_path, 'classes/com/package/name');

  const page_resources = _path.default.join(common_resources_path, 'pages');

  const class_target = _path.default.join(common_target_path, 'classes/com/package/name');

  const page_target = _path.default.join(common_target_path, 'pages');

  _shelljs.default.cd(`d:/project-name-resources/project`).exec(`git pull`);

  copyDir(class_resources, class_target);
  copyDir(page_resources, page_target);

  _shelljs.default.exec(`net stop tomcat && net start tomcat`);
 
  res.status(200).json({
    content: 'WOW Awesome!!!!!!'
  });
});

function copyDir(from, to) {
  _fsExtra.default.copy(from, to, err => {
    if (err) {
      console.log('An error occured while copying the folder.');
      return console.error(err);
    }

    console.log('Copy completed!');
  });
}

app.listen(3000, () => console.log(`Server running`));

这写得其实也是非常的简陋,然后shelljs也是调用bat会有问题,就是死活启动不了tomcat,气得我不行,同时深感自己基础知识匮乏,心有余而力不足。

后又用PM2给Node项目起了个守护进程,防止挂掉。

于是我的部署流程就变成了,本地热更新代码之后,git push一下,那边就不用我操心了,码云的Webhook地址就填的我用java写的那个接口。

但这个方案还是存在问题,shelljs去执行前半部分的流程都好好的,但重启不了tomcat,出现过好几次,我这边push完代码,我就不管了,然后就有人找我说项目挂掉啦,咳咳。

我只好远程登上去,手动启动一下tomcat服务。

这不稳定的玩意,还得想办法。

然后我搜了一下如何用Go调用bat文件,写了这个:

package main

import (
    "fmt"
    "net/http"
	"os/exec"
	"time"
	"log"
	"os"
)

func main() {
    http.HandleFunc("/", execBat)
    http.ListenAndServe(":3000", nil)
}

func execBat(w http.ResponseWriter, r *http.Request) {
    // shellPath := "C:/Users/cat/Desktop/test.bat"
    shellPath := "D:/xxx/replace-classes-pages.bat"
    command := exec.Command(shellPath) 
    err := command.Start()
	if nil != err {
		fmt.Println(err)
	}
	fmt.Println("Process PID:", command.Process.Pid)
	err = command.Wait() 
	if nil != err {
		fmt.Println(err)
	}
	fmt.Println("ProcessState PID:", command.ProcessState.Pid())
	nowDateTime := time.Now().Format("2006-01-02 15:04:05")
	fmt.Println("deploy sucess!! at ", nowDateTime)

	logPath := "D:/xxx/xx-ci.log"
    logFile,err  :=  os.OpenFile(logPath,os.O_CREATE|os.O_WRONLY|os.O_APPEND, 7777)
    defer logFile.Close()
    if err != nil {
        log.Fatalln("open file error !")
    }
	debugLog := log.New(logFile,"[Debug]",log.Llongfile)
	debugLog.SetFlags(debugLog.Flags() | log.LstdFlags)
	debugLog.Println("deploy success! Save 10 minutes of your life!!")

这个exec执行bat文件比shelljs 好用多了,完美的可以重启tomcat,并且我加了一行简陋的日志,这样我就可以看到每次的部署记录了,其实gitee的WebHooks后台也是有请求记录的,只是我Java写的那个服务没有好好给它返回值,所以得不到确切消息,这个也留待后续优化。

go build -ldflags "-H=windowsgui" example.go生成一个 exe程序,设置成开机自启,再把tomcat服务设置成自启动,这样也不用怕腊鸡windows哪天莫名奇妙的自动重启了。

现在就舒服多了,我只要从SVN拉一下代码,等Jrebel热部署一下,然后git commit -am "update comment" , git push。就搞定了,整个部署过程从20多分钟,变成了1分钟。

不过目前仍然存在很大问题:由于编译放在了本地做,所以只能我自己的这台主机push,其他小伙伴们没法部署。这个之后还是得想办法把编译放在服务器上,然后通过大家push代码或者PR的时候触发WebHooks。

目前这个项目还存在着太多太多问题:

  • 没有包管理也没有分支管理
  • jsp给页面逻辑写得稀碎
  • 代码和数据库丝毫不遵循命名规范
  • 老旧代码太多,焦油坑无人敢踩
  • 还包含了其他想目,剪不断理还乱
  • 权限角色菜单管理的那个模块做的什么玩意,又慢又丑,设计也不合理
  • 代码里各种奇葩写法
  • 没有测试服务器,数据备份机制也没有
  • 仅有我目前整理的很小一部分文档和需求
  • 客户可以随意变更需求,而我们这边还得照着做?
  • ...

可以说是非常难受了,而且最关键的是,做这个项目我毫无成就感,甚至想去转Go了,以后就可以再也不用碰这种无聊的业务项目了。