这是我参与「第五届青训营 」笔记创作活动的第3天。
课堂主要内容
- Gorm
- Kitex
- Hertz
Gorm
连接数据库:
dsn := "username:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
在dsn里可以设置登录数据库的用户名密码,数据库地址端口等一众配置信息。还可以在&gorm.Config{}设置数据库。就比如:
&gorm.Config{
SkipDefaultTransaction: true, // 关闭默认事务
PrepareStmt: true // 使用PrepareStmt缓存预编译语句可以提高后续调用的速度
}
创建对应表的结构
type User struct {
gorm.Model
UserName string
Password string
}
func (user User) TableName() string {
return "user"
}
其中gorm.Model其中包括:ID, CreatedAt、UpdatedAt , DeletedAt等字段。TableName是该结构在数据库中的表名,如果不指定就默认使用蛇形。
增删改查
新增:
db.Create(&User{UserName: "user1", Password: "password1"})
查询:
res := []User
db.Where("UserName = ?", "no1").Find(&res) //用where条件语句
db.Where(&User{UserName: "no1", Password: ""}).Find(&res) //这里struct中的零值会被忽略
db.Where(map[string]interface{}{"UserName": "no1", Password: ""}).Find(&res) //而map中的零值不会被忽略掉
修改:
db.Where("UserName = ?", "no1").Updates(User{UserName: "no2", Password: "password2"})
删除: 删除分为硬删和软删
db.Where("UserName = ?", "no2").Delete(User{}) //硬
// 软删除
// 在model添加gorm.DeletedAt类型字段该model调用delete即可
// 而且可以用Unscoped()来获取被软删的数据
users := []User{}
db.Unscoped().Where("UserName = ?", "no2").Find(&users)
事务:
手动事务:
// 事务
// 事务一旦开始,你就应该使用 tx 处理数据
tx := db.Begin() // 开始事务
tx.commit() // 提交事务
tx.Rollback() // 回滚
自动事务:
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
hook:
gorm 提供hook的能力 即在执行函数之前或之后自动调用的函数
func (u *User) BeforCreate(tx *gorm.DB) (err error) {
if u.Age < 0 {
return errors.New("can't sace invalid data")
}
return nil
}
Kitex
定义idl,echo.thrift
namespace go api
struct Request {
1: string message
}
struct Response {
1: string message
}
service Hello {
Response echo(1: Request req)
}
然后通过以下命令生成代码,your_module_name和your_service_name替换成go.mod文件里的module名和自己的服务名
kitex -module your_module_name -service your_service_name xxx.thrift
生成后项目目录结构如下,在这个目录下用go run .即可运行该服务
.
|-- build.sh
|-- echo.thrift
|-- handler.go
|-- kitex_gen
| `-- api
| |-- echo
| | |-- client.go
| | |-- echo.go
| | |-- invoker.go
| | `-- server.go
| |-- echo.go
| `-- k-echo.go
|-- main.go
`-- script
|-- bootstrap.sh
`-- settings.py
我们需要实现的接口函数逻辑,在handler.go中
package main
import (
"context"
"example/kitex_gen/api"
)
// EchoImpl implements the last service interface defined in the IDL.
type EchoImpl struct{}
// Echo implements the EchoImpl interface.
func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
// TODO: Your code here...
return
}
至此一个简易的rpc服务端就完事了 rpc的客户端调用kitex_gen和kitex包的代码就行
package main
import (
"context"
"log"
"time"
"github.com/cloudwego/kitex-examples/hello/kitex_gen/api"
"github.com/cloudwego/kitex-examples/hello/kitex_gen/api/hello"
"github.com/cloudwego/kitex/client"
)
func main() {
client, err := hello.NewClient("hello", client.WithHostPorts("0.0.0.0:8888"))
if err != nil {
log.Fatal(err)
}
for {
req := &api.Request{Message: "my request"}
resp, err := client.Echo(context.Background(), req)
if err != nil {
log.Fatal(err)
}
log.Println(resp)
time.Sleep(time.Second)
}
}
Hertz
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main() {
h := server.Default()
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
})
h.Spin()
}
使用Hertz官方实例的代码就可以启动一个简单web服务了 而且Hertz还支持路由分组
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main(){
h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
v1 := h.Group("/v1")
v1.GET("/get", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "get")
})
v1.POST("/post", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "post")
})
v2 := h.Group("/v2")
v2.PUT("/put", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "put")
})
v2.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "delete")
})
h.Spin()
}
Hertz的路由类型有三种:静态路由、参数路由、通配路由
路由的优先级:静态路由 > 命名路由 > 通配路由
静态路由就是上面的路由方式
参数路由:
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main(){
h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
// This handler will match: "/hertz/version", but will not match : "/hertz/" or "/hertz"
h.GET("/hertz/:version", func(ctx context.Context, c *app.RequestContext) {
version := c.Param("version")
c.String(consts.StatusOK, "Hello %s", version)
})
h.Spin()
}
通配路由:
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol/consts"
)
func main(){
h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
// However, this one will match "/hertz/v1/" and "/hertz/v2/send"
h.GET("/hertz/:version/*action", func(ctx context.Context, c *app.RequestContext) {
version := c.Param("version")
action := c.Param("action")
message := version + " is " + action
c.String(consts.StatusOK, message)
})
h.Spin()
}