beego功能丰富,就像国产的长城、吉利、长安等,虽然三大件不如gin,但在使用友好方面、符合国内时代场景,特别是web场景。beego自己买地自己建楼自己买材料自己设计自己装修,剩下的软装开发者你自己来,甚至提供工具上门。

如果是做web或api项目,在国内讲究快速实现快速验证快速推出原则,建议使用beego。beego里有的模块或功能,不能说其他框架没有,只是beego直接内置了或指定了或建议了,避免开发者陷入选择或自己设计维护的困境。

注意:beego官方文档,不少地方都丢失了细节,略显仓促的文档,有时需要中文英文文档相互对比着看。

目录

beego框架

├── conf
│   └── app.conf
├── controllers
│   ├── admin
│   └── default.go
├── main.go
├── models
│   └── models.go
├── static
│   ├── css
│   ├── ico
│   ├── img
│   └── js
└── views
    ├── admin
    └── index.tpl
#安装
go get github.com/beego/beego/v2@latest
#升级
go get -u github.com/beego/beego/v2

bee工具

bee提供了一些命令,支持开发快速搭建项目

go get -u github.com/beego/bee/v2
Usage:

    bee command [arguments]

The commands are:

    version     show the bee & beego version
    migrate     run database migrations
    api         create an api application base on beego framework
    bale        packs non-Go files to Go source files    
    new         create an application base on beego framework
    run         run the app which can hot compile
    pack        compress an beego project
    fix         Fixes your application by making it compatible with newer versions of Beego
    dlv         Start a debugging session using Delve
    dockerize   Generates a Dockerfile for your Beego application
    generate    Source code generator
    hprose      Creates an RPC application based on Hprose and Beego frameworks
    pack        Compresses a Beego application into a single file
    rs          Run customized scripts
    run         Run the application by starting a local development server
    server      serving static content over HTTP on port
    
Use bee help [command] for more information about a command.
    

配置

提供了一套用于组织配置的机制,配置中心化,避免配置散乱耦合。

提供了环境配置

配置机制,减少开发者对配置组织的考虑,比如不用去考虑环境配置等问题,把更多精力用于业务逻辑实现。

[shehui]
    app_id = wxf0f13956e2d18xxx
    app_secret = app_secretxxxx
    name = shehui
    key = shehui
    version = 1
appname = pdd-mini-go
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
EnableDocs = true
sqlconn = 
  
inlcude mini.conf
appname, _ := beego.AppConfig.String("appname")
minname, _ := beego.AppConfig.String("shehui::name")

路由

丰富的路由设置,默认支持RESTful,有常规的get、post等路由设置,有自定义路由,有autorouter,支持正则路由,支持注解路由,支持表达式路由,支持命名空间,没有你想要的他没有的路由。

ns := beego.NewNamespace("/v1",
    beego.NSNamespace("/object",
        beego.NSInclude(
            &controllers.ObjectController{},
        ),
    ),
    beego.NSNamespace("/user",
        beego.NSInclude(
            &controllers.UserController{},
        ),
    ),
)
beego.AddNamespace(ns)

ns2 := beego.NewNamespace("/shehui",
    beego.NSNamespace("/index",
        beego.NSInclude(
            &controllers.IndexController{},
        ),
    ),
)
beego.AddNamespace(ns2)
// @router /login [post]
func (this *IndexController) Login() {
}
// @router /:uid [get]
func (u *UserController) Get() {
}
// @router / [get]
func (u *UserController) GetAll() {
      
}
// @router / [post]
func (u *UserController) Post() {
}

Controller

web.InsertFilter(pattern string, pos int, filter FilterFunc, opts ...FilterOpt)
func (this *AddController) Get() {
    mystruct := { ... }
    this.Data["json"] = &mystruct
    this.ServeJSON()
}
u := User{"man", 40}
valid := validation.Validation{}
valid.Required(u.Name, "name")
valid.MaxSize(u.Name, 15, "nameMax")
valid.Range(u.Age, 0, 18, "age")
// 验证函数写在 "valid" tag 的标签里
// 各个函数之间用分号 ";" 分隔,分号后面可以有空格
// 参数用括号 "()" 括起来,多个参数之间用逗号 "," 分开,逗号后面可以有空格
// 正则函数(Match)的匹配模式用两斜杠 "/" 括起来
// 各个函数的结果的 key 值为字段名.验证函数名
type user struct {
    Id     int
    Name   string `valid:"Required;Match(/^Bee.*/)"` // Name 不能为空并且以 Bee 开头
    Age    int    `valid:"Range(1, 140)"` // 1 <= Age <= 140,超出此范围即为不合法
    Email  string `valid:"Email; MaxSize(100)"` // Email 字段需要符合邮箱格式,并且最大长度不能大于 100 个字符
    Mobile string `valid:"Mobile"` // Mobile 必须为正确的手机号
    IP     string `valid:"IP"` // IP 必须为一个正确的 IPv4 地址
}
func main() {
    valid := validation.Validation{}
    u := user{Name: "Beego", Age: 2, Email: "dev@web.me"}
    b, err := valid.Valid(&u)
    if err != nil {
        // handle error
    }
    if !b {
        // validation does not pass
        // blabla...
        for _, err := range valid.Errors {
            log.Println(err.Key, err.Message)
        }
    }
}

