├── common
├── compute
│ ├── doc.go
│ ├── compute.go
│ └── functions.go
├── doc.go
├── .DS_Store
├── time.go
├── rpc.go
├── hash.go
├── global.go
├── dataparam.go
└── mapdata.go
├── dispatcher
├── doc.go
├── summary.go
└── dispatcher.go
├── input
├── .DS_Store
├── testinput
│ ├── testinput_test.go
│ └── testinput.go
├── csvinput
│ ├── csvinput_test.go
│ └── csvinput.go
└── input.go
├── service
├── .DS_Store
├── tcp
│ ├── tcpclient.go
│ └── tcp.go
├── http
│ ├── httpclient.go
│ └── http.go
├── cell
│ ├── column.go
│ ├── order.go
│ ├── cell.go
│ └── time.go
├── online
│ ├── online.go
│ ├── session.go
│ └── client.go
├── service.go
├── rpc
│ └── rpc.go
└── protocol
│ └── protocol.go
├── README.md
├── test.csv
├── cache
├── doc.go
├── cache_test.go
├── timecache_test.go
├── redis_test.go
├── cache.go
├── redis.go
└── timecache.go
├── main_test.go
├── relation.xml
├── .gitignore
├── log
├── log.xml
└── log.go
├── config
├── relation.go
├── rpc.go
├── config_test.go
└── config.go
├── config.xml
├── main.go
├── rpc.xml
└── LICENSE
/common/compute/doc.go:
--------------------------------------------------------------------------------
1 | //公式计算
2 | package compute
3 |
--------------------------------------------------------------------------------
/common/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | 公共模块
3 | */
4 | package common
5 |
--------------------------------------------------------------------------------
/dispatcher/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | 还没有想到更好的方法
3 | */
4 | package dispatcher
5 |
--------------------------------------------------------------------------------
/common/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seefan/monitor/HEAD/common/.DS_Store
--------------------------------------------------------------------------------
/input/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seefan/monitor/HEAD/input/.DS_Store
--------------------------------------------------------------------------------
/service/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seefan/monitor/HEAD/service/.DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | monitor
2 | =======
3 | 对分钟级汇总数据进行处理,组合成不同维度的指标,并支持以TCP或WEBSOCKET方式推送给客户端。
4 |
5 | 实时数据监控
6 |
--------------------------------------------------------------------------------
/common/compute/compute.go:
--------------------------------------------------------------------------------
1 | package compute
2 |
3 | import (
4 | //"github.com/robertkrimen/otto"
5 | )
6 |
--------------------------------------------------------------------------------
/test.csv:
--------------------------------------------------------------------------------
1 | 1,2,3,1,2012-11-11 12:00
2 | 2,4,5,10,2012-11-11 12:00
3 | 1,2,3,3,2012-11-11 12:00
4 | 2,4,5,100,2012-11-11 12:00
--------------------------------------------------------------------------------
/cache/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | System:Period:[数据类型ID]=数据时间粒度
3 | [时间]:[数据类型ID]:[网元ID]=该网元指标
4 | System:Relation:[ID]=该节点下属所有子节点ID的数组
5 | */
6 | package cache
7 |
--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "monitor/input/testinput"
5 | "testing"
6 | )
7 |
8 | func Test(t *testing.T) {
9 | testinput.Test_TestInput(t)
10 | }
11 |
--------------------------------------------------------------------------------
/relation.xml:
--------------------------------------------------------------------------------
1 | BSC1110100113100
--------------------------------------------------------------------------------
/cache/cache_test.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | //import (
4 | // "testing"
5 | // "time"
6 | //)
7 |
8 | //func Test_Set(t *testing.T) {
9 | // Set("test1", time.Now())
10 | // t.Log("set test1")
11 | //}
12 |
13 | //func Test_Get(t *testing.T) {
14 | // _, ok := Get("test1")
15 | // if !ok {
16 | // t.Error("not found test1")
17 | // }
18 | //}
19 |
--------------------------------------------------------------------------------
/common/time.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | //按时间粒度取时间的格式
4 | func GetTimeFormat(period int) string {
5 | switch period {
6 | case 1, 5:
7 | return "2006-01-02 15:04"
8 | case 10, 15, 30:
9 | return "2006-01-02 15:0"
10 | case 60:
11 | return "2006-01-02 15"
12 | case 1440:
13 | return "2006-01-02"
14 | default:
15 | return "2006-01-02 15:04"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 | test
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 | monitor
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | main_test.go
21 | .DS_Store
22 | *.exe
23 |
--------------------------------------------------------------------------------
/common/rpc.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | type RPCResponseSql struct {
8 | Cols map[string]int
9 | Rows [][]interface{}
10 | }
11 | type RPCRequestSql struct {
12 | Id string
13 | Param map[string]interface{}
14 | Params []map[string]interface{}
15 | }
16 |
17 | func (this *RPCRequestSql) ToString() string {
18 | return fmt.Sprintf("Sql Id:%s\tParam:%v", this.Id, this.Param)
19 | }
20 |
--------------------------------------------------------------------------------
/service/tcp/tcpclient.go:
--------------------------------------------------------------------------------
1 | package tcp
2 |
3 | import (
4 | "monitor/log"
5 | "monitor/service/online"
6 | "net"
7 | )
8 |
9 | //tcp client
10 | type TcpClient struct {
11 | online.Client
12 | Connect *net.TCPConn
13 | }
14 |
15 | func (this *TcpClient) Close() {
16 | this.Client.Close()
17 | if this.Connect != nil {
18 | if err := this.Connect.Close(); err != nil {
19 | log.Errorf("client %s close with error:%s", this.Name, err.Error())
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/input/testinput/testinput_test.go:
--------------------------------------------------------------------------------
1 | package testinput
2 |
3 | import (
4 | "monitor/config"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func Test_TestInput(t *testing.T) {
10 | c, err := config.Read("../../config.xml")
11 | if err != nil {
12 | t.Error(err.Error())
13 | }
14 | for _, cfg := range c.Inputs.TestInputs {
15 | test := New(cfg)
16 | go func() {
17 | for out := range test.Output {
18 | t.Log(out)
19 | }
20 | }()
21 | test.Start()
22 | time.Sleep(time.Second)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/service/http/httpclient.go:
--------------------------------------------------------------------------------
1 | package http
2 |
3 | import (
4 | "code.google.com/p/go.net/websocket"
5 | "monitor/log"
6 | "monitor/service/online"
7 | )
8 |
9 | //http client
10 | type HttpClient struct {
11 | online.Client
12 | Connect *websocket.Conn
13 | }
14 |
15 | func (this *HttpClient) Close() {
16 | this.Client.Close()
17 | if this.Connect != nil {
18 | if err := this.Connect.Close(); err != nil {
19 | log.Errorf("client %s close with error:%s", this.Name, err.Error())
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/input/csvinput/csvinput_test.go:
--------------------------------------------------------------------------------
1 | package csvinput
2 |
3 | import (
4 | "monitor/common"
5 | "monitor/config"
6 | "testing"
7 | )
8 |
9 | func Test_TestInput(t *testing.T) {
10 | c, err := config.Read("../../config.xml")
11 | if err != nil {
12 | t.Error(err.Error())
13 | }
14 |
15 | for _, cfg := range c.Inputs.CsvInputs {
16 | test := New(cfg)
17 | common.IsRun = true
18 | test.Config.FileName = "../../test.csv"
19 | test.Start()
20 | for o := range test.Output {
21 | println(o.Time)
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/cache/timecache_test.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | //import (
4 | // "monitor/common"
5 | // "monitor/config"
6 | // "monitor/input/testinput"
7 | // "testing"
8 | //)
9 |
10 | //func Test_Add(t *testing.T) {
11 | // mc := NewTimeCache("test", "CID")
12 | // c, err := config.Read("../config.xml")
13 | // if err != nil {
14 | // t.Fatal(err.Error())
15 | // }
16 | // Init(c, nil)
17 | // input := testinput.New(c.Inputs.TestInputs[0])
18 | // common.IsRun = true
19 | // input.Start()
20 | // for rows := range input.Output {
21 | // mc.Add(rows)
22 | // }
23 | //}
24 |
--------------------------------------------------------------------------------
/common/hash.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | func HashCode(in string) int32 {
8 | // Initialize output
9 | var hash int32
10 | // Empty string has a hashcode of 0
11 | if len(in) == 0 {
12 | return hash
13 | }
14 | // Convert string into slice of bytes
15 | b := []byte(in)
16 | // Build hash
17 | for i := range b {
18 | char := b[i]
19 | hash = ((hash << 5) - hash) + int32(char)
20 | }
21 | return hash
22 | }
23 | func HashString(in string) string {
24 | code := HashCode(in)
25 | return strconv.Itoa(int(code))
26 | }
27 |
--------------------------------------------------------------------------------
/common/global.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "runtime"
5 | "time"
6 | )
7 |
8 | var (
9 | MaxCPU int //最大可用的CPU数
10 | StoreMinute time.Duration = 2
11 | StoreCount int = 20
12 | IsRun bool //系统是否正在运行
13 | IsDebug bool //是否为调试状态
14 | )
15 |
16 | const (
17 | TimeFormat string = "2006-01-02 15:04"
18 | MiniTimeFormat string = "02:15:04"
19 | NewLine byte = '\n'
20 | BufferSize = 1024
21 | )
22 |
23 | func init() {
24 | MaxCPU = runtime.NumCPU() - 1
25 | }
26 |
--------------------------------------------------------------------------------
/log/log.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/service/cell/column.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | import (
4 | "monitor/cache"
5 | "monitor/common"
6 | "monitor/common/compute"
7 | // "monitor/log"
8 | "time"
9 | )
10 |
11 | //单时间点,多指标,单网元
12 | //取一个网元指定时间的多指标,主要用于查询某网元的详细指标
13 | func GetColumnsData(dp *common.DataParam, t time.Time) *common.OutputParam {
14 | tmp := []*common.DataRow{}
15 | period := 1
16 | if p, ok := cache.Get(cache.FormatKey("System", "Period", dp.DataKey)); ok {
17 | period = compute.AsInt(p)
18 | }
19 | //key := cache.FormatKey(t.Format(common.GetTimeFormat(period)), dp.DataKey, dp.LimitId[0])
20 | //log.Info("id=%s", key)
21 | if r, ok := cache.GetDataRow(dp.DataKey, t.Format(common.GetTimeFormat(period)), dp.LimitId[0]); ok {
22 | tmp = append(tmp, r)
23 | }
24 |
25 | //限制输出
26 | result := new(common.OutputParam)
27 | if len(tmp) > 0 {
28 | result.RowMap = make(map[string]int)
29 | for i, k := range dp.OutputKey {
30 | result.RowMap[k] = i
31 | }
32 | result.Time = time.Now()
33 | result.Rows = append(result.Rows, createRow(dp, tmp[0]))
34 | }
35 | return result
36 | }
37 |
--------------------------------------------------------------------------------
/config/relation.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/xml"
5 | "io/ioutil"
6 | "os"
7 | )
8 |
9 | //数据关系,用于标记一类数据的父子关系
10 | type RelationChild struct {
11 | Id string
12 | Childs []string `xml:"Childs>Child"`
13 | }
14 |
15 | //数据关系配置
16 | type RelationConfig struct {
17 | Relations []*RelationParent `xml:"Relations>Relation"`
18 | }
19 |
20 | //数据关系,用于标记一类数据的父子关系的集合
21 | type RelationParent struct {
22 | Id string
23 | Childs []*RelationChild `xml:"Childs>Child"`
24 | }
25 |
26 | //从文件中读取出Config
27 | func ReadRelation(file string) (*RelationConfig, error) {
28 | content, err := ioutil.ReadFile(file)
29 | if err != nil {
30 | return nil, err
31 | }
32 | var result RelationConfig
33 | err = xml.Unmarshal(content, &result)
34 | if err != nil {
35 | return nil, err
36 | }
37 | return &result, nil
38 | }
39 |
40 | //将Config保存为文件
41 | func (this *RelationConfig) Write(file string) error {
42 | bt, err := xml.Marshal(this)
43 | if err != nil {
44 | return err
45 | }
46 | if err = ioutil.WriteFile(file, bt, os.ModePerm); err != nil {
47 | return err
48 | }
49 | return nil
50 | }
51 |
--------------------------------------------------------------------------------
/input/input.go:
--------------------------------------------------------------------------------
1 | package input
2 |
3 | import (
4 | "monitor/common"
5 | "monitor/config"
6 | "monitor/log"
7 | )
8 |
9 | type InputStartFace interface {
10 | Start()
11 | GetConfig() *config.Input
12 | GetOutput() chan *common.DataRows
13 | }
14 | type InputFace struct {
15 | Output chan *common.DataRows
16 | RowMap *common.DataRowMap
17 | }
18 |
19 | func (this *InputFace) InitColumn(c *config.Expression, k []string) {
20 | this.RowMap = new(common.DataRowMap)
21 | if c == nil {
22 | log.Infoln("output column is nil")
23 | return
24 | }
25 | i := 0
26 | this.RowMap.Key = make(map[string]int)
27 | if c.Sum != nil {
28 | this.RowMap.Sum = make(map[string]int)
29 | for _, v := range c.Sum {
30 | this.RowMap.Sum[v] = i
31 | this.RowMap.Key[v] = i
32 | i = i + 1
33 | }
34 | }
35 | if c.Max != nil {
36 | this.RowMap.Max = make(map[string]int)
37 | for _, v := range c.Max {
38 | this.RowMap.Key[v] = i
39 | this.RowMap.Max[v] = i
40 | i = i + 1
41 | }
42 | }
43 | if c.Min != nil {
44 | this.RowMap.Min = make(map[string]int)
45 | for _, v := range c.Min {
46 | this.RowMap.Key[v] = i
47 | this.RowMap.Min[v] = i
48 | i = i + 1
49 | }
50 | }
51 |
52 | if k != nil {
53 | for _, v := range k {
54 | this.RowMap.Key[v] = i
55 | i = i + 1
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/config/rpc.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/xml"
5 | "io/ioutil"
6 | "os"
7 | )
8 |
9 | type RPCConfig struct {
10 | DataBases []*DataBase `xml:"DataBases>DataBase"` //多个数据源
11 | Sqls []*Sql `xml:"Sqls>Sql"` //Sql配置,根据id决定不同的功能
12 | RPC *Service //对外的RPC服务
13 | }
14 |
15 | //数据库配置
16 | type DataBase struct {
17 | Id string `xml:"id,attr"`
18 | DSN string
19 | Host string
20 | Port int
21 | SID string
22 | User string
23 | Password string
24 | IsPrimary bool //是否是主库
25 | }
26 |
27 | //Sql结构
28 | type Sql struct {
29 | Id string //
30 | SqlString string //
31 | Params []string `xml:"Params>Param"` //param name
32 | From string //dbid from database
33 | }
34 |
35 | //从文件中读取出Config
36 | func ReadRPC(file string) (*RPCConfig, error) {
37 | content, err := ioutil.ReadFile(file)
38 | if err != nil {
39 | return nil, err
40 | }
41 | var result RPCConfig
42 | err = xml.Unmarshal(content, &result)
43 | if err != nil {
44 | return nil, err
45 | }
46 | return &result, nil
47 | }
48 |
49 | //将Config保存为文件
50 | func (this *RPCConfig) Write(file string) error {
51 | bt, err := xml.Marshal(this)
52 | if err != nil {
53 | return err
54 | }
55 | if err = ioutil.WriteFile(file, bt, os.ModePerm); err != nil {
56 | return err
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 | 192.168.8.113637912trueCIDSTART_TIMEN2N3N6falsetrueCID2006-01-02 15:041START_TIMECIDSTART_TIMEN2N3N6truetrueCID2006-01-02 15:041test.csvACELLBSCCIDSTART_TIMEtrue0.0.0.06780true0.0.0.06781true0.0.0.06782true30true
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | // "io/ioutil"
5 | "monitor/cache"
6 | "monitor/common"
7 | "monitor/config"
8 | "monitor/dispatcher"
9 | "runtime"
10 | "time"
11 | )
12 |
13 | func main() {
14 | runtime.GOMAXPROCS(common.MaxCPU)
15 | println("cpu num is ", common.MaxCPU)
16 | cfg, err := config.Read("config.xml")
17 | if err != nil {
18 | panic(err.Error())
19 | }
20 | rcfg, err := config.ReadRelation("relation.xml")
21 | if err != nil {
22 | panic(err.Error())
23 | }
24 | cache.Init(cfg, rcfg)
25 | LoadBaseData()
26 | dispatcher.Start(cfg)
27 | time.Sleep(time.Hour)
28 | //if str, err := ioutil.ReadFile("config/config.xml"); err == nil {
29 | // ioutil.WriteFile("config.xml", str, os.ModePerm)
30 | //} else {
31 | // panic(err.Error())
32 | //}//
33 |
34 | //test.Test_Dispatcher()
35 | //test.Test_MutiCache()
36 | //test.Test_Push()
37 | //test.Test_WebSocket()
38 | a, err := time.Parse(common.TimeFormat, "2014-02-12 12:59")
39 | b, err := time.Parse(common.TimeFormat, "2014-02-12 13:00")
40 | println(a.Before(b), err)
41 | println(a.After(b))
42 | println(a.Sub(b))
43 | println(a.String())
44 | println(b.String())
45 | }
46 | func LoadBaseData() {
47 | cache.Set("Relation:1", []string{"1", "2", "3", "4", "5"})
48 | cache.Set("Relation:11", []string{"1", "2", "3", "4", "5"})
49 | cache.Set("Relation:2", []string{"1", "2", "3", "4", "5"})
50 | }
51 |
--------------------------------------------------------------------------------
/rpc.xml:
--------------------------------------------------------------------------------
1 | 192.168.8.1131521orcldsamdsamtrueacellSELECT sum(n2) n2,sum(n3) n3,sum(n6) n6,cid, to_char( start_time,'yyyy-mm-dd hh24:mi') start_time from DS_PRD_A_CELL_M t where start_time>=to_date(':start_time','yyyy-mm-dd hh24:mi:ss') and start_time<to_date(':end_time','yyyy-mm-dd hh24:mi:ss') group by cid,to_char( start_time,'yyyy-mm-dd hh24:mi')start_timeend_timedb113loginSELECT USER_PWD FROM CFG_USER_G WHERE USER_NAME = :loginloginall_nodeSELECT SCENEID, NEID FROM CFG_SCENE_NEinsert_nodeinsert into CFG_SCENE_NE (SCENEID, NEID) values(:parentid,:childid) parentidchildiddelete_nodedelete from CFG_SCENE_NE where SCENEID=:parentid and NEID=:childid) parentidchildid0.0.0.06782true
--------------------------------------------------------------------------------
/service/cell/order.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | import (
4 | "monitor/cache"
5 | "monitor/common"
6 | // "monitor/log"
7 | "monitor/common/compute"
8 | "sort"
9 | "time"
10 | )
11 |
12 | var (
13 | //Config *config.Config
14 | )
15 |
16 | //单时间点,单指标,多网元
17 | //主要查多个网元指定时间的排序
18 | func GetOrderData(dp *common.DataParam, t time.Time) *common.OutputParam {
19 | tmp := []*common.DataRow{}
20 | period := 1
21 | if p, ok := cache.Get(cache.FormatKey("System", "Period", dp.DataKey)); ok {
22 | period = compute.AsInt(p)
23 | }
24 | // limit := []string{}
25 | if len(dp.LimitNodeId) > 0 { //双重限制,与LimitId同时起作用
26 |
27 | } else {
28 | for _, v := range dp.LimitId { //取出所有的数据
29 | // key := cache.FormatKey(t.Format(common.GetTimeFormat(period)), dp.DataKey, v)
30 | if r, ok := cache.GetDataRow(dp.DataKey, t.Format(common.GetTimeFormat(period)), v); ok { //都是最新一个时间点的,都在内存
31 | tmp = append(tmp, r)
32 | }
33 | }
34 | }
35 | //排序
36 | sort.Sort(common.ByKey{tmp, dp.OrderKey})
37 | //限制输出
38 | result := new(common.OutputParam)
39 | if len(tmp) > 0 {
40 | result.RowMap = make(map[string]int)
41 | for i, k := range dp.OutputKey {
42 | result.RowMap[k] = i
43 | }
44 | result.Time = time.Now()
45 | }
46 | if dp.OrderBy == common.Desc {
47 | for i := len(tmp); i > 0 && i > len(tmp)-dp.DataRange; i-- {
48 | result.Rows = append(result.Rows, createRow(dp, tmp[i-1]))
49 | }
50 | } else {
51 | for i := 0; i < len(tmp) && i < dp.DataRange; i++ {
52 | result.Rows = append(result.Rows, createRow(dp, tmp[i]))
53 | }
54 | }
55 | return result
56 | }
57 |
--------------------------------------------------------------------------------
/service/cell/cell.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | import (
4 | "monitor/cache"
5 | "monitor/common"
6 | "monitor/log"
7 | "time"
8 | )
9 |
10 | //处理单元入口
11 | func GetData(dp *common.DataParam, t time.Time) *common.OutputParam {
12 | if dp.TimeRange == 1 && len(dp.LimitId) == 1 { //单网元,单时间点,用于指标详细
13 | log.Infoln("开始处理过程,单网元,单时间点,用于指标详细")
14 | return GetColumnsData(dp, t)
15 | } else if dp.TimeRange > 1 && len(dp.LimitId) == 1 { //单网元,多时间点,用于走势图
16 | log.Infoln("开始处理过程,单网元,多时间点,用于走势图")
17 | return GetTimeLineData(dp, t)
18 | } else if dp.TimeRange == 1 && (len(dp.LimitId) > 1 || len(dp.LimitNodeId) > 0) { //单时间点,多网元,用于排行榜
19 | log.Infoln("开始处理过程,单时间点,多网元,用于排行榜")
20 | return GetOrderData(dp, t)
21 | } else { //多时间点,多网元,暂不支持
22 | log.Error("开始处理过程,不被支持的过程", dp.ToString())
23 | return nil
24 | }
25 | }
26 |
27 | //从内部存贮创建一个新行
28 | func createRow(dp *common.DataParam, r *common.DataRow) []interface{} {
29 | var row []interface{}
30 | for _, k := range dp.OutputKey {
31 | if v, ok := r.GetValue(k); ok {
32 | row = append(row, v)
33 | } else { //回填数据,从缓存中按回填key取值,key值在dp中指定
34 | //log.Infoln("key is ", dp.FillKey)
35 | v = getFillValue(dp.FillKey, r, k)
36 | row = append(row, v)
37 | }
38 | }
39 | return row
40 | }
41 |
42 | //回填
43 | func getFillValue(fillKey string, r *common.DataRow, k string) interface{} {
44 | if len(fillKey) > 0 {
45 | key := cache.FormatKey(fillKey, r.GetKey(fillKey), k)
46 | // log.Infoln("key is ", key)
47 | if tv, ok := cache.Get(key); ok {
48 | return tv
49 | }
50 | }
51 | return nil
52 | }
53 | func getStoreCache() *common.DataRow {
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/service/online/online.go:
--------------------------------------------------------------------------------
1 | package online
2 |
3 | import (
4 | "encoding/json"
5 | "monitor/common"
6 | "monitor/log"
7 | "sync"
8 | "time"
9 | )
10 |
11 | var (
12 | onlineClient = make(map[string]*Client)
13 | currentTime time.Time //time from data push
14 | lock sync.RWMutex
15 | )
16 |
17 | //通知所有客户端有时间消息
18 | func SendTimeMessageToAll(tm *common.TimeMessage) {
19 | log.Infoln("send message is ", tm.Time, tm.Key, len(onlineClient))
20 | for _, c := range onlineClient {
21 | go c.Processing(tm)
22 | }
23 | }
24 |
25 | //取得当前时间
26 | func GetCurrentTime() time.Time {
27 | lock.RLock()
28 | defer lock.RUnlock()
29 | return currentTime
30 | }
31 |
32 | //设置当前时间
33 | func SetCurrentTime(t time.Time) {
34 | lock.Lock()
35 | defer lock.Unlock()
36 | currentTime = t
37 | }
38 |
39 | //send cmd to all client
40 | func SendToAll(cmd interface{}) {
41 | if jscmd, err := json.Marshal(cmd); err == nil {
42 | for _, c := range onlineClient {
43 | c.SendBytes(jscmd)
44 | }
45 | }
46 | }
47 |
48 | //将客户端加到在线列表
49 | func Set(c *Client) {
50 | if _, ok := onlineClient[c.UUID]; !ok { //说明已存在同名用户
51 | onlineClient[c.UUID] = c
52 | }
53 |
54 | }
55 |
56 | //取指定客户端
57 | func Get(name string) (client *Client, ok bool) {
58 | if v, exists := onlineClient[name]; exists {
59 | client = v
60 | ok = true
61 | }
62 | return nil, false
63 | }
64 |
65 | //将客户端从在线列表中删除
66 | func Delete(name string) {
67 | if c, ok := onlineClient[name]; ok {
68 | var cc ClientClose
69 | cc = c
70 | cc.Close()
71 | delete(onlineClient, name)
72 | log.Infof("client: %s close and remove from online", c.Name)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/service/cell/time.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | import (
4 | "monitor/cache"
5 | "monitor/common"
6 | "monitor/common/compute"
7 | // "monitor/log"
8 | "sort"
9 | "time"
10 | )
11 |
12 | //时间线,单指标,单网元
13 | //查询一段时间内指定网元,某指标的走势
14 | func GetTimeLineData(dp *common.DataParam, t time.Time) *common.OutputParam {
15 | tmp := []*common.DataRow{}
16 | //log.Info("timeline @", dp.Id, t)
17 | period := 1
18 | if p, ok := cache.Get(cache.FormatKey("System", "Period", dp.DataKey)); ok {
19 | period = compute.AsInt(p)
20 | }
21 | for i := 0; i < dp.TimeRange; i++ { //取出所有的数据
22 | tmpTime := t.Add(time.Minute * time.Duration(-i))
23 | // key := cache.FormatKey(tmpTime.Format(common.GetTimeFormat(period)), dp.DataKey, dp.LimitId[0])
24 | //log.Info("search key is ", key)
25 | if r, ok := cache.GetDataRow(dp.DataKey, tmpTime.Format(common.GetTimeFormat(period)), dp.LimitId[0]); ok {
26 | tmp = append(tmp, r)
27 | //log.Infoln("find cache", r.Row)
28 | }
29 | }
30 | //log.Info("result1")
31 | //排序
32 | sort.Sort(common.ByTime{tmp})
33 | //log.Info("result2")
34 | result := new(common.OutputParam)
35 | if len(tmp) > 0 {
36 | result.RowMap = make(map[string]int)
37 | for i, k := range dp.OutputKey {
38 | result.RowMap[k] = i
39 | }
40 | result.Time = t
41 | }
42 | //log.Info("result3")
43 | //限制输出
44 | //start := 0
45 | //if len(tmp) >= dp.DataRange {
46 | // start = len(tmp) - dp.DataRange
47 | //}
48 | //for i := start; i < len(tmp); i++ {
49 | // result.Rows = append(result.Rows, createRow(dp, tmp[i]))
50 | //}
51 | for _, r := range tmp {
52 | result.Rows = append(result.Rows, createRow(dp, r))
53 | }
54 | //log.Info("result")
55 | return result
56 | }
57 |
--------------------------------------------------------------------------------
/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | //"sync"
5 | // "monitor/cache"
6 | "monitor/common"
7 | "monitor/config"
8 | "monitor/service/http"
9 | "monitor/service/online"
10 | "monitor/service/rpc"
11 | "monitor/service/tcp"
12 | "time"
13 | "uway/log"
14 | )
15 |
16 | var (
17 | changes = make(chan *common.TimeMessage, 10)
18 | PushDelay float64 = 60
19 | delayTime time.Time = time.Now().Truncate(time.Second)
20 | )
21 |
22 | //启用服务
23 | func Start(conf *config.Config) {
24 | if conf.Tcp.Enable {
25 | tcp.StartTcpService(conf.Tcp.Host, conf.Tcp.Port)
26 | }
27 | if conf.Http.Enable {
28 | http.StartHttpService(conf.Http.Host, conf.Http.Port)
29 | }
30 | PushDelay = conf.PushDelay
31 | rpc.ServerIP = conf.RPC.Host
32 | rpc.Port = conf.RPC.Port
33 | go Run()
34 | log.Info("service is starting...")
35 | }
36 |
37 | func Run() {
38 | go timeTick()
39 | for tm := range changes {
40 | if len(tm.Key) != 0 && online.GetCurrentTime().Before(tm.Time) { //push time
41 | online.SetCurrentTime(tm.Time)
42 | }
43 |
44 | //两种情况,实时推送(pushdelay==0 ,tm是由数据推送的,key不为空);定时推送(pushdelay>0 并超时,不考虑key);
45 | if PushDelay == 0 && len(tm.Key) != 0 || PushDelay > 0 && time.Since(delayTime).Seconds() >= PushDelay {
46 | delayTime = time.Now()
47 | tm.Time = online.GetCurrentTime()
48 | //log.Infof("now push data @ ", tm.Key, tm.Time)
49 | online.SendTimeMessageToAll(tm)
50 | }
51 | }
52 | }
53 | func Close() {
54 | close(changes)
55 | }
56 |
57 | //定时产生时间点
58 | func timeTick() {
59 | times := time.Tick(time.Second * 10)
60 | for common.IsRun {
61 | <-times
62 | tm := &common.TimeMessage{}
63 | changes <- tm
64 | }
65 | }
66 |
67 | //通知时间点变化
68 | func MessageTimeChange(t *common.TimeMessage) {
69 | //log.Info("time change", t.Time)
70 | changes <- t
71 | }
72 |
--------------------------------------------------------------------------------
/cache/redis_test.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "fmt"
5 | "monitor/common"
6 | "monitor/config"
7 | "strings"
8 | "testing"
9 | )
10 |
11 | func Test_Redis(t *testing.T) {
12 | cfg, err := config.Read("../config.xml")
13 | if err != nil {
14 | t.Fatal(err.Error())
15 | }
16 | Init(cfg, nil)
17 | if c, err := GetClient("d"); err != nil {
18 | t.Error(err.Error())
19 | } else {
20 | RedisStatus()
21 | if result, err := c.Redis.AllKeys(); err == nil {
22 | t.Log("allkey", strings.Join(result, ","))
23 | } else {
24 | t.Error(err.Error())
25 | }
26 | if err := c.Redis.Set("test", []byte("dfdsiedskioe")); err != nil {
27 | t.Error(err.Error())
28 | }
29 | if ss, err := c.Redis.Get("test"); err != nil {
30 | t.Error(err.Error())
31 | } else {
32 | t.Log("test=", string(ss))
33 | }
34 | //for i := 0; i < 100000; i++ {
35 | // c.Redis.Hset("tt", fmt.Sprintf("%s", i), []byte("test"))
36 | //}
37 | //for i := 0; i < 100000; i++ {
38 | // c.Redis.Set(fmt.Sprintf("teaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadsafsdafdsfdsafasdfdsafdsafsadfasfasfasfasfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaast%s", i), []byte("test"))
39 | //}
40 | c.Close()
41 |
42 | for i := 0; i < 100000; i++ {
43 | if cc, err := GetClient(fmt.Sprintf("%s", i)); err != nil {
44 | t.Error(err.Error())
45 | } else {
46 | cc.Redis.Set(fmt.Sprintf("%s:%d", "teaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadsafsdafdsfdsafasdfdsafdsafsadfasfasfasfasfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaast", i), []byte("test"))
47 | cc.Close()
48 | }
49 |
50 | }
51 |
52 | }
53 |
54 | }
55 | func Test_Code(t *testing.T) {
56 | row := &common.DataRow{}
57 | row.Row = append(row.Row, 1)
58 | row.RowMap = new(common.DataRowMap)
59 | row.RowMap.Key = make(map[string]int)
60 | row.RowMap.Key["id"] = 0
61 | v, _ := encode(*row)
62 | vr, _ := decode(v)
63 | println("vr=", vr.RowMap.Key)
64 | }
65 |
--------------------------------------------------------------------------------
/service/online/session.go:
--------------------------------------------------------------------------------
1 | package online
2 |
3 | import (
4 | "monitor/log"
5 | "time"
6 | )
7 |
8 | var (
9 | sessions = make(map[string]*Session) //存贮在线用户的uid和key
10 | )
11 |
12 | const Expired = 60 * 30
13 |
14 | //init
15 | func init() {
16 | log.Info("Session is started,expired every %d seconds", Expired)
17 | go func() {
18 | var i int
19 | timer := time.Tick(time.Minute)
20 | for t := range timer {
21 | i = 0
22 | lock.Lock()
23 | for k, s := range sessions {
24 | if s.IsExpired() {
25 | delete(sessions, k)
26 | i = i + 1
27 | }
28 | }
29 | lock.Unlock()
30 | log.Infof("clear %d sessions at %v,used %v", i, t, time.Since(t))
31 | }
32 | }()
33 | }
34 |
35 | //web专用session
36 | type Session struct {
37 | UID string
38 | Name string
39 | Key string
40 | LastTime time.Time
41 | }
42 |
43 | //是否过期
44 | func (this *Session) IsExpired() bool {
45 | return time.Since(this.LastTime).Seconds() > Expired
46 | }
47 |
48 | //设置值
49 | func SetSession(uid string, name string, key string) {
50 | s := &Session{UID: uid, Key: key, LastTime: time.Now()}
51 | lock.Lock()
52 | defer lock.Unlock()
53 | sessions[uid] = s
54 | }
55 |
56 | //取取,如果过期,串也为空
57 | func GetSession(id string) (name string, key string, ok bool) {
58 | lock.RLock()
59 | defer lock.RUnlock()
60 |
61 | if s, exists := sessions[id]; exists {
62 | key = s.Key
63 | name = s.Name
64 | ok = true
65 | s.LastTime = time.Now()
66 | }
67 | //log.Info("get session id ", id, name, key, ok)
68 | return
69 | }
70 |
71 | //更新Session的过期时间
72 | func FreshSession(id string) {
73 | lock.Lock()
74 | defer lock.Unlock()
75 | if s, ok := sessions[id]; ok {
76 | s.LastTime = time.Now()
77 | }
78 | }
79 |
80 | //删除一个Session
81 | func RemoveSession(id string) {
82 | lock.Lock()
83 | defer lock.Unlock()
84 | if _, ok := sessions[id]; ok {
85 | delete(sessions, id)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/log/log.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | //lg "code.google.com/p/log4go"
5 | "log"
6 | )
7 |
8 | func init() {
9 | //lg.LoadConfiguration("log.xml")
10 | }
11 |
12 | // Tracef formats message according to format specifier
13 | // and writes to default logger with log level = Trace.
14 | func Tracef(format string, params ...interface{}) {
15 | //lg.Trace(format, params...)
16 | log.Printf(format, params...)
17 | }
18 |
19 | // Debugf formats message according to format specifier
20 | // and writes to default logger with log level = Debug.
21 | func Debugf(format string, params ...interface{}) {
22 | log.Printf(format, params...)
23 | }
24 |
25 | // Infof formats message according to format specifier
26 | // and writes to default logger with log level = Info.
27 | func Infof(format string, params ...interface{}) {
28 | log.Printf(format, params...)
29 | }
30 |
31 | // Warnf formats message according to format specifier and writes to default logger with log level = Warn
32 | func Warnf(format string, params ...interface{}) {
33 | log.Printf(format, params...)
34 | }
35 |
36 | // Errorf formats message according to format specifier and writes to default logger with log level = Error
37 | func Errorf(format string, params ...interface{}) {
38 | log.Printf(format, params...)
39 | }
40 |
41 | // Trace formats message using the default formats for its operands and writes to default logger with log level = Trace
42 | func Trace(v ...interface{}) {
43 | log.Print(v)
44 | }
45 |
46 | // Debug formats message using the default formats for its operands and writes to default logger with log level = Debug
47 | func Debug(v ...interface{}) {
48 | log.Print(v)
49 | }
50 |
51 | // Info formats message using the default formats for its operands and writes to default logger with log level = Info
52 | func Info(v ...interface{}) {
53 | log.Print(v)
54 | }
55 | func Infoln(v ...interface{}) {
56 | log.Println(v)
57 | }
58 |
59 | // Warn formats message using the default formats for its operands and writes to default logger with log level = Warn
60 | func Warn(v ...interface{}) {
61 | log.Print(v)
62 | }
63 |
64 | // Error formats message using the default formats for its operands and writes to default logger with log level = Error
65 | func Error(v ...interface{}) {
66 | log.Print(v)
67 | }
68 |
--------------------------------------------------------------------------------
/service/rpc/rpc.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "fmt"
5 | "monitor/log"
6 | // "net/http"
7 | "monitor/common"
8 | "net/rpc"
9 | )
10 |
11 | var (
12 | ServerIP string
13 | Port int
14 | )
15 |
16 | //执行特定id的sql,并返回数据
17 | func BySql(id string) (map[string]int, [][]interface{}, error) {
18 | if client, err := rpc.DialHTTP("tcp", fmt.Sprintf("%s:%d", ServerIP, Port)); err != nil {
19 | var reply = new(common.RPCResponseSql)
20 | args := common.RPCRequestSql{Id: id}
21 | err = client.Call("Oracle.BySql", args, &reply)
22 | if err != nil {
23 | log.Error("bysql error:", err)
24 | }
25 | return reply.Cols, reply.Rows, err
26 | } else {
27 | log.Errorf("rcp %s:%d error", ServerIP, Port, err.Error())
28 | }
29 | return nil, nil, fmt.Errorf("sql %s not exists", id)
30 | }
31 |
32 | //执行带参数的特定id的sql,并返回数据
33 | func BySqlParamName(id string, p map[string]interface{}) (map[string]int, [][]interface{}, error) {
34 | if client, err := rpc.DialHTTP("tcp", fmt.Sprintf("%s:%d", ServerIP, Port)); err != nil {
35 | var reply = new(common.RPCResponseSql)
36 | args := common.RPCRequestSql{Id: id, Param: p}
37 | err = client.Call("Oracle.BySqlParamName", args, &reply)
38 | if err != nil {
39 | log.Error("bysql error:", err)
40 | }
41 | return reply.Cols, reply.Rows, err
42 | } else {
43 | log.Errorf("rcp %s:%d error", ServerIP, Port, err.Error())
44 | }
45 | return nil, nil, fmt.Errorf("sql %s not exists", id)
46 | }
47 |
48 | //执行带参数的特定id的sql
49 | func ExecSqlParamName(id string, p map[string]interface{}) error {
50 | if client, err := rpc.DialHTTP("tcp", fmt.Sprintf("%s:%d", ServerIP, Port)); err != nil {
51 | var reply = new(common.RPCResponseSql)
52 | args := common.RPCRequestSql{Id: id, Param: p}
53 | return client.Call("Oracle.ExecuteByParamName", args, &reply)
54 | } else {
55 | return fmt.Errorf("rcp %s:%d error", ServerIP, Port, err.Error())
56 | }
57 | }
58 |
59 | //批量执行带参数的特定id的sql
60 | func BatchExecSqlParamName(id string, p []map[string]interface{}) error {
61 | if client, err := rpc.DialHTTP("tcp", fmt.Sprintf("%s:%d", ServerIP, Port)); err != nil {
62 | var reply = new(common.RPCResponseSql)
63 | args := common.RPCRequestSql{Id: id, Params: p}
64 | return client.Call("Oracle.BatchExecuteByParamName", args, &reply)
65 | } else {
66 | return fmt.Errorf("rcp %s:%d error", ServerIP, Port, err.Error())
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/cache/cache.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "fmt"
5 | "monitor/common"
6 | "monitor/config"
7 | "monitor/log"
8 | // "sync"
9 | )
10 |
11 | var (
12 | // lock sync.RWMutex
13 | TimeChanged func(*common.TimeMessage)
14 | staticCache = make(map[string]interface{})
15 | timeCache = make(map[string]*TimeCache)
16 | )
17 |
18 | //取缓存的数据行,如果在内部缓存中没有就到外部缓存找。前提是时间已过期
19 | func GetDataRow(dataKey string, timeKey string, id string) (*common.DataRow, bool) {
20 | //lock.RLock()
21 | //defer lock.RUnlock()
22 | if data, ok := timeCache[dataKey]; ok {
23 | if v, ok := data.Get(timeKey, id); ok {
24 | //println("neibu cache")
25 | return v.(*common.DataRow), true
26 | } else { //在内部缓存没有找到,在外部缓存查找
27 | //println("waibu cache")
28 | if !data.ExistsTime(timeKey) { //内部时间已过期
29 | //println("waibu store cache")
30 | return getFromStore(FormatStoreKey(dataKey, id), timeKey)
31 | }
32 | }
33 | }
34 | return nil, false
35 | }
36 |
37 | //get key value
38 | func Get(key string) (interface{}, bool) {
39 | if v, ok := staticCache[key]; ok {
40 | return v, true
41 | }
42 | return nil, false
43 | }
44 | func Set(key string, value interface{}) {
45 | staticCache[key] = value
46 | }
47 |
48 | //按固定格式连接关键字,组成key
49 | func FormatKey(inKeyType string, inkeyValue interface{}, outKeyName string) string {
50 | return fmt.Sprintf("%s:%v:%s", inKeyType, inkeyValue, outKeyName)
51 | }
52 |
53 | //外面用key
54 | func FormatStoreKey(inKeyType string, inkeyValue interface{}) string {
55 | return fmt.Sprintf("%s:%v", inKeyType, inkeyValue)
56 | }
57 |
58 | func CloseCache() {
59 | for _, v := range timeCache {
60 | v.Close()
61 | }
62 | }
63 |
64 | //增加一条记录
65 | func AddRowsToCache(nodeName string, rows *common.DataRows) {
66 | //log.Infof("receive %s--%s", nodeName, rows.Time)
67 | if c, ok := timeCache[nodeName]; ok {
68 | c.Add(rows)
69 | }
70 | //log.Infof("end receive %s--%s", nodeName, rows.Time)
71 | }
72 |
73 | //初始化数据关系
74 | func initRelation(cfg *config.RelationConfig) {
75 | for _, r := range cfg.Relations {
76 | Set(FormatKey("System", "Relation", r.Id), r.Childs)
77 | }
78 | }
79 |
80 | //初始化缓存
81 | func Init(cfg *config.Config, rcfg *config.RelationConfig) {
82 | log.Info("cache initing")
83 | initRedis(cfg.Redises)
84 | initTimeCache(cfg)
85 | if rcfg != nil {
86 | initRelation(rcfg)
87 | }
88 | }
89 | func addTimeCache(input *config.Input) {
90 | if input.Enable && input.EnableStore {
91 | c := NewTimeCache(input.Id, input.PrimaryKey.Value)
92 | timeCache[input.Id] = c
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/input/csvinput/csvinput.go:
--------------------------------------------------------------------------------
1 | package csvinput
2 |
3 | import (
4 | "io/ioutil"
5 | "monitor/common"
6 | "monitor/config"
7 | "monitor/input"
8 | "monitor/log"
9 | "strconv"
10 | "strings"
11 | "time"
12 | )
13 |
14 | type CsvInput struct {
15 | input.InputFace
16 | Config *config.CsvInput
17 | TIME time.Time
18 | }
19 |
20 | //start oracle face
21 | func New(cfg *config.CsvInput) (db *CsvInput) {
22 | db = new(CsvInput)
23 | db.Config = cfg
24 | db.TIME = time.Now().Truncate(time.Minute)
25 | db.Output = make(chan *common.DataRows, 100)
26 | db.InitColumn(cfg.OutputColumn, cfg.OutputKey)
27 | return
28 | }
29 |
30 | func (this *CsvInput) Start() {
31 | go this.Run()
32 | }
33 | func (this *CsvInput) GetOutput() chan *common.DataRows {
34 | return this.Output
35 | }
36 | func (this *CsvInput) GetConfig() *config.Input {
37 | return &this.Config.Input
38 | }
39 |
40 | //run service
41 | func (this *CsvInput) Run() {
42 | log.Infoln("CsvInput is starting")
43 | log.Infof("open file %s", this.Config.FileName)
44 |
45 | g := float64(this.Config.Period)
46 | if bts, err := ioutil.ReadFile(this.Config.FileName); err == nil {
47 | strs := strings.Split(string(bts), "\n")
48 | for common.IsRun {
49 | beginget := time.Now()
50 | mod := this.TIME.Minute() % 3
51 | // log.Infof("w count is %d", mod+1)
52 | for i := 0; i <= mod; i++ {
53 | row := new(common.DataRows)
54 | row.Time = time.Now().Format(this.Config.TimeFormat)
55 | row.RowMap = this.RowMap
56 | for _, str := range strs {
57 | if this.Config.OutputColumn == nil {
58 | log.Infoln("output column is nil")
59 | break
60 | }
61 | cols := strings.Split(str, ",")
62 | var r []interface{}
63 | for _, c := range cols {
64 | if d, err := strconv.ParseFloat(c, 10); err == nil {
65 | r = append(r, d)
66 | } else {
67 | r = append(r, 0)
68 | }
69 | }
70 | row.Rows = append(row.Rows, r)
71 | }
72 | this.Output <- row
73 | //log.Infof("push row is %v", row.Time)
74 | }
75 | //计算下一次运行的时间
76 |
77 | this.TIME = this.TIME.Add(time.Minute * time.Duration(g))
78 | log.Infof("next time is %v", this.TIME)
79 | sec := time.Since(beginget).Seconds()
80 |
81 | //计算下一次运行的时间
82 | if sec < g*60 { //如果一个处理周期内处理完,就延时,没有处理完就立即执行,如果正式运行期间还需要进行时间纠正
83 | log.Infof("sleep seconds %f", (g*60)-sec)
84 | time.Sleep(time.Second * (time.Duration(g*60 - sec))) //sleep one minute-sec
85 | }
86 | }
87 |
88 | } else {
89 | log.Error(err.Error())
90 | }
91 | close(this.Output)
92 | }
93 |
--------------------------------------------------------------------------------
/common/dataparam.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "encoding/json"
5 | "monitor/common/compute"
6 | "monitor/log"
7 | "time"
8 | )
9 |
10 | const (
11 | Desc = iota
12 | Asc
13 | )
14 |
15 | func (this *RequestData) GetString(key string) string {
16 | if v, ok := this.Param[key]; ok {
17 | return compute.AsString(v)
18 | }
19 | return ""
20 | }
21 | func (this *RequestData) GetStringArray(key string) []string {
22 | if v, ok := this.Param[key]; ok {
23 | if v, ok := v.([]interface{}); ok {
24 | re := []string{}
25 | for _, s := range v {
26 | re = append(re, compute.AsString(s))
27 | }
28 | return re
29 | } else {
30 | log.Infof("param %s value error", key, v)
31 | }
32 | }
33 | return nil
34 | }
35 | func (this *RequestData) GetFloat(key string) float64 {
36 | if v, ok := this.Param[key]; ok {
37 | compute.AsFloat64(v)
38 | }
39 | return -1
40 | }
41 | func (this *RequestData) GetInt64(key string) int64 {
42 | if v, ok := this.Param[key]; ok {
43 | return compute.AsInt64(v)
44 | }
45 | return -1
46 | }
47 | func (this *RequestData) GetInt(key string) int {
48 | if v, ok := this.Param[key]; ok {
49 | return compute.AsInt(v)
50 | }
51 | return -1
52 | }
53 |
54 | //input data
55 | type RequestData struct {
56 | Pid int
57 | Param map[string]interface{}
58 | }
59 |
60 | // response data
61 | type ResponseData struct {
62 | Pid int //协议号
63 | Status int //状态
64 | Output interface{} //输出内容
65 | Param interface{}
66 | }
67 |
68 | //数据推送请求参数
69 | type DataParam struct {
70 | FillKey string //回填from的key,不指定就不进行回填
71 | TimeRange int //时间点个数
72 | DataRange int //最多显示多少条数据,多时间点时,此项无效
73 | DataKey string //取哪类数据,与数据节点id对应
74 | LimitId []string //限制只取哪几个id的数据
75 | LimitNodeId string //限制父节点id,与LimitId互斥 [排行榜专用]
76 | OrderKey string //排序指标 [排行榜专用]
77 | OrderBy int //排序方式, [排行榜专用]
78 | OutputKey []string //输出的字段
79 | Id string //请求ID,在客户端要区分开,不同的请求使用不同的id,在不需要时要删除该推送请求
80 | }
81 |
82 | func (this *DataParam) ToString() string {
83 | if s, err := json.Marshal(this); err == nil {
84 | return string(s)
85 | } else {
86 | return err.Error()
87 | }
88 |
89 | }
90 |
91 | //数据输出结构
92 | type OutputParam struct {
93 | Time time.Time
94 | RowMap map[string]int
95 | Rows [][]interface{}
96 | }
97 |
98 | //func (this *DataParam) Hashcode() int32 {
99 | // tostring := fmt.Sprint(this.GetType, this.DataRange, this.DataType, strings.Join(this.LimitId, ":"), this.PrimaryKey, this.Order, strings.Join(this.OutputKey, ":"), this.Id)
100 | // return hash.HashCode(tostring)
101 | //}
102 |
103 | //用于提示时间消息的结构
104 | type TimeMessage struct {
105 | Key string
106 | Time time.Time
107 | }
108 |
--------------------------------------------------------------------------------
/service/online/client.go:
--------------------------------------------------------------------------------
1 | package online
2 |
3 | import (
4 | "encoding/json"
5 | "monitor/common"
6 | "monitor/log"
7 | "monitor/service/cell"
8 | "sync"
9 | "time"
10 | )
11 |
12 | //client
13 | type Client struct {
14 | Name string
15 | OutputChan chan []byte
16 | IsRun bool //是否正在运行
17 | IsLogin bool //是否已登陆
18 | IsPlayBack bool //是否是处于回放状态
19 | PlayBackTime time.Time //回放时下发数据时间点
20 | dataParams map[string]*common.DataParam
21 | lock sync.RWMutex
22 | UUID string
23 | LastUpdateTime time.Time
24 | Key string
25 | }
26 |
27 | func (this *Client) UpdateTime() {
28 | this.LastUpdateTime = time.Now()
29 | FreshSession(this.UUID)
30 | }
31 |
32 | //客户端关闭接口
33 | type ClientClose interface {
34 | Close()
35 | }
36 |
37 | //send cmd to client
38 | func (this *Client) Send(cmd *common.ResponseData) {
39 | if jscmd, err := json.Marshal(cmd); err == nil {
40 | this.OutputChan <- jscmd
41 | }
42 | }
43 |
44 | //read cmd from byte
45 | func (this *Client) Read(data []byte, cmd *common.RequestData) {
46 | if err := json.Unmarshal(data, cmd); err != nil {
47 | log.Error("cmd format error,cmd string is %s,error is %s", string(data), err.Error())
48 | }
49 | }
50 |
51 | //注册一个新的推送类型
52 | func (this *Client) AddRequest(req *common.DataParam) {
53 | this.lock.Lock()
54 | if this.dataParams == nil {
55 | this.dataParams = make(map[string]*common.DataParam)
56 | }
57 | this.dataParams[req.Id] = req
58 | this.lock.Unlock()
59 | //println("register request", req.Id)
60 | }
61 |
62 | //删除一个推送类型
63 | func (this *Client) RemoveRequest(reqId string) {
64 | //println("delete request", reqId)
65 | this.lock.Lock()
66 | delete(this.dataParams, reqId)
67 | this.lock.Unlock()
68 | }
69 |
70 | //send cmd to client
71 | func (this *Client) SendBytes(jscmd []byte) {
72 | this.OutputChan <- jscmd
73 | }
74 |
75 | //新收到一个时间点变化,收集数据并下发
76 | func (this *Client) Processing(tm *common.TimeMessage) {
77 | //首先看本类数据是需要下发,查字典
78 | //println("get time message is ", tm.Time.String(), tm.Key)
79 | this.lock.RLock()
80 | for _, v := range this.dataParams {
81 | //log.Info("dataparams is ", v.DataKey == tm.Key, len(tm.Key) == 0)
82 | if v.DataKey == tm.Key || len(tm.Key) == 0 { //同类数据
83 | //log.Info("celling is ", v.Id)
84 | go this.celling(v, tm.Time)
85 | }
86 | }
87 | this.lock.RUnlock()
88 | }
89 |
90 | //处理各类推送数据
91 | func (this *Client) celling(dp *common.DataParam, t time.Time) {
92 | re := cell.GetData(dp, t)
93 | //log.Info("get dp %s is ", dp.Id, re != nil)
94 | if re != nil {
95 | out := common.ResponseData{2, 0, re, dp}
96 | this.Send(&out)
97 | }
98 | }
99 |
100 | //客户端关闭方法
101 | func (this *Client) Close() {
102 | close(this.OutputChan)
103 | }
104 |
--------------------------------------------------------------------------------
/input/testinput/testinput.go:
--------------------------------------------------------------------------------
1 | package testinput
2 |
3 | import (
4 | "math/rand"
5 | "monitor/common"
6 | "monitor/config"
7 | "monitor/input"
8 | "monitor/log"
9 | "time"
10 | )
11 |
12 | type TestInput struct {
13 | input.InputFace
14 | Config *config.TestInput
15 | TIME time.Time
16 | }
17 |
18 | //start oracle face
19 | func New(cfg *config.TestInput) (db *TestInput) {
20 | db = new(TestInput)
21 | db.Config = cfg
22 | db.Output = make(chan *common.DataRows, 100)
23 | db.TIME = time.Now().Truncate(time.Minute)
24 | db.InitColumn(cfg.OutputColumn, cfg.OutputKey)
25 | return
26 | }
27 | func (this *TestInput) Start() {
28 | go this.Run()
29 | }
30 | func (this *TestInput) GetOutput() chan *common.DataRows {
31 | return this.Output
32 | }
33 | func (this *TestInput) GetConfig() *config.Input {
34 | return &this.Config.Input
35 | }
36 |
37 | //run service
38 | func (this *TestInput) Run() {
39 | log.Infoln("TestInput is starting")
40 | g := float64(this.Config.Period)
41 | for common.IsRun {
42 | beginget := time.Now()
43 | for i := 0; i < 60; i++ {
44 | row := new(common.DataRows)
45 | row.RowMap = this.RowMap
46 | if this.Config.OutputColumn == nil {
47 | log.Infoln("output column is nil")
48 | break
49 | }
50 | rnd := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
51 | for j := 0; j < 100000; j++ {
52 | var r []interface{}
53 | if this.Config.OutputColumn.Sum != nil {
54 | for k := 0; k < len(this.Config.OutputColumn.Sum); k++ {
55 | r = append(r, rnd.Float32())
56 | }
57 | }
58 | if this.Config.OutputColumn.Max != nil {
59 | for k := 0; k < len(this.Config.OutputColumn.Max); k++ {
60 | r = append(r, rnd.Float32())
61 | }
62 | }
63 | if this.Config.OutputColumn.Min != nil {
64 | for k := 0; k < len(this.Config.OutputColumn.Min); k++ {
65 | r = append(r, rnd.Float32())
66 | }
67 | }
68 |
69 | if this.Config.OutputKey != nil {
70 | for _, k := range this.Config.OutputKey {
71 | var v interface{}
72 | if k == this.Config.TimeKey {
73 | v = time.Now()
74 | } else {
75 | v = i * j
76 | }
77 | r = append(r, v)
78 | }
79 | }
80 | row.Rows = append(row.Rows, r)
81 | }
82 |
83 | row.Time = this.TIME.Format(this.Config.TimeFormat)
84 |
85 | //log.Infof("testinput 正在输出数据...%v", row.Time)
86 | this.Output <- row
87 |
88 | }
89 | //计算下一次运行的时间
90 |
91 | this.TIME = this.TIME.Add(time.Minute * time.Duration(g))
92 | log.Infof("next time is %v", this.TIME)
93 | //计算时间间隔
94 | sec := time.Since(beginget).Seconds()
95 |
96 | //计算下一次运行的时间
97 | if sec < g*60 { //如果一个处理周期内处理完,就延时,没有处理完就立即执行,如果正式运行期间还需要进行时间纠正
98 | log.Infof("sleep seconds %f", (g*60)-sec)
99 | time.Sleep(time.Second * (time.Duration(g*60 - sec))) //sleep one minute-sec
100 | }
101 |
102 | }
103 | close(this.Output)
104 | }
105 |
--------------------------------------------------------------------------------
/dispatcher/summary.go:
--------------------------------------------------------------------------------
1 | package dispatcher
2 |
3 | import (
4 | "monitor/cache"
5 | "monitor/common"
6 | "monitor/config"
7 | "monitor/log"
8 | "sync"
9 | )
10 |
11 | //运算结点,每类数据在一个节点运算,这样需要进行读写锁。
12 | type Summary struct {
13 | Config *config.Summary //配置
14 | Output chan *common.DataRows //数据输出流,根据配置由Summary创建
15 | dict map[string][]string //父子关系的逆向字典
16 | Id string
17 | NeedReloadRelation bool //是否需要重新加载父子关系
18 | lock sync.Mutex
19 | }
20 |
21 | //创建一个新的节点
22 | func NewSummary(cfg *config.Summary) *Summary {
23 | log.Infof("node %s is created...", cfg.Id)
24 | n := &Summary{Config: cfg}
25 | n.Id = cfg.Id
26 | n.Output = make(chan *common.DataRows, 100)
27 | n.init()
28 | return n
29 | }
30 |
31 | func (this *Summary) init() {
32 | //处理字典
33 | this.dict = make(map[string][]string)
34 | this.ReloadRelation()
35 | }
36 |
37 | /*
38 | 输入数据(小区)根据逆向字典生成输出(场景)数据(也可以使用协程),每输出(场景)一个协程。(优先,效率高,云计算)
39 | 节点间流转数据结构:包括sum,max,min,count,key等的数组为一条记录。按顺序对应字段名。
40 | 节点只处理单数据源,无需字段描述,输出时带上数据结构或标明数据结构。
41 | */
42 |
43 | //开始一个新的处理过程
44 | func (this *Summary) Write(row *common.DataRows) {
45 | if common.IsRun {
46 | if this.NeedReloadRelation {
47 | this.ReloadRelation()
48 | this.NeedReloadRelation = false
49 | }
50 | // log.Infof("get row count", len(row.Rows))
51 | go this.RunIt(row)
52 | }
53 | }
54 |
55 | //刷新节点关系树
56 | func (this *Summary) ReloadRelation() {
57 | this.lock.Lock()
58 | defer this.lock.Unlock()
59 | if rs, ok := cache.Get("System:Relation:" + this.Id); ok {
60 | if vs, ok := rs.([]*config.RelationChild); ok {
61 | this.dict = make(map[string][]string)
62 | for _, r := range vs { //遍历所有关系表
63 | for _, key := range r.Childs { //遍历所有子key
64 | if v, ok := this.dict[key]; ok { //如果字典中存在就增加
65 | this.dict[key] = append(v, r.Id)
66 | } else {
67 | this.dict[key] = []string{r.Id}
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 | //close node
76 | func (this *Summary) Close() {
77 | close(this.Output)
78 | }
79 | func (this *Summary) RunIt(row *common.DataRows) {
80 | //log.Infoln("收到一批数据", row.Time)
81 | //将每个输入对应到特定的父级数据
82 | rmap := make(map[string]*common.DataRow)
83 | cmap := row.CloneMap()
84 |
85 | for _, v := range row.Rows {
86 | //find key
87 | key := row.GetKey(this.Config.Relation.ChildKey, v)
88 | if pkeys, ok := this.dict[key]; ok {
89 | for _, pkey := range pkeys {
90 | if r, ok := rmap[pkey]; ok {
91 | r.Merge(v)
92 | } else {
93 | rrow := new(common.DataRow)
94 | rrow.RowMap = cmap
95 | rrow.Time = row.Time
96 | rrow.Row = v
97 | rmap[pkey] = rrow
98 | }
99 | }
100 | }
101 | }
102 | //合并输出
103 | result := new(common.DataRows)
104 | result.Time = row.Time
105 | result.RowMap = row.CloneMap()
106 | result.RowMap.Key[this.Config.Relation.PrimaryKey] = cmap.ColumnCount
107 | result.RowMap.ColumnCount = cmap.ColumnCount + 1
108 | for k, v := range rmap {
109 | v.Row = append(v.Row, k)
110 | result.Rows = append(result.Rows, v.Row)
111 | }
112 | if common.IsRun {
113 | log.Info(result)
114 | this.Output <- result
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/service/tcp/tcp.go:
--------------------------------------------------------------------------------
1 | //每个请求会登陆一个Client,如果登陆成功就保存起来,继续运行。每个Client有两个协程,分别读取和写入。
2 | //写入使用一个chanel,保证同步
3 | //每个读写请求一但出错,立即抛弃Client,等待客户端再次登陆。连接会自动关闭
4 | //
5 | //
6 | //
7 |
8 | package tcp
9 |
10 | import (
11 | "bufio"
12 | // "encoding/json"
13 | "fmt"
14 | "net"
15 | // "time"
16 | "code.google.com/p/go-uuid/uuid"
17 | "monitor/common"
18 | "monitor/log"
19 | "monitor/service/online"
20 | "monitor/service/protocol"
21 | )
22 |
23 | var (
24 | maxRead = 1024
25 |
26 | // Message = make(chan time.Time)
27 | )
28 |
29 | //tcp service
30 | type TcpService struct {
31 | Host string
32 | Port int
33 | IsRun bool
34 | }
35 |
36 | //init tcp service
37 | func (this *TcpService) init(host string, port int) {
38 | this.Host = host
39 | this.Port = port
40 | }
41 |
42 | //create new http service
43 | //with host and port
44 | func StartTcpService(host string, port int) {
45 | h := new(TcpService)
46 | h.init(host, port)
47 | go h.Run()
48 |
49 | }
50 |
51 | //tcp service start
52 | func (this *TcpService) Run() {
53 | tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", this.Host, this.Port))
54 | if err != nil {
55 | panic("resolve tcp address error")
56 | }
57 | link, err := net.ListenTCP("tcp", tcpAddr)
58 | if err != nil {
59 | panic("start tcp service error")
60 | }
61 | defer link.Close()
62 | this.IsRun = true
63 | log.Infof("tcp service started at %s:%d", this.Host, this.Port)
64 | for {
65 | conn, err := link.AcceptTCP()
66 | if err != nil {
67 | log.Error(err)
68 | continue
69 | }
70 |
71 | connFrom := conn.RemoteAddr().String()
72 | log.Infof("accept new client from %s\n", connFrom)
73 | c := new(TcpClient)
74 | c.Connect = conn
75 | c.UUID = uuid.New()
76 | c.IsRun = true
77 | c.OutputChan = make(chan []byte, 10)
78 | go this.HandleRequest(c)
79 | go this.HandleResponse(c)
80 | }
81 | }
82 |
83 | //send cmd to client
84 | func (this *TcpService) HandleResponse(c *TcpClient) {
85 | defer online.Delete(c.Name)
86 | c.Connect.SetWriteBuffer(common.BufferSize)
87 | //c.Connect.SetNoDelay(true)
88 | var err error
89 | for cmd := range c.OutputChan {
90 | //cmd := <-c.OutputChan
91 | cmd = append(cmd, '\n')
92 | if _, err = c.Connect.Write(cmd); err != nil {
93 | log.Error("send cmd error and login out,%s\n", err.Error())
94 | c.IsRun = false
95 | break //结束协程
96 | }
97 | }
98 | }
99 |
100 | //handle tcp request
101 | func (this *TcpService) HandleRequest(c *TcpClient) {
102 | defer online.Delete(c.Name)
103 | nr := bufio.NewReader(c.Connect)
104 | var err error
105 | var data []byte
106 | cmd := new(common.RequestData)
107 | for this.IsRun && c.IsRun {
108 | data, err = nr.ReadBytes(common.NewLine) //读取一行数据
109 | if err != nil {
110 | c.IsRun = false
111 | log.Error("tcp service read error", err.Error())
112 | break
113 | }
114 | c.Read(data, cmd)
115 | //if err := json.Unmarshal(data, &cmd); err != nil {
116 | // log.Error("cmd format error,", err.Error())
117 | //}
118 | //log.Info("server cmd", cmd)
119 | protocol.Exec(cmd, &c.Client)
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/config/config_test.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func Test_config(t *testing.T) {
8 | cfg := new(Config)
9 | exp := new(Expression)
10 | exp.Sum = []string{"N2", "N3", "N6"}
11 | baseInput := Input{
12 | Id: "ACELL",
13 | Enable: true,
14 | OutputKey: []string{"CID", "START_TIME"},
15 | OutputColumn: exp,
16 | TimeFormat: "2006-01-02 15:04",
17 | PrimaryKey: &Key{Type: "number", Value: "CID"},
18 | EnableStore: true,
19 | Period: 1,
20 | }
21 | cfg.Inputs = new(DataFace)
22 |
23 | rs := []*Redis{&Redis{Host: "192.168.8.113", Port: 6379, DB: 12, Id: "redis113", Enable: true}}
24 | cfg.Redises = rs
25 |
26 | ga := new(Summary)
27 | ga.Id = "BSC"
28 | ga.From = "ACELL"
29 | ga.TimeName = "START_TIME"
30 | ga.EnableStore = true
31 | ga.Relation = &Relation{ChildKey: "CID", PrimaryKey: "BSC"}
32 | cfg.Summarys = []*Summary{ga}
33 | test := &TestInput{
34 | Input: baseInput,
35 | TimeKey: "START_TIME",
36 | }
37 | test.Enable = false
38 | csv := &CsvInput{
39 | Input: baseInput,
40 | FileName: "test.csv",
41 | }
42 | csv.Enable = true
43 | csv.EnableStore = true
44 | cfg.Inputs.TestInputs = []*TestInput{test}
45 | cfg.Inputs.CsvInputs = []*CsvInput{csv}
46 | cfg.Http = &Service{Host: "0.0.0.0", Port: 6780, Enable: true}
47 | cfg.Tcp = &Service{Host: "0.0.0.0", Port: 6781, Enable: true}
48 | cfg.RPC = &Service{Host: "0.0.0.0", Port: 6782, Enable: true}
49 | cfg.PushDelay = 30
50 | cfg.IsDebug = true
51 | if err := cfg.Write("../config.xml"); err != nil {
52 | t.Error(err.Error())
53 | }
54 | if cfg, err := Read("../config.xml"); err != nil {
55 | t.Error(err.Error())
56 | } else {
57 | t.Log(cfg)
58 | }
59 | }
60 | func Test_rpc(t *testing.T) {
61 | cfg := new(RPCConfig)
62 | cfg.DataBases = []*DataBase{&DataBase{Id: "db113", Host: "192.168.8.113", Port: 1521, User: "dsam", Password: "dsam", SID: "orcl", IsPrimary: true}}
63 | cfg.Sqls = []*Sql{
64 | &Sql{Id: "acell", SqlString: "SELECT sum(n2) n2,sum(n3) n3,sum(n6) n6,cid, to_char( start_time,'yyyy-mm-dd hh24:mi') start_time from DS_PRD_A_CELL_M t where start_time>=to_date(':start_time','yyyy-mm-dd hh24:mi:ss') and start_time 0 {
10 | result = 0
11 | for _, d := range params {
12 | result += AsFloat64(d)
13 | }
14 | }
15 | return
16 | }
17 |
18 | func Div(params ...interface{}) (result float64) {
19 | if len(params) > 0 {
20 | result = AsFloat64(params[0])
21 | for i := 1; i < len(params); i++ {
22 | result -= AsFloat64(params[i])
23 | }
24 | }
25 | return
26 | }
27 | func Max(params ...interface{}) (result float64) {
28 | if len(params) > 0 {
29 | result = AsFloat64(params[0])
30 | for i := 1; i < len(params); i++ {
31 | if d := AsFloat64(params[i]); d > result {
32 | result = d
33 | }
34 | }
35 | }
36 | return
37 | }
38 | func Min(params ...interface{}) (result float64) {
39 | if len(params) > 0 {
40 | result = AsFloat64(params[0])
41 | for i := 1; i < len(params); i++ {
42 | if d := AsFloat64(params[i]); d < result {
43 | result = d
44 | }
45 | }
46 | }
47 | return
48 | }
49 | func AsString(src interface{}) string {
50 | switch v := src.(type) {
51 | case string:
52 | return v
53 | case []byte:
54 | return string(v)
55 | case int8, int16, int32, int64, int, uint, uint16, uint32, uint64, uint8:
56 | return fmt.Sprintf("%d", v)
57 | case float32, float64:
58 | return fmt.Sprintf("%f", v)
59 | }
60 | return fmt.Sprintf("%v", src)
61 | }
62 | func AsFloat64(src interface{}) (result float64) {
63 | result = 0
64 | switch src.(type) {
65 | case float64:
66 | result = src.(float64)
67 | case float32:
68 | result = float64(src.(float32))
69 | case int8:
70 | result = float64(src.(int8))
71 | case int16:
72 | result = float64(src.(int16))
73 | case int32:
74 | result = float64(src.(int32))
75 | case int64:
76 | result = float64(src.(int64))
77 | case uint8:
78 | result = float64(src.(uint8))
79 | case uint16:
80 | result = float64(src.(uint16))
81 | case uint32:
82 | result = float64(src.(uint32))
83 | case uint64:
84 | result = float64(src.(uint64))
85 | default:
86 | if dc, err := strconv.ParseFloat(AsString(src), 10); err == nil {
87 | result = dc
88 | }
89 | }
90 | return
91 | }
92 | func AsInt64(src interface{}) (result int64) {
93 | result = 0
94 | switch src.(type) {
95 | case int8:
96 | result = int64(src.(int8))
97 | case int16:
98 | result = int64(src.(int16))
99 | case int32:
100 | result = int64(src.(int32))
101 | case int64:
102 | result = int64(src.(int64))
103 | case uint8:
104 | result = int64(src.(uint8))
105 | case uint16:
106 | result = int64(src.(uint16))
107 | case uint32:
108 | result = int64(src.(uint32))
109 | case uint64:
110 | result = int64(src.(uint64))
111 | default:
112 | if dc, err := strconv.ParseInt(AsString(src), 10, 64); err == nil {
113 | result = dc
114 | }
115 | }
116 | return
117 | }
118 | func AsInt(src interface{}) (result int) {
119 | result = 0
120 | switch v := src.(type) {
121 | case int8:
122 | result = int(v)
123 | case int16:
124 | result = int(v)
125 | case int32:
126 | result = int(v)
127 | case int64:
128 | result = int(v)
129 | case uint8:
130 | result = int(v)
131 | case uint16:
132 | result = int(v)
133 | case uint32:
134 | result = int(v)
135 | case uint64:
136 | result = int(v)
137 | case float32:
138 | result = int(v)
139 | case float64:
140 | result = int(v)
141 | default:
142 | if dc, err := strconv.ParseInt(AsString(src), 10, 64); err == nil {
143 | result = int(dc)
144 | }
145 | }
146 | return
147 | }
148 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/xml"
5 | "io/ioutil"
6 | "os"
7 | )
8 |
9 | //配置
10 | type Config struct {
11 | Redises []*Redis `xml:"Redises>Redis"` //多个缓存
12 | Inputs *DataFace //多个输入
13 | Summarys []*Summary `xml:"Summarys>Summary"` //多个计算单元
14 | Http *Service //对外的HTTP服务
15 | Tcp *Service //对外的TCP服务
16 | RPC *Service //外部的RPC服务
17 | PushDelay float64 //发送数据的时间间隔,如果设置为0,就进行实时推送,单位为秒
18 | IsDebug bool //是否为调试状态,[调试时直接登陆,不验证密码;]
19 | }
20 |
21 | //将Config转成xml格式
22 | func (this *Config) String() string {
23 | if bt, err := xml.Marshal(this); err == nil {
24 | return string(bt)
25 | } else {
26 | return err.Error()
27 | }
28 | }
29 |
30 | //将Config直接转成字节数组
31 | func (this *Config) Bytes() []byte {
32 | if bt, err := xml.Marshal(this); err == nil {
33 | return bt
34 | }
35 | return nil
36 | }
37 |
38 | //从文件中读取出Config
39 | func Read(file string) (*Config, error) {
40 | content, err := ioutil.ReadFile(file)
41 | if err != nil {
42 | return nil, err
43 | }
44 | var result Config
45 | err = xml.Unmarshal(content, &result)
46 | if err != nil {
47 | return nil, err
48 | }
49 | return &result, nil
50 | }
51 |
52 | //将Config保存为文件
53 | func (this *Config) Write(file string) error {
54 | bt, err := xml.Marshal(this)
55 | if err != nil {
56 | return err
57 | }
58 | if err = ioutil.WriteFile(file, bt, os.ModePerm); err != nil {
59 | return err
60 | }
61 | return nil
62 | }
63 |
64 | //支持的数据输入接口
65 | type DataFace struct {
66 | TcpInputs []*TcpInput
67 | TestInputs []*TestInput
68 | CsvInputs []*CsvInput
69 | RedisInputs []*RedisInput
70 | }
71 |
72 | //一个数据库的输入项
73 | type SqlInput struct {
74 | Input
75 | SqlId string //sql语句,用于查询数据库,注意与输出列对应
76 | }
77 |
78 | //一个测试用的输入项,用于测试系统运行状况
79 | type TestInput struct {
80 | Input
81 | TimeKey string //时间字段的名称,默认为START_TIME,用于推动数据流动
82 | }
83 |
84 | //csv文件输入,用于测试数据准确性
85 | type CsvInput struct {
86 | Input
87 | FileName string //文件名
88 | }
89 |
90 | //从Redis实时接收数据
91 | type RedisInput struct {
92 | Input
93 | From string //redis的id
94 | SubId string //订阅的id
95 | }
96 |
97 | //基础输入参数结构
98 | type Input struct {
99 | Id string `xml:"id,attr"`
100 | OutputKey []string `xml:"OutputKeys>Key"` //输出的关键字段key
101 | OutputColumn *Expression `xml:"OutputColumns>Column"` //输出列
102 | Enable bool //是否启用
103 | EnableStore bool //是否进行存贮
104 | PrimaryKey *Key //主键字段名
105 | TimeFormat string //时间的格式串
106 | Period int //粒度,以分钟为单位,重复输入的时间间隔。主要用于处理时间字段。
107 | }
108 |
109 | //tcp的输入参数
110 | type TcpInput struct {
111 | Input
112 | InputKey []string //输入字段的列名,按位置对应
113 | }
114 |
115 | //通用key,value
116 | type Key struct {
117 | Type string `xml:"type,attr"` //类型,包括int float time string
118 | Value string
119 | }
120 |
121 | //汇总单元,对数据按指定规则进行汇聚
122 | type Summary struct {
123 | Id string `xml:"id,attr"`
124 | From string
125 | Outputs []*Key `xml:"Outputs>Output"` //输出的key
126 | Relation *Relation //该计算单元所有需要计算的,类似bsc->cell
127 | TimeName string //时间字段的名称,默认为START_TIME,用于推动数据流动
128 | EnableStore bool //是否进行存贮
129 | }
130 |
131 | //对应的父子关系
132 | type Relation struct {
133 | PrimaryKey string //主键名
134 | ChildKey string //对应子级的字段名,与PrimaryKey联合使用
135 | }
136 |
137 | //指标计算方式
138 | type Expression struct {
139 | Sum []string
140 | Max []string
141 | Min []string
142 | }
143 |
144 | //Redis配置
145 | type Redis struct {
146 | Id string `xml:"id,attr"`
147 | Host string
148 | Port int
149 | Password string
150 | DB int //DB号
151 | Enable bool //是否启用
152 | }
153 |
154 | //Tcp服务
155 | type Service struct {
156 | Host string //ip地址
157 | Port int //端口
158 | Enable bool //是否启用
159 | }
160 |
--------------------------------------------------------------------------------
/dispatcher/dispatcher.go:
--------------------------------------------------------------------------------
1 | package dispatcher
2 |
3 | import (
4 | "monitor/cache"
5 | "monitor/common"
6 | "monitor/config"
7 | "monitor/input"
8 | "monitor/input/csvinput"
9 | "monitor/input/testinput"
10 | "monitor/log"
11 | "monitor/service"
12 | )
13 |
14 | var (
15 | inputs = make(map[string]input.InputStartFace)
16 | summarys = make(map[string]*Summary)
17 | link = make(map[string][]string)
18 | )
19 |
20 | //启动处理引擎
21 | func Start(conf *config.Config) {
22 | log.Infof("dispatcher is starting")
23 | common.IsRun = true
24 | common.IsDebug = conf.IsDebug
25 | createDataPeriod(conf)
26 | service.Start(conf)
27 | createInput(conf)
28 | createSummary(conf)
29 | //cron.Start(conf)
30 | Run()
31 | }
32 |
33 | //创建数据类型与时间粒度的关系
34 | func createDataPeriod(conf *config.Config) {
35 | for _, c := range conf.Inputs.CsvInputs {
36 | createPeriodFromInput(&c.Input)
37 | }
38 | for _, c := range conf.Inputs.RedisInputs {
39 | createPeriodFromInput(&c.Input)
40 | }
41 | for _, c := range conf.Inputs.TcpInputs {
42 | createPeriodFromInput(&c.Input)
43 | }
44 | for _, c := range conf.Inputs.TestInputs {
45 | createPeriodFromInput(&c.Input)
46 | }
47 | }
48 | func createPeriodFromInput(conf *config.Input) {
49 | cache.Set(cache.FormatKey("System", "Period", conf.Id), conf.Period)
50 | }
51 |
52 | //关闭处理引擎
53 | func Close() {
54 | common.IsRun = false
55 | for k, _ := range inputs {
56 | delete(inputs, k)
57 | }
58 | for k, _ := range summarys {
59 | delete(summarys, k)
60 | }
61 | cache.CloseCache()
62 | }
63 |
64 | //按配置把输入和输出连接起来
65 | func Run() {
66 | //接收时间通知事件,将时间变化通知给服务
67 | cache.TimeChanged = service.MessageTimeChange
68 |
69 | for k, v := range inputs {
70 | go runInput(k, v)
71 | }
72 | for k, v := range summarys {
73 | go runSummary(k, v)
74 | }
75 |
76 | }
77 |
78 | //处理数据输入流
79 | func runInput(inputName string, in input.InputStartFace) {
80 | begin:
81 | out := in.GetOutput()
82 | for r := range out { //收到一条输出数据,两种选择,一是传给需要的节点,二是保存到缓存
83 | go func(r *common.DataRows) {
84 | if ns, ok := link[inputName]; ok { //如果有节点需要本节点的输出
85 | for _, fname := range ns { //给所有节点
86 | summarys[fname].Write(r) //写数据
87 | }
88 | }
89 | //如果需要保存到CACHE,这里处理
90 | //log.Infof("input is %v", in.GetConfig().EnableStore, inputName, r.Time)
91 | cache.AddRowsToCache(inputName, r)
92 | }(r)
93 | }
94 | if common.IsRun { //如果程序还没有结束,就重新启动INPUT
95 | in.Start()
96 | goto begin
97 | }
98 | }
99 |
100 | //处理节点去运算
101 | func runSummary(inputName string, node *Summary) {
102 | for rows := range node.Output { //接收数据
103 | if ns, ok := link[inputName]; ok { //如果有节点需要本节点的输出
104 | for _, fname := range ns { //给所有节点
105 | summarys[fname].Write(rows) //写数据
106 | }
107 | }
108 | //如果需要保存到CACHE,这里处理
109 | // log.Infof("node is %v", node.Config.EnableStore, inputName, rows.Time)
110 |
111 | cache.AddRowsToCache(inputName, rows)
112 |
113 | }
114 | }
115 |
116 | //按参数创建所有的输入,并加到一个字典里
117 | func createSummary(conf *config.Config) {
118 | for _, n := range conf.Summarys {
119 | summarys[n.Id] = NewSummary(n) //创建一个节点
120 | makeLink(n.Id)
121 | }
122 | }
123 |
124 | //创建节点间,数据输入流单的映射关系
125 | func makeLink(id string) {
126 | for _, fn := range summarys { //处理节点之间的引用关系
127 | if fn.Config.From == id { //找到引用当前节点的节点
128 | if ns, ok := link[id]; ok {
129 | ns = append(ns, fn.Id)
130 | link[id] = ns
131 | } else {
132 | link[id] = []string{fn.Id}
133 | }
134 | }
135 | }
136 | }
137 |
138 | //按参数创建所有的输入,并加到一个字典里
139 | func createInput(conf *config.Config) {
140 | for _, s := range conf.Inputs.TestInputs {
141 | if !s.Enable {
142 | continue
143 | }
144 | in := testinput.New(s)
145 | in.Start()
146 |
147 | inputs[s.Id] = in
148 | makeLink(s.Id)
149 | }
150 | for _, s := range conf.Inputs.CsvInputs {
151 | if !s.Enable {
152 | continue
153 | }
154 | in := csvinput.New(s)
155 | in.Start()
156 |
157 | inputs[s.Id] = in
158 | makeLink(s.Id)
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/cache/redis.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "bytes"
5 | "container/ring"
6 | "encoding/gob"
7 | "fmt"
8 | "github.com/alphazero/Go-Redis"
9 | "monitor/common"
10 | "monitor/config"
11 | "monitor/log"
12 | "sync"
13 | "time"
14 | )
15 |
16 | var (
17 | pool = make(map[string]chan *RedisClient)
18 | errRedis = make(map[string]*config.Redis) //重连机制,定时重试创建全新的chan,成功后从这里删除
19 | keyToClient = make(map[byte]string)
20 | ids *ring.Ring
21 | redislock sync.RWMutex
22 | )
23 |
24 | func RedisStatus() {
25 | fmt.Println("pool size:", len(pool))
26 | }
27 |
28 | type RedisClient struct {
29 | Redis redis.Client
30 | Id string
31 | }
32 |
33 | //id是否可用
34 | type IdAvailable struct {
35 | Id string
36 | IsAvailable bool
37 | }
38 |
39 | func (this *RedisClient) Close() {
40 | //println("close client at %s", this.Id)
41 | pool[this.Id] <- this
42 | }
43 |
44 | const (
45 | PoolSize = 4
46 | )
47 |
48 | //初始化redis连接池
49 | func initRedis(confs []*config.Redis) {
50 | ids = ring.New(len(confs))
51 | for _, conf := range confs {
52 | if !conf.Enable {
53 | continue
54 | }
55 | id := &IdAvailable{Id: conf.Id}
56 | ids.Value = id
57 | spec := redis.DefaultSpec().Db(conf.DB).Password(conf.Password).Host(conf.Host).Port(conf.Port)
58 | //log.Info("redis init at ", spec)
59 | c := make(chan *RedisClient, PoolSize)
60 | for j := 0; j < PoolSize; j++ {
61 | if client, err := redis.NewSynchClientWithSpec(spec); err != nil {
62 | goto err
63 | } else {
64 | c <- &RedisClient{Id: conf.Id, Redis: client}
65 | }
66 | }
67 | pool[conf.Id] = c
68 | id.IsAvailable = true
69 | ids = ids.Next()
70 | err: //如果创建CLIENT时出错,就抛弃这个台机器
71 | errRedis[conf.Id] = conf
72 | ids = ids.Next()
73 | }
74 | }
75 |
76 | //取一个client的id,如果没有映射就要新增
77 | func getId(key string) (string, error) {
78 | bytes := []byte(key)
79 | if len(bytes) > 0 {
80 | redislock.Lock()
81 | defer redislock.Unlock()
82 | b := bytes[0]
83 | //log.Info("client b is %s", b)
84 | if id, ok := keyToClient[b]; ok { //如果有记录有对应关系
85 | //log.Info("client id is %s", id)
86 | return id, nil
87 | } else {
88 | //首先检查是否
89 | for tid := ids.Value; tid != nil && tid.(*IdAvailable).IsAvailable; ids = ids.Next() {
90 | id = tid.(*IdAvailable).Id
91 | //log.Info("client id is %s", id)
92 | break
93 | }
94 | return id, nil
95 | }
96 | }
97 | return "", fmt.Errorf("error get id with %s", key)
98 | }
99 |
100 | //创建一个连接
101 | func GetClient(key string) (*RedisClient, error) {
102 | if id, err := getId(key); err == nil {
103 | if c, ok := pool[id]; ok {
104 | timeout := time.After(time.Second * 10)
105 | //client := <-c
106 |
107 | select {
108 | case client := <-c:
109 | //log.Info("get client id is %s", client.Id)
110 | return client, nil
111 | case <-timeout:
112 | return nil, fmt.Errorf("get connection time out")
113 | }
114 | return <-c, nil
115 | }
116 | }
117 | return nil, fmt.Errorf("get client error ")
118 | }
119 |
120 | //将数据保存到外部缓存中,同时也支持从外部缓存取数据,在这里进行屏蔽,其它地方不操作外部缓存
121 | //保存到外部存贮
122 | func saveToStore(key string, value *common.DataRow, expired int) {
123 | //begin := time.Now()
124 | if len(pool) > 0 {
125 | if c, err := GetClient(value.Time); err == nil {
126 | defer c.Close()
127 | if bs, err := encode(*value); err == nil {
128 | c.Redis.Hset(key, value.Time, bs)
129 | c.Redis.Expire(key, int64(expired*60)) //秒
130 | }
131 | } else {
132 | log.Error("get client error,begin recycle", err.Error())
133 | //回收连接,过段时间再试
134 | }
135 | }
136 | //diff := time.Since(begin)
137 | //log.Infof("save to store at %v seconds", diff.Seconds())
138 | }
139 |
140 | //从外部取数据
141 | func getFromStore(key string, keyTime string) (*common.DataRow, bool) {
142 | if c, err := GetClient(keyTime); err == nil {
143 | defer c.Close()
144 | if bts, err := c.Redis.Hget(key, keyTime); err == nil {
145 | if r, err := decode(bts); err == nil {
146 | return r, true
147 | }
148 | }
149 | }
150 | return nil, false
151 | }
152 |
153 | //数据编码
154 | func encode(vs common.DataRow) ([]byte, error) {
155 | buff := new(bytes.Buffer)
156 | enc := gob.NewEncoder(buff)
157 | err := enc.Encode(vs)
158 | return buff.Bytes(), err
159 | }
160 |
161 | //数据解码
162 | func decode(vs []byte) (*common.DataRow, error) {
163 | buff := bytes.NewBuffer(vs)
164 | dec := gob.NewDecoder(buff)
165 | result := new(common.DataRow)
166 | err := dec.Decode(result)
167 | return result, err
168 | }
169 |
--------------------------------------------------------------------------------
/cache/timecache.go:
--------------------------------------------------------------------------------
1 | /*
2 | 针对不同的数据类型,采用不同的缓存方式
3 | 1、普通不过期缓存,使用Set和Get方法统计处理,三段式Key值
4 | 2、定时过期数据,按StoreMinute进行存贮,过期清理
5 | */
6 | package cache
7 |
8 | import (
9 | "monitor/common"
10 | "monitor/common/compute"
11 | "monitor/config"
12 | "monitor/log"
13 | "sync"
14 | "time"
15 | )
16 |
17 | type TimeCache struct {
18 | Id string
19 | KeyName string //数据列字段名
20 | input chan *common.DataRows //数据输入窗口
21 | caches map[string]*TimeCacheItem //所有的缓存数据
22 | lock sync.RWMutex
23 | }
24 | type TimeCacheItem struct {
25 | Cache map[string]interface{}
26 | ExpiredTime time.Time
27 | }
28 |
29 | func (this *TimeCache) init() {
30 | this.input = make(chan *common.DataRows, 100)
31 | this.caches = make(map[string]*TimeCacheItem)
32 | }
33 | func (this *TimeCache) ExistsTime(timeKey string) bool {
34 | this.lock.RLock()
35 | defer this.lock.RUnlock()
36 | _, ok := this.caches[timeKey]
37 | return ok
38 | }
39 | func (this *TimeCache) Get(timeKey string, id string) (interface{}, bool) {
40 | this.lock.RLock()
41 | defer this.lock.RUnlock()
42 | if item, ok := this.caches[timeKey]; ok {
43 | if value, ok := item.Cache[id]; ok {
44 | return value, true
45 | }
46 | }
47 | return nil, false
48 | }
49 |
50 | //创建一个新的缓存实例
51 | func NewTimeCache(id string, keyName string) *TimeCache {
52 | c := &TimeCache{Id: id, KeyName: keyName}
53 | c.Start()
54 | return c
55 | }
56 |
57 | //启动实例
58 | func (this *TimeCache) Start() {
59 | this.init()
60 | go this.Run()
61 | go this.Clean(30)
62 | }
63 |
64 | //清理过期数据
65 | func (this *TimeCache) Clean(s time.Duration) {
66 | tc := time.Tick(time.Second * s)
67 | for t := range tc {
68 | ts := []string{}
69 | for k, v := range this.caches {
70 | if v.ExpiredTime.Before(t) {
71 | // log.Infof("data %s is expired %v,now is %s", k, v.ExpiredTime, t.String())
72 | ts = append(ts, k)
73 | }
74 | }
75 | this.lock.Lock()
76 | for _, k := range ts {
77 | delete(this.caches, k)
78 | }
79 | this.lock.Unlock()
80 | }
81 | }
82 |
83 | //close it
84 | func (this *TimeCache) Close() {
85 | close(this.input)
86 | }
87 |
88 | //增加一条数据
89 | func (this *TimeCache) Add(rows *common.DataRows) {
90 | // log.Info("receive new rows@", rows.Time)
91 | this.input <- rows
92 | }
93 |
94 | //运行计算
95 | func (this *TimeCache) Run() {
96 | log.Infoln("cache is started", this.Id)
97 | period := 1
98 | if p, ok := Get(FormatKey("System", "Period", this.Id)); ok {
99 | period = compute.AsInt(p)
100 | }
101 | timeFormat := common.GetTimeFormat(period)
102 |
103 | for rs := range this.input { //接收数据
104 | //log.Infof("time changed ", rs.Time)
105 | currTime, err := time.Parse(timeFormat, rs.Time)
106 | if err != nil {
107 | log.Error("cache time format is error", err.Error())
108 | currTime = time.Now()
109 | }
110 | cacheItem, ok := this.caches[rs.Time] //这个时间点是否已记录
111 | if !ok {
112 | cacheItem = &TimeCacheItem{Cache: make(map[string]interface{})}
113 | this.caches[rs.Time] = cacheItem
114 | cacheItem.ExpiredTime = time.Now().Add(time.Minute * common.StoreMinute)
115 | }
116 |
117 | for _, vs := range rs.Rows { //分析每一条数据
118 | row := rs.CreateDataRow(vs)
119 | key := row.GetKey(this.KeyName)
120 | //log.Infoln("add to cache", key)
121 | this.lock.Lock()
122 | if r, ok := cacheItem.Cache[key]; ok { //如果同类数据已存在,就进行数据合并
123 | row.Merge(r.(*common.DataRow).Row)
124 | }
125 | cacheItem.Cache[key] = row
126 | this.lock.Unlock()
127 | //log.Infoln(key, row.Row)
128 | // log.Info("key=%s", key)
129 | go saveToStore(FormatStoreKey(this.Id, key), row, period*common.StoreCount) //保存到外部存贮,并保存粒度*保存个数分钟
130 | }
131 |
132 | //通知外到有新数据到来
133 | if TimeChanged != nil {
134 | TimeChanged(&common.TimeMessage{Key: this.Id, Time: currTime})
135 | }
136 | }
137 | }
138 |
139 | func CountCache() int {
140 | re := 0
141 | for _, v := range timeCache {
142 | for _, cv := range v.caches {
143 | re += len(cv.Cache)
144 | }
145 | }
146 | return re
147 | }
148 |
149 | //初始化时间缓存节点
150 | func initTimeCache(cfg *config.Config) {
151 | for _, input := range cfg.Inputs.CsvInputs {
152 | addTimeCache(&input.Input)
153 | }
154 | for _, input := range cfg.Inputs.RedisInputs {
155 | addTimeCache(&input.Input)
156 | }
157 | for _, input := range cfg.Inputs.TcpInputs {
158 | addTimeCache(&input.Input)
159 | }
160 | for _, input := range cfg.Inputs.TestInputs {
161 | addTimeCache(&input.Input)
162 | }
163 | for _, input := range cfg.Summarys {
164 | c := NewTimeCache(input.Id, input.Relation.PrimaryKey)
165 | timeCache[input.Id] = c
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/common/mapdata.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 | "monitor/common/compute"
6 | "strconv"
7 | )
8 |
9 | //数据集,批处理用
10 | type DataRows struct {
11 | Time string
12 | RowMap *DataRowMap
13 | Rows [][]interface{}
14 | }
15 | type DataRow struct {
16 | Time string
17 | RowMap *DataRowMap
18 | Row []interface{}
19 | }
20 |
21 | //创建一个数据行结构
22 | func (this *DataRows) CreateDataRow(v []interface{}) *DataRow {
23 | return &DataRow{
24 | Time: this.Time,
25 | RowMap: this.RowMap,
26 | Row: v,
27 | }
28 | }
29 |
30 | //节点中流转的数据结构
31 | type DataRowMap struct {
32 | Key map[string]int
33 | Sum map[string]int
34 | Max map[string]int
35 | Min map[string]int
36 | Count map[string]int
37 | ColumnCount int //列统计
38 | }
39 |
40 | func (this *DataRows) CloneMap() (rowmap *DataRowMap) {
41 | rowmap = new(DataRowMap)
42 | rowmap.Key = make(map[string]int)
43 | rowmap.Sum = make(map[string]int)
44 | rowmap.Max = make(map[string]int)
45 | rowmap.Min = make(map[string]int)
46 | rowmap.Count = make(map[string]int)
47 | if this.RowMap == nil {
48 | println("column map is not found")
49 | return
50 | }
51 | i := 0
52 | for k, v := range this.RowMap.Key {
53 | rowmap.Key[k] = v
54 | i = i + 1
55 | }
56 | for k, v := range this.RowMap.Sum {
57 | rowmap.Sum[k] = v
58 | i = i + 1
59 | }
60 | for k, v := range this.RowMap.Max {
61 | rowmap.Max[k] = v
62 | i = i + 1
63 | }
64 | for k, v := range this.RowMap.Min {
65 | rowmap.Min[k] = v
66 | i = i + 1
67 | }
68 | for k, v := range this.RowMap.Count {
69 | rowmap.Count[k] = v
70 | i = i + 1
71 | }
72 | rowmap.ColumnCount = i
73 | return
74 | }
75 |
76 | //取得指标的key值
77 | func (this *DataRows) GetKey(key string, row []interface{}) string {
78 | if this.RowMap == nil {
79 | return fmt.Sprintf("column map is not found")
80 | }
81 | if i, ok := this.RowMap.Key[key]; ok {
82 | return compute.AsString(row[i])
83 | }
84 | //log.Infoln(this.RowMap)
85 | return fmt.Sprintf("column %s is not found", key)
86 | }
87 |
88 | //取得特定的值
89 | func (this *DataRow) GetValue(key string) (interface{}, bool) {
90 | if i, ok := this.RowMap.Key[key]; ok {
91 | return this.Row[i], true
92 | }
93 | return nil, false
94 | }
95 |
96 | //合并数据
97 | func (this *DataRow) Merge(row []interface{}) {
98 | if this.RowMap.Sum != nil {
99 | this.Sum(row)
100 | }
101 | if this.RowMap.Sum != nil {
102 | this.Max(row)
103 | }
104 | if this.RowMap.Sum != nil {
105 | this.Min(row)
106 | }
107 | if this.RowMap.Sum != nil {
108 | this.Count(row)
109 | }
110 | }
111 |
112 | //按sum计算指标
113 | func (this *DataRow) Sum(row []interface{}) {
114 | for _, i := range this.RowMap.Sum {
115 | this.Row[i] = compute.Add(this.Row[i], row[i])
116 | }
117 | }
118 |
119 | //按max计算指标
120 | func (this *DataRow) Max(row []interface{}) {
121 | for _, i := range this.RowMap.Max {
122 | this.Row[i] = compute.Add(this.Row[i], row[i])
123 | }
124 | }
125 |
126 | //按min计算指标
127 | func (this *DataRow) Min(row []interface{}) {
128 | for _, i := range this.RowMap.Min {
129 | this.Row[i] = compute.Add(this.Row[i], row[i])
130 | }
131 | }
132 |
133 | //按count计算指标
134 | func (this *DataRow) Count(row []interface{}) {
135 | for _, i := range this.RowMap.Count {
136 | this.Row[i] = compute.Add(this.Row[i], 1)
137 | }
138 | }
139 |
140 | //取得指标的key值
141 | func (this *DataRow) GetKey(key string) string {
142 | if this.RowMap == nil {
143 | return fmt.Sprintf("column map is not found")
144 | }
145 | if i, ok := this.RowMap.Key[key]; ok {
146 | switch v := this.Row[i].(type) {
147 | case string:
148 | return v
149 | case []byte:
150 | return string(v)
151 | case int8, int16, int32, int64, int, uint, uint16, uint32, uint64, uint8:
152 | return fmt.Sprintf("%d", v)
153 | case float32:
154 | return strconv.FormatFloat(float64(v), 'f', 0, 32)
155 | case float64:
156 | return strconv.FormatFloat(v, 'f', 0, 64)
157 | }
158 | return fmt.Sprintf("%v", this.Row[i])
159 | }
160 | //log.Infoln(this.RowMap)
161 | return fmt.Sprintf("column %s is not found", key)
162 | }
163 |
164 | type DataRowSorter []*DataRow
165 |
166 | func (this DataRowSorter) Len() int {
167 | return len(this)
168 | }
169 |
170 | func (this DataRowSorter) Swap(i, j int) {
171 | this[i], this[j] = this[j], this[i]
172 | }
173 |
174 | type ByTime struct {
175 | DataRowSorter
176 | }
177 |
178 | func (this ByTime) Less(i, j int) bool {
179 | return this.DataRowSorter[i].Time < this.DataRowSorter[j].Time
180 | }
181 |
182 | type ByKey struct {
183 | DataRowSorter
184 | Key string
185 | }
186 |
187 | func (this ByKey) Less(i, j int) bool {
188 | if v1, ok := this.DataRowSorter[i].GetValue(this.Key); ok {
189 | if v2, ok := this.DataRowSorter[j].GetValue(this.Key); ok {
190 | return compute.AsFloat64(v1) < compute.AsFloat64(v2)
191 | }
192 | }
193 | return true
194 | }
195 |
--------------------------------------------------------------------------------
/service/http/http.go:
--------------------------------------------------------------------------------
1 | /*
2 | 用cookie保持连接,每10分钟换一个auth值。
3 | 与tcp服务不同,websocket的连接断开后不需要重新登陆,只需要验证Auth的登陆信息即可。
4 | 不同的单元可以保持不同的连接,简化客户端的代码
5 | 不同的连接产生不同的Client,同一个连接内不允许多次登陆。多次登陆也不会重新生成Client
6 | 登陆后生成key,用于同一用户多连接,and reset key
7 | */
8 | package http
9 |
10 | import (
11 | "code.google.com/p/go-uuid/uuid"
12 | "code.google.com/p/go.net/websocket"
13 | "fmt"
14 | "monitor/common"
15 | "monitor/log"
16 | "monitor/service/online"
17 | "monitor/service/protocol"
18 | "net/http"
19 | "time"
20 | )
21 |
22 | var (
23 | session = make(map[string]string) //在线用户
24 | )
25 |
26 | const (
27 | Sec_Auth_Uid = "Sec_Auth_Uid"
28 | Sec_Auth_Key = "Sec_Auth_Key"
29 | )
30 |
31 | //http service
32 | type HttpService struct {
33 | Host string
34 | Port int
35 | IsRun bool
36 | }
37 |
38 | //init tcp service
39 | func (this *HttpService) init(host string, port int) {
40 | this.Host = host
41 | this.Port = port
42 | this.IsRun = true
43 | }
44 |
45 | //create new http service
46 | //with host and port
47 | func StartHttpService(host string, port int) {
48 | h := new(HttpService)
49 | h.init(host, port)
50 | go h.Run()
51 |
52 | }
53 |
54 | //处理单独的Http请求
55 | func (this *HttpService) HandleHttp(w http.ResponseWriter, req *http.Request) {
56 | req.ParseForm()
57 | uid := req.Header.Get(Sec_Auth_Uid)
58 | key := req.Header.Get(Sec_Auth_Key)
59 | c := this.getClient(uid, key)
60 | defer c.Close()
61 |
62 | if data := req.FormValue("json"); len(data) > 0 {
63 | cmd := new(common.RequestData)
64 | c.Read([]byte(data), cmd)
65 | protocol.Exec(cmd, &c.Client)
66 | } else {
67 | cmd := common.ResponseData{0, -1, "json string is empty", ""}
68 | c.Send(&cmd)
69 | }
70 | for {
71 | timeout := time.After(time.Nanosecond * 10) //10ns就超时
72 | select {
73 | case data := <-c.OutputChan:
74 | if _, err := w.Write(data); err != nil {
75 | log.Error("%s send cmd error and login out,%s\n", c.Name, err.Error())
76 | }
77 | break
78 | case <-timeout:
79 | goto end
80 | }
81 | }
82 | end:
83 | }
84 |
85 | //按cookei取在线用户
86 | func (this *HttpService) getClient(uid string, key string) *HttpClient {
87 | c := new(HttpClient)
88 | if s, _, ok := online.GetSession(uid); ok && s == key { //key相同时认为已登陆过的
89 | if tc, ok := online.Get(uid); ok { //第一个登陆的Client的uuid被当作了uid
90 | c.Name = tc.Name
91 | c.IsLogin = tc.IsLogin
92 | c.IsPlayBack = c.IsPlayBack
93 | }
94 | }
95 | if c == nil {
96 | c = new(HttpClient)
97 | }
98 | c.UUID = uuid.New()
99 | c.IsRun = true
100 | c.OutputChan = make(chan []byte, 10)
101 | return c
102 | }
103 |
104 | ////设置cookie
105 | //func (this *HttpService) setCookie(c *HttpClient, w http.ResponseWriter, age int) {
106 | // id := &http.Cookie{Name: "monitor_http_key", Value: common.HashString(c.Name + ":monitor"), Path: "/", MaxAge: age, HttpOnly: true, Secure: true}
107 | // name := &http.Cookie{Name: "monitor_http_name", Value: c.Name, Path: "/", MaxAge: age, HttpOnly: true, Secure: true}
108 | // http.SetCookie(w, id)
109 | // http.SetCookie(w, name)
110 | //}
111 |
112 | //处理Socket请求
113 | func (this *HttpService) HandleSocket(ws *websocket.Conn) {
114 | connFrom := ws.RemoteAddr()
115 | log.Infof("accept new http client from %s\n", connFrom)
116 | uid := ws.Request().Header.Get(Sec_Auth_Uid)
117 | key := ws.Request().Header.Get(Sec_Auth_Key)
118 | c := this.getClient(uid, key)
119 | c.Connect = ws
120 | go this.HandleResponse(c)
121 | this.HandleRequest(c)
122 | }
123 |
124 | //主运行方法
125 | //根目录为标准Http请求
126 | //socket为Socket请求
127 | func (this *HttpService) Run() {
128 | this.IsRun = true
129 | http.Handle("/", http.FileServer(http.Dir("."))) // <-- note this line
130 | http.HandleFunc("/http", this.HandleHttp)
131 | http.Handle("/socket", websocket.Handler(this.HandleSocket))
132 | log.Infof("http service started at %s:%d", this.Host, this.Port)
133 | log.Info("http socket host on /http and /socket ")
134 | if err := http.ListenAndServe(fmt.Sprintf("%s:%d", this.Host, this.Port), nil); err != nil {
135 | log.Error("http service start error", err.Error())
136 | }
137 | }
138 |
139 | //send cmd to client
140 | func (this *HttpService) HandleResponse(c *HttpClient) {
141 | var err error
142 | defer online.Delete(c.UUID)
143 | for cmd := range c.OutputChan {
144 | //cmd := <-c.OutputChan
145 | cmd = append(cmd, '\n')
146 | if err = websocket.Message.Send(c.Connect, string(cmd)); err != nil {
147 | log.Error("%s send cmd error and login out,%s\n", c.Name, err.Error())
148 | c.IsRun = false
149 | break
150 | }
151 | c.UpdateTime()
152 | }
153 | }
154 |
155 | //handle tcp request
156 | func (this *HttpService) HandleRequest(c *HttpClient) {
157 | var err error
158 | defer online.Delete(c.UUID)
159 | cmd := new(common.RequestData)
160 | var data []byte
161 | for this.IsRun && c.IsRun {
162 | if err = websocket.Message.Receive(c.Connect, &data); err != nil {
163 | log.Infof("%s can't received cmd", c.Name, err.Error())
164 | c.IsRun = false
165 | break
166 | }
167 | c.Read(data, cmd)
168 | protocol.Exec(cmd, &c.Client)
169 | c.UpdateTime()
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/service/protocol/protocol.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | import (
4 | "fmt"
5 | "monitor/common"
6 | "monitor/log"
7 | "monitor/service/online"
8 | "monitor/service/rpc"
9 | "time"
10 | )
11 |
12 | //pid process
13 | type PidProcess func(cmd *common.RequestData, c *online.Client)
14 |
15 | var (
16 | pid map[int]PidProcess //all pid
17 | now time.Time //start time
18 | )
19 |
20 | //init pid
21 | func init() {
22 | pid = map[int]PidProcess{ //以0为区分,0以下的id都不需要进行登陆
23 | 0: pid0, //运行状态测试
24 | -1: pid_1, //登陆
25 | -2: pid_2, //退出
26 | -3: pid_3, //用临时key重新登陆,用户登陆后会临时产生
27 | 2: pid2, //数据推送注册
28 | 3: pid3, //数据库的读操作
29 | 4: pid4, //数据库的写操作
30 | 5: pid5, //更新节点内的父子关系
31 | }
32 | now = time.Now()
33 | }
34 |
35 | //exec pid function
36 | func Exec(cmd *common.RequestData, c *online.Client) {
37 |
38 | //没有登陆就必须登陆系统,小于等于0的协议不需要登陆。
39 | if cmd.Pid <= 0 {
40 | pid[cmd.Pid](cmd, c)
41 | } else if !c.IsLogin {
42 | cmd := common.ResponseData{1, 0, "need login", nil}
43 | c.Send(&cmd)
44 | } else {
45 | if p, ok := pid[cmd.Pid]; ok {
46 | p(cmd, c)
47 | } else {
48 | pidok(cmd, c)
49 | }
50 | }
51 | }
52 |
53 | //数据库read操作
54 | func pid3(cmd *common.RequestData, c *online.Client) {
55 | if checkParamError(cmd, c, "Id", "Param") {
56 | return
57 | }
58 | id := cmd.GetString("Id")
59 | m := make(map[string]interface{})
60 | col, rows, err := rpc.BySqlParamName(id, m)
61 | if err != nil {
62 | log.Error(err.Error())
63 | writePrror("db error", cmd, c)
64 | return
65 | }
66 | dp := new(common.OutputParam)
67 | dp.RowMap = col
68 | dp.Rows = rows
69 | dp.Time = time.Now()
70 | writepid(c, cmd, 0, dp)
71 | }
72 |
73 | //数据库write操作
74 | func pid4(cmd *common.RequestData, c *online.Client) {
75 | if checkParamError(cmd, c, "Id", "Param") {
76 | return
77 | }
78 | id := cmd.GetString("Id")
79 | m := make(map[string]interface{})
80 | err := rpc.ExecSqlParamName(id, m)
81 | if err != nil {
82 | log.Error(err.Error())
83 | writePrror("db error", cmd, c)
84 | return
85 | }
86 | writepid(c, cmd, 0, nil)
87 | }
88 |
89 | //注册推送数据的请求
90 | func pid2(cmd *common.RequestData, c *online.Client) {
91 | if checkParamError(cmd, c, "DataKey", "DataRange", "Id", "LimitId", "OrderBy", "OrderKey", "OutputKey", "TimeRange") {
92 | return
93 | }
94 | dp := new(common.DataParam)
95 | dp.DataKey = cmd.GetString("DataKey")
96 | dp.DataRange = cmd.GetInt("DataRange")
97 | dp.Id = cmd.GetString("Id")
98 | dp.LimitId = cmd.GetStringArray("LimitId")
99 | dp.OrderBy = cmd.GetInt("OrderBy")
100 | dp.OrderKey = cmd.GetString("OrderKey")
101 | dp.OutputKey = cmd.GetStringArray("OutputKey")
102 | dp.FillKey = cmd.GetString("FillKey")
103 | dp.TimeRange = cmd.GetInt("TimeRange")
104 | c.AddRequest(dp)
105 | //同时直接返回一次数据,否则界面会有空白期
106 | tm := &common.TimeMessage{Time: online.GetCurrentTime()}
107 | c.Processing(tm)
108 | }
109 |
110 | //更新节点内的父子关系,有3类变化,新增、删除
111 | func pid5(cmd *common.RequestData, c *online.Client) {
112 | if checkParamError(cmd, c, "DataKey", "Insert", "Delete", "Id") {
113 | return
114 | }
115 | }
116 |
117 | //尚未支持
118 | func pidok(cmd *common.RequestData, c *online.Client) {
119 | writepid(c, cmd, -1, "not surport")
120 | }
121 |
122 | // test service
123 | func pid0(cmd *common.RequestData, c *online.Client) {
124 | if checkParamError(cmd, c, "Test") {
125 | return
126 | }
127 |
128 | test := cmd.GetInt("Test")
129 | var result interface{}
130 | switch test {
131 | case 0:
132 | result = true
133 | case 1:
134 | result = time.Now().Format(common.TimeFormat)
135 | case 2:
136 | result = time.Since(now).String()
137 | case 3:
138 | result = now.Format(common.TimeFormat)
139 | default:
140 | result = fmt.Sprintf("not surport %d", test)
141 | }
142 | writepid(c, cmd, 0, result)
143 | }
144 |
145 | //check param exists
146 | func checkParamError(cmd *common.RequestData, c *online.Client, keys ...string) bool {
147 | for _, k := range keys {
148 | if _, ok := cmd.Param[k]; !ok {
149 | writePrror(fmt.Sprintf("param '%s' not exists", k), cmd, c)
150 | return true
151 | }
152 | }
153 | return false
154 | }
155 |
156 | //return error status=-1
157 | func writePrror(msg string, cmd *common.RequestData, c *online.Client) {
158 | writepid(c, cmd, -1, msg)
159 | }
160 |
161 | //key login in
162 | func pid_3(cmd *common.RequestData, c *online.Client) {
163 | if checkParamError(cmd, c, "UID", "Key") {
164 | return
165 | }
166 |
167 | login := cmd.GetString("UID")
168 | pwd := cmd.GetString("Key")
169 |
170 | loginSuccess := false
171 | //log.Info("pid -3 uid=key", login, pwd)
172 | //check session
173 | if name, key, ok := online.GetSession(login); ok && key == pwd {
174 | c.Name = name
175 | c.Key = key
176 | loginSuccess = true
177 | //log.Info("get session success", key)
178 | }
179 |
180 | //check
181 | re := make(map[string]interface{})
182 | if loginSuccess {
183 | c.IsLogin = true
184 | re["LoginState"] = 0
185 | re["Message"] = "login success"
186 | re["UID"] = c.UUID
187 | re["Key"] = c.Key
188 | } else {
189 | re["LoginState"] = -1
190 | re["Message"] = "login failed"
191 | }
192 | online.Set(c)
193 | writepid(c, cmd, 0, re)
194 | }
195 |
196 | // login in
197 | func pid_1(cmd *common.RequestData, c *online.Client) {
198 | if checkParamError(cmd, c, "Login", "Password") {
199 | return
200 | }
201 |
202 | login := cmd.GetString("Login")
203 | pwd := cmd.GetString("Password")
204 | c.Name = login
205 | log.Infof("user %s login", c.Name, common.IsDebug)
206 | loginSuccess := false
207 | if common.IsDebug { //调试时直接登陆,不验证密码
208 | loginSuccess = true
209 | } else {
210 | param := make(map[string]interface{})
211 | param["login"] = login
212 | _, epwd, err := rpc.BySqlParamName("login", param)
213 | if err != nil {
214 | log.Error(err.Error())
215 | }
216 | if err == nil && len(epwd) > 0 {
217 | loginSuccess = epwd[0][0] == pwd //需要对密码进行加密再进行比较
218 | }
219 | }
220 |
221 | //check
222 | re := make(map[string]interface{})
223 | if loginSuccess {
224 | c.IsLogin = true
225 | c.Key = common.HashString(c.UUID + ":" + c.Name)
226 | online.SetSession(c.UUID, c.Name, c.Key)
227 | re["LoginState"] = 0
228 | re["Message"] = "login success"
229 | re["UID"] = c.UUID
230 | re["Key"] = c.Key
231 | online.Set(c)
232 | } else {
233 | re["LoginState"] = -1
234 | re["Message"] = "login failed"
235 | }
236 | writepid(c, cmd, 0, re)
237 | }
238 |
239 | // //senc data to online.Client
240 | // func pid2(cmd *common.RequestData, c *online.Client) {
241 | // log.Println("pid 2")
242 | // //if param, ok := cmd.Param.(string); ok {
243 | // if start_time, err := time.Parse(common.TimeFormat, cmd.Param["time"]); err == nil {
244 | // log.Println(start_time)
245 | // out := online.ResponseData{1, 0, nil, cmd.Param}
246 | // //get from redis and send to all online.Client
247 | // this.SendToAll(out)
248 | // }
249 | // }
250 |
251 | //login out
252 | func pid_2(cmd *common.RequestData, c *online.Client) {
253 | //if param, ok := cmd.Param.(string); ok {
254 | log.Debug("login out ", c.Name)
255 | writepid(c, cmd, 0, nil)
256 | c.IsRun = false
257 | online.RemoveSession(c.UUID)
258 | //}
259 | }
260 |
261 | //write pid to responseout ResponseData
262 | func writepid(c *online.Client, cmd *common.RequestData, status int, result interface{}) {
263 | out := common.ResponseData{cmd.Pid, status, result, cmd.Param}
264 | c.Send(&out)
265 | }
266 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------