Go 订单同步

  • pqdong 

核心:多协程轮询,同步信息

从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网络请求的次数,但是也不能过分的优化,过早的优化是万恶之源,至于一次数据请求的数据量大小,其实差异不是很大,更何况有缓存。而数据库快,主要快在数据库是长连接,数据库池的概念等。

思考不如实践,关于接口拿数据时的静态信息和动态信息

发表评论

电子邮件地址不会被公开。 必填项已用*标注