使用golang实现任务池
为了减少服务请求过于频繁,使用打包的方式进行数据处理. 并约束服务的时效在规定的范围中,避免数据失效
一.思路分析
1.为了不降低三方请求响应, 设计一个专门接受请求的缓冲池,将用户请求的数据直接放入缓冲池中
2.启用单独的任务分发进程,负责时刻监听请求数据以及定时维护过期请求数据
3.启用单独的过期数据处理进程,对过期数据单独进行处理
4.启用打包处理进程,将数据按照设定打包
二.程序实现
程序使用golang进行开发设计
使用毫秒的处理时间
package pool
import (
"errors"
"fmt"
"time"
)
type PoolHandler struct {
MaxMillSecond int64 // 最大执行毫秒数
TaskMax int // 最大任务数
PoolData *TaskListData // 缓存池数据
PackData chan []*TaskData // 打包数据
OverdueData chan []*TaskData // 过期数据
EarliestExpTime int64 // 最早过期时间
Lock chan bool // 分发操作锁
TmpData []*TaskData
}
// 任务列表数据
type TaskListData struct {
Data []*TaskData
}
type TaskData struct {
StartTime int64 // 开始时间 毫秒
Name string
Sex int
}
// 实例化处理器
func NewPoolHandler(maxMillSecond int64, taskMax int) *PoolHandler {
obj := &PoolHandler{
MaxMillSecond: maxMillSecond,
TaskMax: taskMax,
PoolData: &TaskListData{},
PackData: make(chan []*TaskData),
OverdueData: make(chan []*TaskData),
Lock: make(chan bool),
}
// 任务分化器
go obj.DispatcherTask()
// 超时处理
go obj.OverTaskHandle()
// 打包处理
go obj.PackTaskHandle()
return obj
}
// 任务生成器
func (s *PoolHandler) CreateTask(task *TaskData) (err error) {
if s == nil {
return errors.New("没有实例化对象")
}
task.StartTime = time.Now().UnixNano() / 1e6
s.TmpData = append(s.TmpData, task)
s.PoolData.Data = append(s.PoolData.Data,s.TmpData...)
s.TmpData = nil
s.Lock <- true
return
}
// 任务分发器
func (s *PoolHandler) DispatcherTask() {
// 定时检查数据是否过期
go func() {
timer := time.NewTicker(time.Millisecond * time.Duration(s.MaxMillSecond))
defer timer.Stop()
for range timer.C {
s.Lock <- true
}
}()
// 新数据来时 进行数据处理
go s.HandleData()
}
// 数据解析
func (s *PoolHandler) HandleData() {
for range s.Lock {
// 当前读取数据量
poolData := s.PoolData.Data
l := len(poolData)
// 如果没有数据 直接返回
if l < 1 {
continue
}
// 记录早过期时间
if s.EarliestExpTime < 1 {
s.EarliestExpTime = poolData[0].StartTime
}
// 获取当前毫秒
tNow := time.Now().UnixNano() / 1e6
// 过期任务存储
overdueData := make([]*TaskData, 0)
// 未过期的任务
packData := make([]*TaskData, 0)
// 记录最早的创建任务的时间
for i := 0; i < l; i++ {
task := poolData[i]
if tNow - s.MaxMillSecond >= s.EarliestExpTime && tNow - task.StartTime > s.MaxMillSecond {
overdueData = append(overdueData, task)
continue
}
packData = append(packData, task)
}
// 分发过期任务
overLen := len(overdueData)
if overLen > 0 {
s.OverdueData <- overdueData
// 删除已处理的过期信息
s.PoolData.Data = append(s.PoolData.Data[overLen:])
}
// 分发未过期的任务
currLen := len(packData)
// 记录最早过期时间
if currLen > 1 {
s.EarliestExpTime = packData[0].StartTime
}
// 如果处理数据不足 则不进行处理
if currLen < s.TaskMax {
continue
}
// 任务打包
if currLen >= s.TaskMax {
currI := currLen / s.TaskMax
for i := 0; i < currI; i++ {
pos := i*s.TaskMax
s.PackData <- packData[pos:pos+s.TaskMax]
// 删除已处理的信息
s.PoolData.Data = append(s.PoolData.Data[s.TaskMax:])
}
}
}
}
// 过期数据处理
func (s *PoolHandler) OverTaskHandle() {
for data := range s.OverdueData {
for _,v := range data {
fmt.Println("OverTaskHandle:",v.Name)
}
}
}
// 未过期打包数据处理
func (s *PoolHandler) PackTaskHandle() {
for data := range s.PackData {
for _,v := range data {
fmt.Println("PackTaskHandle:",v.Name)
}
}
}
三.使用方法
建议开启全局参数,保存实例化, 因为内部存在go的协程
obj := pool.NewPoolHandler(5000, 5)
for i := 0; i < 10; i++ {
req := &pool.TaskData{
Name: "你" + strconv.Itoa(i),
Sex: 2+i,
}
obj.CreateTask(req)
time.Sleep(time.Second*2)
}
for i := 10; i < 27; i++ {
req := &pool.TaskData{
Name: "你" + strconv.Itoa(i),
Sex: 2+i,
}
obj.CreateTask(req)
}
四.输出结果
OverTaskHandle: 你0
OverTaskHandle: 你1
OverTaskHandle: 你2
OverTaskHandle: 你3
OverTaskHandle: 你4
OverTaskHandle: 你5
OverTaskHandle: 你6
OverTaskHandle: 你7
PackTaskHandle: 你8
PackTaskHandle: 你9
PackTaskHandle: 你10
PackTaskHandle: 你11
PackTaskHandle: 你12
PackTaskHandle: 你13
PackTaskHandle: 你14
PackTaskHandle: 你15
PackTaskHandle: 你16
PackTaskHandle: 你17
PackTaskHandle: 你18
PackTaskHandle: 你19
PackTaskHandle: 你20
PackTaskHandle: 你21
PackTaskHandle: 你22
OverTaskHandle: 你23
OverTaskHandle: 你24
OverTaskHandle: 你25
OverTaskHandle: 你26