01 . Go框架之Beego简介部署及程序流程分析

时间:2024-01-27 10:05:42

Beego简介

beego是一个使用Go语言来开发WEB引用的GoWeb框架,该框架起始于2012年,由一位中国的程序员编写并进行公开,其目的就是为大家提供一个高效率的web应用开发框架。该框架采用模块封装,使用简单,容易学习。方便技术开发者快速学习并进行实际开发。对程序员来说,beego掌握起来非常简单,只需要关注业务逻辑实现即可,框架自动为项目需求提供不同的模块功能。

在对beego框架有了一个简单基本的了解之后,我们给大家介绍一下beego的一些特性。

Beego框架的主要特性

1)简单化:RESTful支持,MVC模型;可以使用bee工具来提高开发效率,比如监控代码修改进行热编译,自动化测试代码,以及自动化打包部署等丰富的开发调试功能。

2)智能化:beego框架封装了路由模块,支持智能路由,智能监控,并可以监控内存消耗,CPU使用以及goroutine的运行状况,方便开发者对线上应用进行监控分析。

3)模块化:beego根据功能对代码进行节耦封装,形成了Session,Cache,Log,配置解析,性能监控,上下文操作,ORM等独立的模块,方便开发者进行使用。

4)高性能:beego采用Go原生的http请求,goroutine的并发效率应付大流量的Web应用和API应用。

beego的几个特性

一方面在面试过程中,如果有面试官问起大家关于goweb开发的相关知识的时候,有可能问beego框架有优势或者有什么特点,这个时候需要大家能够对beego框架这些特点做一个介绍;

另一方面,咱们在后面实际的学习和编码过程中,大家感受一下这些特性在实际项目中为我们带来的便利和高效。

beego安装

我们使用go get命令来进行beego的安装。

注意

在安装的时候,有一点大家要注意,beego框架要求Go 语言版本1.1+以上,这个我们大家的版本应该至少也是1.9的吧,应该都没有。或者大家现在在命令行中,执行go version查看一下自己的go语言版本,如下所示:

添加环境变量
cat .bash_profile 
export GOROOT=/usr/local/go
export GOARCH=amd64
export GOOS=darwin
export GOPATH=/Users/youmen/Go_wunai
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOPATH/bin
安装beego
export GO111MODULE=on
export GOPROXY=https://goproxy.cn

ulimit -n 5000
go get -u github.com/astaxie/beego
go get -u github.com/beego/bee
bee new beego
cd ${GOPATH}/src
// cd进入项目目录
// 输入go mod init
bee run

// 如果想编译成linux可以直接执行的二进制文件
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build hello.go 

命令行工具Bee

现在,尝试来进行对代码进行修改,就简单的添加一行打印日志:beego.Info("代码修改")。这个时候,如果我们要想让代码生效,必须把原来正在执行的程序停止,然后重新执行编码和运行命令,我们来实际尝试一下。

经过我们的尝试,我们可以发现,确实第二次的重新启动的程序打印出了两条后台日志,这说明我们修改的代码生效了。

bee简介

bee是一个开发工具,是协助Beego框架开发项目时进行创建项目,运行项目,热部署等相关的项目管理的工具。beego是源码,负责开发,bee是工具,负责构建和管理项目。介绍beego的特性的时候说过其中一点beego支持代码热部署吧。所谓热部署就是,当修改代码的时候,可以不用停止服务重新启动,内置的beego就能够实时感知源代码程序编码,并进行实时生效,这就是热部署,这样就大大方便了开发人员和维护人员要频繁的进行服务停止,服务重启的过程。

Bee安装

如同beego一样,如果要使用bee,首先要进行安装,同样的道理,先使用go get命令安装bee工具。bee安装命令:

go get github.com/beego/bee
Bee功能命令

安装结束以后,进入到GOPATH对应的目录中,然后进入到bin目录下,可以看到有bee这个可执行文件,这就是安装的bee工具的可执行文件,在该目录中,我们在终端中执行以下bee命令,然后回车,会发现列出很多关于bee的用法,如下图:

new命令
// bee new ProjectName

该命令表示新建一个全新的web项目,有一点需要注意:该命令必须在src目录下执行,才能生效,自动生成web项目的目录结构。如果在其他目录下面执行bee new命令,也同样会是在src目录下面生成对应新项目的目录结构,这是bee工具在构建项目的时候默认寻找创建的目录。

