记录首次从零到部署全栈开发经历

  • 2020-05-19
  • 0
  • 0

前言

自己想全栈开发个产品这个想法有很久了,去年帮一朋友做了一个全栈的网站,但因为特别简单,只有一个接口,就是查询,数据都是他提供的,我只是写了个查询接口和两个前端页面,不能算是真全栈,那时候还是用的python写的后端,还特意花了点时间简单上手了python:grimacing:,讲道理好久没碰python,现在又忘记了:joy:

今年年初在规划今年计划时候,就想着自己动手全栈撸一套博客,把现在这个博客迁移到自己的程序上,现在博客用的是wordpress,觉得有点臃肿,而且自己的程序,自己想要什么功能,就做什么功能,很方便,也能扩展自己的技术面。

然而计划赶不上变化,母胎单身24年的我今年脱单啦,感谢上帝让我遇见我的女孩:kissing_heart:,然后女友说让我做个记录我们生活点滴的网站,于是乎自己动手撸博客变成自己动手从零到部署撸一个产品

产品简介

产品来源于生活,用来帮助情侣/恋人记录生活点滴,我们阻止不了时光流逝,但我们能把它记录下来,想想10年后、20年后再一起翻看这些回忆,这是多么珍贵的一堆数据,甚至老了,还可以把这些讲给孩子们听(前提是数据一定要保存好,我把数据放在阿里云,应该不会又问题:joy:)

产品我把它命名为we,下面是我刚开始构思这个产品想的一些需求,这只是当时自己头脑风暴想的一些,给自己一个大概方向,产品不一定是按照这个需求走,只是把所想到有可能会做的都列出来了:joy:

开发

虽然上面列了那么多需求,但是第一次全栈一起搞,不可能一口吃个大胖子:joy:

v0.1版本功能

  • [x] 首页(首页暂时还没什么好计划,临时放了个背景图,加相识天数计时)
  • [x] 时光机(发布时光、查看时光、评论时光)
  • [x] 我的(消息列表)
  • [x] 登录
  • [ ] 注册(接口已经写了,但是前端还没有写注册的功能)

下版待完成功能

  • [ ] 发布时光支持图片(图片使用阿里云oss保存)
  • [ ] 纪念日功能
  • [ ] 姨妈助手

技术栈

一开始前端考虑用uni-app,后端服务用serverless,但是想了下我这个还是不上架小程序,小程序受平台限制,万一运营不当,封了,虽然写h5也可以用uni-app,但我只搞h5的话,还专门去学习这个,就学习成本高了,前端就决定用vue撸吧,以后真有打算上小程序,再去看看这写跨平台应用框架吧,不是说Taro2.0将要支持vue了吗,到时候再看看;后端打算用阿里平台的serverless(函数计算)产品写,结果捣鼓了半天,本地开发环境没捣鼓出来(终究还是太菜了),虽然本地开发没搞好,也可以再阿里云的网页端写,但不方便麻烦,就放弃了,既然是做的前端,那还是用node搞吧,选择用阿里的egg.js框架,原因是阿里云的函数计算可以部署egg.js应用,到时候把服务布到函数计算上,省去服务器运维的一些工作还是不错的,最终技术栈如下:

前端

vue全家桶 + vant UI库(项目vue-cli4.2构建)

开源地址:https://github.com/iamobj/we

后端

node.js + egg.js + mongodb

