最近邀请两位同学wengdongyang@github和569835014@github一起协作开发了一个项目(非商业项目,主要是最近正好比较闲,当项目写练练手),目前只开发了一期(实现一个项目最小闭环),功能上还是非常简陋的。
目录
一、初期项目分工
具体分工的时候,因为我是做哪个方向都行,所以让队友们先选方向,最后定的开发范围如下:
- weng同学负责后台管理系统,技术栈上选择了react + ant-design pro;
- 数字君负责提供服务端接口,技术栈上选择了nest.js;
- 我负责前端官网,技术栈上选择了next.js。
二、实际开发
后面到实际开发阶段时,数字君可能是因为过年忙或者其他原因进度明显滞后,weng同学的后台管理系统迟迟无法进入联调阶段,所以我把服务端的接口给写好和weng同学先联调了。由于需求并不复杂,出于学习的目的,这次开发服务端接口时我用了Spring Boot 2。用下来感觉java现在写起来也不是特别麻烦了,而且丢一个jar包到服务器上部署的这个操作非常方便,不像node项目通常还需要在服务端上进行依赖安装,如果有些项目里存在比较容易安装失败的依赖的话,或者服务器网络不佳的话,还是挺费事的。
后面数字君又开始说要继续开发了,不清楚啥时候开发好,如果二期或者最多三期前能开发好,后面会考虑服务端切到nest.js项目。
三、项目部署
项目整体部署还是比较容易的,这里分开讨论。
3.1、后台系统的部署(ant-design pro)
为了方便weng同学部署后台项目,我用宝塔界面创建了一个ftp账号,然后在后台项目里添加了一个npm run deploy命令方便直接在开发者电脑上通过一个命令进行文件部署。具体操作是先在项目根目录下写了一个deploy.js文件,代码示意如下(账密等信息非真实信息):
const path = require('path'); const FtpDeploy = require('ftp-deploy'); const ftpDeploy = new FtpDeploy(); const ftpConfig = { user: 'user', password: 'password', host: '123.456.78.901', port: 25, localRoot: path.join(__dirname, './build'), remoteRoot: '/path', // 这里的路径就是相对于该ftp账号可访问的最上层路径而言的 include: ['*', '**/*', '.*'], exclude: ['node_modules/**', 'node_modules/**/.*', '.git/**', '.idea/**', '.code/**', '.vscode/**'], deleteRemote: true, forcePasv: true, sftp: false }; ftpDeploy .deploy(ftpConfig) .then((res) => console.log(`finished: ${res.map((arr) => arr.join('\n')).join('\n')}`)) .catch((err) => console.log(err)); ftpDeploy.on('uploaded', function (data) { console.log(`[${data.transferredFileCount}/${data.totalFilesCount}]: ${data.filename}`); }); ftpDeploy.on('upload-error', function (data) { console.log(data.err); });
然后打开package.json文件,在scripts字段下新增一行"deploy": "node ./deploy.js"
,这样后续发布时只需要在开发机上执行npm run deploy
即可。
注意,上面这个例子里没有区分测试、产线等多套环境的情况,请根据实际情况添加相应配置。
3.2、前台系统的部署(next.js)
前台系统我用的是next.js框架,这是一个同时集成了服务端渲染和客户端渲染的react框架。相比传统的多个页面文件(不管是多个纯静态的html文件还是服务端直接输出的多个页面)会有更好的用户体验。并且现在最新的next.js已经具备了对typescript的开箱支持。
该项目的部署上,我用了部署node项目的常规工具——pm2。该项目package.json中的scripts字段我是这么写的:
{ "dev": "next -p 8083", "build": "next build && next start -p 8083", "pm2:start": "pm2 start --name lookjs-pc-8083 npm -- run build", "pm2:restart": "pm2 restart lookjs-pc-8083", "pm2:stop": "pm2 stop lookjs-pc-8083", "pm2:delete": "pm2 delete lookjs-pc-8083", "pm2:status": "pm2 status lookjs-pc-8083", "pm2:log": "pm2 log lookjs-pc-8083", "type-check": "tsc" }
这里面dev命令是用于在开发机上本地开发用的,next build用于生成待部署/运行的最终代码,next start则是用来运行next build生成的最终代码的,其他几个pm2相关的命令作用如命名所示,不赘述了。
进行部署操作的流程是,登入服务器终端,然后git clone将代码拉取到指定位置,首次的话执行npm run pm2:start即可。后续有代码更新后,在服务器上执行git pull更新代码,然后再执行npm run pm2:restart即可。
3.3、服务端项目的部署
项目初始文件是根据spring官网[Spring Quickstart Guide](https://spring.io/quickstart)操作生成的。所以部署时可以直接用官方提供的脚本进行打包和运行。
生成jar包的命令如下所示。该命令运行完毕后会在项目根目录下的target目录中生成需要的jar包。
./mvnw clean package
运行jar包(具体jar包名根据实际文件名修改)的命令如下所示。
java -jar target/demo-0.0.1-SNAPSHOT.jar
需要注意的是,直接用上面这样的命令运行jar包的话,一旦关闭终端,程序也就停止了。实际部署时需要处理一下,像下面这样处理(假设日志输出到out.text文件中):
nohup java -jar demo-0.0.1-SNAPSHOT.jar >out.text 2>&1&
四、碰到的问题
后面几次部署的时候发现服务器上明明已经是最新的文件了,但是通过域名在浏览器上访问前端官网首页时,显示的还是旧的文件。我试过把服务器上的对应服务关掉,也试过在浏览器里强刷页面和勾选开发者工具Networt面板下的disable cahche(勾选disable cache前服务端返回的状态码是304,勾选后是200,但都没用),都会访问到旧的页面。还是可以请求到旧页面内容。特意登录服务器执行“curl 本地地址”后发现拉取到的是最新文件无误了。并且在浏览器上通过给url后面增加查询参数的方式访问后,访问到的也的确是最新文件了。经过多种尝试和搜索,发现nginx本身自己也有缓存策略的,出现这个问题的原因是浏览器访问到的一直是nginx自己缓存的一份内容。解决方案是找到nginx配置文件,我用的是宝塔系统来进行服务器常规运维的,在nginx.confi中找到了include proxy.conf这句引用,然后顺藤摸瓜最终可以找到类似下面这样的key:value对,如下所示:
proxy_temp_path /www/server/nginx/proxy_temp_dir; proxy_cache_path /www/server/nginx/proxy_cache_dir levels=1:2 keys_zone=cache_one:20m inactive=1d max_size=5g; client_body_buffer_size 512k; proxy_connect_timeout 60; proxy_read_timeout 60; proxy_send_timeout 60; proxy_buffer_size 32k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_temp_file_write_size 128k; proxy_next_upstream error timeout invalid_header http_500 http_503 http_404; proxy_cache cache_one;
直接把proxy_temp_path和proxy_cache_path两个目录下的文件全部删除,这时候再在浏览器上访问页面,可以发现访问到的内容变成最新的了。
Well done。