随着 Go 语言的崛起以及公司内部对 Go 语言的使用越来越广泛,需要一个统一的开发框架和规范,为我们业务的开发提供保障。 GXE(Go XTHK Engine 是心田花开 Go 语言业务开发平台,面向全心田花开的在线业务支撑平台。可以用来快速开发 API、Web 及后端服务等各种应用。
$go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/work/go"
GORACE=""
GOROOT="/home/work/.jumbo/lib/go"
GOTOOLDIR="/home/work/.jumbo/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
这里需要注意的环境变量主要有GOROOT, GOPATH两个变量:
GOROOT: 代表的是GO的源码地址,因为引用系统的包时需要这个变量在里面查找所需要的系统包。
GOPATH: 代表的是Go开发过程中的工作目录,GOPATH 目录约定有三个子目录:
src 存放源代码(比如:.go .c .h .s等)pkg 编译后生成的文件(比如:.a)bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中,如果有多个gopath,那么使用${GOPATH//://bin:}/bin添加所有的bin目录)当然也可以自己定义这些路径的位置,通过以下设置:
export GOROOT=$HOME/go
export GOPATH=$HOME/gopath
最后还需要设置PATH, 这样我们可以直接运行go及go install后的命令
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
Go提供了方便的安装包,支持Windows、Linux、Mac等系统。这种方式适合快速安装,可根据自己的系统位数下载好相应的安装包,一路next就可以轻松安装了。推荐这种方式 Go提供了每个平台打好包的一键安装,这些包默认会安装到如下目录:/usr/local/go (Windows系统:c:\Go),当然你可以改变他们的安装位置,但是改变之后你必须在你的环境变量中设置如下信息:
export GOROOT=$HOME/go
export GOPATH=$HOME/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
上面这些命令对于Mac和Unix用户来说最好是写入.bashrc或者.zshrc文件,对于windows用户来说当然是写入环境变量。
ps:下载地址:Golang Downloads
Go源码安装:这是一种标准的软件安装方式。对于经常使用Unix类系统的用户,尤其对于开发者来说,从源码安装可以自己定制。 Go 1.5彻底移除C代码,Runtime、Compiler、Linker均由Go编写,实现自举。只需要安装了上一个版本,即可从源码安装。 在Go 1.5前,Go的源代码中,有些部分是用Plan 9 C和AT&T汇编写的,因此假如你要想从源码安装,就必须安装C的编译工具。 在Mac系统中,只要你安装了Xcode,就已经包含了相应的编译工具。 在类Unix系统中,需要安装gcc等工具。例如Ubuntu系统可通过在终端中执行sudo apt-get install gcc libc6-dev来安装编译工具。 在Windows系统中,你需要安装MinGW,然后通过MinGW安装gcc,并设置相应的环境变量。 你可以直接去官网下载源码,找相应的goVERSION.src.tar.gz的文件下载,下载之后解压缩到$HOME目录,执行如下代码:
cd go/src
./all.bash
运行all.bash后出现"ALL TESTS PASSED"字样时才算安装成功。 上面是Unix风格的命令,Windows下的安装方式类似,只不过是运行all.bat,调用的编译器是MinGW的gcc。 如果是Mac或者Unix用户需要设置几个环境变量,如果想重启之后也能生效的话把下面的命令写到.bashrc或者.zshrc里面, export GOPATH=$HOME/gopath export PATH=$PATH:$HOME/go/bin:$GOPATH/bin 如果你是写入文件的,记得执行bash .bashrc或者bash .zshrc使得设置立马生效。 如果是window系统,就需要设置环境变量,在path里面增加相应的go所在的目录,设置gopath变量。 从go 1.8开始,GOPATH环境变量现在有一个默认值,如果它没有被设置。 它在Unix上默认为$HOME/go,在Windows上默认为%USERPROFILE%/go。
ps:下载地址:Golang Downloads
在安装GXE之前要先配置好自己自己的Go开发环境
参考官方文档:https://golang.org/doc/install
下载安装Go版本,GXE目前是基于Go1.9版本开发的。请大家安装对应的版本并设置好自己的GOPATH, GOROOT变量。
在安装好Go语言并设置好相关环境后,我们就可以安装GXE相关的内容了
如果只有一个GOPATH或者要安装到默认GOPATH下,直接运行下面命令:
go mod init
上述命令运行成功后, 会安装GXE相关的三个部分:
GXE开发框架,包含常用的包
创建后的应用目录如下:
地址/项目名称/app ├── build.sh ├── conf │ ├── app.toml │ ├── log.toml │ └── cal │ └── services ├── controllers │ └── index.go ├── main.go ├── middlewares │ ├── log.go │ └── print.go ├── models ├── routers │ └── router.go └── template
目录结构中我们也可以看出来这是一个典型的 MVC 架构的应用,main.go 是入口文件。
GXE 项目创建之后,我们就开始运行项目,首先进入创建的项目,我们使用 go run 来运行该项目:
chdir app root dir: 地址/项目名称/app
env.SetRootPath: 地址/项目名称/app
env.SetConfDirName: conf
env.SetIDC: test
env.SetAppName: app
env.SetRunMode: debug
loadConf: { h 24 info FNST %L: %D %t %S %M true 0 []} <nil>
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /debug/pprof/ --> github.com/DeanThompson/ginpprof.IndexHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/heap --> github.com/DeanThompson/ginpprof.HeapHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/goroutine --> github.com/DeanThompson/ginpprof.GoroutineHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/block --> github.com/DeanThompson/ginpprof.BlockHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/threadcreate --> github.com/DeanThompson/ginpprof.ThreadCreateHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/cmdline --> github.com/DeanThompson/ginpprof.CmdlineHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/profile --> github.com/DeanThompson/ginpprof.ProfileHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/symbol --> github.com/DeanThompson/ginpprof.SymbolHandler.func1 (3 handlers)
[GIN-debug] POST /debug/pprof/symbol --> github.com/DeanThompson/ginpprof.SymbolHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/trace --> github.com/DeanThompson/ginpprof.TraceHandler.func1 (3 handlers)
[GIN-debug] GET /debug/pprof/mutex --> github.com/DeanThompson/ginpprof.MutexHandler.func1 (3 handlers)
[GIN-debug] GET / --> cnb.cool/lingyiweiyao/gxe/app/controllers.Index (5 handlers)
[GIN-debug] Listening and serving HTTP on 0.0.0.0:8089
在浏览器中输入http://localhost:8089
就会看到输出:Hello GXE
这样一个基于GXE的新的应用就生成并可以运行了。
数据库的配置文件都放在项目的 conf/db/cluster/ 路径下,支持读写分离。一个典型的数据配置文件内容如下:
cluster_name = "demo_read"
username = "GXE_r"
password = "GXE_passwd"
default_db = "GXE_demo"
db_driver = "mysql"
conn_timeout = 1000
read_timeout = 1000
write_timeout = 1000
##### IDC: tc #####
[[host]]
idc = "tc"
[[host.addr]]
ip = "10.99.53.43"
port = 5623
[[host.addr]]
ip = "10.99.53.43"
port = 5623
##### IDC: test #####
[[host]]
idc = "test"
[[host.addr]]
ip = "10.99.53.43"
port = 5623
cluster_name: 每个配置都有一个名字,用来与其他的配置进行区分,db 包会根据这个名字来查找对应的配置内容。
username: 数据库用户名
password: 数据库密码
default_db: 默认数据库名称
db_driver: 驱动类型,支持 mysql
conn_timeout: 连接超时,单位ms
read_timeout: 读超时,单位ms
write_timeout: 写超时,单位ms
host.idc: 指机房对应的名称,表示这个机房下的机器配置
host.addr.ip: 数据库 IP
host.addr.port: 数据库端口
此外,支持以 BNS 方式配置数据库资源。
前面我们已经创建了 GXE 项目,而且我们也看到它已经运行起来了,那么是如何运行起来的呢?让我们从入口文件先分析起来吧:
package main
import (
"地址/项目名称/app/routers"
"cnb.cool/lingyiweiyao/gxe"
)
func main() {
gxe.DefaultApp.InitWebEngine()
routers.Init(GXE.DefaultApp)
gxe.DefaultApp.RunWebEngine()
}
首先GXE.DefaultApp.InitWebEngine()函数会对初始化一个web内核,GXE的web内核目前是基于开源项目Gin实现的,但是对其进行了封装,以便于以后升级或替换web内核不会对业务代码产生影响。
初始化的过程中我们还会去解析相关的配置文件,获取当前运行的模式, 当为debug模式时我们还会集成Go的pprof工具进行程序的性能分析。
所以我们会在运行程序的时候看到/debug/pprof/这样的URL地址。
接着routers.Init(GXE.DefaultApp)函数对前一步生成的Web内核进行了路由的注册。
路由注册对应的包在routers/router.go中,具体实现为:
package routers
import (
"cnb.cool/lingyiweiyao/gxe"
"地址/项目名称/app/controllers"
"地址/项目名称/app/middlewares"
)
// Init web路由初始化
func Init(app *GXE.App) {
eng := app.WebEngine()
eng.Use(middlewares.Print, middlewares.Logware)
eng.GET("/", controllers.Index)
}
可以看出eng.GET("/", controllers.Index)这里注册了一个默认的路由,对应的处理函数是cotrollers.Index, 当用户访问这个路径时就会执行controllers.Index里的逻辑。
再回到main.go中,最后一行GXE.DefaultApp.RunWebEngine()会开始WebServer的监听工作,处理接收到的请求。
其内部的实现原理就是先从第一步初始化过程中获取到要监听的端口,然后调用web内核Gin的Run函数,对端口进行监听。
前面我们了解了如何把用户的请求分发到控制器,这小节我们就介绍大家如何来写控制器,首先我们还是从源码分析入手:
package controllers
import (
"cnb.cool/lingyiweiyao/gxe"
"net/http"
)
// Index app index
func Index(ctx *GXE.WebContext) {
name := ctx.DefaultQuery("name", "Guest")
ctx.String(http.StatusOK, "Hello %s", name)
}
上面代码展示了首先我们建一个控制器包controllers/index.go, 里面定义了一个Index函数,这个函数就是我们之前在router里注册的URL/对应的处理函数。控制器的参数都是固定的类型*GXE.WebContext, 这个参数内容很丰富,主要是本次请求所携带的数据。函数首先接受一个name的query参数,如果没有就会有一个默认值Guest, 最后我们输出这结构,ctx.String表示以字符串的形式输出。
内部原理依赖于Gin的实现。待补充...
我们知道 Web 应用中我们用的最多的就是数据库操作,而 model 层一般用来做这些操作,我们的 gps new 例子不存在 Model 的演示。说的简单一点,如果您的应用足够简单,那么 Controller 可以处理一切的逻辑,如果您的逻辑里面存在着可以复用的东西,那么就抽取出来变成一个模块。因此 Model 就是逐步抽象的过程,一般我们会在 Model 里面处理一些数据读取,如下是一个DB操作的封装:
package models
import (
"fmt"
"hash/crc32"
"地址/项目名称/app/models/database"
"log"
"strconv"
)
// UpdateUserRecord update UserRecord by uid&mini_app_id if uid > 0, else by cuid.
func UpdateUserRecord(uid int64, cuid string, where database.Where, update database.Data) (int64, error) {
table := tableName(uid, cuid)
if uid > 0 {
where["uid ="] = uid
} else {
where["cuid ="] = cuid
}
res, _ := database.Update(table, where, update)
affectedRow, err := res.RowsAffected()
if err != nil {
return 0, err
}
return affectedRow, nil
}
所以如果您的应用足够简单,那么就不需要 Model 了;如果你的模块开始多了,需要复用,需要逻辑分离了,那么 Model 是必不可少的。
GXE支持多种输出格式,包括String, JSON, XML, HTML等。
对应的我们可以使用下面的函数来输出:
JSON(code int, obj interface{})
String(code int, format string, values ...interface{})
Value(key interface{}) interface{}
XML(code int, obj interface{})
YAML(code int, obj interface{})
HTML(code int, name string, obj interface{})
GXE 已经代码提交阶段添加单元测试,代码提交后会自动进行所有的单元测试,也可以本地进入项目目录下执行 go test ./... 单元测试,遍历所有的单元测试文件。