后端代码暂时不考虑开源,请见谅,说下后端几个值得参考的点:

  • 资源文件存放方案(如图片/音频等资源)
    所有文件资源我都放在阿里云oss上,数据库里存的只是文件的路径(方便资源迁移或者oss域名变更),如/avatar/default.png,拼接域名我放在前端使用vue过滤器处理,没有在后端拼接好再给前端,因为后端拼接有点麻烦,文件字段没有规律,无法实现自动化,只能在返回每个表的数据的时候,手动去针对文件字段拼接,太麻烦了,放到前端,对使用到的资源用过滤器拼接就好了(拼好后的字符串https://*****.com/avatar/default.png),可能我太菜,或许还有更好的方案
  • 用户登录鉴权
    使用的egg-jwt插件
    怎么使用可以自行百度/谷歌,我是看的这篇文章
    如果有能力,还是不要用这种,因为这种token是没有存放到数据库的,不方便维护,比如想要实现用户只能在一个地点登录,这个就做不到,因为我猜它是根据账号、密码、过期时间等变量通过算法算出给你一串字符,每次登录都会重新生成一个新的,上一次登录的,还没到过期时间就不会失效,如果你想要实现当前登录要挤掉其它地点得登录状态要利用Redis实现。这块我不是必需,放在以后有时间再来调整,就先用这种做登录。(主要是学不动:joy:先用这种打通我前后端,后期再慢慢来调整和优化​)
  • mongodb
    使用的是egg-mongoose插件
    怎么使用可以看官方文档或者自行百度/谷歌
    这里就贴下mongoose中文文档
  • 参数校验
    使用egg-validate插件对请求参数的校验
    校验规则编写看这里parameter(没记错的话,Vue的一些UI库的表单验证规则也是用的这个)
  • vscode断点调试egg.js
    官方文档有写了怎么使用vscode调试egg.js
    官方也提供了一个扩展自动生产调试配置,很方便,按照文档那个操作来就行了,需要注意的是,要先停止项目,然后再按照步骤,它会自动使用调试模式启动项目
  • 接口路由地址建议加个/api前缀,部署的时候方便设置反向代理
  • 接口管理我计划用YApi,搭配使用apifox,接口调试、mock、自动化测试会非常方便,这块我是计划这么做,因为时间关系加上目前接口还不多,接口文档这块还没去做,后面会做这块,方便管理接口,后面做了再单独写篇文章
  • 其它建议
    1、数据库的一些常量可以用一个定义常量的文件保存起来,前端项目也用这个文件,维护一个文件,比如性别数字1表示男生,数字2表示女生,不维护一个常量文件,以后自己看到这些数字都不知道代表的什么意思
    2、数据库字段用的是驼峰,因为前端js写变量,我也是用的驼峰,统一方便取值、传值
总结

整个项目利用的下班时间和周末来完成,断断续续历经了刚好一个月完成了第一版,以为会很难,但真正行动起来,其实也就那么回事,遇到问题,去百度/谷歌/github,总能找到解决办法,不要让思想上以为的困难束缚了行动上的积极,难道解决一个个问题,最终看到自己完成了,这种成就感,它不香吗,就跟玩游戏打怪升级一样太有意思了

部署

准备工作

一台centos7系统的服务器并安装好宝塔面板(一款管理服务器面板程序,这个就不讲教程了,安装很简单,按照官方文档来就可以)如果你能命令行玩转linux就不用装了:grinning:(大佬带带我)​

安装好宝塔面板进入面板,安装所需的环境后,再到软件商店找到Docker管理器并安装

docker部署mongodb

飞机票

docker部署egg.js

  1. 先修改项目根目录下的package.json,把start这行命令里的--daemon去掉,项目在docker容器里要前台运行,docker容器相对宿主机是后台运行

  2. 在项目根目录下新建一个名为Dockerfile的文件(无后缀),将以下内容复制到文件里,并将/usr/src/node-app/egg-we全部替换为你像设置的路径(此路径为docker容器里的路径,可自行设置)

    # 设置基础镜像,如果本地没有该镜像,会从Docker.io服务器pull镜像 我本地开发的node是12.13.0,这里也用这个版本保持一致
    FROM node:12.13.0-alpine
    # 设置时区
    RUN apk --update add tzdata \
       && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
       && echo "Asia/Shanghai" > /etc/timezone \
       && apk del tzdata
    
    # 创建app目录
    RUN mkdir -p /usr/src/node-app/egg-we
    
    # 设置工作目录
    WORKDIR /usr/src/node-app/egg-we
    
    # 拷贝package.json文件到工作目录
    # !!重要:package.json需要单独添加。
    # Docker在构建镜像的时候,是一层一层构建的,仅当这一层有变化时,重新构建对应的层。
    # 如果package.json和源代码一起添加到镜像,则每次修改源码都需要重新安装npm模块,这样木有必要。
    # 所以,正确的顺序是: 添加package.json;安装npm模块;添加源代码。
    COPY package.json /usr/src/node-app/egg-we/package.json
    
    # 安装npm依赖(使用淘宝的镜像源)
    # 如果使用的境外服务器,无需使用淘宝的镜像源,即改为`RUN npm i`。
    RUN npm i --registry=https://registry.npm.taobao.org
    
    # 拷贝所有源代码到工作目录
    COPY . /usr/src/node-app/egg-we
    
    # 暴露容器端口即项目start的端口
    EXPOSE 9001
    
    # 启动node应用
    CMD npm run start
    
  3. 打包项目上传到服务器并解压,node_modules文件夹和package-lock.json文件可以不用打包,项目依赖在服务器上重新装

  4. 服务器进入到项目的根目录,执行以下命令,安装docker镜像
    docker build -t node/egg-we .(注意后面还有点)
    -t参数是对该镜像设置标识/别名的意思,自己随便定义,这里设置镜像名称为node/egg-we。静静等待它执行完所有步骤,会有success的提示。
    安装成功后可以执行docker images可以看到刚刚创建好的镜像

  5. 执行以下命令,使用刚创建好的镜像来启动一个容器
    docker run -d -v /www/wwwlogs/egg-we:/root/logs/egg-we --net=host --name egg-we node/egg-we
    -d参数表示后台运行docker容器
    -v参数表示把容器本地数据文件挂载到宿主主机的路径(目录映射),/root/logs/${projectName}/ 是egg默认的日志目录,其中projectName变量就是项目packjson.json文件种的name,把项目日志映射到宿主主机,方便查看日志
    --net=host参数表示使用host网络模式与宿主主机共享网络来连接服务器本地部署的mongodb数据库,如果你数据库连接不是用的服务器本地连接,而是可以通过公网连接访问,那可以不需要这个参数
    --name参数是设置容器标识/别名,自己随便定义,这里设置容器别名为egg-we
    node/egg-we指向的是镜像,即docker使用哪个镜像来创建容器,可以用之前创建的镜像别名也可以用写镜像的id

  6. 执行以下命令检查容器是否启动成功
    docker ps
    如果刚才启动成功,就会显示出来,没显示出来就说明启动失败,可以执行docker logs -f 容器ID或者容器别名命令,查看容器内的执行日志,找到启动失败的原因。这里我想查看容器日志就执行docker logs -f egg-we
    找到失败原因,如果是项目问题,解决后上传项目代码到服务器,之后的操作就和更新项目一样

  7. 更新egg.js项目
    ① 先找到需要更新的项目容器ID或者别名,执行docker ps查看容器信息
    ② 然后执行docker stop 容器ID或者容器别名停止容器
    ③ 执行docker rm 容器ID或者容器别名删除容器
    ④ 执行docker images查看这个容器的镜像信息
    ⑤ 执行docker rmi 镜像ID或者镜像别名
    ⑥ 把项目代码打包更新到服务器目录下
    ⑦ 按照上面的步骤重新构建镜像和启动容器

部署前端项目

  1. 将前端项目打包好的文件上传到服务器,我是在/www/wwwroot/目录下新建一个目录放并解压,宝塔默认网站文件在这个目录下
  2. 进入服务器的宝塔面版,选择网站,添加站点,按照格式填写网站需要绑定的域名(域名需要解析到当前服务器),然后选择刚刚创建的项目目录,ftp、数据库这些都不创建(ftp看自己需要,如果需要别人来更新上传代码,就创建个ftp,让他人通过ftp连接来更新服务器文件),php版本选纯静态,然后点提交按钮,在列表里就会看到刚刚创建好的站点。
  3. 此时已经可以通过域名访问网站了,但是请求接口还会报错,这时我们来设置反向代理,把所有/api路径指向后端服务。点击刚刚创建站点的设置,在弹框里找到反向代理,添加反向代理,代理名称自己随意起,目标URL填本地egg-we服务地址,如下图

    如果要缓存就开启缓存,我这里反向代理的接口就不做缓存,要保证接口数据是实时的,然后点提交,此时列表就多了一条记录,还没完,然后再点击配置文件,做如下图修改保存

开启全站HTTPS

开启HTTPS很简单,宝塔面板提供了站点SSL设置,很简单,再网站列表找到网站点设置,在弹出框选择SSL,宝塔提供了两个免费HTTPS的渠道,一个是自己的宝塔SSL(有效期一年,SSL证书过期后,再自己来续下就可以了,很方便简单),还有是Let’s Encrypt的(每次申请只有3个月,但宝塔会自动续签),还可以自己去阿里云申请免费的SSL证书(这里就不详细讲怎么免费申请阿里云的SSL证书,自行百度吧),部署好SSL证书,再开启强制HTTPS就行了,这样全站就开启HTTPS了,非常的方便,到这里,整个网站就算部署完成了。

项目体验地址

项目是移动端项目,手机直接扫码,电脑打开网址后f12切换到移动端浏览

测试环境:http://nas.assetss.cn:10004(演示地址用的测试环境,线上生产环境就不放了,生产服务器是小机子,经不住玩,测试环境的机子是我家自己分配的,如果地址进不去,可能我电脑关机或者断网了:joy:测试环境就没部署https证书了,生产环境有部署)
测试账号:

  • 男号
    18500000001—-we123456
  • 女号
    18500000002—-we123456

总结

遇到的问题
  1. 接口被缓存
    开始不知道开启了缓存,然后去使用网站发布一条新内容,发现接口查询不出新发布的,然后F12查看接口请求看到接口size列都是显示的disk cache接口没有发请求到服务器而是直接拿的本地的缓存:disappointed_relieved:,把反向代理关闭即可。如果是反向代理文件,比如一些静态文件,可以设置点缓存时间减轻服务器压力
  2. docker部署项目一定要设置好时区,特别是数据库,否则时间相关操作可能会不准确
  3. mongodb存储时间差了8个小时,mongodb自带的Date是UTC的时间,中国是东八区,所以差了8个小时,但是这个问题不大,这个时间传给前端,前端再new Date(后台返回的时间)就会转换为用户本地时区的正确时间
  4. 关于mongodb自增长id,mongodb没有像SQL一样又自动增长的功能,但菜鸟教程有篇文章教怎么实现mongodb自增长id,刚开始我也是这么做的,但后面又撤掉了,为什么要用自增长id,我不用自增长id也可以,没必要为了炫技去使用这个,消耗性能不说,关键是这么做好像没带来什么实质性的好处
  5. mongodb数据字段名建议用驼峰命名,前端js写参数都是用的驼峰,这样一致前端传过来不用再做处理
  6. egg.js有提供方法快速在一个路径生成CRUD路由结构,如果你想对某个接口做单独处理,需要在resources方法前单独注册,比如要给某个接口加token验证

最后

这是我第一个正真的全栈项目了,虽然功能不多,但麻雀虽小,五脏俱全,前后端基本的一些技术都有使用到了,收货还是蛮多的,不知道其它语言写后端服务怎么样,但用node写,感觉和写js差不多:joy:,不知道大佬们用node写服务是啥感觉,大佬们应该都是ts写,ts玩还不是用的特别溜,且网上说egg使用ts好像有问题,也怕要用的一些npm包没有ts类型包,还要自己写d.ts:joy:

计划做但还没做的事

  1. 因为生产环境的机子很小,现在用起来都有点卡,接口反应不是很快,计划有时间把项目部署到阿里云的函数计算(serverless),函数计算文档有示例怎么部署egg.js项目,到时候迁移过去,再写篇文章
  2. mongodb数据库自动备份,生产环境的数据一定是要定时备份的,现在还没做自动备份,有时间再研究下怎么写个脚本定时备份mongodb数据
  3. 自动化部署,有时间来研究研究,预计效果达到代码仓库的master分支更新代码,就自动部署更新到服务器上,这样就非常方便,释放重复劳动力的时间,节省下来的时间,可以用来陪陪女友呀是不,哈哈哈:joy:

评论

还没有任何评论,你来说两句吧