核心:多协程轮询,同步信息
从model中的init起协程同步状态,通过改变时间间隔降,动态的改变两次请求之间的时间间隔,低瞬间大量的处理(风暴)
// 同步课程信息
if userAdmin := beego.AppConfig.DefaultBool("EnableAdmin", false); userAdmin {
interval := beego.AppConfig.DefaultInt("PayInterval", 1200)
go syncCourseInfo(time.Duration(interval) * time.Second)
//定时解锁课时,错开一些时间,避免同时处理大量任务,也可以选择将这些任务放到一起,依次处理
time.AfterFunc(time.Duration(interval)*time.Second/2, func() {
go updateLessonLock(time.Duration(interval) * time.Second)
})
//启动更新课程相关
go updateUserCourse()
}
轮询更新time.AfterFunc()
/ 周期循环
func syncCourseInfo(interval time.Duration) {
// 获取所有未结课的课程信息
var courseIDs []int
orm.NewOrm().Raw("SELECT id FROM course WHERE end_time>? OR end_time IS NULL", time.Now().Format("2006-01-02 15:04:05")).QueryRows(&courseIDs)
for _, courseID := range courseIDs {
t1 := time.Now()
err := PullCourse(courseID, 0)
if err == nil {
log.Printf("sync course %d success using %dms\n", courseID, time.Now().Sub(t1)/time.Millisecond)
} else {
log.Printf("sync course %d error %s\n", courseID, err.Error())
}
}
// 下次更新
time.AfterFunc(interval, func() {
syncCourseInfo(interval)
})
}
超时等待机制,以及不要在一个大事务中处理过多的数据,容易引发死锁,通过md5进行新旧数据验证
// PullCourse 向第三方服务器申请相关课程的详细信息,并更新课程的课时信息,以及学生的分组信息
// 同一时间只有一个实例可以执行,所以函数运行过程加锁,如果等待锁的时间超过waitTime,则返回错误
// 将处理整个订单的过程分为若干不相关步骤,避免一个Transaction内处理过多数据,时间过长引发死锁
func PullCourse(courseID int, waitTime time.Duration) (err error) {
//超时等待,select监听不到通道内的信息则阻塞当前操作
select {
case <-time.After(waitTime):
return fmt.Errorf("wait time out")
case blkChan <- struct{}{}:
}
defer func() {
<-blkChan
}()
........
// 查看数据是不是处理过,一定要查看最新的,因为有可能旧的数据和最新的重复
var ymsg Msg
curMD5 := getMd5(data)
log.Println("md5", curMD5)
if err = ymsg.GetLastestCourseInfo(courseID); err == nil && ymsg.MD5 == curMD5 {
//该数据已经处理过,直接返回
return
}
defer func() {
// 所有数据均处理成功,记录当前请求,下次遇到一样的,不再处理
log.Printf("pull youdao course %d result %s\n", courseID, err)
if err == nil {
ymsg.Message = base64.StdEncoding.EncodeToString([]byte(data))
ymsg.Type = "youdao_course_" + strconv.Itoa(courseID)
ymsg.MD5 = curMD5
ymsg.Create()
}
}()
还有要注意的是goto标签跳转的使用和多协程异步通信非阻塞操作,能够很好的完成并发操作,提高效率
关于网络请求和数据库请求:
同样是网络请求,直接调用接口请求和数据库请求的主要区别在于,网络请求大部分都是短链接,时间消耗在三次握手上,所以要减少http网络请求的次数,但是也不能过分的优化,过早的优化是万恶之源,至于一次数据请求的数据量大小,其实差异不是很大,更何况有缓存。而数据库快,主要快在数据库是长连接,数据库池的概念等。
思考不如实践,关于接口拿数据时的静态信息和动态信息