前言

早先在做K8S相关的一些开发时,就曾计划扩增go相关技能,看了两本书其中一本感觉不好,推荐《Go语言编程入门与实战技巧》黄靖钧 黑色封皮的。个人读起来感觉比较透彻。写了一些小东西,遇到一些小问题点,稍作梳理。

gorm

算是go比较常用的orm,Gihub仓库地址,以及中文文档 如果从未涉及,初次使用会有一些磕绊。

model定义

model定义用途在于从数据中取出对应数据,并且可以初始化创建数据表。

type User struct {
	ID          uint       `json:"id" gorm:"PRIMARY_KEY"`                       
	Password    string     `json:"password" gorm:"type:varchar(254)"`           
	IsSuperuser bool       `json:"is_superuser"`                                
	Username    string     `json:"username" gorm:"type:varchar(150);unique_index`        
	Email       string     `json:"email" gorm:"varchar(128)"`                                                      
	DateJoined  *time.Time `json:"date_joined" sql:"default:CURRENT_TIMESTAMP"` 
}
func main() {
	db, err := gorm.Open("mysql", "root:pwd@(127.0.0.1:3306)/test_db?charset=utf8&parseTime=True&loc=Local")
	db.AutoMigrate(User)
}

1,对于主键ID定义PRIMARY_KEY 标示后会auto_increment
2,字段内除了gorm原有的语法限定支持,还支持 sql语法设置
3,设置时间字段自动填充当前时间如 DateJoined
4,设置时间自动随行数据内容更新后填充当前时间 sql:“default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP”

查询

查询的语法在上面中文文档中都有,但是对个人而言,有些特笨的问题,而且查了后发现有人跟我遇到过一样的问题。

func main() {
	var user, targetUser User
	db.First(&user, "id = ?", 1)
	db.Create(&targetUser)
}

1,如果是Django的orm,是把数据对象new出来后return给调用者,gorm是先声明user后,First查询数据后直接赋值给user对象,数据就在user对象中
2,如果是Create方法,新建后的用户数据就在targetUser中,包括自动生成的ID信息
除此之外,gorm的查询还支持一些sql写法,但是遇到一个问题,需要用DISTINCT去重统计,其写法如下

db.Where("id = ?", id).Select("count(distinct(companies.id))").Count(&companyCount)

详情请见

关联关系

在使用文档中注明了包含,外键这种如何定义,但是使用时遇到了一些问题
用AutoMigrate创建的表名是有一定规则的,驼峰转纯小写以 “_” 连接落到数据库中,在创建关联时如果外键名称能够一致,则能够用
Related查询可以直接查出,否则需要再次查询时指定

type Token struct {
	ID      uint       `json:"id" gorm:"PRIMARY_KEY"`
	token   string     `json:"token" gorm:"type:varchar(40);unique_index "`
	UserID  uint       `json:"user_id" gorm:"unique_index"`
	User    User       `gorm:"ForeignKey:UserID;AssociationForeignkey:Refer"`
	// AuthUserID  uint `json:"auth_user_id" gorm:"unique_index"`
	// User        User `gorm:"ForeignKey:AuthUserID;AssociationForeignkey:Refer"`
}
var token Token
db.First(&token, "key=?", key).Related(&token.User)
// 如果将 UserID, User换成注释中样式,则model中定义的ForeignKey:AuthUserID并不会指定到User关联,可以
db.First(&token, "key=?", key).Related(&token.User, "AuthUserID")

logrus

logrus的Github仓库地址,上面有基本使用示例,遇到为题在于如何Format,如果仅仅想按照给出的既定形式

time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach"

如果想要TextFormatter,设置成形如示例中格式输出至控制台,可以设定:

log := logrus.New()
log.Formatter = &logrus.TextFormatter{
	DisableColors: true,
    TimestampFormat : "2006-01-02 15:04:05",
    FullTimestamp:true,
    ForceFormatting: true,
}
log.SetLevel(logrus.DebugLevel)

但是此种设置输出控制台没有问题,且带颜色区分,但是输入文件会有颜色字符在前置,可以用logrus-easy-formatter处理,此种format正好符合个人期望

var Logger = logrus.New()
func InitLog(st *setting.Setting) {
	Logger.Formatter = &easy.Formatter{
		TimestampFormat: "2006-01-02 15:04:05",
		LogFormat: "[%lvl%]: %time% - %msg%\n",
	}

	logFile, err := os.OpenFile(st.LogDir, os.O_APPEND | os.O_CREATE | os.O_RDWR, 0666)
	if err != nil {
		panic(err)
	}
	out := io.MultiWriter(os.Stdout, logFile)
	Logger.SetOutput(out)
	Logger.SetLevel(logrus.DebugLevel)
}

gin

gin跟beego比起来,就类似Flask跟Django,简单易用。Github仓库地址,基础使用示例文档中也有,我们主要看下middleware的使用。

func MyMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		log.Println("Request in") // ①
		c.Next() // next handler func
		log.Println("Response out") // ②
	}
}

func main() {
	r := gin.New()
	// Global middleware
	// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
	// By default gin.DefaultWriter = os.Stdout
	r.Use(gin.Logger()) // M1
	// Recovery middleware recovers from any panics and writes a 500 if there was one.
	r.Use(gin.Recovery()) //M2
	
	//使用自定义middleware
	// r.Use(gin.MyMiddleware())
	...
}

用户自定义的中间件

1,如果使用 r := gin.Default() 则意味着将上面三者 NewUse LoggerUse Recovery 一并开启
2,自定义middleware形如上例,User中的对象就是 gin.HandlerFunc
3,中间件的灵魂是c.Next()
因为中间件的调用顺序是洋葱似的以上面 Use Logger M1 和 Use Recovery M2 为例,运行顺序应该是:
request -> M1 -> M2 -> func -> M2 -> M1 -> response
这其中 c.Next() 就完成了 M1 -> M2 的切换。可以看下Context定义,其维护了一个handlers HandlersChain这中type HandlersChain []HandlerFunc 所以,Next()方法就是将 index后移,继续下个方法执行
4,对应的有c.Abort() 立马返回响应

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