Model

beego自带了一个ORM框架,但仍处于开发阶段,参考了Django的ORM和SQLAlchemy。

也可以考虑第三方的orm,比如xorm、GORM等

// 构建查询对象
qb.Select("user.name",
    "profile.age").
    From("user").
    InnerJoin("profile").On("user.id_user = profile.fk_user").
    Where("age > ?").
    OrderBy("name").Desc().
    Limit(10).Offset(0)

// 导出 SQL 语句
sql := qb.String()

// 执行 SQL 语句
o := orm.NewOrm()
o.Raw(sql, 20).QueryRows(&users)

View

{{. | FuncA | FuncB | FuncC}}
autorender = false
this.TplName = "admin/add.tpl"
func hello(in string)(out string){
    out = in + "world"
    return
}

web.AddFuncMap("hi",hello) //定义模板函数
定义之后你就可以在模板中这样使用了:

{{.Content | hi}}

web.SetStaticPath("/static","public")
web.SetStaticPath("/images","images") 
web.SetStaticPath("/css","css") 
web.SetStaticPath("/js","js")

应用部署

nohup ./beepkg &
server {
    listen       80;
    server_name  .a.com;

    charset utf-8;
    access_log  /home/a.com.access.log;

    location /(css|js|fonts|img)/ {
        access_log off;
        expires 1d;

        root "/path/to/app_a/static";
        try_files $uri @backend;
    }

    location / {
        try_files /_not_exists_ @backend;
    }

    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;

        proxy_pass http://127.0.0.1:8080;
    }
}

server {
    listen       80;
    server_name  .b.com;

    charset utf-8;
    access_log  /home/b.com.access.log  main;

    location /(css|js|fonts|img)/ {
        access_log off;
        expires 1d;

        root "/path/to/app_b/static";
        try_files $uri @backend;
    }

    location / {
        try_files /_not_exists_ @backend;
    }

    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;

        proxy_pass http://127.0.0.1:8081;
    }
}

热升级

热升级和热编译是不同的概念,热编译是通过监控文件的变化重新编译,然后重启进程,例如 bee run 就是这样的工具。

了解 nginx 的同学都知道,nginx 是支持热升级的,可以用老进程服务先前链接的链接,使用新进程服务新的链接,即在不停止服务的情况下完成系统的升级与运行参数修改

在web服务中热升级是很有必要的,我们的目标就是为了不终止web服务,但在分布式高并发场景下,web服务可能会出问题,这些属于高可用的范畴,我们可以通过其他其他策略来弥补。

grace模块为beego提供了一个热重启的模块。

进程内监控

这个中文文档翻译的不够准确,也显得生硬不接地气,英文live monitor,我们可以理解为对项目的实时监控。

#开启监控
EnableAdmin = true
# 修改监控实时查看监听地址
AdminAddr = "localhost"
AdminPort = 8088

API自动化文档

自动化文档对于团队开发是一把好用的利器,有助于编码规范,有助于内部协同,适合敏捷开发理念:

Working software over comprehensive documentation

软件能够运行 优于 详尽的文档

那现在我们能运行,还顺带有了详尽的api文档,不需要额外更换场景去编写文档,费时费力。

当然自动化文档对编码有一点的要求:

// @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
ns := beego.NewNamespace("/v1",
    beego.NSNamespace("/object",
        beego.NSInclude(
            &controllers.ObjectController{},
        ),
    ),
    beego.NSNamespace("/user",
        beego.NSInclude(
            &controllers.UserController{},
        ),
    ),
)
beego.AddNamespace(ns)
// @Title Get
// @Description get user by uid
// @Param	uid		path 	string	true		"The key for staticblock"
// @Success 200 {object} models.User
// @Failure 403 :uid is empty
// @router /:uid [get]
func (u *UserController) Get() {
    uid := u.GetString(":uid")
    if uid != "" {
        user, err := models.GetUser(uid)
        if err != nil {
            u.Data["json"] = err.Error()
        } else {
            u.Data["json"] = user
        }
    }
    u.ServeJSON()
}
bee run -gendoc=true -downdoc=true

cache模块

beego的cache模块只为了满足简单的数据缓存效果,支持Set、Get、Delete、Incr、Decr、IsExist、GetMulti、Put、ClearAll、SatrtAndGC方法

支持4种驱动引擎:memory、file、memcache、redis

目前项目呼声较高的一般是redis,毕竟redis除了数据缓存外,还支持多种数据结构,比如队列、hash、链表、订阅及事务。

一般建议使用第三方开源的redis库: github.com/garyburd/redigo/redis,当然beego缓存模块中redis也是基于redigo。go操作Redis · Go语言中文文档

logs模块

Go内置的log库功能有限,例如无法满足记录不同级别日志的情况,我们在实际的项目中根据自己的需要选择使用第三方的日志库,如logrus、zap等。

