golang 用户管理系统

最近开始接触golang,写了一个简单的用户管理系统练手:

  1. webserver 基于gin开发
  2. tcpserver 基于grpc
  3. redis / db

支持登入、登出;上传头像、修改昵称;尽量编写了足够的单元测试,并且代码都通过golint和go vet的检查。

项目地址在这:Git地址

 

代码结构说明如下:

|-- conf        // yaml格式的http server, tcp server配置;及配置解析代码
|-- doc         // 包括设计文档、测试文档
|-- gpool       // grpc 连接池
|-- code        // 定义的错误码和提示消息
|-- utils       // 公共函数
|-- httpserver  // http server代码
|   |-- static  // web相关代码 html/css/js
|   |-- upload/images       // 头像存储地址
|   |-- handler.go          // 请求处理handler
|   |-- testhandler.go      // 供压测和测试用的接口
|   `-- main.go             // 入口文件
|-- proto       // grpc 协议文件
|-- rpcclient   // grpc 客户端代码
|-- tcpserver
    |-- cache.go       // redis
    |-- db.go          // db 操作
    |-- user_dao.go    // redis/db 操作交互
    |-- userserver.go  // grpc server
    `-- main.go        // 入口文件

 

主要功能点

  • 认证

用户登录时,会生成唯一的sessionid作为token,作为key缓存到redis中。其对应的value是全部的用户信息。这样做除了加快查询外,后续的其他接口的操作都需要用户传递用户名过来(假设用户名是全局唯一的),鉴权的时候不仅校验token,同时也比对用户名,防止token冲突的情况发生。用户登录的时候密码都以md5加密传输过来,然后在db中存储的用户密码是采用“密码加盐”的方式。

当然,这样没有办法避免中间人攻击。更可靠的是采用https传输。同时在登录的时候,前后端通过协商的方式生成秘钥对,进行加解密。

  • 日志

日志采用beego/logs,开始的时候考虑用open tracing与grpc结合起来做调用链追踪。后来为了简化起见,只是在请求一到达webserver的时候就生成唯一的请求ID,同时约定日志的第一个字段就是该ID。

然后通过context的metadata传递到grpc server。后者取出这个ID,以同样的方式记录日志。做到单个请求可追溯,同时又不破坏正常请求的传输。

  • MySQL

mysql采用gorm,由于考虑到数据量大的情况,可能需要做分表。因此在gorm db操作的时候要注意:

// User gorm user object
type User struct {
    ID       int32       `gorm:"type:int(11);primary key"`
    Username string      `gorm:"type:varchar(64);unique;not null"`
    Nickname string      `gorm:"type:varchar(128)"`
    Passwd   string      `gorm:"type:varchar(32);not null"`
    Skey     string      `gorm:"type:varchar(16);not null"`
    Headurl  string      `gorm:"type:varchar(128);unique;not null"`
    Uptime   int64      `gorm:"type:datetime"`
}

// TableName gorm use this to get tablename
// NOTE : it only works int where caulse
func (u User) TableName() string {
    var value int
    for _, c := range []rune(u.Username) {
        value = value + int(c)
    }
    return fmt.Sprintf("userinfo_tab_%d", value % 20)
}

在查询的时候也可以指定tab名字:

db.Table(user.TableName()).Where(&User{Username:username}).First(&quser)

配置文件采用yaml的方式,redis则使用go-redis

 

  • grpc连接池

grpc虽然支持http2.0多路复用,但是并发高的时候,还是需要连接池来做连接复用。

之前打算用sync.Pool来做连接池,后来有看到说sync.Pool主要是作为对象池,用来做连接池不太合适。于是边用chan 封装了一个简单的连接池。

posted @ 2021-07-05 11:41  dreamw  阅读(1170)  评论(0编辑  收藏  举报