api命令
// bee api ProjectNames

该命令表示用来创建开发API应用。很多用户写前端的后台接口也是通过go来写,因此bee专门提供了一个写api接口应用的命令。通过目录结构可以看到,和Web项目相比,API项目少了static和views目录,多了一个test目录,test是用来进行写测试用例代码的。

run命令
// bee run	

命令用来运行项目,并且能够通过监控文件系统,实时进行代码的热部署更新。也就是,我们代码逻辑进行了修改,不用停止应用,在前端页面就能看到改变。

pack命令
// bee pack

pack命令用来发布应用的时候的打包操作,该命令会把项目大包成zip包,然后我们就可以在部署的时候直接把大包后的项目上传到服务器,然后解压进行部署。

version命令
// bee version

Version命令来查看当前bee,beego,go的版本。

使用Bee工具

常见的几个bee工具命令就是上面的几个,立即来练习使用一下。打开命令行终端,进入到GOPATH所对应的目录,然后进入到src目录中。

创建项目

使用bee run命令来新建一个案例项目,比如我们新建一个BeegoDemo2的项目,我们执行命令:bee new BeegoDemo2,命令执行效果如下:

bee new test
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.11.0
2020/07/25 16:35:17 WARN     ▶ 0001 You current workdir is not inside $GOPATH/src.
2020/07/25 16:35:17 INFO     ▶ 0002 Creating application...
	create	 /Users/youmen/Go_wunai/src/test/
	create	 /Users/youmen/Go_wunai/src/test/conf/
	create	 /Users/youmen/Go_wunai/src/test/controllers/
	create	 /Users/youmen/Go_wunai/src/test/models/
	create	 /Users/youmen/Go_wunai/src/test/routers/
	create	 /Users/youmen/Go_wunai/src/test/tests/
	create	 /Users/youmen/Go_wunai/src/test/static/
	create	 /Users/youmen/Go_wunai/src/test/static/js/
	create	 /Users/youmen/Go_wunai/src/test/static/css/
	create	 /Users/youmen/Go_wunai/src/test/static/img/
	create	 /Users/youmen/Go_wunai/src/test/views/
	create	 /Users/youmen/Go_wunai/src/test/conf/app.conf
	create	 /Users/youmen/Go_wunai/src/test/controllers/default.go
	create	 /Users/youmen/Go_wunai/src/test/views/index.tpl
	create	 /Users/youmen/Go_wunai/src/test/routers/router.go
	create	 /Users/youmen/Go_wunai/src/test/tests/default_test.go
	create	 /Users/youmen/Go_wunai/src/test/main.go
2020/07/25 16:35:17 SUCCESS  ▶ 0003 New application successfully created!
项目结构
cd ${GOPATH}/src
tree test 
go mod init
test
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── main.go
├── models
├── routers
│   └── router.go
├── static
│   ├── css
│   ├── img
│   └── js
│       └── reload.min.js
├── tests
│   └── default_test.go
└── views
    └── index.tpl
运行项目
bee run
______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v1.11.0
2020/07/25 16:37:40 INFO     ▶ 0001 Using \'test\' as \'appname\'
2020/07/25 16:37:40 INFO     ▶ 0002 Initializing watcher...
go: finding module for package github.com/astaxie/beego
go: found github.com/astaxie/beego in github.com/astaxie/beego v1.12.2
test/controllers
test/routers
2020/07/25 16:37:43 SUCCESS  ▶ 0003 Built Successfully!
2020/07/25 16:37:43 INFO     ▶ 0004 Restarting \'test\'...
2020/07/25 16:37:43 SUCCESS  ▶ 0005 \'./test\' is running...
2020/07/25 16:37:44.611 [I] [asm_amd64.s:1373]  http server Running on http://:8080

手动创建项目

创建main.go
package main

import (
    "fmt"
    // 导入beego包
    "github.com/astaxie/beego"
    "github.com/astaxie/beego/context"
)