beego也提供了这样的日志模块,支持多种引擎日志写入。

package main

import (
    "github.com/beego/beego/v2/core/logs"
)

func main() {    
    //an official log.Logger 一个实例
    l := logs.GetLogger()
    l.Println("this is a message of http")
    //an official log.Logger with prefix ORM 另一个实例
    logs.GetLogger("ORM").Println("this is a message of orm")

    //全局logs
    logs.Debug("my book is bought in the year of ", 2016)
    logs.Info("this %s cat is %v years old", "yellow", 3)
    logs.Warn("json is a type of kv like", map[string]int{"key": 2016})
    logs.Error(1024, "is a very", "good game")
    logs.Critical("oh,crash")


    //还可以这样实例化一个log
    log := logs.NewLogger()
    log.SetLogger(logs.AdapterConsole)
    log.Debug("this is a debug message")
}

logs.Async()
logs.Async(1e3)
  logs.SetLogger(logs.AdapterConsole, `{"level":1,"color":true}`)
logs.SetLogger(logs.AdapterFile, `{"filename":"test.log"}`)
logs.SetLogger(logs.AdapterMultiFile, `{"filename":"test.log","separate":["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"]}`)

httplib客户端请求

Go语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现。

而beego提供httplib 库主要用来模拟客户端发送 HTTP 请求,类似于 Curl 工具,支持 JQuery 类似的链式操作。使用起来相当的方便。

客户端发送HTTP请求,想用哪个,看开发者对哪种写法更加顺手。

context模块

context上下文模块主要是针对 HTTP 请求中,request 和 response 的进一步封装,他包括用户的输入和输出,用户的输入即为 request,context 模块中提供了 Input 对象进行解析,用户的输出即为 response,context 模块中提供了 Output 对象进行输出。

控制器对象都带有Ctx这个上下文对象:

task模块

task可以支持我们实现秒级别的定时任务,用于定时汇报进程或goroutine的内存信息,定时触发GC,定时清理一些日志数据等

范例:


tk1 := task.NewTask("tk1", "0 12 * * * *", func(ctx context.Context) error { fmt.Println("tk1"); return nil })
  
//  0 0,10,20,30,40,50 * * * *            每隔 10 分 执行
//  0 */10 * * * *                     每隔 10 分 执行
//  0 * 1 * * *                       从 1:0 到 1:59 每隔 1 分钟 执行
//  0 0 1 * * *                       1:00 执行
//  0 0 */1 * * *                      毎时 0 分 每隔 1 小时 执行
//  0 0 * * * *                       毎时 0 分 每隔 1 小时 执行
//  0 2 8-20/3 * * *                   8:02,11:02,14:02,17:02,20:02 执行
//  0 30 5 1,15 * *                    1 日 和 15 日的 5:30 执行

err := tk.Run()
if err != nil {
    t.Fatal(err)
}
task.AddTask("tk1", tk1)
task.StartTask()
defer task.StopTask()

task会被列入beego的实时项目监控中任务列表中。详见进程内监控

utils模块

我们经常需要打印一些参数进行调试,但是默认的参数打印总是不是很完美,也没办法定位代码行之类的,主要包括了两个函数:

  • Display() 直接打印结果到 console
  • GetDisplayString() 返回打印的字符串

两个函数的功能一模一样,第一个是直接打印到 console,第二个是返回字符串,方便用户存储到日志或者其他存储。

这两个函数和php的print_r结构很相似

admin模块

在 v2.x 里面,我们将原本的toolbox拆分为两块,一块是admin,即治理模块;另外一块是task。

admin模块中的几个功能:健康检查、性能调试、访问统计、计划任务。

type DatabaseCheck struct {
}

func (dc *DatabaseCheck) Check() error {
    if dc.isConnected() {
        return nil
    } else {
        return errors.New("can't connect database")
    }
}
admin.AddHealthCheck("database",&DatabaseCheck{})
$ curl http://localhost:8088/healthcheck    
* database: OK

config模块

beego提供了一个用来解析配置文件的库,支持解析ini、json、xml、yaml格式配置文件。

国际化

i18n 模块主要用于实现站点或应用的国际化功能,实现多语言界面与反馈,增强用户体验。

beego.AddFuncMap("i18n", i18n.Tr)
{{i18n .Lang "hi%d" 12}}
about = About

[about]
about = About Us
{{i18n .Lang "about"}}
{{i18n .Lang "about.about"}}
{{i18n .Lang "about."}}
{{i18n .Lang ".about."}}
go get github.com/beego/i18n/beei18n
beei18n sync srouce_file.ini other1.ini other2.ini

更多

小结

beego有的,gin也能实现。

语言无好坏,框架无大小,尽在开发者对软件工程三要素过程、方法、工具的理解,需求就是问题,问题的解决,需要人去思考分析问题,需要人去设计解决问题的过程步骤,需要人去思考每个过程步骤环节有哪些解决方法方案,采用哪个方法会更优,每个方法方案可以使用什么工具来提高执行效率,从而在高效率、低成本、可维护下解决问题。