yaml支持单项式和列表

This commit is contained in:
huangsimin 2018-12-06 14:44:10 +08:00
parent 9853a68f66
commit 88303b9e51
10 changed files with 116 additions and 592 deletions

View File

@ -3,6 +3,7 @@ package imitate
import (
"errors"
"io/ioutil"
"net/http"
"os"
"reflect"
"strings"
@ -48,17 +49,53 @@ func (curls *YamlCurls) MarshalYAML() (interface{}, error) {
return content, nil
}
type YamlProxies []string
// UnmarshalYAML YamlCurls反序列化函数
func (proxies *YamlProxies) UnmarshalYAML(unmarshal func(interface{}) error) error {
var buf interface{}
err := unmarshal(&buf)
if err != nil {
return nil
}
switch tbuf := buf.(type) {
case string:
*proxies = append(*proxies, tbuf)
case []interface{}:
for _, ifa := range tbuf {
*proxies = append(*proxies, ifa.(string))
}
default:
return errors.New("read curls is error, " + reflect.TypeOf(buf).String())
}
return nil
}
// MarshalYAML 序列化函数
func (proxies *YamlProxies) MarshalYAML() (interface{}, error) {
content := "["
for _, curl := range []string(*proxies) {
content += "\"" + curl + "\"" + ", "
}
content = strings.TrimRight(content, ", ")
content += "]"
return content, nil
}
// Config 任务加载的默认配置
type Config struct {
Session int `yaml:"session"`
Mode int `yaml:"mode"`
Proxies []string `yaml:"proxies"`
Retry int `yaml:"retry"`
Priority int `yaml:"priority"`
Curls YamlCurls `yaml:"curls"`
Session int `yaml:"session"`
Mode int `yaml:"mode"`
Proxies YamlProxies `yaml:"proxies"`
Retry int `yaml:"retry"`
Priority int `yaml:"priority"`
Curls YamlCurls `yaml:"curls"`
ExecuteInterval `yaml:"execute_interval"`
ExecuteAt `yaml:"execute_at"`
Crontab string `yaml:"crontab"`
Device string `yaml:"device"`
Platform string `yaml:"platform"`
@ -76,19 +113,7 @@ func newDefaultConfig() *Config {
Mode: 0,
Retry: 0,
Priority: 10000,
ExecuteInterval: ExecuteInterval{
TimeInterval: -1,
},
ExecuteAt: ExecuteAt{
Year: -1,
Month: -1,
Day: -1,
Hour: -1,
Min: -1,
Sec: -1,
},
Crontab: "",
Device: "",
Platform: "",
@ -110,8 +135,8 @@ func NewConfig(p string) *Config {
}
conf := newDefaultConfig()
err = yaml.NewDecoder(f).Decode(conf)
if err != nil {
panic(err)
}
@ -120,7 +145,8 @@ func NewConfig(p string) *Config {
}
func parseCurl(curl string) string {
if curl[0] == '@' {
switch curl[0] {
case '@':
curlfile, err := os.Open(curl[1:])
defer curlfile.Close()
if err != nil {
@ -128,6 +154,16 @@ func parseCurl(curl string) string {
}
curldata, err := ioutil.ReadAll(curlfile)
return strings.Trim(string(curldata), "\r\n ")
case '#':
resp, err := http.Get(curl[1:])
if err != nil {
panic(err)
}
curldata, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return strings.Trim(string(curldata), "\r\n ")
}
return strings.Trim(curl, "\r\n ")

View File

@ -8,10 +8,12 @@ import (
)
func TestNewYaml(t *testing.T) {
data := spew.Sdump(NewConfig("test.yaml"))
if !(regexp.MustCompile(`Device: \(string\) \(len=12\) "eson-OnePlus"`).MatchString(data) && regexp.MustCompile(`Sec: \(int\) 30`).MatchString(data) && regexp.MustCompile(`http://is.snssdk.com/2/article/information/v24/\?`).MatchString(data)) {
test := NewConfig("test.yaml")
data := spew.Sdump(test)
if !(regexp.MustCompile(`Device: \(string\) \(len=12\) "eson-OnePlus"`).MatchString(data) && regexp.MustCompile(`http://is.snssdk.com/2/article/information/v24/\?`).MatchString(data)) {
t.Error(data)
}
t.Error(data)
}
func TestCase(t *testing.T) {

View File

@ -1,88 +0,0 @@
package imitate
import (
"time"
)
// ExecuteAt 特定的时间任务 接口源自于 IExecute
type ExecuteAt struct {
Year int `yaml:"year"`
Month int `yaml:"month"`
Day int `yaml:"day"`
Hour int `yaml:"hour"`
Min int `yaml:"min"`
Sec int `yaml:"sec"`
TriggerTime int64 // 下次的触发时间点
StartStatus bool // 一个值判断这个时间表是否有效
}
// SetStartStatus 设置执行计划是否生效
func (ea *ExecuteAt) SetStartStatus(status bool) {
ea.StartStatus = status
}
// GetTriggerTime 获取计划的触发时间
func (ea *ExecuteAt) GetTriggerTime() int64 {
if ea.StartStatus {
return ea.TriggerTime
}
return -1
}
// TimeTo 是否到了该触发的时间
func (ea *ExecuteAt) TimeTo() int64 {
return time.Now().Unix() - ea.TriggerTime
}
// GetStartStatus 获取计划的触发时间是否在生效
func (ea *ExecuteAt) GetStartStatus() bool {
return ea.StartStatus
}
// CalculateTrigger 计算触发特定时间任务的时间点 执行后 可以通过GetTriggerTime确认触发时间
func (ea *ExecuteAt) CalculateTrigger() {
now := time.Now()
year := ea.Year
if ea.Year <= 0 {
year = now.Year()
}
month := time.Month(ea.Month)
if ea.Month <= 0 {
month = now.Month()
}
day := ea.Day
if ea.Day <= 0 {
day = now.Day()
}
hour := ea.Hour
if ea.Hour < 0 {
hour = now.Hour()
}
min := ea.Min
if ea.Min < 0 {
min = now.Minute()
}
sec := ea.Sec
if ea.Sec < 0 {
sec = now.Second()
}
ea.TriggerTime = time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local).Unix()
}
// FromValues 从数值 里获取执行时间表的结构
func (ea *ExecuteAt) FromValues(year int, month int, day int, hour int, min int, sec int) {
ea.Year = year
ea.Month = month
ea.Day = day
ea.Hour = hour
ea.Min = min
ea.Sec = sec
}

View File

@ -1,47 +0,0 @@
package imitate
import (
"time"
)
// ExecuteInterval 时间间隔的类型
type ExecuteInterval struct {
TimeInterval int64 `yaml:"sec"` // 时间间隔
TriggerTime int64 // 执行时间间隔触发时间
StartStatus bool // 判断是否按照时间间隔执行
}
// SetStartStatus 设置执行计划是否生效
func (ei *ExecuteInterval) SetStartStatus(status bool) {
ei.StartStatus = status
}
// GetTriggerTime 获取计划的触发时间
func (ei *ExecuteInterval) GetTriggerTime() int64 {
if ei.StartStatus {
return ei.TriggerTime
}
return -1
}
// TimeTo 是否到了该触发的时间
func (ei *ExecuteInterval) TimeTo() int64 {
return time.Now().Unix() - ei.TriggerTime
}
// GetStartStatus 获取计划的触发时间是否在生效
func (ei *ExecuteInterval) GetStartStatus() bool {
return ei.StartStatus
}
// CalculateTrigger 计算触发特定时间任务的时间点
func (ei *ExecuteInterval) CalculateTrigger() {
now := time.Now()
ei.TriggerTime = now.Unix() + ei.TimeInterval
}
// FromValue 生成计划表
func (ei *ExecuteInterval) FromValue(vsleep int64) {
ei.TimeInterval = vsleep
}

View File

@ -1,51 +0,0 @@
package imitate
// type ExecuteRecord struct {
// IsSuccess bool
// FailCount int
// Message []string
// }
// IExecute 计划执行的时间接口
type IExecute interface {
SetStartStatus(status bool) // SetStart 设置执行计划是否生效
GetStartStatus() bool // IsStart 获取计划的触发时间是否在生效
GetTriggerTime() int64 // GetTriggerTime 获取计划的触发时间
TimeTo() int64 // TimeTo 是否到了该触发的时间
// SetSuccessStatus(status bool) // SetSuccessStatus 设置成功的状态 将记录于历史
// History() []ExecuteRecord // History 记录一些历史, 可能会持久到数据库. 暂时不要
CalculateTrigger() // CalculateTrigger 计算触发特定时间任务的时间点
}
// ExecutePlan 执行时间的计划表
// type ExecutePlan CircularLinked
// // PlanResult 执行计划表的结果
// type PlanResult struct {
// Exec IExecute
// Resp *requests.Response
// }
// // NewExecutePlan create a plan
// func NewExecutePlan() *ExecutePlan {
// plan := &ExecutePlan{}
// plan.ExecuteQueue = NewCircularLinked()
// return plan
// }
// // AppendIExecute 添加执行计划任务
// func (ep *ExecutePlan) AppendIExecute(e IExecute) {
// ep.ExecuteQueue.Append(e)
// }
// // ClearIExecute 清除执行计划任务
// func (ep *ExecutePlan) ClearIExecute() {
// ep.ExecuteQueue = NewCircularLinked()
// }
// // CountIExecute 清除执行计划任务
// func (ep *ExecutePlan) CountIExecute() uint64 {
// return ep.ExecuteQueue.Size()
// }

View File

@ -1,239 +1 @@
package imitate
import (
"log"
"strings"
"github.com/davecgh/go-spew/spew"
)
// Node 循环链表 三色标记 不确定是否会清除循环引用, 网上说会
type Node struct {
value interface{}
prev *Node
next *Node
}
// GetValue 获取到Node的值
func (node *Node) GetValue() interface{} {
return node.value
}
// SetValue 获取到Node的值
func (node *Node) SetValue(value interface{}) {
node.value = value
}
// CircularLinked 循环链表
type CircularLinked struct {
cursor *Node
head *Node
tail *Node
size uint64
}
// NewCircularLinked create a CircularLinked
func NewCircularLinked(values ...interface{}) *CircularLinked {
list := &CircularLinked{}
if len(values) > 0 {
list.Append(values...)
}
return list
}
// Cursor get current Cursor
func (list *CircularLinked) Cursor() *Node {
if list.cursor == nil {
list.cursor = list.head
}
return list.cursor
}
// CursorNext get next Cursor
func (list *CircularLinked) CursorNext() *Node {
if list.cursor == nil {
list.cursor = list.head
}
list.cursor = list.cursor.next
return list.cursor
}
// CursorPrev get prev Cursor
func (list *CircularLinked) CursorPrev() *Node {
if list.cursor == nil {
list.cursor = list.head
}
list.cursor = list.cursor.prev
return list.cursor
}
// CursorToHead cursor move to head
func (list *CircularLinked) CursorToHead() *Node {
list.cursor = list.head
return list.cursor
}
// CursorToTail cursor move to tail
func (list *CircularLinked) CursorToTail() *Node {
list.cursor = list.tail
return list.cursor
}
// GetLoopValues 获取从头到尾的值
func (list *CircularLinked) GetLoopValues() []*Node {
var result []*Node
if list.head != nil {
result = append(result, list.head)
for cur := list.head.next; cur != list.head; cur = cur.next {
result = append(result, cur)
}
}
return result
}
// Append a value (one or more) at the end of the list (same as Append())
func (list *CircularLinked) Append(values ...interface{}) {
for _, value := range values {
node := &Node{value: value}
if list.size == 0 {
list.head = node
list.tail = node
node.next = node
node.prev = node
} else {
list.tail.next = node
node.next = list.head
node.prev = list.tail
list.tail = node
}
list.size++
}
}
// Remove 移除一些节点
func (list *CircularLinked) Remove(node *Node) {
switch list.size {
case 0:
list.errorNotInList(node)
case 1:
if list.head == node {
list.head = nil
list.tail = nil
node.next = nil
node.prev = nil
list.cursor = nil
list.size--
} else {
list.errorNotInList(node)
}
case 2:
node.prev = nil
node.next = nil
switch node {
case list.head:
list.head = list.tail
list.tail.prev = list.head
list.head.next = list.tail
list.cursor = list.head
list.size--
case list.tail:
list.tail = list.head
list.tail.prev = list.head
list.head.next = list.tail
list.cursor = list.head
list.size--
default:
list.errorNotInList(node)
}
default:
switch node {
case list.head:
_, next := list.cutAndSplice(node)
list.size--
list.head = next
if list.cursor == node {
list.cursor = next
}
case list.tail:
prev, _ := list.cutAndSplice(node)
list.size--
list.tail = prev
if list.cursor == node {
list.cursor = prev
}
default:
_, next := list.cutAndSplice(node)
list.size--
if list.cursor == node {
list.cursor = next
}
}
}
}
// LookCursor for list show
func (list *CircularLinked) LookCursor() string {
cursor := list.Cursor()
content := "->["
cur := list.head
if list.size != 0 {
for size := uint64(0); size < list.size; size++ {
if cursor == cur {
content += "(" + spew.Sprint(cur.value) + ")" + ", "
} else {
content += spew.Sprint(cur.value) + ", "
}
cur = cur.next
}
}
content = strings.TrimRight(content, ", ")
showlen := len(content)
if showlen >= 64 {
showlen = 64
}
content += "]" + content[0:showlen] + " ..."
return content
}
// Clear for list show
func (list *CircularLinked) Clear() {
if list.size != 0 {
list.head.prev = nil
list.tail.next = nil
list.head = nil
list.tail = nil
list.cursor = nil
list.size = 0
}
}
// Size for list show
func (list *CircularLinked) Size() uint64 {
return list.size
}
func (list *CircularLinked) errorNotInList(node *Node) {
log.Println("the node value ", spew.Sprint(node), " is not in list")
}
// cutAndSplice 不考虑边界情况 上层使用的是否判断
func (list *CircularLinked) cutAndSplice(node *Node) (prev, next *Node) {
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
node.prev = nil
node.next = nil
return prev, next
}

View File

@ -1,67 +1 @@
package imitate
import (
"testing"
)
func TestCircularLinkedLookUp(t *testing.T) {
cl := NewCircularLinked(1, 2, 3, 4, 5, 6)
if !(cl.head.value.(int) == 1 && cl.tail.value.(int) == 6) {
t.Error(cl.LookCursor())
}
cl = NewCircularLinked(6, 2, 3, 4, 5, 1)
if !(cl.head.value.(int) == 6 && cl.tail.value.(int) == 1) {
t.Error("New List is error:", cl.LookCursor())
}
if cl.CursorNext().value.(int) != 2 {
t.Error("CursorNext error:", cl.LookCursor())
}
cl = NewCircularLinked(0, 1, 2, 3, 4, 5)
for i := 0; i < 6; i++ {
if cl.Cursor().value.(int) != i {
t.Error("CursorNext error:", cl.LookCursor())
}
cl.CursorNext()
}
for i := 0; i < 6; i++ {
if cl.Cursor().value.(int) != i {
t.Error("CursorNext loop error:", cl.LookCursor())
}
cl.CursorNext()
}
cl = NewCircularLinked(6, 2, 3, 4, 5, 1)
cl.Remove(cl.Cursor())
if cl.Cursor().value != 2 {
t.Error("Remove Head is error", cl.LookCursor())
}
cl.Remove(cl.CursorToTail())
if cl.Cursor().value != 5 {
t.Error("Remove CursorToTail is error", cl.LookCursor())
}
cl.Remove(cl.CursorToHead())
if cl.Cursor().value != 3 {
t.Error("Remove CursorToHead is error", cl.LookCursor())
}
limitCount := 0
for cl.Size() > 0 {
cl.Remove(cl.Cursor())
limitCount++
if limitCount >= 10 {
t.Error("Not Clear", cl.LookCursor())
break
}
}
cl.Remove(cl.CursorToHead()) // nil is not in list is success!
if cl.head != nil || cl.tail != nil || cl.cursor != nil {
t.Error("Remove Boundary error")
}
}

106
task.go
View File

@ -57,9 +57,6 @@ func splitTasks(conf *Config) []ITask {
panic(err)
}
at := *&conf.ExecuteAt
interval := *&conf.ExecuteInterval
task := makeRegisterType(curl.ITask).Elem().(ITask)
switch conf.Mode {
case 0:
@ -73,16 +70,9 @@ func splitTasks(conf *Config) []ITask {
}
ptask := makeRegisterType(ncurl.ITask).Elem().(ITask)
for _, exec := range task.GetPlans() {
switch v := exec.(type) {
case *ExecuteAt:
clone := &*v
ptask.AppendPlans(clone)
case *ExecuteInterval:
clone := &*v
ptask.AppendPlans(clone)
}
}
// for _, exec := range task {
// }
ptask.AppendProxies(proxy)
tasks = append(tasks, task)
@ -94,9 +84,9 @@ func splitTasks(conf *Config) []ITask {
// Execute 人的执行所有任务
func (person *Person) Execute() {
for _, task := range person.Tasks {
ExecuteOnPlan(task)
}
// for _, task := range person.Tasks {
// // ExecuteOnPlan(task)
// }
}
// ITask 继承这个接口的类
@ -106,9 +96,6 @@ type ITask interface {
SetCurl(Curl *curl2info.CURL)
GetCurl() *curl2info.CURL
AppendPlans(Plans ...IExecute)
GetPlans() []IExecute
GetProxies() []string
AppendProxies(proxies ...string)
}
@ -119,59 +106,58 @@ type Task struct {
curl *curl2info.CURL
workflow *requests.Workflow
plans []IExecute
proxies []string
}
//
func (task *Task) SetCurl(curl *curl2info.CURL) {
task.curl = curl
}
// func (task *Task) SetCurl(curl *curl2info.CURL) {
// task.curl = curl
// }
func (task *Task) GetCurl() *curl2info.CURL {
return task.curl
}
// func (task *Task) GetCurl() *curl2info.CURL {
// return task.curl
// }
//
func (task *Task) AppendPlans(plans ...IExecute) {
if len(plans) != 0 {
for _, plan := range plans {
task.plans = append(task.plans, plan)
}
}
}
// //
// func (task *Task) AppendPlans(plans ...IExecute) {
// if len(plans) != 0 {
// for _, plan := range plans {
// task.plans = append(task.plans, plan)
// }
// }
// }
func (task *Task) GetPlans() []IExecute {
return task.plans
}
// func (task *Task) GetPlans() []IExecute {
// return task.plans
// }
func (task *Task) AppendProxies(proxies ...string) {
for _, proxy := range proxies {
task.proxies = append(task.proxies, proxy)
}
}
// func (task *Task) AppendProxies(proxies ...string) {
// for _, proxy := range proxies {
// task.proxies = append(task.proxies, proxy)
// }
// }
func (task *Task) GetProxies() []string {
return task.proxies
}
// func (task *Task) GetProxies() []string {
// return task.proxies
// }
// InitTask 生成一个新任务
func InitTask(task ITask, Curl *curl2info.CURL, Plans ...IExecute) {
// // InitTask 生成一个新任务
// func InitTask(task ITask, Curl *curl2info.CURL, Plans ...IExecute) {
// task.Conf = NewConfig(conf)
task.SetCurl(Curl)
task.AppendPlans(Plans...)
}
// // task.Conf = NewConfig(conf)
// task.SetCurl(Curl)
// task.AppendPlans(Plans...)
// }
// ExecuteOnPlan 按照计划执行任务并返回结果
func ExecuteOnPlan(task ITask) {
for _, exec := range task.GetPlans() {
if exec.TimeTo() >= 0 {
task.Execute() // 事件 在这里变化
exec.CalculateTrigger()
}
}
}
// // ExecuteOnPlan 按照计划执行任务并返回结果
// func ExecuteOnPlan(task ITask) {
// for _, exec := range task.GetPlans() {
// if exec.TimeTo() >= 0 {
// task.Execute() // 事件 在这里变化
// exec.CalculateTrigger()
// }
// }
// }
// Execute 根据curl信息执行, TODO: 通用方法设置, 多太实现
// func (task *Task) Execute() {

View File

@ -33,12 +33,12 @@ func (tt *Toutiao) Execute() {
}
func TestExecutePlan(t *testing.T) {
person := NewPerson("test.yaml")
// person := NewPerson("test.yaml")
time.Sleep(time.Second * 2)
for _, task := range person.Tasks.GetLoopValues() {
task.GetValue().(*Task).ExecuteOnPlan()
}
// for _, task := range person.Tasks.GetLoopValues() {
// task.GetValue().(*Task).ExecuteOnPlan()
// }
}

View File

@ -1,16 +1,15 @@
session : 1
mode : 0
# proxies : "socks5://10.10.10.1:8080" // 支持, 列表 与 单项字符串
proxies : ["socks5://10.10.10.1:8080", "socks5://10.10.10.1:8082", "socks5://10.10.10.1:8083", "socks5://10.10.10.1:8085", "socks5://10.10.10.1:8087", "socks5://10.10.10.1:8088", "socks5://10.10.10.1:8089", "socks5://10.10.10.1:8090"]
retry : 0
timeout: 12
priority : 10000
# curls: "@test.curl"
# curls: "@test.curl" // 支持, 列表 与 单项字符串
curls : ["@test.curl", "curl 'https://segmentfault.com/a/1190000004850183' -H 'authority: segmentfault.com' -H 'cache-control: max-age=0' -H 'upgrade-insecure-requests: 1' -H 'user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1' -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: zh' -H 'cookie: _ga=GA1.2.923011700.1533555581; PHPSESSID=web1~3uf1ijg2h8nctqdof7aa27g1p8; Hm_lvt_e23800c454aa573c0ccb16b52665ac26=1542945905,1542945911,1542967158,1542969075; Hm_lpvt_e23800c454aa573c0ccb16b52665ac26=1542969075' --compressed"]
# next_do : "doothers"
device : "eson-OnePlus"
platform : "Android"
area_cc : 4401
@ -19,13 +18,4 @@ media : 55
spider_id : 73
catch_account_id : 123
execute_interval: # 时间间隔执行
sec: 2
execute_at : # (-1, -1, -1, -1, -1, 30) 时间 每30秒执行 min 设置20的时候 = 每小时的20分钟30秒执行
year : -1
month : -1
day : -1
hour : -1
min : -1
sec : 30
crontab: "2s"