func main() {
    // 路由 url => controller
    // 处理器函数 处理器 函数
    // 函数, 结构体
    // beego/context/Context

    // 绑定函数
    // 以GET方式请求/通过绑定函数处理
    // 固定路由
    beego.Get("/",func(ctx *context.Context){
        // 用户数据的获取
        name := ctx.Input.Query("name")

        // 给用户响应数据
        ctx.Output.Context.WriteString(fmt.Sprintf("您输入的名字是: %s",name))
    })

    // Post
    beego.Post("/", func(ctx *context.Context) {
        name := ctx.Input.Query("name")
        ctx.Output.Context.WriteString(fmt.Sprintf("(Post)您输入的名字是: %s",name))
    })

    // 任意请求
    beego.Any("/any", func(ctx *context.Context) {
        name := ctx.Input.Query("name")
        ctx.Output.Context.WriteString(fmt.Sprintf("(%s)您输入的名字是: %s",ctx.Input.Method(),name))
    })
    
    // 启动Beego程序
    beego.Run("127.0.0.1")
}


youmen@youmendeMacBook-Pro elk % curl http://127.0.0.1:8080/
您输入的名字是: % 

// 可以使用Postman

执行流程分析

Go语言执行的时候是执行main包下面的init函数,main函数依次执行。因此,先找到main.go文件。

app.conf
appname = cmdb
httpaddr = "127.0.0.1"
httpport = 8080
runmode = dev

main.go
package main

import (
	_ "beego_demo1/routers"
	"github.com/astaxie/beego"
)

func main() {
	beego.Info("Beego_Demo1")
	beego.Run("127.0.0.1:8080")
}

上就是main.go文件内容,那么代码如何执行呢?首先,import导入了两个包,一个是routers,一个是beego。而在routers包前面,可以看到有一个“_”,这表明是引入routers包,并执行init方法。这里涉及一个知识点,就是Go语言的执行过程,这里给大家一张程序执行流程图

根据上图,可以得知程序首先到routers包下执行init方法。到router.go里面看一下具体的代码:

可以看到在router.go文件中有一个init方法,看到beego.Router()这句代码。router表示的是路由的意思,这个函数的功能是映射 URL 到 controller,第一个参数是 URL (用户请求的地址),这里注册的是 /,也就是访问的不带任何参数的 URL,第二个参数是对应的 Controller,即将把请求分发到那个控制器来执行相应的逻辑,现在去这里设置的MainController中去看一下有什么方法:

package controllers

import (
	"github.com/astaxie/beego"
)

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {
	c.Data["Website"] = "youmen.me"
	c.Data["Email"] = "163@.com"
	c.TplName = "index.tpl"
}

MainController结构体及函数声明在default.go文件中。而这里就看到一个Get方法,方法中有三行代码。

上文在浏览器中访问的是:http://localhost:8080,这是一个get请求,请求到了后台以后,什么请求参数都没有,因此,就会被“/”拦截,执行到MainController中的代码,因为是get请求,所以这里自动找到Get函数并进行执行。

在get函数里面,有三句代码,前两句c.Data[]= ""表示设置返回的数据字段及内容,最后一句c.TplName表示设置处理该请求指向某个模板文件,这里指向了index.tpl,那么index.tpl文件在哪里呢?可以查看项目目录结构,在views下面,views下面存放一些模板文件。

模板文件

简单解释一下,通常的页面都是使用静态的html+css+js等这些静态代码来进行页面的布局,页面效果控制等,而把页面的数据使用变量表示,这样,在进行页面展示的时候,就能够自动的填充页面里面的变量的值;这些静态的代码文件统称为模板文件。每个模板文件就是像一个模板一样,样式效果都固定,只是根据数据不一样进行渲染和展示。

服务器处理客户端过程
// 客户端请求: http协议 => url,method,params
// 服务器处理客户端过程: url => handler => params => db => templates => html

// 定义处理器
// params

// 处理器调用数据库对数据进行处理(增/删/改/查)
// 处理器调用模板基础去渲染页面
// 定义url处理器管理

// 客户端渲染: http渲染页面
Beego.Run()逻辑

init方法分析完毕后,程序会继续往下执行,就到了main函数,在main函数中执行:beego.Run()代码。分析一下Run代码的逻辑,在Run方法内部,主要做了几件事:

// 1)解析配置文件,也就是我们的app.conf文件,比如端口,应用名称等信息。
// 2)检查是否开启session,如果开启session,就会初始化一个session对象。
// 3)是否编译模板,beego框架会在项目启动的时候根据配置把views目录下的所有模板进行预编译,
// 然后存放在map中,这样可以有效的提高模板运行的效率,不需要进行多次编译。
// 4)监听服务端口。根据app.conf文件中的端口配置,启动监听。

