由于原本是在单机下运行,且原本的消息id依赖与mysql的自增id
然而这样的id有较大的缺点:
1. 消息的条数会堆积很多,如果只由一张表来存储可能不够,所以后续也需要将消息表进行分表操作(待实现),这样使用自增id就会导致由重复id了。
2.自增id的规律比较明显,不安全。
3.使用mysql数据库自增id会影响数据库性能,当大量的消息来的时候需要进行大量的自增id操作,由于mysql操作时会加锁,这样会影响性能。
所以我决定将消息表改为全局唯一id。
方法一 使用redis生成唯一id
使用64位的数字来存储id,前32位为时间戳,后32位为序列号,使用redis来存储每个时间戳的最大序列号。当每个节点收到消息后,会访问redis来得到最新id,id的唯一性就由redis来保证了。这样能够保证每条消息的唯一性了。
由于id依赖于redis生成,性能较高。
方法二
使用雪花算法生成唯一id,雪花算法有时间戳(41),机器id(10),序列号(12),这样就不会在不同机器上生成相同id了。可以直接调用github.com/bwmarrin/snowflake包来获得id。
这种方法不依赖与redis,直接在应用程序中调用就行。在这里我使用第二种方法进行。
添加雪花算法生成id的方法
1.在utils包下创建snowflake.go文件
package utils
import (
"time"
sf "github.com/bwmarrin/snowflake"
)
var node *sf.Node
func InitSnowFlake(startTime string, machineID int64) (err error) {
var st time.Time
st, err = time.Parse("2006-01-02", startTime)
if err != nil {
return
}
sf.Epoch = st.UnixNano() / 1000000
node, err = sf.NewNode(machineID)
return
}
func GenID() int64 {
return node.Generate().Int64()
}
2.接着在main函数种添加其初始化即可
func main() {
//传入参数为端口号
if len(os.Args) < 2 {
fmt.Println("Usage: go run main.go <port_number>")
return
}
//雪花算法初始化
tempid, _ := strconv.Atoi(os.Args[1])
machineid := int64(tempid) - 8080
if err:=utils.InitSnowFlake("2020-09-10", machineid);err!=nil {
fmt.Println("InitSnowFlak error")
return
}
utils.InitConfig()
utils.InitMySQL()
utils.InitRedis()
InitTimer()
r := router.Router()
r.Run(":" + os.Args[1])
// r.Run(viper.GetString("port.server")) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
修改数据库的message表
将gorm.model去掉,保留其原本的三个时间变量。再为其生成一个唯一messageid。
type Message struct {
//消息id
MessageId int64 `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
UserId int64 // 发送者
TargetId int64 //接收者
Type int //发送类型 1私聊 2群聊 3心跳
Media int //消息类型 1文字 2表情包 3语音 4图片
Content string //消息内容
CreateTime uint64 //创建时间
ReadTime uint64 //读取时间
Pic string //图片
Url string //URL相关
Desc string //描述
Amount int //其他数字统计
}