基于vue-manage-system和beego搭建web服务
日常工作中,经常会碰到一些需要处理的比较琐碎的事项,每次需要投入的绝对时间不多,但每周或者每月需要投入时间进行处理,且基本都是重复性的工作,作为一名混迹程序员圈子多年的码农,实在难以忍受。痛定思痛,决定运用平生所学,自力更生,自己搭建一个web系统,来解决当前遇到的问题。
一、确定大方向
说干就干,首先,先确定大方向,前端首先想到使用H5实现,接下来对比了常用框架Vue和React,说实在的,有点选择困难,最后,从实用高效的角度,咨询了一下从事H5开发的同事,他们当前使用的主力框架是Vue,那就没什么好纠结了,直接选择Vue,后面有什么问题,咨询请教也相对便捷;考虑到开发效率的问题,咨询H5开发同事,直接选用了vue-manage-system后台管理系统开发框架。至于后台,基于我多年的实践,以及网上研究对比,选择了go语言,也是基于开发效率的考虑,选择了beego作为后台开发框架,选择该框架的主要理由如下,比较知名,国人开发的,中文文档相对比较完善,学习成本相对较低。
二、vue开发
下面将分别就vue开发环境的搭建使用、实践过程中遇到的坑以及最终打包发布等事项进行说明。
1. 搭建vue开发环境
(1)下载安装Nodejs
先从Nodejs下载地址(nodejs.org/en/download… + X, A),前面括号中内容是快捷键相关说明,先按wondows图标加上X键,再按A键,即可启动一个以管理员身份运行的cmd窗口。然后切换到前面所下载msi文件存放目录, 执行下面命令,即可完成安装。
msiexec /package node-v16.13.1-x64.msi
这里有一个情况需要特别说明的是,Nodejs安装的时候(我用的 node-v14.17.6-x64.msi 版本),其内部自带了一个python3.9的版本,且安装完成之后,默认就是用的这个版本,如果系统原本安装了其他版本,且有一些组件已经安装,则需要重新安装这些组件。
(2)安装vue相关组件和工具
整体安装步骤可参考下面链接: cn.vuejs.org/v2/guide/in… www.cnblogs.com/sunny3158/p…
安装@vue/cli包,详情可参见官方链接:cli.vuejs.org/zh/guide/in…
npm install -g @vue/cli
# OR
yarn global add @vue/cli
安装webpack,它是打包js的工具
npm install -g webpack
如果想纯粹研究Vue的使用,可以考虑使用Vue官方推荐的HBuilder X开发工具(www.dcloud.io/hbuilderx.h…),该工具有图形化引导页面,可方便创建新项目进行研究。
(3)VSCode vue开发配置
先安装VSCode,再安装Vetur插件即可满足基本需求。
2. 开发环境使用
(1) vue-manage-system相关
我们从gitlab上下载vue-manage-system项目,然后进行依赖安装,在VSCode中进行代码编辑,在控制台中根据需要运行命令开启服务,并在浏览器中开启页面查看效果。
git clone https://github.com/lin-xin/vue-manage-system.git // 把模板下载到本地
cd vue-manage-system // 进入模板目录
npm install // 安装项目依赖,等待安装完成之后,安装失败可用 cnpm 或 yarn
// 开启服务器,浏览器访问 http://localhost:8080
npm run serve
// 执行构建命令,生成的dist文件夹放在服务器下即可访问
npm run build
(2) 内部界面自定义调整
vue-manage-system项目内部使用的组件主要是Element,其具体使用方式请参考官方网站(element.eleme.cn/#/zh-CN),了解了使用规则之后,就可以根据个体需求进行相应调整了。
3. 跨域相关
考虑到我们当前采用的架构是前后端分离的,跨域是一个无法回避的问题,对于web端,当前我们采取的是修改vue.config.js文件中的配置,具体样例如下:(更详细的说明可参考博文:Vue 本地跨域处理(包含cookie))
module.exports = {
publicPath: './',
assetsDir: 'static',
productionSourceMap: false,
devServer: {
host: 'localhost', //target host
port: 8080,
proxy: {
'/v1':{
target:'http://service-dev.what.com',
changeOrigin:true,
pathRewrite:{
'/v1':'/v1'
}
}
},
disableHostCheck: true
}
}
4. 打包发布
前面“开发环境使用”那里已有说明如何用命令行打包,在此就不重复说明了,打包发布重点需要考虑三方面的问题,一是多环境支持,如何使用一个包同时支持开发、测试、生产或其他更多环境;二是如何管理版本号,方便将功能或问题表现和其对应代码相关联;三是如何部署。下面将分别就这三个问题进行说明;
(1) 多环境支持
多环境支持的主要思路是,为多个环境分别定义不同的H5访问域名和后台访问域名,且H5域名和后台域名一一对应,这样,获取到了当前域名,即可确定应该访问的后台域名,从而自动实行环境切换。具体代码调整见utils\request.js文件,样例如下,其中域名映射逻辑在remote.js中实现,大家可根据实际情况实现自己的域名映射逻辑。
import remote from './remote';
let remoteUrl = remote.getBaseURL();
const service = axios.create({
baseURL: remoteUrl,
withCredentials: true,
timeout: 5000
});
(2) 版本号更新
当前考虑配置一个单独的Version.js文件,打包时,在外部用脚本更新相应字段信息,仅供参考。
(3) 部署
使用nginx进行部署,在nginx配置文件中新增一项配置如下,重启nginx使配置生效。如果是有真实的域名,记得先配置好域名解析。
server {
listen 80;
server_name client.what.com;
location / {
root /your/vue/directory/dist;
index index.html;
}
}
如果没有真实的域名,则需要在用户系统host文件中增加一条配置(假设nginx的ip地址是192.168.10.252):
192.168.10.252 client.what.com
三、beego开发
下面将分别就beego开发环境的搭建使用、实践过程中遇到的坑以及最终打包发布等事项进行说明。
1. 搭建beego开发环境
依据我前面实践,beego在win10下运行会遇到一些问题,所以我最终选择了centos 7.7作为开发用操作系统,接下来的所有说明都是以此为前提条件的。
(1) go语言编译器安装
先去官网golang.google.cn/下载最新的安装包(当前是go1.17.5.linux-amd64.tar.gz),然后将该安装包解压到/usr/local目录下,生成go目录/usr/local/go。可以以root或sudo的形式调用以下命令完成:
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.5.linux-amd64.tar.gz
将/usr/local/go/bin添加到系统PATH中。可以在$HOME/.profile 或者 /etc/profile (为所有用户)添加下面内容:
export PATH=$PATH:/usr/local/go/bin
最后可运行下面命令验证安装是否正常,可以正常展示go语言版本号即正常。
$ go version
官方文档参考:golang.google.cn/doc/install
(2) beego安装
beego安装,请直接参考官方链接beego.vip/quickstart,这里就不重复描述了。
(3)VSCode go开发配置
先安装VSCode,再安装官方的Go插件即可满足基本需求。如果要进行远程开发(譬如在win10机器上远程连接到centos7.7进行开发),可参考如下链接配置: marketplace.visualstudio.com/items?itemN… 另外,如果习惯了Eclipse的快捷键类型,可以考虑安装 Eclipse Keymap 插件,将VSCode的快捷键样式切换成Eclipse类型的。
2. 开发环境使用
(1) bee 的api 命令
bee 的 api 命令是新建一个 API 项目,我们在命令行下执行 bee api <项目名> 就可以创建一个新的项目。
bee api apiproject
create app folder: /gopath/src/apiproject
create conf: /gopath/src/apiproject/conf
create controllers: /gopath/src/apiproject/controllers
create models: /gopath/src/apiproject/models
create tests: /gopath/src/apiproject/tests
create conf app.conf: /gopath/src/apiproject/conf/app.conf
create controllers default.go: /gopath/src/apiproject/controllers/default.go
create tests default.go: /gopath/src/apiproject/tests/default_test.go
create models object.go: /gopath/src/apiproject/models/object.go
create main.go: /gopath/src/apiproject/main.go
但是注意该命令必须在 GOPATH/src 相应目录下生成如下目录结构的项目:
apiproject
├── conf
│ └── app.conf
├── controllers
│ └── object.go
│ └── user.go
├── docs
│ └── doc.go
├── main.go
├── models
│ └── object.go
│ └── user.go
├── routers
│ └── router.go
└── tests
└── default_test.go
(2) bee 的run 命令
我们在开发 Go 项目的时候最大的问题是经常需要自己手动去编译再运行,bee run 命令是监控 beego 的项目,通过 fsnotify监控文件系统。但是注意该命令必须在 $GOPATH/src/apiproject 下执行。 这样我们在开发过程中就可以实时的看到项目修改之后的效果:
bee run
13-11-25 09:53:04 [INFO] Uses 'apiproject' as 'apiproject'
13-11-25 09:53:04 [INFO] Initializing watcher...
13-11-25 09:53:04 [TRAC] Directory(/gopath/src/apiproject/controllers)
13-11-25 09:53:04 [TRAC] Directory(/gopath/src/apiproject/models)
13-11-25 09:53:04 [TRAC] Directory(/gopath/src/apiproject)
13-11-25 09:53:04 [INFO] Start building...
13-11-25 09:53:16 [SUCC] Build was successful
13-11-25 09:53:16 [INFO] Restarting apiproject ...
13-11-25 09:53:16 [INFO] ./apiproject is running...
我们打开浏览器就可以看到效果 http://localhost:8080/:
(3) VSCode上编辑编译运行
在VSCode上面打开/gopath/src/apiproject所在目录,打开文件进行编辑,保存好之后,进入VSCode上的控制台,执行bee run 命令,即可进行编译运行,从而可验证实际效果。
官网页面参考:beego.vip/docs/instal…
3. beego集成mysql数据库
数据库直接使用默认的mysql,为了简化mysql服务器的搭建工作,使用docker(对应官网地址docs.docker.com/get-started…)启动mysql官方提供的image(对应官方地址hub.docker.com/_/mysql),加上合适的配置,即可快速启动一个mysql服务。beego关联mysql,请直接参考官方文档beego.vip/docs/mvc/mo…,这里就不再详细说明了。 如果需要直接查看mysql数据库数据,可使用工具HeidiSQL,官网地址:www.heidisql.com/。
4. 使用orm
beego ORM的使用请参考官方文档beego.vip/docs/mvc/mo…,在此也不再详细说明了。
5. 模块划分及整体逻辑配置
模块划分我的理解是以路由为引子,理解了路由的路径,也就理解了模块划分及其逻辑意义,官方文档可参考beego.vip/docs/mvc/co…。
在代码层面,如果要添加新的路由,先查看routers/router.go文件,参照原有样例代码,为新的controller添加相应路由信息,下面的样例,我为controllers/week.go文件中的WeekController类型增加了"/week"的相对路由信息。
// @APIVersion 1.0.0
// @Title beego Test API
// @Description beego has a very cool tools to autogenerate documents for your API
// @Contact astaxie@gmail.com
// @TermsOfServiceUrl http://beego.me/
// @License Apache 2.0
// @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html
package routers
import (
"manager/controllers"
beego "github.com/beego/beego/v2/server/web"
)
func init() {
ns := beego.NewNamespace("/v1",
beego.NSNamespace("/object",
beego.NSInclude(
&controllers.ObjectController{},
),
),
beego.NSNamespace("/user",
beego.NSInclude(
&controllers.UserController{},
),
),
beego.NSNamespace("/week",
beego.NSInclude(
&controllers.WeekController{},
),
),
)
beego.AddNamespace(ns)
}
controllers/week.go文件代码样例如下:
package controllers
import (
"fmt"
"manager/models"
"gitee.com/cfh008/runutils"
)
// Operations about Users
type WeekController struct {
// beego.Controller
BaseController
}
// @Title Current
// @Description 获取当前填报周期日期
// @Success 200
// @router /current [post]
func (u *WeekController) Current() {
defer func() {
if r := recover(); r != nil {
panicInfo := fmt.Sprintf("%v panic info: %v", runutils.RunFuncName(), r)
panic(panicInfo)
}
}()
// reqBody := u.Data[REQUEST_BODY_KEY].(*RequestBody)
resBody := u.Data[RESPONSE_BODY_KEY].(*ResponseBody)
if u.IsLogin {
resBody.Code = 0
resBody.Msg = "获取成功"
resBody.Data = models.GetCurrentWeek()
u.Data[SYS_JSON_KEY] = resBody
} else {
resBody.Code = ERROR_USER_NOT_LOGIN
resBody.Msg = ERROR_INFO_MAP[resBody.Code]
u.Data[SYS_JSON_KEY] = resBody
}
u.ServeJSON()
}
上述代码样例中有“@router”的注解,在执行"bee run"命令时,会自动将注解解析并在routers/commentsRouter_controllers.go文件中生成相应路由配置。另外,涉及到数据的部分,调用的是"manager/models"模块的方法,即涉及到和数据库打交道的部分,都统一封装在了models模块。
6. 跨域相关
beego配置支持跨域代码样例如下:
package controllers
import (
"manager/models"
"github.com/beego/beego/v2/adapter"
"github.com/beego/beego/v2/adapter/plugins/cors"
beego "github.com/beego/beego/v2/server/web"
)
type BaseController struct {
beego.Controller
User models.User // 登录的用户
IsLogin bool // 标识用户是否登陆
}
func init() {
// 配置CORS 跨域请求
adapter.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowOrigins: []string{"http://service.what.com", "*"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
AllowCredentials: true,
}))
}
func (ctl *BaseController) Prepare() {
ctl.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", ctl.Ctx.Request.Header.Get("Origin"))
......
}
主体功能代码是参考 beego:跨域问题 博客实现的,但具体细节,有根据实际情况进行调整。
7. 打包发布
打包发布重点需要考虑三方面的问题,一是打包及多环境支持,如何使用一个包同时支持开发、测试、生产或其他更多环境;二是如何管理版本号,方便将功能或问题表现和其对应代码相关联;三是如何部署。下面将分别就这三个问题进行说明;
(1) 打包及多环境支持
项目中有文件conf/app.conf,样例如下:
appname = apiproject
runmode = dev
# runmode = prod
autorender = false
copyrequestbody = true
EnableDocs = true
sqlconn =
sessionon = true
[prod]
httpport = 8880
mysqluser = "root"
mysqlpass = "yourpassword"
mysqlurls = "192.168.10.250"
mysqlport = "3306"
mysqldb = "beego"
[dev]
httpport = 8881
mysqluser = "root"
mysqlpass = "yourpassword"
mysqlurls = "192.168.10.250"
mysqlport = "3307"
mysqldb = "beego"
[test]
httpport = 8882
mysqluser = "root"
mysqlpass = "yourpassword"
mysqlurls = "192.168.10.250"
mysqlport = "3308"
mysqldb = "beego"
使用代码读取配置信息样例如下:
package models
import (
"fmt"
"sync"
"gitee.com/cfh008/ioutils"
"github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/core/logs"
beego "github.com/beego/beego/v2/server/web"
_ "github.com/go-sql-driver/mysql"
)
var modelsOnce sync.Once
func Init() {
modelsOnce.Do(func() {
// 默认为开发模式值,便于内部执行单元测试,需要和app.conf文件中的dev模块值保持一致
user := "root"
pwd := "passwordForMysql"
url := "192.168.10.250"
port := "3307"
dbName := "beego"
var doErr error = nil
// 只有存在配置文件时,才从配置文件中读取参数
if ioutils.Exists("conf/app.conf") {
user, doErr = beego.AppConfig.String("mysqluser")
if doErr != nil {
logs.Error("call beego.AppConfig.String failed with %v", doErr)
return
}
pwd, doErr = beego.AppConfig.String("mysqlpass")
if doErr != nil {
logs.Error("call beego.AppConfig.String failed with %v", doErr)
return
}
url, doErr = beego.AppConfig.String("mysqlurls")
if doErr != nil {
logs.Error("call beego.AppConfig.String failed with %v", doErr)
return
}
port, doErr = beego.AppConfig.String("mysqlport")
if doErr != nil {
logs.Error("call beego.AppConfig.String failed with %v", doErr)
return
}
dbName, doErr = beego.AppConfig.String("mysqldb")
if doErr != nil {
logs.Error("call beego.AppConfig.String failed with %v", doErr)
return
}
}
dataSourceFormat := "%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&loc=Local"
// 样例: "root:passwordForMysql@tcp(192.168.10.250:3307)/orm_test?charset=utf8mb4&loc=Local"
dataSource := fmt.Sprintf(dataSourceFormat, user, pwd, url, port, dbName)
// logs.Info("dataSource(%v)", dataSource)
doErr = orm.RegisterDriver("mysql", orm.DRMySQL)
if doErr != nil {
logs.Error("call orm.RegisterDriver failed with %v", doErr)
return
}
// 参数1 数据库的别名,用来在 ORM 中切换数据库使用
// 参数2 driverName
// 参数3 对应的链接字符串
// 参数4(可选) 设置最大空闲连接
// 参数5(可选) 设置最大数据库连接 (go >= 1.2)
// 使用 go-sql-driver 驱动时,请注意参数设置
// 从某一版本开始,驱动默认使用 UTC 时间,而非本地时间,所以请指定时区参数或者全部以 UTC 时间存取
// 例如:root:root@/orm_test?charset=utf8&loc=Asia%2FShanghai
maxIdle := 30
maxConn := 30
doErr = orm.RegisterDataBase("default", "mysql", dataSource, orm.MaxIdleConnections(maxIdle), orm.MaxOpenConnections(maxConn))
if doErr != nil {
logs.Error("call orm.RegisterDataBase failed with %v", doErr)
return
}
doErr = orm.RunSyncdb("default", false, true)
if doErr != nil {
logs.Error("call orm.RunSyncdb failed with %v", doErr)
return
}
})
}
上面样例中,一共有三个环境的配置,分别是prod、dev 和test,要打哪个环境的包,则先将最上面的runmode配置成相应的值(当前配置的是dev),然后在控制台调用如下命令:
bee pack
如果一切正常,则会在项目下面生成apiproject.tar.gz压缩包,里面包含了上面的配置文件,以及编译生成的可执行文件apiproject,实际运行时,可执行文件会从配置文件中读取相应配置信息,如果找不到该配置文件,runmode值默认是prod,所以,理论上该包是支持多环境的,只是解压完毕后,需要将配置文件中runmode值调整成相应环境的值,再运行可执行文件启动服务。 官方发布部署说明可参见:beego.vip/docs/deploy…
(2) 版本号更新
当前找到了一个觉得可以采用的方案,有待后续实践,具体链接如下: golang程序添加版本号 实际验证发现,使用beego命令,不大好操作,当前考虑使用脚本在打包前更新文件版本信息,然后打包,打包完毕之后,再恢复文件或者提交到版本库,仅供参考。
(3) 部署
先将前面的apiproject.tar.gz解压到系统某个目录,如/opt/apiproject,然后切换到该目录下调用下面命令启动服务:
nohup ./apiproject &
假设上述机器的IP是"192.168.10.251",我们部署配置的端口是8880。 然后使用nginx进行重定向,在nginx配置文件中新增一项配置如下,重启nginx使配置生效。如果是有真实的域名,记得先配置好域名解析。
server {
listen 80;
server_name service.what.com;
location / {
proxy_pass http://192.168.10.251:8880;
}
}
如果没有真实的域名,则需要在用户系统host文件中增加一条配置(假设nginx的ip地址是192.168.10.252):
192.168.10.252 service.what.com
特别说明: 实际部署时,web端和后台分别是部署在同一域名下的不同二级域名下的,所以不存在跨域的问题,在内部开发或测试环境,没有采用在同一域名不同二级域名下的部署形式,则需要进行跨域配置,确保功能可正常运行。
四、后续规划
当前整体状态是,可以进行开发部署,但部署操作,还存在不少需要人工介入的重复操作,后续需要考虑的方向主要有两个,一是打包部署,统一集成到jenkins这类CI/CD工具上;二是最终部署形式,不论是web端,还是后台,最好采取docker image的形式发布,使得发布标准化。