官网Beego请求流程

通过文字来描述如下:

  1. 在监听的端口接收数据,默认监听在 8080 端口。
  2. 用户请求到达 8080 端口之后进入 beego 的处理逻辑。
  3. 初始化 Context 对象,根据请求判断是否为 WebSocket 请求,如果是的话设置 Input,同时判断请求的方法是否在标准请求方法中(GET、POST、PUT、DELETE、PATCH、OPTIONS、HEAD),防止用户的恶意伪造请求攻击造成不必要的影响。
  4. 执行 BeforeRouter 过滤器,当然在 beego 里面有开关设置。如果用户设置了过滤器,那么该开关打开,这样可以提高在没有开启过滤器的情况下提高执行效率。如果在执行过滤器过程中,responseWriter 已经有数据输出了,那么就提前结束该请求,直接跳转到监控判断。
  5. 开始执行静态文件的处理,查看用户的请求 URL 是否和注册在静态文件处理 StaticDir 中的 prefix 是否匹配。如果匹配的话,采用 http 包中默认的 ServeFile 来处理静态文件。
  6. 如果不是静态文件开始初始化 session 模块(如果开启 session 的话),这个里面大家需要注意,如果你的 BeforeRouter 过滤器用到了 session 就会报错,你应该把它加入到 AfterStatic 过滤器中。
  7. 开始执行 AfterStatic 过滤器,如果在执行过滤器过程中,responseWriter 已经有数据输出了,那么就提前结束该请求,直接跳转到监控判断。
  8. 执行过过滤器之后,开始从固定的路由规则中查找和请求 URL 相匹配的对象。这个匹配是全匹配规则,即如果用户请求的 URL 是 /hello/world,那么固定规则中 /hello 是不会匹配的,只有完全匹配才算匹配。如果匹配的话就进入逻辑执行,如果不匹配进入下一环节的正则匹配。
  9. 正则匹配是进行正则的全匹配,这个正则是按照用户添加 beego 路由顺序来进行匹配的,也就是说,如果你在添加路由的时候你的顺序影响你的匹配。和固定匹配一样,如果匹配的话就进行逻辑执行,如果不匹配进入 Auto 匹配。
  10. 如果用户注册了 AutoRouter,那么会通过 controller/method 这样的方式去查找对应的 Controller 和他内置的方法,如果找到就开始执行逻辑,如果找不到就跳转到监控判断。
  11. 如果找到 Controller 的话,那么就开始执行逻辑,首先执行 BeforeExec 过滤器,如果在执行过滤器过程中,responseWriter 已经有数据输出了,那么就提前结束该请求,直接跳转到监控判断。
  12. Controller 开始执行 Init 函数,初始化基本的一些信息,这个函数一般都是 beego.Controller 的初始化,不建议用户继承的时候修改该函数。
  13. 是否开启了 XSRF,开启的话就调用 Controller 的 XsrfToken,然后如果是 POST 请求就调用 CheckXsrfCookie 方法。
  14. 继续执行 Controller 的 Prepare 函数,这个函数一般是预留给用户的,用来做 Controller 里面的一些参数初始化之类的工作。如果在初始化中 responseWriter 有输出,那么就直接进入 Finish 函数逻辑。
  15. 如果没有输出的话,那么根据用户注册的方法执行相应的逻辑,如果用户没有注册,那么就调用 http.Method 对应的方法(Get/Post 等)。执行相应的逻辑,例如数据读取,数据赋值,模板显示之类的,或者直接输出 JSON 或者 XML。
  16. 如果 responseWriter 没有输出,那么就调用 Render 函数进行模板输出。
  17. 执行 Controller 的 Finish 函数,这个函数是预留给用户用来重写的,用于释放一些资源。释放在 Init 中初始化的信息数据。
  18. 执行 AfterExec 过滤器,如果有输出的话就跳转到监控判断逻辑。
  19. 执行 Controller 的 Destructor,用于释放 Init 中初始化的一些数据。
  20. 如果这一路执行下来都没有找到路由,那么会调用 404 显示找不到该页面。
  21. 最后所有的逻辑都汇聚到了监控判断,如果用户开启了监控模块(默认是开启一个 8088 端口用于进程内监控),这样就会把访问的请求链接扔给监控程序去记录当前访问的 QPS,对应的链接访问的执行时间,请求链接等。