笔者最近在学习go语言后端开发,开个新坑,权当笔记了(实现一个简易的抖音后端)。
go语言后端开发的优势
- 高并发:Go语言的goroutine和channel机制使得它能够很方便地实现高并发。goroutine是轻量级线程,可以在一个进程内并发执行上千个,而不会占用太多的内存。channel则可以安全地在不同的goroutine之间传递数据,避免了传统线程间共享变量的问题。
- 快速编译:Go语言的编译速度非常快,因为它使用静态编译。这意味着您可以在短时间内快速构建和测试您的应用程序,从而提高了开发效率。
- 内存管理:Go语言自带垃圾回收器,可以自动管理内存。这意味着您无需手动分配和释放内存,从而避免了许多与内存管理相关的错误。
- 安全性:Go语言在语言层面上提供了许多安全机制,例如类型检查和空指针检查,从而避免了许多常见的安全漏洞。
- 简单易学:Go语言的语法简洁、清晰,易于学习和阅读,这使得开发人员可以更快地上手并写出高质量的代码。
- 开源:Go语言是开源的,有庞大的社区支持和大量的开源库和框架可供使用。这些库和框架可以帮助开发人员更快地构建应用程序,从而加快开发速度。
- 跨平台支持:Go语言的编译器可以生成多个平台的可执行文件,这意味着您可以使用同一份代码在不同的操作系统上运行您的应用程序。
综上,选择go语言开发有着很多天然的优势,选择go就是选择成功!!!
废话不多说,接下来进入本文的第一个设计模块
用户表设计
github上有许多现成的goweb开发的脚手架模板,自己去寻找吧,少年!(找不到可以后台找我要)学习本项目默认你已经学会基本go的语法以及gin框架哦。
项目开始,我们先设计一个user表:
user | ||||||||
id | user_id | username | password | phone | gender | create_time | update_time |
其中,id为主键,username和user_id为唯一索引。
建表语句如下:
-- ----
-- user
-- ----
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`username` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(64) COLLATE utf8mb4_general_ci NOT NULL,
`phone` varchar(11) COLLATE utf8mb4_general_ci,
`email` varchar(64) COLLATE utf8mb4_general_ci,
`gender` tinyint(4) NOT NULL DEFAULT '0',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`) USING BTREE,
UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
雪花算法引入
在介绍雪花算法前,我们思考一个问题,为什么不直接使用id作为用户标识,而是新建一个user_id?
主要有两个原因:
- 使用自增id,其他人可以推断出当前的用户数
- 分库分表时,id是可能会重复,这是不被允许的
由此,大型项目中往往需要一个分布式的数据id生成器,分布式id有以下特点:
- 全局唯一性:不能出现重复的ID标识
- 递增性:确保ID是递增的
- 高可用性:确保任何时候都可以生成正确的ID
- 高性能性:在高并发的情况下依然表现良好
比如:双十一期间,电商平台有大量订单,在这些信息插入数据库前,我们要分配给这些数据唯一ID,然后保存到数据库中,这个ID最好能保留时间信息,这样即时分库分表也可以进行排序。
由此,我们选择性能较高的分布式ID生成算法,snowflake
由这个算法所生成的序列由64位整数生成:
64bit | |||
1bit,始终为0 | 41bit 时间戳 | 10bit 工作机器id | 12bit 序列号 |
- 第一位,占用1bit,始终为0
- 时间戳,占用41bit,单位为毫秒,总共可容纳69年的时间
- 工作机器id,占用10bit,前5bit为数据中心id,后5bit为工作节点id,最多可容纳1024个节点
- 序列号,占用12bit,用来记录同一毫秒内产生的不同id,从0开始,最高到4095
可见,雪花算法的引入对于大型项目开发有着非常良好的作用。
代码实现
在项目根目录下新建 pkg/snowflake/snowflake.go
下载相关包:
go get github.com/bwmarrin/snowflake
在snowflake.go文件中填入以下代码:
package snowflake
import (
sf "github.com/bwmarrin/snowflake"
"time"
)
var node *sf.Node
// 初始化雪花算法节点
func Init(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
}
// 生成ID
func GetID() int64 {
return node.Generate().Int64()
}
在上述代码中,Init函数初始化了节点,GetID()函数用于返回用节点生成的ID值,其中,第17行除以1000000是因为UnixNano()函数返回的时间值为纳米级别,而我们的时间戳基准是毫米级别,所以需要进行换算。
在settings/settings.go文件中新增StartTime和MachineID字段
StartTime string `mapstructure:"start_time"`
MachineID int64 `mapstructure:"machine_id"`
最后在config.yaml文件中新增start_time和machine_id字段的值
start_time: "2023-03-11"
machine_id: "1"
其中,start_time可以设置任意值(我这里就选了今天),machine_id在自己开发时就写1即可。
在main.go中引入雪花算法
// 初始化雪花算法
if err := snowflake.Init(settings.Conf.StartTime, settings.Conf.MachineID); err != nil {
fmt.Printf("init snowflake failed, err:%v\n", err)
return
}
最后成功运行就ok啦!
朋友们,下节再见😋😋😋