这是我参与「第五届青训营」伴学笔记创作活动的第 3 天
昨天,也就是1月17日,青训进行的讲授是关于ORM、RPC和HTTP框架。由于我之前接触过Java后端开发的一些知识,因此ORM框架和HTTP框架对于我来说不算过于陌生。比如在Java中ORM框架比较有名的应该是MyBatis但是本次介绍的ORM框架GORM更类似Java中JPA(Java Persistence API)中的Criteria-API,与raw sql相比,更倾向于通过函数的链式调用完成CRUD的操作。其中让我比较感兴趣的是一个是其原生支持的软删除功能,通过给数据模型添加一个字段gorm.DeletedAt来生成对应的表即可使表支持软删除,其底层的工作原理其实就是给表增加了一个nullable的Timestamp列,当执行删除操作时,软删除只是将相应的deleted_at列属性从null设为删除时间。而使用Unscope方法可以搜索到已经被然删除的行db.Unscoped().Where("age = 20").Find(&users),也可以进一步永久删除这一行db.Unscoped().Delete(&order)。另外GORM还通过为数据模型的struct添加Before或After Hook方法达到类似Spring AOP的效果。
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
if u.Role == "admin" {
return errors.New("admin user not allowed to delete")
}
return
}
而在讲述GORM的连接原理和事务操作时,讲师提到GORM的连接底层是连接池不断复用连接,因此GORM的连接通常是一次连接全局使用,这样带来的问题就是在执行较长事务时为了保证事务在同一个连接上执行,在创建事务时需要创建一个新的事务对象来固定这个连接。由于事务要对错误进行Rollback提交需要Commit,但是这样需要时刻注意这两种处理,并且在事务发生嵌套时不好进行代码的编写,因此GORM还给出了一种类似Spring AOP中@Transactional的方法进行事务操作的执行,减少在事务执行失败时手写Rollback的情况。
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// 连接DB
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// 执行一个嵌套事务,自动进行回滚和提交操作
db.Transaction(func(tx *gorm.DB) error {
tx.Create(&user1)
tx.Transaction(func(tx2 *gorm.DB) error {
tx2.Create(&user2)
return errors.New("rollback user2")
})
return nil
})
// 手动开启一个事务
tx := db.Begin()
// 遇到错误时手动回滚事务
tx.Rollback()
// 否则,手动提交事务
tx.Commit()
}
而介绍的HTTP框架是与Spring MVC中的Controller类似的部分,用来对外界的用户请求进行操作。
我之前没有怎么接触的是RPC框架,之前看过一些gRPC的介绍,但是没有真正的使用过。以前曾经用过SOAP和webservice。RPC框架主要是在分布式服务中,不同服务进行通信的框架,通过IDL(Interface description language)来定义不同服务通信的接口,并通过RPC架构配备的代码生成工具根据IDL生成类似Controller的服务通信框架,并通过client RPC调用其他需要的服务。
本次的课程让我初步从大单体服务的思维转化成分布式微服务的思想,知道了RPC框架该如何使用来完成服务间的协作。