├── version.go
├── clusterserver
├── clusterglobal.go
├── master.go
└── clusterserver.go
├── iface
├── icommand.go
├── icommandinterpreter.go
├── idatapack.go
├── iconnectionmgr.go
├── imsghandle.go
├── iwriter.go
├── iclient.go
├── iconnection.go
├── iserver.go
└── iprotocol.go
├── utils
├── uuid_test.go
├── uuidfactory.go
├── tools.go
└── globalobj.go
├── .gitignore
├── sys_rpc
├── root_rpc.go
├── master_rpc.go
└── child_rpc.go
├── timer
├── safetimer_test.go
├── timer.go
├── safetimer.go
└── hashwheel.go
├── logger
├── logger_test.go
└── logger.go
├── fnet
├── connectionmgr.go
├── datapack.go
├── tcpclient.go
├── msghandle.go
├── protocol.go
└── connection.go
├── cluster
├── rpc.go
├── clusterconf.go
├── cmdinterpreter.go
├── asyncresult.go
├── child.go
├── rpcpack.go
├── telnetprotocol.go
├── rpchandle.go
└── rpcprotocol.go
├── telnetcmd
├── pprofcpucommand.go
└── mastercommand.go
├── xingo.go
├── README.md
├── fserver
└── server.go
├── db
└── mongo
│ ├── dboperate_test.go
│ └── dboperate.go
└── LICENSE
/version.go:
--------------------------------------------------------------------------------
1 | package xingo
2 |
3 | const version = "0.0.1"
4 |
--------------------------------------------------------------------------------
/clusterserver/clusterglobal.go:
--------------------------------------------------------------------------------
1 | package clusterserver
2 |
3 | var GlobalMaster *Master
4 | var GlobalClusterServer *ClusterServer
5 |
--------------------------------------------------------------------------------
/iface/icommand.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type ICommand interface {
4 | Run([]string) string
5 | Help() string
6 | Name() string
7 | }
8 |
--------------------------------------------------------------------------------
/iface/icommandinterpreter.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type ICommandInterpreter interface {
4 | AddCommand(ICommand)
5 | Excute(string) string
6 | IsQuitCmd(string) bool
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/utils/uuid_test.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "testing"
4 |
5 | func Test(t *testing.T) {
6 | a := NewUUIDGenerator("hhh")
7 | for {
8 | t.Log(a.Get())
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/iface/idatapack.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type Idatapack interface {
4 | GetHeadLen() int32
5 | Unpack([]byte) (interface{}, error)
6 | Pack(uint32, interface{}) ([]byte, error)
7 | }
8 |
--------------------------------------------------------------------------------
/iface/iconnectionmgr.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type Iconnectionmgr interface {
4 | Add(Iconnection)
5 | Remove(Iconnection) error
6 | Get(uint32) (Iconnection, error)
7 | Len() int
8 | }
9 |
--------------------------------------------------------------------------------
/iface/imsghandle.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type Imsghandle interface {
4 | DeliverToMsgQueue(interface{})
5 | DoMsgFromGoRoutine(interface{})
6 | AddRouter(interface{})
7 | StartWorkerLoop(int)
8 | }
9 |
--------------------------------------------------------------------------------
/iface/iwriter.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type IWriter interface {
4 | Send([]byte) error
5 | GetProperty(string) (interface{}, error)
6 | SetProperty(string, interface{})
7 | RemoveProperty(string)
8 | }
9 |
--------------------------------------------------------------------------------
/iface/iclient.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | import "net"
4 |
5 | type Iclient interface {
6 | Start()
7 | Stop(bool)
8 | GetConnection() *net.TCPConn
9 | Send([]byte) error
10 | GetProperty(string) (interface{}, error)
11 | SetProperty(string, interface{})
12 | RemoveProperty(string)
13 | }
14 |
--------------------------------------------------------------------------------
/.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 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | .idea
26 |
--------------------------------------------------------------------------------
/iface/iconnection.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | import (
4 | "net"
5 | )
6 |
7 | type Iconnection interface {
8 | Start()
9 | Stop()
10 | GetConnection() *net.TCPConn
11 | GetSessionId() uint32
12 | Send([]byte) error
13 | SendBuff([]byte) error
14 | RemoteAddr() net.Addr
15 | LostConnection()
16 | GetProperty(string) (interface{}, error)
17 | SetProperty(string, interface{})
18 | RemoveProperty(string)
19 | }
20 |
--------------------------------------------------------------------------------
/iface/iserver.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type Iserver interface {
8 | Start()
9 | Stop()
10 | Serve()
11 | GetConnectionMgr() Iconnectionmgr
12 | GetConnectionQueue() chan interface{}
13 | AddRouter(router interface{})
14 | CallLater(durations time.Duration, f func(v ...interface{}), args ...interface{})
15 | CallWhen(ts string, f func(v ...interface{}), args ...interface{})
16 | CallLoop(durations time.Duration, f func(v ...interface{}), args ...interface{})
17 | }
18 |
--------------------------------------------------------------------------------
/iface/iprotocol.go:
--------------------------------------------------------------------------------
1 | package iface
2 |
3 | type IClientProtocol interface {
4 | OnConnectionMade(fconn Iclient)
5 | OnConnectionLost(fconn Iclient)
6 | StartReadThread(fconn Iclient)
7 | InitWorker(int32)
8 | AddRpcRouter(interface{})
9 | GetMsgHandle() Imsghandle
10 | GetDataPack() Idatapack
11 | }
12 |
13 | type IServerProtocol interface {
14 | OnConnectionMade(fconn Iconnection)
15 | OnConnectionLost(fconn Iconnection)
16 | StartReadThread(fconn Iconnection)
17 | InitWorker(int32)
18 | AddRpcRouter(interface{})
19 | GetMsgHandle() Imsghandle
20 | GetDataPack() Idatapack
21 | }
22 |
--------------------------------------------------------------------------------
/sys_rpc/root_rpc.go:
--------------------------------------------------------------------------------
1 | package sys_rpc
2 |
3 | import (
4 | "github.com/viphxin/xingo/cluster"
5 |
6 | "github.com/viphxin/xingo/clusterserver"
7 | "github.com/viphxin/xingo/logger"
8 | "github.com/viphxin/xingo/utils"
9 | )
10 |
11 | type RootRpc struct {
12 | }
13 |
14 | /*
15 | 子节点连上来的通知
16 | */
17 | func (this *RootRpc) TakeProxy(request *cluster.RpcRequest) {
18 | name := request.Rpcdata.Args[0].(string)
19 | logger.Info("child node " + name + " connected to " + utils.GlobalObject.Name)
20 | //加到childs并且绑定链接connetion对象
21 | clusterserver.GlobalClusterServer.AddChild(name, request.Fconn)
22 | }
23 |
--------------------------------------------------------------------------------
/utils/uuidfactory.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | const (
8 | MAXUINT32 = 4294967295
9 | DEFAULT_UUID_CNT_CACHE = 512
10 | )
11 |
12 | type UUIDGenerator struct {
13 | Prefix string
14 | idGen uint32
15 | internalChan chan uint32
16 | }
17 |
18 | func NewUUIDGenerator(prefix string) *UUIDGenerator {
19 | gen := &UUIDGenerator{
20 | Prefix: prefix,
21 | idGen: 0,
22 | internalChan: make(chan uint32, DEFAULT_UUID_CNT_CACHE),
23 | }
24 | gen.startGen()
25 | return gen
26 | }
27 |
28 | func (this *UUIDGenerator) startGen() {
29 | go func() {
30 | for {
31 | if this.idGen+1 > MAXUINT32 {
32 | this.idGen = 1
33 | } else {
34 | this.idGen += 1
35 | }
36 | this.internalChan <- this.idGen
37 | }
38 | }()
39 | }
40 |
41 | func (this *UUIDGenerator) Get() string {
42 | idgen := <-this.internalChan
43 | return fmt.Sprintf("%s%d", this.Prefix, idgen)
44 | }
45 |
46 | func (this *UUIDGenerator) GetUint32() uint32 {
47 | return <-this.internalChan
48 | }
--------------------------------------------------------------------------------
/timer/safetimer_test.go:
--------------------------------------------------------------------------------
1 | package timer
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "testing"
7 | "time"
8 | "sync/atomic"
9 | "github.com/viphxin/xingo/logger"
10 | )
11 |
12 | func test(a ...interface{}) {
13 | fmt.Println(a[0], "============", a[1])
14 | }
15 |
16 | var (
17 | tt = int64(0)
18 | )
19 |
20 | func Test(t *testing.T) {
21 |
22 | s := NewSafeTimerScheduel()
23 | go func() {
24 | for {
25 | df := <-s.GetTriggerChannel()
26 | df.Call()
27 | atomic.AddInt64(&tt, -1)
28 | }
29 | }()
30 | go func() {
31 | i := 0
32 | for i<50000{
33 | s.CreateTimer(int64(rand.Int31n(3600*1e3)), test, []interface{}{22, 33})
34 | atomic.AddInt64(&tt, 1)
35 | time.Sleep(1 * time.Second)
36 | i += 1
37 | }
38 | }()
39 | go func(){
40 | ii := 0
41 | for ii < 50000{
42 | s.CreateTimer(int64(rand.Int31n(3600*1e3)), test, []interface{}{22, 33})
43 | atomic.AddInt64(&tt, 1)
44 | time.Sleep(1 * time.Second)
45 | ii += 1
46 | }
47 | }()
48 |
49 | for{
50 | time.Sleep(60*time.Second)
51 | logger.Info("last timer: ", atomic.LoadInt64(&tt))
52 | }
53 | }
--------------------------------------------------------------------------------
/logger/logger_test.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "runtime"
5 | "strconv"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func runlog(i int) {
11 | Debug("Debug>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
12 | Info("Info>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
13 | Warn("Warn>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
14 | Error("Error>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
15 | Fatal("Fatal>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
16 | }
17 |
18 | func Test(t *testing.T) {
19 | runtime.GOMAXPROCS(runtime.NumCPU())
20 |
21 | //指定是否控制台打印,默认为true
22 | SetConsole(true)
23 | //指定日志文件备份方式为文件大小的方式
24 | //第一个参数为日志文件存放目录
25 | //第二个参数为日志文件命名
26 | //第三个参数为备份文件最大数量
27 | //第四个参数为备份文件大小
28 | //第五个参数为文件大小的单位
29 | //logger.SetRollingFile("d:/logtest", "test.log", 10, 5, logger.KB)
30 |
31 | //指定日志文件备份方式为日期的方式
32 | //第一个参数为日志文件存放目录
33 | //第二个参数为日志文件命名
34 | SetRollingDaily("./logtest", "test.log")
35 |
36 | //指定日志级别 ALL,DEBUG,INFO,WARN,ERROR,FATAL,OFF 级别由低到高
37 | //一般习惯是测试阶段为debug,生成环境为info以上
38 | SetLevel(ERROR)
39 |
40 | for i := 10000; i > 0; i-- {
41 | go runlog(i)
42 | time.Sleep(1000 * time.Millisecond)
43 | }
44 | time.Sleep(15 * time.Second)
45 | }
46 |
--------------------------------------------------------------------------------
/timer/timer.go:
--------------------------------------------------------------------------------
1 | package timer
2 |
3 | import (
4 | "time"
5 | "reflect"
6 | "fmt"
7 | "github.com/viphxin/xingo/logger"
8 | )
9 |
10 | type DelayCall struct {
11 | f func(v ...interface{})
12 | args []interface{}
13 | }
14 |
15 | func (this *DelayCall)Call(){
16 | defer func(){
17 | if err := recover(); err != nil{
18 | logger.Error(this.String(), "Call Error: ", err)
19 | }
20 | }()
21 |
22 | this.f(this.args...)
23 | }
24 |
25 | func (this *DelayCall)String() string{
26 | funcType := reflect.TypeOf(this.f)
27 | return fmt.Sprintf("DelayCall function: %s. args: %v.", funcType.Name(), this.args)
28 | }
29 |
30 | type Timer struct {
31 | durations time.Duration
32 | delayCall *DelayCall
33 | }
34 |
35 | func NewTimer(durations time.Duration, f func(v ...interface{}), args []interface{}) *Timer{
36 | return &Timer{
37 | durations : durations,
38 | delayCall: &DelayCall{
39 | f: f,
40 | args: args,
41 | },
42 | }
43 | }
44 |
45 | func (this *Timer)Run(){
46 | go func() {
47 | time.Sleep(this.durations)
48 | this.delayCall.Call()
49 | }()
50 | }
51 |
52 | func (this *Timer)GetDurations() time.Duration{
53 | return this.durations
54 | }
55 |
56 | func (this *Timer)GetFunc() *DelayCall{
57 | return this.delayCall
58 | }
59 |
--------------------------------------------------------------------------------
/fnet/connectionmgr.go:
--------------------------------------------------------------------------------
1 | package fnet
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/viphxin/xingo/iface"
7 | "github.com/viphxin/xingo/logger"
8 | "sync"
9 | )
10 |
11 | type ConnectionMgr struct {
12 | connections map[uint32]iface.Iconnection
13 | conMrgLock sync.RWMutex
14 | }
15 |
16 | func (this *ConnectionMgr) Add(conn iface.Iconnection) {
17 | this.conMrgLock.Lock()
18 | defer this.conMrgLock.Unlock()
19 | this.connections[conn.GetSessionId()] = conn
20 | logger.Debug(fmt.Sprintf("Total connection: %d", len(this.connections)))
21 | }
22 |
23 | func (this *ConnectionMgr) Remove(conn iface.Iconnection) error {
24 | this.conMrgLock.Lock()
25 | defer this.conMrgLock.Unlock()
26 | _, ok := this.connections[conn.GetSessionId()]
27 | if ok {
28 | delete(this.connections, conn.GetSessionId())
29 | logger.Info(len(this.connections))
30 | return nil
31 | } else {
32 | return errors.New("not found!!")
33 | }
34 |
35 | }
36 |
37 | func (this *ConnectionMgr) Get(sid uint32) (iface.Iconnection, error) {
38 | this.conMrgLock.Lock()
39 | defer this.conMrgLock.Unlock()
40 | v, ok := this.connections[sid]
41 | if ok {
42 | delete(this.connections, sid)
43 | return v, nil
44 | } else {
45 | return nil, errors.New("not found!!")
46 | }
47 | }
48 |
49 | func (this *ConnectionMgr) Len() int {
50 | this.conMrgLock.Lock()
51 | defer this.conMrgLock.Unlock()
52 | return len(this.connections)
53 | }
54 |
55 | func NewConnectionMgr() *ConnectionMgr {
56 | return &ConnectionMgr{
57 | connections: make(map[uint32]iface.Iconnection),
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/utils/tools.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/logger"
6 | "net/http"
7 | "reflect"
8 | "runtime/debug"
9 | "time"
10 | )
11 |
12 | func HttpRequestWrap(uri string, targat func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
13 | return func(response http.ResponseWriter, request *http.Request) {
14 | defer func() {
15 | if err := recover(); err != nil {
16 | debug.PrintStack()
17 | logger.Info("===================http server panic recover===============")
18 | }
19 | }()
20 | st := time.Now()
21 | logger.Debug("User-Agent: ", request.Header["User-Agent"])
22 | targat(response, request)
23 | logger.Debug(fmt.Sprintf("%s cost total time: %f ms", uri, time.Now().Sub(st).Seconds()*1000))
24 | }
25 | }
26 |
27 | func ReSettingLog() {
28 | // --------------------------------------------init log start
29 | logger.SetConsole(GlobalObject.SetToConsole)
30 | if GlobalObject.LogFileType == logger.ROLLINGFILE {
31 | logger.SetRollingFile(GlobalObject.LogPath, GlobalObject.LogName,
32 | GlobalObject.MaxLogNum, GlobalObject.MaxFileSize, GlobalObject.LogFileUnit)
33 | } else {
34 | logger.SetRollingDaily(GlobalObject.LogPath, GlobalObject.LogName)
35 | logger.SetLevel(GlobalObject.LogLevel)
36 | }
37 | // --------------------------------------------init log end
38 | }
39 |
40 | func XingoTry(f reflect.Value, args []reflect.Value, handler func(interface{})) {
41 | defer func() {
42 | if err := recover(); err != nil {
43 | logger.Info("-------------panic recover---------------")
44 | if handler != nil {
45 | handler(err)
46 | }
47 | }
48 | }()
49 | f.Call(args)
50 | }
--------------------------------------------------------------------------------
/sys_rpc/master_rpc.go:
--------------------------------------------------------------------------------
1 | package sys_rpc
2 |
3 | import (
4 | "github.com/viphxin/xingo/cluster"
5 | "github.com/viphxin/xingo/clusterserver"
6 | "github.com/viphxin/xingo/logger"
7 | )
8 |
9 | type MasterRpc struct {
10 | }
11 |
12 | func (this *MasterRpc) TakeProxy(request *cluster.RpcRequest) (response map[string]interface{}) {
13 | response = make(map[string]interface{}, 0)
14 | name := request.Rpcdata.Args[0].(string)
15 | logger.Info("node " + name + " connected to master.")
16 | //加到childs并且绑定链接connetion对象
17 | clusterserver.GlobalMaster.AddNode(name, request.Fconn)
18 |
19 | //返回需要链接的父节点
20 | remotes, err := clusterserver.GlobalMaster.Cconf.GetRemotesByName(name)
21 | if err == nil {
22 | roots := make([]string, 0)
23 | for _, r := range remotes {
24 | if _, ok := clusterserver.GlobalMaster.OnlineNodes[r]; ok {
25 | //父节点在线
26 | roots = append(roots, r)
27 | }
28 | }
29 | response["roots"] = roots
30 | }
31 | //通知当前节点的子节点链接当前节点
32 | for _, child := range clusterserver.GlobalMaster.Childs.GetChilds() {
33 | //遍历所有子节点,观察child节点的父节点是否包含当前节点
34 | remotes, err := clusterserver.GlobalMaster.Cconf.GetRemotesByName(child.GetName())
35 | if err == nil {
36 | for _, rname := range remotes {
37 | if rname == name {
38 | //包含,需要通知child节点连接当前节点
39 | //rpc notice
40 | child.CallChildNotForResult("RootTakeProxy", name)
41 | break
42 | }
43 | }
44 | }
45 | }
46 | return
47 | }
48 |
49 | //主动通知master 节点掉线
50 | func (this *MasterRpc) ChildOffLine(request *cluster.RpcRequest) {
51 | name := request.Rpcdata.Args[0].(string)
52 | logger.Info("node " + name + " disconnected offline.")
53 | clusterserver.GlobalMaster.CheckChildsAlive(true)
54 | }
55 |
--------------------------------------------------------------------------------
/cluster/rpc.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "github.com/viphxin/xingo/iface"
5 | "github.com/viphxin/xingo/logger"
6 | "github.com/viphxin/xingo/utils"
7 | "time"
8 | )
9 |
10 | type RpcSignal int32
11 |
12 | const (
13 | REQUEST_NORESULT RpcSignal = iota
14 | REQUEST_FORRESULT
15 | RESPONSE
16 | )
17 |
18 | type XingoRpc struct {
19 | conn iface.IWriter
20 | asyncResultMgr *AsyncResultMgr
21 | }
22 |
23 | func NewXingoRpc(conn iface.IWriter) *XingoRpc {
24 | return &XingoRpc{
25 | conn: conn,
26 | asyncResultMgr: AResultGlobalObj,
27 | }
28 | }
29 |
30 | func (this *XingoRpc) CallRpcNotForResult(target string, args ...interface{}) error {
31 | rpcdata := &RpcData{
32 | MsgType: REQUEST_NORESULT,
33 | Target: target,
34 | Args: args,
35 | }
36 | rpcpackege, err := utils.GlobalObject.RpcCProtoc.GetDataPack().Pack(0, rpcdata)
37 |
38 | if err == nil {
39 | this.conn.Send(rpcpackege)
40 | return nil
41 | } else {
42 | logger.Error(err)
43 | return err
44 | }
45 | }
46 |
47 | func (this *XingoRpc) CallRpcForResult(target string, args ...interface{}) (*RpcData, error) {
48 | asyncR := this.asyncResultMgr.Add()
49 | rpcdata := &RpcData{
50 | MsgType: REQUEST_FORRESULT,
51 | Key: asyncR.GetKey(),
52 | Target: target,
53 | Args: args,
54 | }
55 | rpcpackege, err := utils.GlobalObject.RpcCProtoc.GetDataPack().Pack(0, rpcdata)
56 | if err == nil {
57 | this.conn.Send(rpcpackege)
58 | resp, err := asyncR.GetResult(2 * time.Second)
59 | if err == nil {
60 | return resp, nil
61 | } else {
62 | //超时了 或者其他原因结果没等到
63 | this.asyncResultMgr.Remove(asyncR.GetKey())
64 | return nil, err
65 | }
66 | } else {
67 | logger.Error(err)
68 | return nil, err
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/cluster/clusterconf.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "io/ioutil"
7 | "github.com/viphxin/xingo/logger"
8 | )
9 |
10 | var cfgpath string
11 |
12 | type ClusterServerConf struct {
13 | Name string
14 | Host string
15 | RootPort int
16 | Http []interface{} //[port, staticfile_path]
17 | Https []interface{} //[port, certFile, keyFile, staticfile_path]
18 | NetPort int
19 | DebugPort int //telnet port
20 | Remotes []string
21 | Module string
22 | Log string
23 | }
24 |
25 | type ClusterConf struct {
26 | Master *ClusterServerConf
27 | Servers map[string]*ClusterServerConf
28 | }
29 |
30 | func NewClusterConf(path string) (*ClusterConf, error) {
31 | cconf := &ClusterConf{}
32 | //集群服务器配置信息
33 | data, err := ioutil.ReadFile(path)
34 | if err != nil {
35 | panic(err)
36 | }
37 | err = json.Unmarshal(data, cconf)
38 | if err != nil {
39 | panic(err)
40 | }
41 | cfgpath = path
42 | return cconf, nil
43 | }
44 |
45 | /*
46 | 获取当前节点的父节点
47 | */
48 | func (this *ClusterConf) GetRemotesByName(name string) ([]string, error) {
49 | server, ok := this.Servers[name]
50 | if ok {
51 | return server.Remotes, nil
52 | } else {
53 | return nil, errors.New("no server found!!!")
54 | }
55 | }
56 |
57 | /*
58 | 获取当前节点的子节点
59 | */
60 | func (this *ClusterConf) GetChildsByName(name string) []string {
61 | names := make([]string, 0)
62 | for sername, ser := range this.Servers {
63 | for _, rname := range ser.Remotes {
64 | if rname == name {
65 | names = append(names, sername)
66 | break
67 | }
68 | }
69 | }
70 | return names
71 | }
72 |
73 | func (this *ClusterConf)Reload(){
74 | //集群服务器配置信息
75 | data, err := ioutil.ReadFile(cfgpath)
76 | if err != nil {
77 | logger.Error(err)
78 | }
79 | err = json.Unmarshal(data, this)
80 | if err != nil {
81 | logger.Error(err)
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/telnetcmd/pprofcpucommand.go:
--------------------------------------------------------------------------------
1 | package telnetcmd
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "runtime/pprof"
7 | )
8 |
9 | type PprofCpuCommand struct {
10 | buffer *bytes.Buffer
11 | profilingBuffer *bytes.Buffer
12 | }
13 |
14 | func NewPprofCpuCommand() *PprofCpuCommand{
15 | return &PprofCpuCommand{new(bytes.Buffer), new(bytes.Buffer)}
16 | }
17 | func (this *PprofCpuCommand)Name()string{
18 | return "pprofcpu"
19 | }
20 |
21 | func (this *PprofCpuCommand)Help()string{
22 | return fmt.Sprintf("pprofcpu:\r\n" +
23 | "----------- start: 开始收集服务cpu占用信息\r\n" +
24 | "----------- stop: 结束数据收集\r\n" +
25 | "----------- profiling: 分析(goroutine, heap, thread, block)")
26 | }
27 |
28 | func (this *PprofCpuCommand)Run(args []string) string{
29 | if len(args) == 0{
30 | return this.Help()
31 | }else{
32 | switch args[0] {
33 | case "start":
34 | pprof.StopCPUProfile()
35 | this.buffer.Reset()
36 | err := pprof.StartCPUProfile(this.buffer)
37 | if err != nil{
38 | return fmt.Sprintf("pprofcpu start error: %s.", err)
39 | }else{
40 | return "pprofcpu start successful. please wait."
41 | }
42 | case "stop":
43 | pprof.StopCPUProfile()
44 | return this.buffer.String()
45 | case "profiling":
46 | var p *pprof.Profile
47 | switch args[1] {
48 | case "goroutine":
49 | p = pprof.Lookup("goroutine")
50 | case "heap":
51 | p = pprof.Lookup("heap")
52 | case "thread":
53 | p = pprof.Lookup("threadcreate")
54 | case "block":
55 | p = pprof.Lookup("block")
56 | default:
57 | return this.Help()
58 | }
59 | this.profilingBuffer.Reset()
60 | err := p.WriteTo(this.profilingBuffer, 1)
61 | if err != nil {
62 | return fmt.Sprintf("pprofcpu profiling error: %s.", err)
63 | }else{
64 | return this.profilingBuffer.String() + "\r\n"
65 | }
66 | default:
67 | return "not found command."
68 |
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/fnet/datapack.go:
--------------------------------------------------------------------------------
1 | package fnet
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "fmt"
7 | "github.com/golang/protobuf/proto"
8 | "github.com/viphxin/xingo/logger"
9 | "github.com/viphxin/xingo/utils"
10 | )
11 |
12 | type PkgData struct {
13 | Len uint32
14 | MsgId uint32
15 | Data []byte
16 | }
17 |
18 | type PBDataPack struct{}
19 |
20 | func NewPBDataPack() *PBDataPack {
21 | return &PBDataPack{}
22 | }
23 |
24 | func (this *PBDataPack) GetHeadLen() int32 {
25 | return 8
26 | }
27 |
28 | func (this *PBDataPack) Unpack(headdata []byte) (interface{}, error) {
29 | headbuf := bytes.NewReader(headdata)
30 |
31 | head := &PkgData{}
32 |
33 | // 读取Len
34 | if err := binary.Read(headbuf, binary.LittleEndian, &head.Len); err != nil {
35 | return nil, err
36 | }
37 |
38 | // 读取MsgId
39 | if err := binary.Read(headbuf, binary.LittleEndian, &head.MsgId); err != nil {
40 | return nil, err
41 | }
42 |
43 | // 封包太大
44 | if (utils.GlobalObject.MaxPacketSize > 0 && head.Len > utils.GlobalObject.MaxPacketSize) ||
45 | (utils.GlobalObject.MaxPacketSize == 0 && head.Len > MaxPacketSize) {
46 | return nil, packageTooBig
47 | }
48 |
49 | return head, nil
50 | }
51 |
52 | func (this *PBDataPack) Pack(msgId uint32, data interface{}) (out []byte, err error) {
53 | outbuff := bytes.NewBuffer([]byte{})
54 | // 进行编码
55 | dataBytes := []byte{}
56 | if data != nil {
57 | dataBytes, err = proto.Marshal(data.(proto.Message))
58 | }
59 |
60 | if err != nil {
61 | logger.Error(fmt.Sprintf("marshaling error: %s", err))
62 | }
63 | // 写Len
64 | if err = binary.Write(outbuff, binary.LittleEndian, uint32(len(dataBytes))); err != nil {
65 | return
66 | }
67 | // 写MsgId
68 | if err = binary.Write(outbuff, binary.LittleEndian, msgId); err != nil {
69 | return
70 | }
71 |
72 | //all pkg data
73 | if err = binary.Write(outbuff, binary.LittleEndian, dataBytes); err != nil {
74 | return
75 | }
76 |
77 | out = outbuff.Bytes()
78 | return
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/sys_rpc/child_rpc.go:
--------------------------------------------------------------------------------
1 | package sys_rpc
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/cluster"
6 | "github.com/viphxin/xingo/clusterserver"
7 | "github.com/viphxin/xingo/logger"
8 | "time"
9 | "github.com/viphxin/xingo/utils"
10 | "os"
11 | )
12 |
13 | type ChildRpc struct {
14 | }
15 |
16 | /*
17 | master 通知父节点上线, 收到通知的子节点需要链接对应父节点
18 | */
19 | func (this *ChildRpc) RootTakeProxy(request *cluster.RpcRequest) {
20 | rname := request.Rpcdata.Args[0].(string)
21 | logger.Info(fmt.Sprintf("root node %s online. connecting...", rname))
22 | clusterserver.GlobalClusterServer.ConnectToRemote(rname)
23 | }
24 |
25 | /*
26 | 关闭节点信号
27 | */
28 | func (this *ChildRpc) CloseServer(request *cluster.RpcRequest){
29 | delay := request.Rpcdata.Args[0].(int)
30 | logger.Warn("server close kickdown.", delay, "second...")
31 | time.Sleep(time.Duration(delay)*time.Second)
32 | utils.GlobalObject.ProcessSignalChan <- os.Kill
33 | }
34 |
35 | /*
36 | 重新加载配置文件
37 | */
38 | func (this *ChildRpc) ReloadConfig(request *cluster.RpcRequest){
39 | delay := request.Rpcdata.Args[0].(int)
40 | logger.Warn("server ReloadConfig kickdown.", delay, "second...")
41 | time.Sleep(time.Duration(delay)*time.Second)
42 | clusterserver.GlobalClusterServer.Cconf.Reload()
43 | utils.GlobalObject.Reload()
44 | logger.Info("reload config.")
45 | }
46 |
47 |
48 | /*
49 | 检查节点是否下线
50 | */
51 | func (this *ChildRpc) CheckAlive(request *cluster.RpcRequest)(response map[string]interface{}){
52 | logger.Debug("CheckAlive!")
53 | response = make(map[string]interface{})
54 | response["name"] = clusterserver.GlobalClusterServer.Name
55 | return
56 | }
57 |
58 | /*
59 | 通知节点掉线(父节点或子节点)
60 | */
61 | func (this *ChildRpc)NodeDownNtf(request *cluster.RpcRequest) {
62 | isChild := request.Rpcdata.Args[0].(bool)
63 | nodeName := request.Rpcdata.Args[1].(string)
64 | logger.Debug(fmt.Sprintf("node %s down ntf.", nodeName))
65 | if isChild {
66 | clusterserver.GlobalClusterServer.RemoveChild(nodeName)
67 | }else{
68 | clusterserver.GlobalClusterServer.RemoveRemote(nodeName)
69 | }
70 | }
--------------------------------------------------------------------------------
/cluster/cmdinterpreter.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "github.com/viphxin/xingo/iface"
5 | "github.com/viphxin/xingo/logger"
6 | "strings"
7 | "fmt"
8 | )
9 |
10 | var (
11 | QUIT_CMD = [3]string{"quit", "q", "exit"}
12 | )
13 |
14 | type CommandInterpreter struct {
15 | commands map[string]iface.ICommand
16 | }
17 |
18 | func NewCommandInterpreter() *CommandInterpreter{
19 | interpreter := &CommandInterpreter{make(map[string]iface.ICommand)}
20 | return interpreter
21 | }
22 |
23 | func (this *CommandInterpreter)AddCommand(cmd iface.ICommand){
24 | this.commands[cmd.Name()] = cmd
25 | logger.Debug("add command ", cmd.Name())
26 | }
27 |
28 | func (this *CommandInterpreter)preExcute(rawCmdExp string) string{
29 | return strings.ToLower(strings.TrimSpace(rawCmdExp))
30 | }
31 |
32 | func (this *CommandInterpreter)IsQuitCmd(rawCmdExp string) bool{
33 | cmdExp := this.preExcute(rawCmdExp)
34 | for _, cmd := range QUIT_CMD{
35 | if cmd == cmdExp{
36 | return true
37 | }
38 | }
39 | return false
40 | }
41 |
42 | func (this *CommandInterpreter)help() string{
43 | helpStr := "有关某个命令的详细信息,请键入 help 命令名"
44 | for _, v := range this.commands{
45 | helpStr = fmt.Sprintf("%s\r\n%s", helpStr, v.Help())
46 | }
47 | return helpStr
48 | }
49 |
50 | func (this *CommandInterpreter)Excute(rawCmdExp string) string{
51 | defer func()string{
52 | if err := recover(); err != nil{
53 | logger.Error("invalid rawCmdExp: ", rawCmdExp)
54 | return "invalid rawCmdExp: " + rawCmdExp
55 | }
56 | return "Unkown ERROR!!!"
57 | }()
58 | if rawCmdExp == ""{
59 | return ""
60 | }
61 | rawCmdExps := strings.Split(rawCmdExp, " ")
62 | if len(rawCmdExps) == 0{
63 | return ""
64 | }
65 | cmdExps := make([]string, 0)
66 | for _, cmd := range rawCmdExps{
67 | cmdExps = append(cmdExps, this.preExcute(cmd))
68 | }
69 |
70 | if command, ok := this.commands[cmdExps[0]]; ok{
71 | return command.Run(cmdExps[1:])
72 | }else{
73 | if cmdExps[0] == "help"{
74 | return this.help()
75 | }else{
76 | return "command not found."
77 | }
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/xingo.go:
--------------------------------------------------------------------------------
1 | package xingo
2 |
3 | import (
4 | _ "github.com/viphxin/xingo/fnet"
5 | _ "github.com/viphxin/xingo/timer"
6 | "github.com/viphxin/xingo/telnetcmd"
7 | "github.com/viphxin/xingo/clusterserver"
8 | "github.com/viphxin/xingo/sys_rpc"
9 | "github.com/viphxin/xingo/utils"
10 | "github.com/viphxin/xingo/fserver"
11 | "github.com/viphxin/xingo/cluster"
12 | "github.com/viphxin/xingo/logger"
13 | "fmt"
14 | "github.com/viphxin/xingo/iface"
15 | )
16 |
17 | func NewXingoTcpServer() iface.Iserver{
18 | //do something
19 | //debugport 是否开放
20 | if utils.GlobalObject.DebugPort > 0{
21 | if utils.GlobalObject.Host != ""{
22 | fserver.NewTcpServer("telnet_server", "tcp4", utils.GlobalObject.Host,
23 | utils.GlobalObject.DebugPort, 100, cluster.NewTelnetProtocol()).Start()
24 | }else{
25 | fserver.NewTcpServer("telnet_server", "tcp4", "127.0.0.1",
26 | utils.GlobalObject.DebugPort, 100, cluster.NewTelnetProtocol()).Start()
27 | }
28 | logger.Debug(fmt.Sprintf("telnet tool start: %s:%d.", utils.GlobalObject.Host, utils.GlobalObject.DebugPort))
29 |
30 | }
31 |
32 | //add command
33 | if utils.GlobalObject.CmdInterpreter != nil{
34 | utils.GlobalObject.CmdInterpreter.AddCommand(telnetcmd.NewPprofCpuCommand())
35 | }
36 |
37 | s := fserver.NewServer()
38 | return s
39 | }
40 |
41 | func NewXingoMaster(cfg string) *clusterserver.Master{
42 | s := clusterserver.NewMaster(cfg)
43 | //add rpc
44 | s.AddRpcRouter(&sys_rpc.MasterRpc{})
45 | //add command
46 | if utils.GlobalObject.CmdInterpreter != nil{
47 | utils.GlobalObject.CmdInterpreter.AddCommand(telnetcmd.NewPprofCpuCommand())
48 | utils.GlobalObject.CmdInterpreter.AddCommand(telnetcmd.NewCloseServerCommand())
49 | utils.GlobalObject.CmdInterpreter.AddCommand(telnetcmd.NewReloadCfgCommand())
50 | }
51 | return s
52 | }
53 |
54 | func NewXingoCluterServer(nodename, cfg string) *clusterserver.ClusterServer{
55 | s := clusterserver.NewClusterServer(nodename,cfg)
56 | //add rpc
57 | s.AddRpcRouter(&sys_rpc.ChildRpc{})
58 | s.AddRpcRouter(&sys_rpc.RootRpc{})
59 | //add cmd
60 | if utils.GlobalObject.CmdInterpreter != nil{
61 | utils.GlobalObject.CmdInterpreter.AddCommand(telnetcmd.NewPprofCpuCommand())
62 | }
63 | return s
64 | }
65 |
--------------------------------------------------------------------------------
/cluster/asyncresult.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/viphxin/xingo/logger"
7 | "github.com/viphxin/xingo/utils"
8 | _ "os"
9 | "sync"
10 | "time"
11 | )
12 |
13 | type AsyncResult struct {
14 | key string
15 | result chan *RpcData
16 | }
17 |
18 | //func GenUUID() string{
19 | // f, _ := os.OpenFile("/dev/urandom", os.O_RDONLY, 0)
20 | // b := make([]byte, 16)
21 | // f.Read(b)
22 | // f.Close()
23 | // return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
24 | //}
25 |
26 | var AResultGlobalObj *AsyncResultMgr = NewAsyncResultMgr()
27 |
28 | func NewAsyncResult(key string) *AsyncResult {
29 | return &AsyncResult{
30 | key: key,
31 | result: make(chan *RpcData, 1),
32 | }
33 | }
34 |
35 | func (this *AsyncResult) GetKey() string {
36 | return this.key
37 | }
38 |
39 | func (this *AsyncResult) SetResult(data *RpcData) {
40 | this.result <- data
41 | }
42 |
43 | func (this *AsyncResult) GetResult(timeout time.Duration) (*RpcData, error) {
44 | select {
45 | case <-time.After(timeout):
46 | logger.Error(fmt.Sprintf("GetResult AsyncResult: timeout %s", this.key))
47 | close(this.result)
48 | return &RpcData{}, errors.New(fmt.Sprintf("GetResult AsyncResult: timeout %s", this.key))
49 | case result := <-this.result:
50 | return result, nil
51 | }
52 | return &RpcData{}, errors.New("GetResult AsyncResult error. reason: no")
53 | }
54 |
55 | type AsyncResultMgr struct {
56 | idGen *utils.UUIDGenerator
57 | results map[string]*AsyncResult
58 | sync.RWMutex
59 | }
60 |
61 | func NewAsyncResultMgr() *AsyncResultMgr {
62 | return &AsyncResultMgr{
63 | results: make(map[string]*AsyncResult, 0),
64 | idGen: utils.NewUUIDGenerator("async_result_"),
65 | }
66 | }
67 |
68 | func (this *AsyncResultMgr) Add() *AsyncResult {
69 | this.Lock()
70 | defer this.Unlock()
71 |
72 | r := NewAsyncResult(this.idGen.Get())
73 | this.results[r.GetKey()] = r
74 | return r
75 | }
76 |
77 | func (this *AsyncResultMgr) Remove(key string) {
78 | this.Lock()
79 | defer this.Unlock()
80 |
81 | delete(this.results, key)
82 | }
83 |
84 | func (this *AsyncResultMgr) GetAsyncResult(key string) (*AsyncResult, error) {
85 | this.RLock()
86 | defer this.RUnlock()
87 |
88 | r, ok := this.results[key]
89 | if ok {
90 | return r, nil
91 | } else {
92 | return nil, errors.New("not found AsyncResult")
93 | }
94 | }
95 |
96 | func (this *AsyncResultMgr) FillAsyncResult(key string, data *RpcData) error {
97 | r, err := this.GetAsyncResult(key)
98 | if err == nil {
99 | this.Remove(key)
100 | r.SetResult(data)
101 | return nil
102 | } else {
103 | return err
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/cluster/child.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/viphxin/xingo/iface"
7 | "github.com/viphxin/xingo/logger"
8 | "math/rand"
9 | "strings"
10 | "sync"
11 | )
12 |
13 | type Child struct {
14 | name string
15 | rpc *XingoRpc
16 | }
17 |
18 | func NewChild(name string, conn iface.IWriter) *Child {
19 | return &Child{
20 | name: name,
21 | rpc: NewXingoRpc(conn),
22 | }
23 | }
24 |
25 | func (this *Child) GetName() string {
26 | return this.name
27 | }
28 |
29 | func (this *Child) CallChildNotForResult(target string, args ...interface{}) error {
30 | return this.rpc.CallRpcNotForResult(target, args...)
31 | }
32 |
33 | func (this *Child) CallChildForResult(target string, args ...interface{}) (*RpcData, error) {
34 | return this.rpc.CallRpcForResult(target, args...)
35 | }
36 |
37 | type ChildMgr struct {
38 | childs map[string]*Child
39 | sync.RWMutex
40 | }
41 |
42 | func NewChildMgr() *ChildMgr {
43 | return &ChildMgr{
44 | childs: make(map[string]*Child, 0),
45 | }
46 | }
47 |
48 | func (this *ChildMgr) AddChild(name string, conn iface.IWriter) {
49 | this.Lock()
50 | defer this.Unlock()
51 |
52 | this.childs[name] = NewChild(name, conn)
53 | logger.Debug(fmt.Sprintf("child %s connected.", name))
54 | }
55 |
56 | func (this *ChildMgr) RemoveChild(name string) {
57 | this.Lock()
58 | defer this.Unlock()
59 |
60 | delete(this.childs, name)
61 | logger.Debug(fmt.Sprintf("child %s lostconnection.", name))
62 | }
63 |
64 | func (this *ChildMgr) GetChild(name string) (*Child, error) {
65 | this.RLock()
66 | defer this.RUnlock()
67 |
68 | child, ok := this.childs[name]
69 | if ok {
70 | return child, nil
71 | } else {
72 | return nil, errors.New(fmt.Sprintf("no child named %s", name))
73 | }
74 | }
75 |
76 | func (this *ChildMgr) GetChildsByPrefix(namePrefix string) []*Child {
77 | this.RLock()
78 | defer this.RUnlock()
79 |
80 | childs := make([]*Child, 0)
81 | for k, v := range this.childs {
82 | if strings.HasPrefix(k, namePrefix) {
83 | childs = append(childs, v)
84 | }
85 | }
86 | return childs
87 | }
88 |
89 | func (this *ChildMgr) GetChilds() []*Child {
90 | this.RLock()
91 | defer this.RUnlock()
92 |
93 | childs := make([]*Child, 0)
94 | for _, v := range this.childs {
95 | childs = append(childs, v)
96 | }
97 | return childs
98 | }
99 |
100 | func (this *ChildMgr) GetRandomChild(namesuffix string) *Child {
101 | childs := make([]*Child, 0)
102 | if namesuffix != "" {
103 | //一类
104 | childs = this.GetChildsByPrefix(namesuffix)
105 | } else {
106 | //所有
107 | childs = this.GetChilds()
108 | }
109 | if len(childs) > 0 {
110 | pos := rand.Intn(len(childs))
111 | return childs[pos]
112 | }
113 | return nil
114 | }
115 |
--------------------------------------------------------------------------------
/cluster/rpcpack.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "errors"
7 | "fmt"
8 | "github.com/viphxin/xingo/fnet"
9 | "github.com/viphxin/xingo/iface"
10 | "encoding/gob"
11 | "github.com/viphxin/xingo/logger"
12 | )
13 |
14 | type RpcData struct {
15 | MsgType RpcSignal `json:"msgtype"`
16 | Key string `json:"key,omitempty"`
17 | Target string `json:"target,omitempty"`
18 | Args []interface{} `json:"args,omitempty"`
19 | Result map[string]interface{} `json:"result,omitempty"`
20 | }
21 |
22 | type RpcPackege struct {
23 | Len int32
24 | Data []byte
25 | }
26 |
27 | type RpcRequest struct {
28 | Fconn iface.IWriter
29 | Rpcdata *RpcData
30 | }
31 |
32 | type RpcDataPack struct{}
33 |
34 | func NewRpcDataPack() *RpcDataPack {
35 | return &RpcDataPack{}
36 | }
37 |
38 | func (this *RpcDataPack) GetHeadLen() int32 {
39 | return 4
40 | }
41 |
42 | func (this *RpcDataPack) Unpack(headdata []byte) (interface{}, error) {
43 | headbuf := bytes.NewReader(headdata)
44 |
45 | rp := &RpcPackege{}
46 |
47 | // 读取Len
48 | if err := binary.Read(headbuf, binary.LittleEndian, &rp.Len); err != nil {
49 | return nil, err
50 | }
51 |
52 | // 封包太大
53 | if rp.Len > fnet.MaxPacketSize {
54 | return nil, errors.New("rpc packege too big!!!")
55 | }
56 |
57 | return rp, nil
58 | }
59 |
60 | //func (this *RpcDataPack) Pack(msgId uint32, pkg interface{}) (out []byte, err error) {
61 | // outbuff := bytes.NewBuffer([]byte{})
62 | // // 进行编码
63 | // dataBytes := []byte{}
64 | // data := pkg.(*RpcData)
65 | // if data != nil {
66 | // dataBytes, err = json.Marshal(data)
67 | // }
68 | //
69 | // if err != nil {
70 | // fmt.Println(fmt.Sprintf("json marshaling error: %s", err))
71 | // }
72 | // // 写Len
73 | // if err = binary.Write(outbuff, binary.LittleEndian, uint32(len(dataBytes))); err != nil {
74 | // return
75 | // }
76 | //
77 | // //all pkg data
78 | // if err = binary.Write(outbuff, binary.LittleEndian, dataBytes); err != nil {
79 | // return
80 | // }
81 | //
82 | // out = outbuff.Bytes()
83 | // return
84 | //
85 | //}
86 |
87 | func (this *RpcDataPack) Pack(msgId uint32, pkg interface{}) (out []byte, err error) {
88 | outbuff := bytes.NewBuffer([]byte{})
89 | // 进行编码
90 | databuff := bytes.NewBuffer([]byte{})
91 | data := pkg.(*RpcData)
92 | if data != nil {
93 | enc := gob.NewEncoder(databuff)
94 | err = enc.Encode(data)
95 | }
96 |
97 | if err != nil {
98 | logger.Error(fmt.Sprintf("rpcpack gob marshaling error: %s", err))
99 | return
100 | }
101 | // 写Len
102 | if err = binary.Write(outbuff, binary.LittleEndian, uint32(databuff.Len())); err != nil {
103 | return
104 | }
105 |
106 | //all pkg data
107 | if err = binary.Write(outbuff, binary.LittleEndian, databuff.Bytes()); err != nil {
108 | return
109 | }
110 |
111 | out = outbuff.Bytes()
112 | return
113 |
114 | }
--------------------------------------------------------------------------------
/timer/safetimer.go:
--------------------------------------------------------------------------------
1 | package timer
2 |
3 | import (
4 | "github.com/viphxin/xingo/logger"
5 | "sync"
6 | "time"
7 | "math"
8 | )
9 |
10 | /*
11 | 协程安全的timer
12 | */
13 | const (
14 | //默认安全时间调度器的容量
15 | TIMERLEN = 2048
16 | //默认最大误差值100毫秒
17 | ERRORMAX = 100
18 | //默认最大触发队列缓冲大小
19 | TRIGGERMAX = 2048
20 | //默认hashwheel分级
21 | LEVEL = 12
22 | )
23 |
24 | func UnixTS() int64 {
25 | return time.Now().UnixNano() / 1e6
26 | }
27 |
28 | type ParamNull struct {}
29 |
30 | type SafeTimer struct {
31 | //延迟调用的函数
32 | delayCall *DelayCall
33 | //调用的时间:单位毫秒
34 | unixts int64
35 | }
36 |
37 | func NewSafeTimer(delay int64, delayCall *DelayCall) *SafeTimer {
38 | unixts := UnixTS()
39 | if delay > 0 {
40 | unixts += delay
41 | }
42 | return &SafeTimer{
43 | delayCall: delayCall,
44 | unixts: unixts,
45 | }
46 | }
47 |
48 | type SafeTimerScheduel struct {
49 | hashwheel *HashWheel
50 | idGen uint32
51 | triggerChan chan *DelayCall
52 | sync.RWMutex
53 | }
54 |
55 | func NewSafeTimerScheduel() *SafeTimerScheduel {
56 | scheduel := &SafeTimerScheduel{
57 | hashwheel: NewHashWheel("wheel_hours", LEVEL, 3600*1e3, TIMERLEN),
58 | idGen: 0,
59 | triggerChan: make(chan *DelayCall, TRIGGERMAX),
60 | }
61 |
62 | //minute wheel
63 | minuteWheel := NewHashWheel("wheel_minutes", LEVEL, 60*1e3, TIMERLEN)
64 | //second wheel
65 | secondWheel := NewHashWheel("wheel_seconds", LEVEL, 1*1e3, TIMERLEN)
66 | minuteWheel.AddNext(secondWheel)
67 | scheduel.hashwheel.AddNext(minuteWheel)
68 |
69 | go scheduel.StartScheduelLoop()
70 | return scheduel
71 | }
72 |
73 | func (this *SafeTimerScheduel) GetTriggerChannel() chan *DelayCall {
74 | return this.triggerChan
75 | }
76 |
77 | func (this *SafeTimerScheduel) CreateTimer(delay int64, f func(v ...interface{}), args []interface{}) (uint32, error) {
78 | this.Lock()
79 | defer this.Unlock()
80 |
81 | this.idGen += 1
82 | err := this.hashwheel.Add2WheelChain(this.idGen,
83 | NewSafeTimer(delay, &DelayCall{
84 | f: f,
85 | args: args,
86 | }))
87 | if err != nil{
88 | return 0, err
89 | }else{
90 | return this.idGen, nil
91 | }
92 | }
93 |
94 | func (this *SafeTimerScheduel) CancelTimer(timerId uint32) {
95 | this.hashwheel.RemoveFromWheelChain(timerId)
96 | }
97 |
98 | func (this *SafeTimerScheduel) StartScheduelLoop() {
99 | logger.Info("xingo safe timer scheduelloop runing.")
100 | for {
101 | triggerList := this.hashwheel.GetTriggerWithIn(ERRORMAX)
102 | //trigger
103 | for _, v := range triggerList {
104 | //logger.Debug("want call: ", v.unixts, ".real call: ", UnixTS(), ".ErrorMS: ", UnixTS()-v.unixts)
105 | if math.Abs(float64(UnixTS()-v.unixts)) > float64(ERRORMAX){
106 | logger.Error("want call: ", v.unixts, ".real call: ", UnixTS(), ".ErrorMS: ", UnixTS()-v.unixts)
107 | }
108 | this.triggerChan <- v.delayCall
109 | }
110 |
111 | //wait for next loop
112 | time.Sleep(ERRORMAX/2*time.Millisecond)
113 | }
114 | }
--------------------------------------------------------------------------------
/cluster/telnetprotocol.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "github.com/viphxin/xingo/iface"
5 | "github.com/viphxin/xingo/logger"
6 | "fmt"
7 | "time"
8 | "bufio"
9 | "strings"
10 | "github.com/viphxin/xingo/utils"
11 | )
12 | /*
13 | debug tool protocol
14 | */
15 |
16 | type TelnetProtocol struct {}
17 |
18 | func NewTelnetProtocol() *TelnetProtocol {
19 | if utils.GlobalObject.CmdInterpreter == nil{
20 | utils.GlobalObject.CmdInterpreter = NewCommandInterpreter()
21 | }
22 | return &TelnetProtocol{}
23 | }
24 |
25 | func (this *TelnetProtocol) GetMsgHandle() iface.Imsghandle {
26 | return nil
27 | }
28 | func (this *TelnetProtocol) GetDataPack() iface.Idatapack {
29 | return nil
30 | }
31 |
32 | func (this *TelnetProtocol) AddRpcRouter(router interface{}) {
33 |
34 | }
35 |
36 | func (this *TelnetProtocol) InitWorker(poolsize int32) {
37 |
38 | }
39 |
40 | func (this *TelnetProtocol)isWriteListIP(ip string) bool{
41 | for _, wip := range utils.GlobalObject.WriteList{
42 | if strings.EqualFold(ip, wip){
43 | return true
44 | }
45 | }
46 | return false
47 | }
48 |
49 | func (this *TelnetProtocol)getConnectionAddr(fconn iface.Iconnection)[]string{
50 | return strings.Split(fconn.RemoteAddr().String(), ":")
51 | }
52 |
53 | func (this *TelnetProtocol) OnConnectionMade(fconn iface.Iconnection) {
54 | logger.Info(fmt.Sprintf("client ID: %d connected. IP Address: %s", fconn.GetSessionId(), fconn.RemoteAddr()))
55 | addr := this.getConnectionAddr(fconn)
56 | if !this.isWriteListIP(addr[0]){
57 | logger.Error("invald IP: ", addr[0])
58 | fconn.LostConnection()
59 | }
60 | }
61 |
62 | func (this *TelnetProtocol) OnConnectionLost(fconn iface.Iconnection) {
63 | logger.Info(fmt.Sprintf("client ID: %d disconnected. IP Address: %s", fconn.GetSessionId(), fconn.RemoteAddr()))
64 | }
65 |
66 | func (this *TelnetProtocol) StartReadThread(fconn iface.Iconnection) {
67 | logger.Info("start receive data from telnet socket...")
68 | fconn.GetConnection().Write([]byte(fmt.Sprintf("-------welcome to xingo telnet tool(node: %s)---------\r\n", utils.GlobalObject.Name)))
69 | for {
70 | if err := fconn.GetConnection().SetReadDeadline(time.Now().Add(time.Minute*3)); err != nil {
71 | logger.Error("telnet connection SetReadDeadline error: ", err)
72 | fconn.LostConnection()
73 | break
74 | }
75 | line, err := bufio.NewReader(fconn.GetConnection()).ReadString('\n')
76 | if err != nil {
77 | logger.Error("telnet connection read line error: ", err)
78 | fconn.Stop()
79 | break
80 | }
81 | line = strings.TrimSuffix(line[:len(line)-1], "\r")
82 | logger.Info(fmt.Sprintf("xingo telnet tool received: %s. ip: %s", line, this.getConnectionAddr(fconn)[0]))
83 | if utils.GlobalObject.CmdInterpreter.IsQuitCmd(line){
84 | logger.Error("telnet exit ")
85 | fconn.LostConnection()
86 | break
87 | }else{
88 | ack := utils.GlobalObject.CmdInterpreter.Excute(line)
89 | fconn.GetConnection().Write([]byte(ack))
90 | }
91 | if err := fconn.GetConnection().SetReadDeadline(time.Time{}); err != nil {
92 | logger.Error("telnet connection SetReadDeadline error: ", err)
93 | fconn.LostConnection()
94 | break
95 | }
96 |
97 | }
98 | }
99 |
100 |
--------------------------------------------------------------------------------
/telnetcmd/mastercommand.go:
--------------------------------------------------------------------------------
1 | package telnetcmd
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/clusterserver"
6 | "strconv"
7 | )
8 |
9 | type CloseServerCommand struct {
10 | }
11 |
12 | func NewCloseServerCommand() *CloseServerCommand{
13 | return &CloseServerCommand{}
14 | }
15 | func (this *CloseServerCommand)Name()string{
16 | return "closeserver"
17 | }
18 |
19 | func (this *CloseServerCommand)Help()string{
20 | return fmt.Sprintf("closeserver:\r\n" +
21 | "----------- all delay: 延迟delay秒时间关闭所有子节点\r\n" +
22 | "----------- node name delay: 延迟delay秒时间关闭指定节点\r\n")
23 | }
24 |
25 | func (this *CloseServerCommand)Run(args []string) string{
26 | if len(args) == 0{
27 | return this.Help()
28 | }else{
29 | switch args[0] {
30 | case "all":
31 | for _, child := range clusterserver.GlobalMaster.Childs.GetChilds() {
32 | if len(args) > 1{
33 | if v, err := strconv.ParseInt(args[1], 10, 64); err == nil{
34 | child.CallChildNotForResult("CloseServer", int(v))
35 | }else{
36 | child.CallChildNotForResult("CloseServer", int(0))
37 | }
38 | }else{
39 | child.CallChildNotForResult("CloseServer", int(0))
40 | }
41 | }
42 | default:
43 | child, err := clusterserver.GlobalMaster.Childs.GetChild(args[0])
44 | if err != nil{
45 | return fmt.Sprintf("no sush node: %s.", args[0])
46 | }else{
47 | if len(args) > 1{
48 | if v, err := strconv.ParseInt(args[1], 10, 64); err == nil{
49 | child.CallChildNotForResult("CloseServer", int(v))
50 | }else{
51 | child.CallChildNotForResult("CloseServer", int(0))
52 | }
53 | }else{
54 | child.CallChildNotForResult("CloseServer", int(0))
55 | }
56 | }
57 | }
58 | }
59 | return "OK"
60 | }
61 |
62 | type ReloadCfgCommand struct {
63 | }
64 |
65 | func NewReloadCfgCommand() *ReloadCfgCommand{
66 | return &ReloadCfgCommand{}
67 | }
68 | func (this *ReloadCfgCommand)Name()string{
69 | return "reloadcfg"
70 | }
71 |
72 | func (this *ReloadCfgCommand)Help()string{
73 | return fmt.Sprintf("reloadcfg:\r\n" +
74 | "----------- all delay: 延迟delay秒时间重新加载所有节点的配置文件\r\n" +
75 | "----------- node name delay: 延迟delay秒时间重新加载指定节点\r\n")
76 | }
77 |
78 | func (this *ReloadCfgCommand)Run(args []string) string{
79 | if len(args) == 0{
80 | return this.Help()
81 | }else{
82 | switch args[0] {
83 | case "all":
84 | for _, child := range clusterserver.GlobalMaster.Childs.GetChilds() {
85 | if len(args) > 1{
86 | if v, err := strconv.ParseInt(args[1], 10, 64); err == nil{
87 | child.CallChildNotForResult("ReloadConfig", int(v))
88 | }else{
89 | child.CallChildNotForResult("ReloadConfig", int(0))
90 | }
91 | }else{
92 | child.CallChildNotForResult("ReloadConfig", int(0))
93 | }
94 | }
95 | default:
96 | child, err := clusterserver.GlobalMaster.Childs.GetChild(args[0])
97 | if err != nil{
98 | return fmt.Sprintf("no sush node: %s.", args[0])
99 | }else{
100 | if len(args) > 1{
101 | if v, err := strconv.ParseInt(args[1], 10, 64); err == nil{
102 | child.CallChildNotForResult("ReloadConfig", int(v))
103 | }else{
104 | child.CallChildNotForResult("ReloadConfig", int(0))
105 | }
106 | }else{
107 | child.CallChildNotForResult("ReloadConfig", int(0))
108 | }
109 | }
110 | }
111 | }
112 | return "OK"
113 | }
114 |
--------------------------------------------------------------------------------
/fnet/tcpclient.go:
--------------------------------------------------------------------------------
1 | package fnet
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/viphxin/xingo/iface"
7 | "github.com/viphxin/xingo/logger"
8 | "net"
9 | "sync"
10 | "time"
11 | )
12 |
13 | const (
14 | MAX_RETRY = 1024 //父节点掉线最大重连次数
15 | RETRY_INTERVAL = 60 //重连间隔60s
16 | )
17 |
18 | type TcpClient struct {
19 | conn *net.TCPConn
20 | addr *net.TCPAddr
21 | protoc iface.IClientProtocol
22 | PropertyBag map[string]interface{}
23 | reconnCB func(iface.Iclient)
24 | maxRetry int
25 | retryInterval int
26 | sendtagGuard sync.RWMutex
27 | propertyLock sync.RWMutex
28 | }
29 |
30 | func NewReConnTcpClient(ip string, port int, protoc iface.IClientProtocol, maxRetry int,
31 | retryInterval int, reconnCB func(iface.Iclient)) *TcpClient {
32 | client := NewTcpClient(ip, port, protoc)
33 | client.maxRetry = maxRetry
34 | client.retryInterval = retryInterval
35 | client.reconnCB = reconnCB
36 | return client
37 | }
38 |
39 | func NewTcpClient(ip string, port int, protoc iface.IClientProtocol) *TcpClient {
40 | addr := &net.TCPAddr{
41 | IP: net.ParseIP(ip),
42 | Port: port,
43 | Zone: "",
44 | }
45 | conn, err := net.DialTCP("tcp", nil, addr)
46 | if err == nil {
47 | client := &TcpClient{
48 | conn: conn,
49 | addr: addr,
50 | protoc: protoc,
51 | PropertyBag: make(map[string]interface{}, 0),
52 | }
53 | go client.protoc.OnConnectionMade(client)
54 | return client
55 | } else {
56 | panic(err)
57 | }
58 |
59 | }
60 |
61 | func (this *TcpClient) Start() {
62 | go this.protoc.StartReadThread(this)
63 | }
64 |
65 | func (this *TcpClient) Stop(isforce bool) {
66 | if this.maxRetry == 0 || isforce {
67 | this.protoc.OnConnectionLost(this)
68 | } else {
69 | //retry
70 | if this.ReConnection() {
71 | //顺序很重要,先把读数据用的goroutine开启
72 | this.Start()
73 | if this.reconnCB != nil {
74 | this.reconnCB(this)
75 | }
76 | }
77 | }
78 | }
79 |
80 | func (this *TcpClient) ReConnection() bool {
81 | logger.Info("reconnection ...")
82 | for i := 1; i <= this.maxRetry; i++ {
83 | logger.Info("retry time ", i)
84 | conn, err := net.DialTCP("tcp", nil, this.addr)
85 | if err == nil {
86 | this.conn = conn
87 | return true
88 | } else {
89 | d, err := time.ParseDuration(fmt.Sprintf("%ds", this.retryInterval))
90 | if err != nil {
91 | time.Sleep(RETRY_INTERVAL * time.Second)
92 | } else {
93 | time.Sleep(d)
94 | }
95 | }
96 | }
97 | return false
98 | }
99 |
100 | func (this *TcpClient) Send(data []byte) error {
101 | this.sendtagGuard.Lock()
102 | defer this.sendtagGuard.Unlock()
103 |
104 | if _, err := this.conn.Write(data); err != nil {
105 | logger.Error(fmt.Sprintf("rpc client send data error.reason: %s", err))
106 | return err
107 | }
108 | return nil
109 | }
110 |
111 | func (this *TcpClient) GetConnection() *net.TCPConn {
112 | return this.conn
113 | }
114 |
115 | func (this *TcpClient) GetProperty(key string) (interface{}, error) {
116 | this.propertyLock.RLock()
117 | defer this.propertyLock.RUnlock()
118 |
119 | value, ok := this.PropertyBag[key]
120 | if ok {
121 | return value, nil
122 | } else {
123 | return nil, errors.New("no property in connection")
124 | }
125 | }
126 |
127 | func (this *TcpClient) SetProperty(key string, value interface{}) {
128 | this.propertyLock.Lock()
129 | defer this.propertyLock.Unlock()
130 |
131 | this.PropertyBag[key] = value
132 | }
133 |
134 | func (this *TcpClient) RemoveProperty(key string) {
135 | this.propertyLock.Lock()
136 | defer this.propertyLock.Unlock()
137 |
138 | delete(this.PropertyBag, key)
139 | }
140 |
--------------------------------------------------------------------------------
/cluster/rpchandle.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | /*
4 | regest rpc
5 | */
6 | import (
7 | "fmt"
8 | "github.com/viphxin/xingo/logger"
9 | "github.com/viphxin/xingo/utils"
10 | "math/rand"
11 | "reflect"
12 | "time"
13 | )
14 |
15 | type RpcMsgHandle struct {
16 | PoolSize int32
17 | TaskQueue []chan *RpcRequest
18 | Apis map[string]reflect.Value
19 | }
20 |
21 | func NewRpcMsgHandle() *RpcMsgHandle {
22 | return &RpcMsgHandle{
23 | PoolSize: utils.GlobalObject.PoolSize,
24 | TaskQueue: make([]chan *RpcRequest, utils.GlobalObject.PoolSize),
25 | Apis: make(map[string]reflect.Value),
26 | }
27 | }
28 |
29 | /*
30 | 处理rpc消息
31 | */
32 | func (this *RpcMsgHandle) DoMsg(request *RpcRequest) {
33 | if request.Rpcdata.MsgType == RESPONSE && request.Rpcdata.Key != "" {
34 | //放回异步结果
35 | AResultGlobalObj.FillAsyncResult(request.Rpcdata.Key, request.Rpcdata)
36 | return
37 | } else {
38 | //rpc 请求
39 | if f, ok := this.Apis[request.Rpcdata.Target]; ok {
40 | //存在
41 | st := time.Now()
42 | if request.Rpcdata.MsgType == REQUEST_FORRESULT {
43 | ret := f.Call([]reflect.Value{reflect.ValueOf(request)})
44 | if len(ret) == 0 {
45 | return
46 | }
47 | packdata, err := utils.GlobalObject.RpcCProtoc.GetDataPack().Pack(0, &RpcData{
48 | MsgType: RESPONSE,
49 | Result: ret[0].Interface().(map[string]interface{}),
50 | Key: request.Rpcdata.Key,
51 | })
52 | if err == nil {
53 | request.Fconn.Send(packdata)
54 | } else {
55 | logger.Error(err)
56 | }
57 | } else if request.Rpcdata.MsgType == REQUEST_NORESULT {
58 | f.Call([]reflect.Value{reflect.ValueOf(request)})
59 | }
60 |
61 | logger.Debug(fmt.Sprintf("rpc %s cost total time: %f ms", request.Rpcdata.Target, time.Now().Sub(st).Seconds()*1000))
62 | } else {
63 | logger.Error(fmt.Sprintf("not found rpc: %s", request.Rpcdata.Target))
64 | }
65 | }
66 | }
67 |
68 | func (this *RpcMsgHandle) DeliverToMsgQueue(pkg interface{}) {
69 | request := pkg.(*RpcRequest)
70 | //add to worker pool
71 | index := rand.Int31n(utils.GlobalObject.PoolSize)
72 | taskQueue := this.TaskQueue[index]
73 | logger.Debug(fmt.Sprintf("add to rpc pool : %d", index))
74 | taskQueue <- request
75 | }
76 |
77 | func (this *RpcMsgHandle) DoMsgFromGoRoutine(pkg interface{}) {
78 | request := pkg.(*RpcRequest)
79 | go this.DoMsg(request)
80 | }
81 |
82 | func (this *RpcMsgHandle) AddRouter(router interface{}) {
83 | value := reflect.ValueOf(router)
84 | tp := value.Type()
85 | for i := 0; i < value.NumMethod(); i += 1 {
86 | name := tp.Method(i).Name
87 |
88 | if _, ok := this.Apis[name]; ok {
89 | //存在
90 | panic("repeated rpc " + name)
91 | }
92 | this.Apis[name] = value.Method(i)
93 | logger.Info("add rpc " + name)
94 | }
95 | }
96 |
97 | func (this *RpcMsgHandle) StartWorkerLoop(poolSize int) {
98 | if utils.GlobalObject.IsThreadSafeMode(){
99 | this.TaskQueue[0] = make(chan *RpcRequest, utils.GlobalObject.MaxWorkerLen)
100 | go func(){
101 | for{
102 | select {
103 | case rpcRequest := <- this.TaskQueue[0]:
104 | this.DoMsg(rpcRequest)
105 | case delayCall := <- utils.GlobalObject.GetSafeTimer().GetTriggerChannel():
106 | delayCall.Call()
107 | }
108 | }
109 | }()
110 | }else{
111 | for i := 0; i < poolSize; i += 1 {
112 | c := make(chan *RpcRequest, utils.GlobalObject.MaxWorkerLen)
113 | this.TaskQueue[i] = c
114 | go func(index int, taskQueue chan *RpcRequest) {
115 | logger.Info(fmt.Sprintf("init rpc thread pool %d.", index))
116 | for {
117 | request := <-taskQueue
118 | this.DoMsg(request)
119 | }
120 |
121 | }(i, c)
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xingo_cluster
2 |
3 | xingo golang游戏开发交流群:535378240
4 | 文档地址: http://www.runingman.net/
5 | ```text
6 | xingo是免费、开源、可定制、可扩展、节点支持“热更新”的高性能分布式服务器开发框架,采用golang语言开发,天生携带
7 | 高并发场景的处理基因,继承了golang语言本身的各种优点,开发简单易上手并且功能强大。它主要实现了高性能的异步网络库,
8 | 分布式节点间的高性能rpc通信,日志管理,数据库支持(暂时只支持mongodb),goroutine安全的定时器,telnet在线服务器调试
9 | 工具等,可用的开发场景包括但不局限于IM即时通讯服务器,游戏服务器(已有多款公司级别的项目选择了xingo)等可以节省大量
10 | 游戏开发时间,让游戏开发人员可以将主要精力放到游戏玩法和游戏逻辑上。真正实现了修改配置文件就可以搭建自定义的分布式服
11 | 务器架构。
12 |
13 | 优势特点:
14 | 1) 开发效率高
15 | 2) 支持自定义的分布式架构,方便横向扩展节点,理论上只要有足够的物理机器,没有承载上限
16 | 3) 支持自定义通信协议
17 | 4) 分布式节点自动发现,自动重连
18 | 5) worker pool工作线程池
19 | 6) telnet在线服务调试工具(使用方便扩展简单)
20 | 7) 内置mongodb数据库支持
21 | 8)goroutine安全的定时器实现
22 | ```
23 | 示例配置:
24 | ```json
25 | {
26 | "master":{"host": "192.168.2.225","rootport":9999},
27 | "servers":{
28 | "gate2":{"host": "192.168.2.225", "rootport":10000,"name":"gate2", "module": "gate", "log": "gate2.log"},
29 | "gate1":{"host": "192.168.2.225", "rootport":10001,"name":"gate1", "module": "gate", "log": "gate1.log"},
30 | "net1":{"host": "192.168.2.225", "netport":11009,"name":"net1","remotes":["gate2", "gate1"],
31 | "module": "net", "log": "net.log"},
32 | "net2":{"host": "192.168.2.225", "netport":11010,"name":"net2","remotes":["gate2", "gate1"],
33 | "module": "net", "log": "net.log"},
34 | "net3":{"host": "192.168.2.225", "netport":11011,"name":"net3","remotes":["gate2", "gate1"],
35 | "module": "net", "log": "net.log"},
36 | "net4":{"host": "192.168.2.225", "netport":11012,"name":"net4","remotes":["gate2", "gate1"],
37 | "module": "net", "log": "net.log"},
38 | "admin":{"host": "192.168.2.225", "remotes":["gate2", "gate1"], "name":"admin", "module": "admin",
39 | "http": [8888, "/static"]},
40 | "game1":{"host": "192.168.2.225", "remotes":["gate2", "gate1"], "name":"game1", "module": "game"}
41 | }
42 | }
43 | ```
44 | 示例架构图:
45 | 
46 |
47 |
48 | 默认通信协议如下(支持自定义协议处理部分代码,支持灵活的重载协议部分代码):
49 |
50 | Len uint32 数据Data部分长度
51 | MsgId uint32 消息号
52 | Data []byte 数据
53 | 消息默认通过google 的protobuf进行序列化
54 |
55 | 服务器全局配置对象为GlobalObject,支持的配置选项及默认值如下:
56 | TcpPort: 8109,//服务器监听端口
57 | MaxConn: 12000,//支持最大链接数
58 | LogPath: "./log",//日志文件路径
59 | LogName: "server.log",//日志文件名
60 | MaxLogNum: 10,//最大日志数
61 | MaxFileSize: 100,//per日志文件大小
62 | LogFileUnit: logger.KB,//日志文件大小对应单位
63 | LogLevel: logger.ERROR,//日志级别
64 | SetToConsole: true,//是否输出到console
65 | LogFileType: 1,//日志切割方式1 按天切割 2按文件大小切割
66 | PoolSize: 10,//api接口工作线程数量
67 | MaxWorkerLen: 1024 * 2,//任务缓冲池大小
68 | MaxSendChanLen: 1024,//发送队列从缓冲池
69 | FrameSpeed: 30,//未使用
70 | MaxPacketSize: 1024,//协议数据包最大包体大小
71 | FrequencyControl: 100/s,// 100/h(每小时一百个包), 100/m(每分钟一百个包), 100/s(每秒一百个包)
72 | OnConnectioned: func(fconn iface.Iconnection) {},//链接建立事件回调
73 | OnClosed: func(fconn iface.Iconnection) {},//链接断开事件回调
74 | OnServerStop: func(), //服务器停服回调
75 | Protoc: iface.IServerProtocol//socket数据pack和unpack的实现,可以通过设置该值重载服务器协议
76 |
77 | 如何使用?
78 | 只需要一步,添加消息路由:
79 | s := fserver.NewServer()
80 | //add api ---------------start
81 | FightingRouterObj := &api.FightingRouter{}
82 | s.AddRouter(FightingRouterObj)
83 | //add api ---------------end
84 | xingo会自动注册FightingRouter中的方法处理对应消息
85 | 例如:msgId =1 则会寻找FightingRouter中的Func_1的方法从进行处理
86 | 具体使用请参考项目:
87 | 帧同步服务器: https://github.com/viphxin/fighting
88 | mmo demo: https://git.oschina.net/viphxin/xingo_demo
89 | xingo_cluster demo: https://github.com/viphxin/xingo_cluster
90 |
91 |
--------------------------------------------------------------------------------
/utils/globalobj.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/viphxin/xingo/iface"
6 | "github.com/viphxin/xingo/logger"
7 | "io/ioutil"
8 | "strconv"
9 | "strings"
10 | "os"
11 | "github.com/viphxin/xingo/timer"
12 | )
13 |
14 | type GlobalObj struct {
15 | TcpServers map[string]iface.Iserver
16 | TcpServer iface.Iserver
17 | OnConnectioned func(fconn iface.Iconnection)
18 | OnClosed func(fconn iface.Iconnection)
19 | OnClusterConnectioned func(fconn iface.Iconnection) //集群rpc root节点回调
20 | OnClusterClosed func(fconn iface.Iconnection)
21 | OnClusterCConnectioned func(fconn iface.Iclient) //集群rpc 子节点回调
22 | OnClusterCClosed func(fconn iface.Iclient)
23 | OnServerStop func() //服务器停服回调
24 | Protoc iface.IServerProtocol
25 | RpcSProtoc iface.IServerProtocol
26 | RpcCProtoc iface.IClientProtocol
27 | Host string
28 | DebugPort int //telnet port 用于单机模式
29 | WriteList []string //telnet ip list
30 | TcpPort int
31 | MaxConn int
32 | IntraMaxConn int //内部服务器最大连接数
33 | //log
34 | LogPath string
35 | LogName string
36 | MaxLogNum int32
37 | MaxFileSize int64
38 | LogFileUnit logger.UNIT
39 | LogLevel logger.LEVEL
40 | SetToConsole bool
41 | LogFileType int32
42 | PoolSize int32
43 | MaxWorkerLen int32
44 | MaxSendChanLen int32
45 | FrameSpeed uint8
46 | Name string
47 | MaxPacketSize uint32
48 | FrequencyControl string // 100/h, 100/m, 100/s
49 | CmdInterpreter iface.ICommandInterpreter //xingo debug tool Interpreter
50 | ProcessSignalChan chan os.Signal
51 | safeTimerScheduel *timer.SafeTimerScheduel
52 | }
53 |
54 | func (this *GlobalObj) GetFrequency() (int, string) {
55 | fc := strings.Split(this.FrequencyControl, "/")
56 | if len(fc) != 2 {
57 | return 0, ""
58 | } else {
59 | fc0_int, err := strconv.Atoi(fc[0])
60 | if err == nil {
61 | return fc0_int, fc[1]
62 | } else {
63 | logger.Error("FrequencyControl params error: ", this.FrequencyControl)
64 | return 0, ""
65 | }
66 | }
67 | }
68 |
69 | func (this *GlobalObj)IsThreadSafeMode()bool{
70 | if this.PoolSize == 1{
71 | return true
72 | }else{
73 | return false
74 | }
75 | }
76 |
77 | func (this *GlobalObj)GetSafeTimer() *timer.SafeTimerScheduel{
78 | return this.safeTimerScheduel
79 | }
80 |
81 | func (this *GlobalObj)Reload(){
82 | //读取用户自定义配置
83 | data, err := ioutil.ReadFile("conf/server.json")
84 | if err != nil {
85 | panic(err)
86 | }
87 | err = json.Unmarshal(data, this)
88 | if err != nil {
89 | panic(err)
90 | }else{
91 | ReSettingLog()
92 | //init safetimer
93 | if GlobalObject.safeTimerScheduel == nil && GlobalObject.IsThreadSafeMode(){
94 | GlobalObject.safeTimerScheduel = timer.NewSafeTimerScheduel()
95 | }
96 | }
97 | }
98 |
99 | var GlobalObject *GlobalObj
100 |
101 | func init() {
102 | GlobalObject = &GlobalObj{
103 | TcpServers: make(map[string]iface.Iserver),
104 | Host: "0.0.0.0",
105 | TcpPort: 8109,
106 | MaxConn: 12000,
107 | IntraMaxConn: 100,
108 | LogPath: "./log",
109 | LogName: "server.log",
110 | MaxLogNum: 10,
111 | MaxFileSize: 100,
112 | LogFileUnit: logger.KB,
113 | LogLevel: logger.DEBUG,
114 | SetToConsole: true,
115 | LogFileType: 1,
116 | PoolSize: 10,
117 | MaxWorkerLen: 1024 * 2,
118 | MaxSendChanLen: 1024,
119 | FrameSpeed: 30,
120 | OnConnectioned: func(fconn iface.Iconnection) {},
121 | OnClosed: func(fconn iface.Iconnection) {},
122 | OnClusterConnectioned: func(fconn iface.Iconnection) {},
123 | OnClusterClosed: func(fconn iface.Iconnection) {},
124 | OnClusterCConnectioned: func(fconn iface.Iclient) {},
125 | OnClusterCClosed: func(fconn iface.Iclient) {},
126 | ProcessSignalChan: make(chan os.Signal, 1),
127 | }
128 | GlobalObject.Reload()
129 | }
130 |
--------------------------------------------------------------------------------
/fnet/msghandle.go:
--------------------------------------------------------------------------------
1 | package fnet
2 |
3 | /*
4 | find msg api
5 | */
6 | import (
7 | "fmt"
8 | "github.com/viphxin/xingo/logger"
9 | "github.com/viphxin/xingo/utils"
10 | "reflect"
11 | "strconv"
12 | "strings"
13 | "time"
14 | "runtime/debug"
15 | )
16 |
17 | type MsgHandle struct {
18 | PoolSize int32
19 | TaskQueue []chan *PkgAll
20 | Apis map[uint32]reflect.Value
21 | }
22 |
23 | func NewMsgHandle() *MsgHandle {
24 | return &MsgHandle{
25 | PoolSize: utils.GlobalObject.PoolSize,
26 | TaskQueue: make([]chan *PkgAll, utils.GlobalObject.PoolSize),
27 | Apis: make(map[uint32]reflect.Value),
28 | }
29 | }
30 |
31 | //一致性路由,保证同一连接的数据转发给相同的goroutine
32 | func (this *MsgHandle) DeliverToMsgQueue(pkg interface{}) {
33 | data := pkg.(*PkgAll)
34 | //index := rand.Int31n(utils.GlobalObject.PoolSize)
35 | index := data.Fconn.GetSessionId() % uint32(utils.GlobalObject.PoolSize)
36 | taskQueue := this.TaskQueue[index]
37 | logger.Debug(fmt.Sprintf("add to pool : %d", index))
38 | taskQueue <- data
39 | }
40 |
41 | func (this *MsgHandle) DoMsgFromGoRoutine(pkg interface{}) {
42 | data := pkg.(*PkgAll)
43 | go func() {
44 | if f, ok := this.Apis[data.Pdata.MsgId]; ok {
45 | //存在
46 | st := time.Now()
47 | f.Call([]reflect.Value{reflect.ValueOf(data)})
48 | logger.Debug(fmt.Sprintf("Api_%d cost total time: %f ms", data.Pdata.MsgId, time.Now().Sub(st).Seconds()*1000))
49 | } else {
50 | logger.Error(fmt.Sprintf("not found api: %d", data.Pdata.MsgId))
51 | }
52 | }()
53 | }
54 |
55 | func (this *MsgHandle) AddRouter(router interface{}) {
56 | value := reflect.ValueOf(router)
57 | tp := value.Type()
58 | for i := 0; i < value.NumMethod(); i += 1 {
59 | name := tp.Method(i).Name
60 | k := strings.Split(name, "_")
61 | index, err := strconv.Atoi(k[1])
62 | if err != nil {
63 | panic("error api: " + name)
64 | }
65 | if _, ok := this.Apis[uint32(index)]; ok {
66 | //存在
67 | panic("repeated api " + string(index))
68 | }
69 | this.Apis[uint32(index)] = value.Method(i)
70 | logger.Info("add api " + name)
71 | }
72 |
73 | //exec test
74 | // for i := 0; i < 100; i += 1 {
75 | // Apis[1].Call([]reflect.Value{reflect.ValueOf("huangxin"), reflect.ValueOf(router)})
76 | // Apis[2].Call([]reflect.Value{})
77 | // }
78 | // fmt.Println(this.Apis)
79 | // this.Apis[2].Call([]reflect.Value{reflect.ValueOf(&PkgAll{})})
80 | }
81 |
82 | func (this *MsgHandle)HandleError(err interface{}){
83 | if err != nil{
84 | debug.PrintStack()
85 | }
86 | }
87 |
88 | func (this *MsgHandle) StartWorkerLoop(poolSize int) {
89 | if utils.GlobalObject.IsThreadSafeMode(){
90 | //线程安全模式所有的逻辑都在一个goroutine处理, 这样可以实现无锁化服务
91 | this.TaskQueue[0] = make(chan *PkgAll, utils.GlobalObject.MaxWorkerLen)
92 | go func(){
93 | logger.Info("init thread mode workpool.")
94 | for{
95 | select {
96 | case data := <- this.TaskQueue[0]:
97 | if f, ok := this.Apis[data.Pdata.MsgId]; ok {
98 | //存在
99 | st := time.Now()
100 | //f.Call([]reflect.Value{reflect.ValueOf(data)})
101 | utils.XingoTry(f, []reflect.Value{reflect.ValueOf(data)}, this.HandleError)
102 | logger.Debug(fmt.Sprintf("Api_%d cost total time: %f ms", data.Pdata.MsgId, time.Now().Sub(st).Seconds()*1000))
103 | } else {
104 | logger.Error(fmt.Sprintf("not found api: %d", data.Pdata.MsgId))
105 | }
106 | case delaytask := <- utils.GlobalObject.GetSafeTimer().GetTriggerChannel():
107 | delaytask.Call()
108 | }
109 | }
110 | }()
111 | }else{
112 | for i := 0; i < poolSize; i += 1 {
113 | c := make(chan *PkgAll, utils.GlobalObject.MaxWorkerLen)
114 | this.TaskQueue[i] = c
115 | go func(index int, taskQueue chan *PkgAll) {
116 | logger.Info(fmt.Sprintf("init thread pool %d.", index))
117 | for {
118 | data := <-taskQueue
119 | //can goroutine?
120 | if f, ok := this.Apis[data.Pdata.MsgId]; ok {
121 | //存在
122 | st := time.Now()
123 | //f.Call([]reflect.Value{reflect.ValueOf(data)})
124 | utils.XingoTry(f, []reflect.Value{reflect.ValueOf(data)}, this.HandleError)
125 | logger.Debug(fmt.Sprintf("Api_%d cost total time: %f ms", data.Pdata.MsgId, time.Now().Sub(st).Seconds()*1000))
126 | } else {
127 | logger.Error(fmt.Sprintf("not found api: %d", data.Pdata.MsgId))
128 | }
129 | }
130 | }(i, c)
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/timer/hashwheel.go:
--------------------------------------------------------------------------------
1 | package timer
2 |
3 | import (
4 | "sync"
5 | "github.com/viphxin/xingo/logger"
6 | "fmt"
7 | "time"
8 | "errors"
9 | )
10 | /*
11 | 分级时间轮
12 | */
13 |
14 | const (
15 | DEFAULT_LEVEL = 12
16 | )
17 |
18 | type HashWheel struct {
19 | title string //时间轮唯一标识
20 | index int //时间轮当前指针
21 | level int //多少级
22 | levelInterval int64 //分级间隔 (ms)
23 | maxCap uint32 //每一级最大容量
24 | timerQueue map[int]map[uint32]*SafeTimer//存储所有timer
25 | nextHashWheel *HashWheel //下级时间轮
26 | sync.RWMutex
27 | }
28 |
29 | func NewHashWheel(title string, level int, linterval int64, maxCap uint32) *HashWheel{
30 | wheel := &HashWheel{
31 | title: title,
32 | index: 0,
33 | level: level,
34 | levelInterval: linterval,
35 | maxCap: maxCap,
36 | timerQueue: make(map[int]map[uint32]*SafeTimer, level),
37 | }
38 | for i := 0; i < wheel.level; i++{
39 | wheel.timerQueue[i] = make(map[uint32]*SafeTimer, maxCap)
40 | }
41 | go wheel.RunWheel()
42 | return wheel
43 | }
44 |
45 | func (this *HashWheel)AddNext(next *HashWheel){
46 | this.nextHashWheel = next
47 | }
48 |
49 | func (this *HashWheel)Count() int{
50 | this.RLock()
51 | defer this.RUnlock()
52 |
53 | c := 0
54 | for i := 0; i < this.level; i++{
55 | c += len(this.timerQueue[i])
56 | }
57 | return c
58 | }
59 |
60 | func (this *HashWheel)_add2WheelChain(tid uint32, t *SafeTimer, forceNext bool) error{
61 | defer func() error{
62 | if err := recover(); err != nil{
63 | logger.Error(fmt.Sprintf("add safetimer to hashwheel err: %s.", err))
64 | return errors.New(fmt.Sprintf("add safetimer to hashwheel err: %s.", err))
65 | }else{
66 | return nil
67 | }
68 | }()
69 |
70 | now := UnixTS()
71 | if t.unixts - now >= this.levelInterval || this.nextHashWheel == nil{
72 | saved := false
73 | for i := this.level - 1; i >= 0; i-- {
74 | if t.unixts - now >= int64(i)*this.levelInterval{
75 | if (i + this.index)%this.level == this.index && forceNext{
76 | this.timerQueue[(i + this.index + 1)%this.level][tid] = t
77 | }else{
78 | this.timerQueue[(i + this.index)%this.level][tid] = t
79 | }
80 | saved = true
81 | break
82 | }
83 | }
84 | if !saved {
85 | if forceNext {
86 | this.timerQueue[(this.index+1)%this.level][tid] = t
87 | }else{
88 | this.timerQueue[this.index][tid] = t
89 | }
90 | }
91 | return nil
92 | }else{
93 | //应该放到下级
94 | return this.nextHashWheel.Add2WheelChain(tid, t)
95 |
96 | }
97 | }
98 |
99 | func (this *HashWheel)Add2WheelChain(tid uint32, t *SafeTimer) error{
100 | this.Lock()
101 | defer this.Unlock()
102 |
103 | return this._add2WheelChain(tid, t, false)
104 | }
105 |
106 | func (this *HashWheel)RemoveFromWheelChain(tid uint32){
107 | this.Lock()
108 | defer this.Unlock()
109 |
110 | for i := 0; i < this.level; i++{
111 | if _, ok := this.timerQueue[i][tid]; ok{
112 | delete(this.timerQueue[i], tid)
113 | return
114 | }
115 | }
116 | //去下级wheel找
117 | if this.nextHashWheel != nil{
118 | this.nextHashWheel.RemoveFromWheelChain(tid)
119 | }
120 | }
121 |
122 | func (this *HashWheel)GetTriggerWithIn(ms int64) map[uint32]*SafeTimer{
123 | leafWheel := this
124 | for leafWheel.nextHashWheel != nil{
125 | leafWheel = leafWheel.nextHashWheel
126 | }
127 |
128 | leafWheel.Lock()
129 | defer leafWheel.Unlock()
130 |
131 | triggerList := make(map[uint32]*SafeTimer)
132 | now := UnixTS()
133 | for k, v := range leafWheel.timerQueue[leafWheel.index]{
134 | if v.unixts - now <= ms {
135 | triggerList[k] = v
136 | }
137 | }
138 |
139 | for k, _ := range triggerList{
140 | delete(leafWheel.timerQueue[leafWheel.index], k)
141 | }
142 | return triggerList
143 |
144 | }
145 |
146 | //时间轮跑起来
147 | func (this *HashWheel)RunWheel() {
148 | for{
149 | time.Sleep(time.Duration(this.levelInterval) * time.Millisecond)
150 | //loop
151 | this.Lock()
152 | CurtriggerList := this.timerQueue[this.index]
153 | this.timerQueue[this.index] = make(map[uint32]*SafeTimer, this.maxCap)
154 | for k, v := range CurtriggerList{
155 | this._add2WheelChain(k, v, true)
156 | }
157 |
158 | NextriggerList := this.timerQueue[(this.index + 1) % this.level]
159 | this.timerQueue[(this.index + 1) % this.level] = make(map[uint32]*SafeTimer, this.maxCap)
160 | for k, v := range NextriggerList{
161 | this._add2WheelChain(k, v, true)
162 | }
163 | //下一格
164 | this.index = (this.index + 1) % this.level
165 | this.Unlock()
166 | }
167 | }
168 |
169 |
170 |
--------------------------------------------------------------------------------
/fnet/protocol.go:
--------------------------------------------------------------------------------
1 | package fnet
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/viphxin/xingo/iface"
7 | "github.com/viphxin/xingo/logger"
8 | "github.com/viphxin/xingo/utils"
9 | "io"
10 | "time"
11 | )
12 |
13 | const (
14 | MaxPacketSize = 1024 * 1024
15 | )
16 |
17 | var (
18 | packageTooBig = errors.New("Too many data to received!!")
19 | )
20 |
21 | type PkgAll struct {
22 | Pdata *PkgData
23 | Fconn iface.Iconnection
24 | }
25 |
26 | type Protocol struct {
27 | msghandle *MsgHandle
28 | pbdatapack *PBDataPack
29 | }
30 |
31 | func NewProtocol() *Protocol {
32 | return &Protocol{
33 | msghandle: NewMsgHandle(),
34 | pbdatapack: NewPBDataPack(),
35 | }
36 | }
37 |
38 | func (this *Protocol) GetMsgHandle() iface.Imsghandle {
39 | return this.msghandle
40 | }
41 | func (this *Protocol) GetDataPack() iface.Idatapack {
42 | return this.pbdatapack
43 | }
44 |
45 | func (this *Protocol) AddRpcRouter(router interface{}) {
46 | this.msghandle.AddRouter(router)
47 | }
48 |
49 | func (this *Protocol) InitWorker(poolsize int32) {
50 | this.msghandle.StartWorkerLoop(int(poolsize))
51 | }
52 |
53 | func (this *Protocol) OnConnectionMade(fconn iface.Iconnection) {
54 | logger.Info(fmt.Sprintf("client ID: %d connected. IP Address: %s", fconn.GetSessionId(), fconn.RemoteAddr()))
55 | utils.GlobalObject.OnConnectioned(fconn)
56 | //加频率控制
57 | this.SetFrequencyControl(fconn)
58 | }
59 |
60 | func (this *Protocol) SetFrequencyControl(fconn iface.Iconnection) {
61 | fc0, fc1 := utils.GlobalObject.GetFrequency()
62 | if fc1 == "h" {
63 | fconn.SetProperty("xingo_fc", 0)
64 | fconn.SetProperty("xingo_fc0", fc0)
65 | fconn.SetProperty("xingo_fc1", time.Now().UnixNano()*1e6+int64(3600*1e3))
66 | } else if fc1 == "m" {
67 | fconn.SetProperty("xingo_fc", 0)
68 | fconn.SetProperty("xingo_fc0", fc0)
69 | fconn.SetProperty("xingo_fc1", time.Now().UnixNano()*1e6+int64(60*1e3))
70 | } else if fc1 == "s" {
71 | fconn.SetProperty("xingo_fc", 0)
72 | fconn.SetProperty("xingo_fc0", fc0)
73 | fconn.SetProperty("xingo_fc1", time.Now().UnixNano()*1e6+int64(1e3))
74 | }
75 | }
76 |
77 | func (this *Protocol) DoFrequencyControl(fconn iface.Iconnection) error {
78 | xingo_fc1, err := fconn.GetProperty("xingo_fc1")
79 | if err != nil {
80 | //没有频率控制
81 | return nil
82 | } else {
83 | if time.Now().UnixNano()*1e6 >= xingo_fc1.(int64) {
84 | //init
85 | this.SetFrequencyControl(fconn)
86 | } else {
87 | xingo_fc, _ := fconn.GetProperty("xingo_fc")
88 | xingo_fc0, _ := fconn.GetProperty("xingo_fc0")
89 | xingo_fc_int := xingo_fc.(int) + 1
90 | xingo_fc0_int := xingo_fc0.(int)
91 | if xingo_fc_int >= xingo_fc0_int {
92 | //trigger
93 | return errors.New(fmt.Sprintf("received package exceed limit: %s", utils.GlobalObject.FrequencyControl))
94 | } else {
95 | fconn.SetProperty("xingo_fc", xingo_fc_int)
96 | }
97 | }
98 | return nil
99 | }
100 | }
101 |
102 | func (this *Protocol) OnConnectionLost(fconn iface.Iconnection) {
103 | logger.Info(fmt.Sprintf("client ID: %d disconnected. IP Address: %s", fconn.GetSessionId(), fconn.RemoteAddr()))
104 | utils.GlobalObject.OnClosed(fconn)
105 | }
106 |
107 | func (this *Protocol) StartReadThread(fconn iface.Iconnection) {
108 | logger.Info("start receive data from socket...")
109 | for {
110 | //频率控制
111 | err := this.DoFrequencyControl(fconn)
112 | if err != nil {
113 | logger.Error(err)
114 | fconn.Stop()
115 | return
116 | }
117 | //read per head data
118 | headdata := make([]byte, this.pbdatapack.GetHeadLen())
119 |
120 | if _, err := io.ReadFull(fconn.GetConnection(), headdata); err != nil {
121 | logger.Error(err)
122 | fconn.Stop()
123 | return
124 | }
125 | pkgHead, err := this.pbdatapack.Unpack(headdata)
126 | if err != nil {
127 | logger.Error(err)
128 | fconn.Stop()
129 | return
130 | }
131 | //data
132 | pkg := pkgHead.(*PkgData)
133 | if pkg.Len > 0 {
134 | pkg.Data = make([]byte, pkg.Len)
135 | if _, err := io.ReadFull(fconn.GetConnection(), pkg.Data); err != nil {
136 | logger.Error(err)
137 | fconn.Stop()
138 | return
139 | }
140 | }
141 |
142 | logger.Debug(fmt.Sprintf("msg id :%d, data len: %d", pkg.MsgId, pkg.Len))
143 | if utils.GlobalObject.PoolSize > 0 {
144 | this.msghandle.DeliverToMsgQueue(&PkgAll{
145 | Pdata: pkg,
146 | Fconn: fconn,
147 | })
148 | } else {
149 | this.msghandle.DoMsgFromGoRoutine(&PkgAll{
150 | Pdata: pkg,
151 | Fconn: fconn,
152 | })
153 | }
154 |
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/clusterserver/master.go:
--------------------------------------------------------------------------------
1 | package clusterserver
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/cluster"
6 | "github.com/viphxin/xingo/fserver"
7 | "github.com/viphxin/xingo/iface"
8 | "github.com/viphxin/xingo/logger"
9 | "github.com/viphxin/xingo/utils"
10 | "sync"
11 | "time"
12 | )
13 |
14 | const (
15 | KEEP_ALIVED_DURATION = 30 //s
16 | )
17 |
18 | type Master struct {
19 | OnlineNodes map[string]bool
20 | Cconf *cluster.ClusterConf
21 | Childs *cluster.ChildMgr
22 | TelnetServer iface.Iserver
23 | sync.RWMutex
24 | }
25 |
26 | func NewMaster(path string) *Master {
27 | logger.SetPrefix(fmt.Sprintf("[%s]", "MASTER"))
28 | cconf, err := cluster.NewClusterConf(path)
29 | if err != nil {
30 | panic("cluster conf error!!!")
31 | }
32 | GlobalMaster = &Master{
33 | OnlineNodes: make(map[string]bool),
34 | Cconf: cconf,
35 | Childs: cluster.NewChildMgr(),
36 | }
37 | //regest callback
38 | utils.GlobalObject.TcpPort = GlobalMaster.Cconf.Master.RootPort
39 | utils.GlobalObject.Protoc = cluster.NewRpcServerProtocol()
40 | utils.GlobalObject.RpcCProtoc = cluster.NewRpcClientProtocol()
41 | utils.GlobalObject.OnClusterConnectioned = DoConnectionMade
42 | utils.GlobalObject.OnClusterClosed = DoConnectionLost
43 | utils.GlobalObject.Name = "master"
44 | if cconf.Master.Log != "" {
45 | utils.GlobalObject.LogName = cconf.Master.Log
46 | utils.ReSettingLog()
47 | }
48 |
49 | //telnet debug tool
50 | if GlobalMaster.Cconf.Master.DebugPort > 0{
51 | if GlobalMaster.Cconf.Master.Host != ""{
52 | GlobalMaster.TelnetServer = fserver.NewTcpServer("telnet_server", "tcp4", GlobalMaster.Cconf.Master.Host,
53 | GlobalMaster.Cconf.Master.DebugPort, 100, cluster.NewTelnetProtocol())
54 | }else{
55 | GlobalMaster.TelnetServer = fserver.NewTcpServer("telnet_server", "tcp4", "127.0.0.1",
56 | GlobalMaster.Cconf.Master.DebugPort, 100, cluster.NewTelnetProtocol())
57 | }
58 | logger.Info(fmt.Sprintf("telnet tool start: %s:%d.",
59 | GlobalMaster.Cconf.Master.Host, GlobalMaster.Cconf.Master.DebugPort))
60 | }
61 | return GlobalMaster
62 | }
63 |
64 | func DoConnectionMade(fconn iface.Iconnection) {
65 | logger.Info("node connected to master!!!")
66 | }
67 |
68 | func DoConnectionLost(fconn iface.Iconnection) {
69 | logger.Info("node disconnected from master!!!")
70 | nodename, err := fconn.GetProperty("child")
71 | if err == nil {
72 | GlobalMaster.RemoveNode(nodename.(string))
73 | }
74 | }
75 |
76 | func (this *Master) StartMaster() {
77 | s := fserver.NewServer()
78 | if GlobalMaster.TelnetServer != nil{
79 | this.TelnetServer.Start()
80 | }
81 | //check node alive tick
82 | s.CallLoop(KEEP_ALIVED_DURATION*time.Second, this.CheckChildsAlive, true)
83 | s.Serve()
84 | }
85 |
86 | func (this *Master) AddRpcRouter(router interface{}) {
87 | //add rpc ---------------start
88 | utils.GlobalObject.Protoc.AddRpcRouter(router)
89 | //add rpc ---------------end
90 | }
91 |
92 | func (this *Master) AddNode(name string, writer iface.IWriter) {
93 | this.Lock()
94 | defer this.Unlock()
95 |
96 | this.Childs.AddChild(name, writer)
97 | writer.SetProperty("child", name)
98 | this.OnlineNodes[name] = true
99 | }
100 |
101 | func (this *Master) RemoveNode(name string) {
102 | this.Lock()
103 | defer this.Unlock()
104 |
105 | this.Childs.RemoveChild(name)
106 | delete(this.OnlineNodes, name)
107 |
108 | }
109 |
110 | func (this *Master)CheckChildsAlive(params ...interface{}) {
111 | childs := this.Childs.GetChilds()
112 | for _, child := range childs {
113 | _, err := child.CallChildForResult("CheckAlive")
114 | if err == nil {
115 | continue
116 | }
117 | //节点掉线通知child节点的父节点
118 | remotes, err := GlobalMaster.Cconf.GetRemotesByName(child.GetName())
119 | if err == nil && len(remotes) > 0 {
120 | for _, remote := range remotes {
121 | remoteProxy, err := GlobalMaster.Childs.GetChild(remote)
122 | if err == nil {
123 | //child是子节点 true
124 | remoteProxy.CallChildNotForResult("NodeDownNtf", true, child.GetName())
125 | }
126 | }
127 | }
128 | //节点掉线通知child节点的子节点
129 | curChilds := GlobalMaster.Cconf.GetChildsByName(child.GetName())
130 | if len(curChilds) > 0 {
131 | for _, curChild := range curChilds {
132 | curChildProxy, err := GlobalMaster.Childs.GetChild(curChild)
133 | if err == nil {
134 | //child是父节点 false
135 | curChildProxy.CallChildNotForResult("NodeDownNtf", false, child.GetName())
136 | }
137 | }
138 | }
139 | this.Childs.RemoveChild(child.GetName())
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/fnet/connection.go:
--------------------------------------------------------------------------------
1 | package fnet
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/viphxin/xingo/iface"
7 | "github.com/viphxin/xingo/logger"
8 | "github.com/viphxin/xingo/utils"
9 | "net"
10 | "sync"
11 | "time"
12 | )
13 |
14 | const (
15 | XINGO_CONN_PROPERTY_CTIME = "xingo_ctime"
16 | XINGO_CONN_PROPERTY_NAME = "xingo_tcpserver_name"
17 | )
18 |
19 | type Connection struct {
20 | Conn *net.TCPConn
21 | isClosed bool
22 | SessionId uint32
23 | Protoc iface.IServerProtocol
24 | PropertyBag map[string]interface{}
25 | sendtagGuard sync.RWMutex
26 | propertyLock sync.RWMutex
27 |
28 | SendBuffChan chan []byte
29 | ExtSendChan chan bool
30 | }
31 |
32 | func NewConnection(conn *net.TCPConn, sessionId uint32, protoc iface.IServerProtocol) *Connection {
33 | fconn := &Connection{
34 | Conn: conn,
35 | isClosed: false,
36 | SessionId: sessionId,
37 | Protoc: protoc,
38 | PropertyBag: make(map[string]interface{}),
39 | SendBuffChan: make(chan []byte, utils.GlobalObject.MaxSendChanLen),
40 | ExtSendChan: make(chan bool, 1),
41 | }
42 | //set connection time
43 | fconn.SetProperty(XINGO_CONN_PROPERTY_CTIME, time.Since(time.Now()))
44 | return fconn
45 | }
46 |
47 | func (this *Connection) Start() {
48 | //add to connectionmsg
49 | serverName, err := this.GetProperty(XINGO_CONN_PROPERTY_NAME)
50 | if err != nil{
51 | logger.Error("not find server name in GlobalObject.")
52 | return
53 | }else{
54 | serverNameStr := serverName.(string)
55 | utils.GlobalObject.TcpServers[serverNameStr].GetConnectionMgr().Add(this)
56 | }
57 |
58 | this.Protoc.OnConnectionMade(this)
59 | this.StartWriteThread()
60 | this.Protoc.StartReadThread(this)
61 | }
62 |
63 | func (this *Connection) Stop() {
64 | // 防止将Send放在go内造成的多线程冲突问题
65 | this.sendtagGuard.Lock()
66 | defer this.sendtagGuard.Unlock()
67 |
68 | if this.isClosed{
69 | return
70 | }
71 |
72 | this.Conn.Close()
73 | this.ExtSendChan <- true
74 | this.isClosed = true
75 | //掉线回调放到go内防止,掉线回调处理出线死锁
76 | go this.Protoc.OnConnectionLost(this)
77 | //remove to connectionmsg
78 | serverName, err := this.GetProperty(XINGO_CONN_PROPERTY_NAME)
79 | if err != nil{
80 | logger.Error("not find server name in GlobalObject.")
81 | return
82 | }else{
83 | serverNameStr := serverName.(string)
84 | utils.GlobalObject.TcpServers[serverNameStr].GetConnectionMgr().Remove(this)
85 | }
86 | close(this.ExtSendChan)
87 | close(this.SendBuffChan)
88 | }
89 |
90 | func (this *Connection) GetConnection() *net.TCPConn {
91 | return this.Conn
92 | }
93 |
94 | func (this *Connection) GetSessionId() uint32 {
95 | return this.SessionId
96 | }
97 |
98 | func (this *Connection) GetProtoc() iface.IServerProtocol {
99 | return this.Protoc
100 | }
101 |
102 | func (this *Connection) GetProperty(key string) (interface{}, error) {
103 | this.propertyLock.RLock()
104 | defer this.propertyLock.RUnlock()
105 |
106 | value, ok := this.PropertyBag[key]
107 | if ok {
108 | return value, nil
109 | } else {
110 | return nil, errors.New("no property in connection")
111 | }
112 | }
113 |
114 | func (this *Connection) SetProperty(key string, value interface{}) {
115 | this.propertyLock.Lock()
116 | defer this.propertyLock.Unlock()
117 |
118 | this.PropertyBag[key] = value
119 | }
120 |
121 | func (this *Connection) RemoveProperty(key string) {
122 | this.propertyLock.Lock()
123 | defer this.propertyLock.Unlock()
124 |
125 | delete(this.PropertyBag, key)
126 | }
127 |
128 | func (this *Connection) Send(data []byte) error {
129 | // 防止将Send放在go内造成的多线程冲突问题
130 | this.sendtagGuard.Lock()
131 | defer this.sendtagGuard.Unlock()
132 |
133 | if !this.isClosed {
134 | if _, err := this.Conn.Write(data); err != nil {
135 | logger.Error(fmt.Sprintf("send data error.reason: %s", err))
136 | return err
137 | }
138 | return nil
139 | } else {
140 | return errors.New("connection closed")
141 | }
142 | }
143 |
144 | func (this *Connection) SendBuff(data []byte) error {
145 | // 防止将Send放在go内造成的多线程冲突问题
146 | this.sendtagGuard.Lock()
147 | defer this.sendtagGuard.Unlock()
148 |
149 | if !this.isClosed {
150 |
151 | // 发送超时
152 | select {
153 | case <-time.After(time.Second * 2):
154 | logger.Error("send error: timeout.")
155 | return errors.New("send error: timeout.")
156 | case this.SendBuffChan <- data:
157 | return nil
158 | }
159 | } else {
160 | return errors.New("connection closed")
161 | }
162 |
163 | }
164 |
165 | func (this *Connection) RemoteAddr() net.Addr {
166 | return (*this.Conn).RemoteAddr()
167 | }
168 |
169 | func (this *Connection) LostConnection() {
170 | this.Conn.Close()
171 | logger.Info("LostConnection session: ", this.SessionId)
172 | }
173 |
174 | func (this *Connection) StartWriteThread() {
175 | go func() {
176 | logger.Debug("start send data from channel...")
177 | for {
178 | select {
179 | case <-this.ExtSendChan:
180 | logger.Info("send thread exit successful!!!!")
181 | return
182 | case data := <-this.SendBuffChan:
183 | //send
184 | if _, err := this.Conn.Write(data); err != nil {
185 | logger.Info("send data error exit...")
186 | return
187 | }
188 | }
189 | }
190 | }()
191 | }
192 |
--------------------------------------------------------------------------------
/fserver/server.go:
--------------------------------------------------------------------------------
1 | package fserver
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/fnet"
6 | "github.com/viphxin/xingo/iface"
7 | "github.com/viphxin/xingo/logger"
8 | "github.com/viphxin/xingo/timer"
9 | "github.com/viphxin/xingo/utils"
10 | "net"
11 | "os"
12 | "os/signal"
13 | "time"
14 | "syscall"
15 | )
16 |
17 | func init() {
18 | utils.GlobalObject.Protoc = fnet.NewProtocol()
19 | // --------------------------------------------init log start
20 | utils.ReSettingLog()
21 | // --------------------------------------------init log end
22 | }
23 |
24 | type Server struct {
25 | Name string
26 | IPVersion string
27 | IP string
28 | Port int
29 | MaxConn int
30 | GenNum *utils.UUIDGenerator
31 | connectionMgr iface.Iconnectionmgr
32 | Protoc iface.IServerProtocol
33 | }
34 |
35 | func NewServer() iface.Iserver {
36 | s := &Server{
37 | Name: utils.GlobalObject.Name,
38 | IPVersion: "tcp4",
39 | IP: "0.0.0.0",
40 | Port: utils.GlobalObject.TcpPort,
41 | MaxConn: utils.GlobalObject.MaxConn,
42 | connectionMgr: fnet.NewConnectionMgr(),
43 | Protoc: utils.GlobalObject.Protoc,
44 | GenNum: utils.NewUUIDGenerator(""),
45 | }
46 | utils.GlobalObject.TcpServer = s
47 |
48 | return s
49 | }
50 |
51 | func NewTcpServer(name string, version string, ip string, port int, maxConn int, protoc iface.IServerProtocol) iface.Iserver {
52 | s := &Server{
53 | Name: name,
54 | IPVersion: version,
55 | IP: ip,
56 | Port: port,
57 | MaxConn: maxConn,
58 | connectionMgr: fnet.NewConnectionMgr(),
59 | Protoc: protoc,
60 | GenNum: utils.NewUUIDGenerator(""),
61 | }
62 | utils.GlobalObject.TcpServer = s
63 |
64 | return s
65 | }
66 |
67 | func (this *Server) handleConnection(conn *net.TCPConn) {
68 | conn.SetNoDelay(true)
69 | conn.SetKeepAlive(true)
70 | // conn.SetDeadline(time.Now().Add(time.Minute * 2))
71 | var fconn *fnet.Connection
72 | if this.Protoc == nil{
73 | fconn = fnet.NewConnection(conn, this.GenNum.GetUint32(), utils.GlobalObject.Protoc)
74 | }else{
75 | fconn = fnet.NewConnection(conn, this.GenNum.GetUint32(), this.Protoc)
76 | }
77 | fconn.SetProperty(fnet.XINGO_CONN_PROPERTY_NAME, this.Name)
78 | fconn.Start()
79 | }
80 |
81 | func (this *Server) Start() {
82 | utils.GlobalObject.TcpServers[this.Name] = this
83 | go func() {
84 | this.Protoc.InitWorker(utils.GlobalObject.PoolSize)
85 | tcpAddr, err := net.ResolveTCPAddr(this.IPVersion, fmt.Sprintf("%s:%d", this.IP, this.Port))
86 | if err != nil{
87 | logger.Fatal("ResolveTCPAddr err: ", err)
88 | return
89 | }
90 | ln, err := net.ListenTCP("tcp", tcpAddr)
91 | if err != nil {
92 | logger.Error(err)
93 | }
94 | logger.Info(fmt.Sprintf("start xingo server %s...", this.Name))
95 | for {
96 | conn, err := ln.AcceptTCP()
97 | if err != nil {
98 | logger.Error(err)
99 | continue
100 | }
101 | //max client exceed
102 | if this.connectionMgr.Len() >= utils.GlobalObject.MaxConn {
103 | conn.Close()
104 | } else {
105 | go this.handleConnection(conn)
106 | }
107 | }
108 | }()
109 | }
110 |
111 | func (this *Server) GetConnectionMgr() iface.Iconnectionmgr {
112 | return this.connectionMgr
113 | }
114 |
115 | func (this *Server) GetConnectionQueue() chan interface{} {
116 | return nil
117 | }
118 |
119 | func (this *Server) Stop() {
120 | logger.Info("stop xingo server ", this.Name)
121 | if utils.GlobalObject.OnServerStop != nil {
122 | utils.GlobalObject.OnServerStop()
123 | }
124 | }
125 |
126 | func (this *Server) AddRouter(router interface{}) {
127 | logger.Info("AddRouter")
128 | utils.GlobalObject.Protoc.GetMsgHandle().AddRouter(router)
129 | }
130 |
131 | func (this *Server) CallLater(durations time.Duration, f func(v ...interface{}), args ...interface{}) {
132 | delayTask := timer.NewTimer(durations, f, args)
133 | delayTask.Run()
134 | }
135 |
136 | func (this *Server) CallWhen(ts string, f func(v ...interface{}), args ...interface{}) {
137 | loc, err_loc := time.LoadLocation("Local")
138 | if err_loc != nil {
139 | logger.Error(err_loc)
140 | return
141 | }
142 | t, err := time.ParseInLocation("2006-01-02 15:04:05", ts, loc)
143 | now := time.Now()
144 | if err == nil {
145 | if now.Before(t) {
146 | this.CallLater(t.Sub(now), f, args...)
147 | } else {
148 | logger.Error("CallWhen time before now")
149 | }
150 | } else {
151 | logger.Error(err)
152 | }
153 | }
154 |
155 | func (this *Server) CallLoop(durations time.Duration, f func(v ...interface{}), args ...interface{}) {
156 | go func() {
157 | delayTask := timer.NewTimer(durations, f, args)
158 | for {
159 | time.Sleep(delayTask.GetDurations())
160 | delayTask.GetFunc().Call()
161 | }
162 | }()
163 | }
164 |
165 | func (this *Server) WaitSignal() {
166 | signal.Notify(utils.GlobalObject.ProcessSignalChan, os.Kill, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
167 | sig := <-utils.GlobalObject.ProcessSignalChan
168 | logger.Info(fmt.Sprintf("server exit. signal: [%s]", sig))
169 | this.Stop()
170 | }
171 |
172 | func (this *Server) Serve() {
173 | this.Start()
174 | this.WaitSignal()
175 | }
176 |
--------------------------------------------------------------------------------
/cluster/rpcprotocol.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/iface"
6 | "github.com/viphxin/xingo/logger"
7 | "github.com/viphxin/xingo/utils"
8 | "io"
9 | "encoding/gob"
10 | "bytes"
11 | )
12 |
13 | type RpcServerProtocol struct {
14 | rpcMsgHandle *RpcMsgHandle
15 | rpcDatapack *RpcDataPack
16 | }
17 |
18 | func NewRpcServerProtocol() *RpcServerProtocol {
19 | return &RpcServerProtocol{
20 | rpcMsgHandle: NewRpcMsgHandle(),
21 | rpcDatapack: NewRpcDataPack(),
22 | }
23 | }
24 |
25 | func (this *RpcServerProtocol) GetMsgHandle() iface.Imsghandle {
26 | return this.rpcMsgHandle
27 | }
28 |
29 | func (this *RpcServerProtocol) GetDataPack() iface.Idatapack {
30 | return this.rpcDatapack
31 | }
32 |
33 | func (this *RpcServerProtocol) AddRpcRouter(router interface{}) {
34 | this.rpcMsgHandle.AddRouter(router)
35 | }
36 |
37 | func (this *RpcServerProtocol) InitWorker(poolsize int32) {
38 | this.rpcMsgHandle.StartWorkerLoop(int(poolsize))
39 | }
40 |
41 | func (this *RpcServerProtocol) OnConnectionMade(fconn iface.Iconnection) {
42 | logger.Info(fmt.Sprintf("node ID: %d connected. IP Address: %s", fconn.GetSessionId(), fconn.RemoteAddr()))
43 | utils.GlobalObject.OnClusterConnectioned(fconn)
44 | }
45 |
46 | func (this *RpcServerProtocol) OnConnectionLost(fconn iface.Iconnection) {
47 | logger.Info(fmt.Sprintf("node ID: %d disconnected. IP Address: %s", fconn.GetSessionId(), fconn.RemoteAddr()))
48 | utils.GlobalObject.OnClusterClosed(fconn)
49 | }
50 |
51 | func (this *RpcServerProtocol) StartReadThread(fconn iface.Iconnection) {
52 | logger.Debug("start receive rpc data from socket...")
53 | for {
54 | //read per head data
55 | headdata := make([]byte, this.rpcDatapack.GetHeadLen())
56 |
57 | if _, err := io.ReadFull(fconn.GetConnection(), headdata); err != nil {
58 | logger.Error(err)
59 | fconn.Stop()
60 | return
61 | }
62 | pkgHead, err := this.rpcDatapack.Unpack(headdata)
63 | if err != nil {
64 | fconn.Stop()
65 | return
66 | }
67 | //data
68 | pkg := pkgHead.(*RpcPackege)
69 | if pkg.Len > 0 {
70 | pkg.Data = make([]byte, pkg.Len)
71 | if _, err := io.ReadFull(fconn.GetConnection(), pkg.Data); err != nil {
72 | fconn.Stop()
73 | return
74 | } else {
75 | rpcRequest := &RpcRequest{
76 | Fconn: fconn,
77 | Rpcdata: &RpcData{},
78 | }
79 |
80 | //err = json.Unmarshal(pkg.Data, rpcRequest.Rpcdata)
81 | //replace json to gob
82 | dec := gob.NewDecoder(bytes.NewReader(pkg.Data))
83 | err = dec.Decode(rpcRequest.Rpcdata)
84 |
85 | if err != nil {
86 | logger.Error(err)
87 | fconn.Stop()
88 | return
89 | }
90 |
91 | logger.Debug(fmt.Sprintf("rpc call. data len: %d. MsgType: %d", pkg.Len, int(rpcRequest.Rpcdata.MsgType)))
92 | if utils.GlobalObject.PoolSize > 0 && rpcRequest.Rpcdata.MsgType != RESPONSE {
93 | this.rpcMsgHandle.DeliverToMsgQueue(rpcRequest)
94 | } else {
95 | this.rpcMsgHandle.DoMsgFromGoRoutine(rpcRequest)
96 | }
97 | }
98 | }
99 | }
100 | }
101 |
102 | type RpcClientProtocol struct {
103 | rpcMsgHandle *RpcMsgHandle
104 | rpcDatapack *RpcDataPack
105 | }
106 |
107 | func NewRpcClientProtocol() *RpcClientProtocol {
108 | return &RpcClientProtocol{
109 | rpcMsgHandle: NewRpcMsgHandle(),
110 | rpcDatapack: NewRpcDataPack(),
111 | }
112 | }
113 |
114 | func (this *RpcClientProtocol) GetMsgHandle() iface.Imsghandle {
115 | return this.rpcMsgHandle
116 | }
117 |
118 | func (this *RpcClientProtocol) GetDataPack() iface.Idatapack {
119 | return this.rpcDatapack
120 | }
121 | func (this *RpcClientProtocol) AddRpcRouter(router interface{}) {
122 | this.rpcMsgHandle.AddRouter(router)
123 | }
124 |
125 | func (this *RpcClientProtocol) InitWorker(poolsize int32) {
126 | this.rpcMsgHandle.StartWorkerLoop(int(poolsize))
127 | }
128 |
129 | func (this *RpcClientProtocol) OnConnectionMade(fconn iface.Iclient) {
130 | utils.GlobalObject.OnClusterCConnectioned(fconn)
131 | }
132 |
133 | func (this *RpcClientProtocol) OnConnectionLost(fconn iface.Iclient) {
134 | utils.GlobalObject.OnClusterCClosed(fconn)
135 | }
136 |
137 | func (this *RpcClientProtocol) StartReadThread(fconn iface.Iclient) {
138 | logger.Debug("start receive rpc data from socket...")
139 | for {
140 | //read per head data
141 | headdata := make([]byte, this.rpcDatapack.GetHeadLen())
142 |
143 | if _, err := io.ReadFull(fconn.GetConnection(), headdata); err != nil {
144 | logger.Error(err)
145 | fconn.Stop(false)
146 | return
147 | }
148 | pkgHead, err := this.rpcDatapack.Unpack(headdata)
149 | if err != nil {
150 | fconn.Stop(false)
151 | return
152 | }
153 | //data
154 | pkg := pkgHead.(*RpcPackege)
155 | if pkg.Len > 0 {
156 | pkg.Data = make([]byte, pkg.Len)
157 | if _, err := io.ReadFull(fconn.GetConnection(), pkg.Data); err != nil {
158 | fconn.Stop(false)
159 | return
160 | } else {
161 | rpcRequest := &RpcRequest{
162 | Fconn: fconn,
163 | Rpcdata: &RpcData{},
164 | }
165 | //err = json.Unmarshal(pkg.Data, rpcRequest.Rpcdata)
166 | //replace json to gob
167 | dec := gob.NewDecoder(bytes.NewReader(pkg.Data))
168 | err = dec.Decode(rpcRequest.Rpcdata)
169 | if err != nil {
170 | logger.Error("json.Unmarshal error!!!")
171 | fconn.Stop(false)
172 | return
173 | }
174 |
175 | logger.Debug(fmt.Sprintf("rpc call. data len: %d. MsgType: %d", pkg.Len, rpcRequest.Rpcdata.MsgType))
176 | if utils.GlobalObject.PoolSize > 0 && rpcRequest.Rpcdata.MsgType != RESPONSE {
177 | this.rpcMsgHandle.DeliverToMsgQueue(rpcRequest)
178 | } else {
179 | this.rpcMsgHandle.DoMsgFromGoRoutine(rpcRequest)
180 | }
181 | }
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/db/mongo/dboperate_test.go:
--------------------------------------------------------------------------------
1 | package mongo
2 |
3 | import (
4 | "testing"
5 | "time"
6 | "gopkg.in/mgo.v2/bson"
7 | "errors"
8 | "gopkg.in/mgo.v2"
9 | "fmt"
10 | "encoding/gob"
11 | "bytes"
12 | )
13 |
14 | func Test_DailDB(t *testing.T){
15 | dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "", "")
16 | //dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "admin", "admin")
17 | t.Log("url: ", dbcfg.String())
18 | dbo := NewDbOperate(dbcfg, 5*time.Second)
19 | dbo.OpenDB(nil)
20 | dbo.CloseDB()
21 | }
22 | //go test -v github.com\viphxin\xingo\db\mongo -run ^Test_CommonOperate$
23 | func Test_CommonOperate(t *testing.T){
24 | dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "", "")
25 | //dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "admin", "admin")
26 | t.Log("url: ", dbcfg.String())
27 | dbo := NewDbOperate(dbcfg, 5*time.Second)
28 | dbo.OpenDB(func(ms *mgo.Session){
29 | ms.DB("").C("test").EnsureIndex(mgo.Index{
30 | Key: []string{"username"},
31 | Unique: true,
32 | })
33 | })
34 |
35 | _, err := dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
36 | if err != nil{
37 | //not do anything
38 | }
39 | //------------------------------------------------------------------------
40 | err = dbo.Insert("test", bson.M{"username": "xingo", "pass": "pass1111"})
41 | if err != nil{
42 | dbo.CloseDB()
43 | t.Fatal(err)
44 | return
45 | }
46 |
47 | err = dbo.Insert("test", bson.M{"username": "xingo_0", "pass": "pass1111"})
48 | if err != nil{
49 | dbo.CloseDB()
50 | t.Fatal(err)
51 | return
52 | }
53 |
54 | err = dbo.DBFindOne("test", bson.M{"username": "xingo"}, func(a bson.M)error{
55 | if a != nil{
56 | t.Log(a)
57 | return nil
58 | }else{
59 | dbo.CloseDB()
60 | t.Fatal("DBFindOne error")
61 | return errors.New("DBFindOne error")
62 | }
63 |
64 | })
65 | if err != nil{
66 | dbo.CloseDB()
67 | t.Fatal(err)
68 | return
69 | }
70 | _, err = dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
71 | if err != nil{
72 | dbo.CloseDB()
73 | t.Fatal(err)
74 | return
75 | }
76 | //-------------------------------------------------------------------------
77 | //bulk
78 | docs := make([]bson.M, 0)
79 | for i := 0; i < 500; i++{
80 | docs = append(docs, bson.M{"username": fmt.Sprintf("xingo_%d", i), "pass": "pass1111"})
81 | }
82 |
83 | err = dbo.BulkInsert("test", docs)
84 | if err != nil{
85 | dbo.CloseDB()
86 | t.Fatal(err)
87 | return
88 | }
89 |
90 | _, err = dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
91 | if err != nil{
92 | dbo.CloseDB()
93 | t.Fatal(err)
94 | return
95 | }
96 |
97 | dbo.CloseDB()
98 | }
99 |
100 | //go test -v github.com\viphxin\xingo\db\mongo -bench ^Benchmark_CommonOperate$
101 | func Benchmark_CommonOperate(b *testing.B){
102 | dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "", "")
103 | //dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "admin", "admin")
104 | b.Log("url: ", dbcfg.String())
105 | dbo := NewDbOperate(dbcfg, 5*time.Second)
106 | dbo.OpenDB(func(ms *mgo.Session){
107 | ms.DB("").C("test").EnsureIndex(mgo.Index{
108 | Key: []string{"username"},
109 | Unique: true,
110 | })
111 | })
112 |
113 | for i := 0; i < b.N; i++ {
114 | _, err := dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
115 | if err != nil {
116 | //not do anything
117 | }
118 | //------------------------------------------------------------------------
119 | err = dbo.Insert("test", bson.M{"username": "xingo", "pass": "pass1111"})
120 | if err != nil {
121 | dbo.CloseDB()
122 | b.Fatal(err)
123 | return
124 | }
125 |
126 | err = dbo.Insert("test", bson.M{"username": "xingo_0", "pass": "pass1111"})
127 | if err != nil {
128 | dbo.CloseDB()
129 | b.Fatal(err)
130 | return
131 | }
132 |
133 | err = dbo.DBFindOne("test", bson.M{"username": "xingo"}, func(a bson.M) error {
134 | if a != nil {
135 | b.Log(a)
136 | return nil
137 | } else {
138 | dbo.CloseDB()
139 | b.Fatal("DBFindOne error")
140 | return errors.New("DBFindOne error")
141 | }
142 |
143 | })
144 | if err != nil {
145 | dbo.CloseDB()
146 | b.Fatal(err)
147 | return
148 | }
149 | _, err = dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
150 | if err != nil {
151 | dbo.CloseDB()
152 | b.Fatal(err)
153 | return
154 | }
155 | //-------------------------------------------------------------------------
156 | //bulk
157 | docs := make([]bson.M, 0)
158 | for i := 0; i < 500; i++ {
159 | docs = append(docs, bson.M{"username": fmt.Sprintf("xingo_%d", i), "pass": "pass1111"})
160 | }
161 |
162 | err = dbo.BulkInsert("test", docs)
163 | if err != nil {
164 | dbo.CloseDB()
165 | b.Fatal(err)
166 | return
167 | }
168 |
169 | _, err = dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
170 | if err != nil {
171 | dbo.CloseDB()
172 | b.Fatal(err)
173 | return
174 | }
175 | }
176 | dbo.CloseDB()
177 | }
178 |
179 | //go test -v github.com\viphxin\xingo\db\mongo -bench ^Benchmark_CommonOperatePP$
180 | func Benchmark_CommonOperatePP(b *testing.B){
181 | dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "", "")
182 | //dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "admin", "admin")
183 | b.Log("url: ", dbcfg.String())
184 | dbo := NewDbOperate(dbcfg, 5*time.Second)
185 | dbo.OpenDB(func(ms *mgo.Session){
186 | ms.DB("").C("test").DropIndex("username")
187 | })
188 | _, err := dbo.DeleteAll("test", bson.M{"pass": "pass1111"})
189 | if err != nil {
190 | dbo.CloseDB()
191 | b.Fatal(err)
192 | return
193 | }
194 | b.RunParallel(func(pb *testing.PB) {
195 | for pb.Next() {
196 | //------------------------------------------------------------------------
197 | err := dbo.Insert("test", bson.M{"username": "xingo", "pass": "pass1111"})
198 | if err != nil {
199 | dbo.CloseDB()
200 | b.Fatal(err)
201 | return
202 | }
203 |
204 | err = dbo.DBFindOne("test", bson.M{"username": "xingo"}, func(a bson.M) error {
205 | if a != nil {
206 | b.Log(a)
207 | return nil
208 | } else {
209 | dbo.CloseDB()
210 | b.Fatal("DBFindOne error")
211 | return errors.New("DBFindOne error")
212 | }
213 |
214 | })
215 | if err != nil {
216 | dbo.CloseDB()
217 | b.Fatal(err)
218 | return
219 | }
220 | //-------------------------------------------------------------------------
221 | //bulk
222 | docs := make([]bson.M, 0)
223 | for i := 0; i < 500; i++ {
224 | docs = append(docs, bson.M{"username": fmt.Sprintf("xingo_%d", i), "pass": "pass1111"})
225 | }
226 |
227 | err = dbo.BulkInsert("test", docs)
228 | if err != nil {
229 | dbo.CloseDB()
230 | b.Fatal(err)
231 | return
232 | }
233 | }
234 | })
235 | dbo.CloseDB()
236 | }
237 |
238 | func Test_GrdiFS(t *testing.T){
239 | dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "", "")
240 | //dbcfg := NewDbCfg("127.0.0.1", 27017, "xingodb", "admin", "admin")
241 | t.Log("url: ", dbcfg.String())
242 | dbo := NewDbOperate(dbcfg, 5*time.Second)
243 | dbo.OpenDB(func(ms *mgo.Session){
244 | ms.DB("").C("test.gfs").EnsureIndexKey("filename")
245 | })
246 | _, err := dbo.RemoveGridFile("test.gfs", "mmomap.db")
247 | if err != nil {
248 | dbo.CloseDB()
249 | t.Fatal(err)
250 | return
251 | }
252 |
253 | type player struct {
254 | UserId int64
255 | Daomond int64
256 | }
257 | type playerQueue struct {
258 | Q []player
259 | }
260 | q := &playerQueue{Q: make([]player, 0)}
261 | for i := int64(0); i < 1000; i++{
262 | q.Q = append(q.Q, player{i, 1000})
263 | }
264 | //create gridfs
265 | buff := new(bytes.Buffer)
266 | enc := gob.NewEncoder(buff)
267 | enc.Encode(q)
268 | err = dbo.WriteGridFile("test.gfs", "mmomap.db", buff.Bytes())
269 | if err != nil{
270 | dbo.CloseDB()
271 | t.Fatal(err)
272 | return
273 | }
274 | //read gridfs
275 | data, err := dbo.OpenGridFile("test.gfs", "mmomap.db")
276 | if err != nil{
277 | dbo.CloseDB()
278 | t.Fatal(err)
279 | return
280 | }
281 | buff.Reset()
282 | buff.Write(data)
283 | dec := gob.NewDecoder(buff)
284 | qq := &playerQueue{Q: make([]player, 0)}
285 | err = dec.Decode(qq)
286 | if err != nil{
287 | dbo.CloseDB()
288 | t.Fatal(err)
289 | return
290 | }else{
291 | t.Log("success!!!!!!!!!!!!, Q len: ", len(qq.Q))
292 | }
293 | dbo.CloseDB()
294 | }
--------------------------------------------------------------------------------
/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | /*
4 | https://github.com/donnie4w
5 | */
6 | import (
7 | "fmt"
8 | "log"
9 | "os"
10 | "runtime"
11 | "strconv"
12 | "sync"
13 | "time"
14 | )
15 |
16 | const (
17 | _VER string = "1.0.2"
18 | )
19 |
20 | type LEVEL int32
21 |
22 | var logLevel LEVEL = 1
23 | var maxFileSize int64
24 | var maxFileCount int32
25 | var dailyRolling bool = true
26 | var consoleAppender bool = true
27 | var RollingFile bool = false
28 | var logObj *_FILE
29 |
30 | const DATEFORMAT = "2006-01-02"
31 |
32 | type UNIT int64
33 |
34 | const (
35 | _ = iota
36 | KB UNIT = 1 << (iota * 10)
37 | MB
38 | GB
39 | TB
40 | )
41 |
42 | const (
43 | ALL LEVEL = iota
44 | DEBUG
45 | INFO
46 | WARN
47 | ERROR
48 | FATAL
49 | OFF
50 | )
51 |
52 | var (
53 | debug_str = "debug"
54 | info_str = "info"
55 | warn_str = "\033[036;1mwarn\033[036;0m"
56 | error_str = "\033[031;1merror\033[031;0m"
57 | fatal_str = "\033[031;1mfatal\033[031;0m"
58 | )
59 |
60 | const (
61 | _ = iota
62 | ROLLINGDAILY
63 | ROLLINGFILE
64 | )
65 |
66 | func init(){
67 | if runtime.GOOS == "windows"{
68 | warn_str = "warn"
69 | error_str = "error"
70 | fatal_str = "fatal"
71 | }
72 | }
73 |
74 | type _FILE struct {
75 | dir string
76 | filename string
77 | _suffix int
78 | isCover bool
79 | _date *time.Time
80 | mu *sync.RWMutex
81 | logfile *os.File
82 | lg *log.Logger
83 | }
84 |
85 | func SetPrefix(title string) {
86 | log.SetPrefix(title)
87 | }
88 |
89 | func SetConsole(isConsole bool) {
90 | consoleAppender = isConsole
91 | }
92 |
93 | func SetLevel(_level LEVEL) {
94 | logLevel = _level
95 | }
96 |
97 | func SetRollingFile(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
98 | maxFileCount = maxNumber
99 | maxFileSize = maxSize * int64(_unit)
100 | RollingFile = true
101 | dailyRolling = false
102 | mkdirlog(fileDir)
103 | logObj = &_FILE{dir: fileDir, filename: fileName, isCover: false, mu: new(sync.RWMutex)}
104 | logObj.mu.Lock()
105 | defer logObj.mu.Unlock()
106 | for i := 1; i <= int(maxNumber); i++ {
107 | if isExist(fileDir + "/" + fileName + "." + strconv.Itoa(i)) {
108 | logObj._suffix = i
109 | } else {
110 | break
111 | }
112 | }
113 | if !logObj.isMustRename() {
114 | logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
115 | logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
116 | } else {
117 | logObj.rename()
118 | }
119 | go fileMonitor()
120 | }
121 |
122 | func SetRollingDaily(fileDir, fileName string) {
123 | RollingFile = false
124 | dailyRolling = true
125 | t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
126 | mkdirlog(fileDir)
127 | logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
128 | logObj.mu.Lock()
129 | defer logObj.mu.Unlock()
130 |
131 | if !logObj.isMustRename() {
132 | logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
133 | logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
134 | } else {
135 | logObj.rename()
136 | }
137 | }
138 |
139 | func mkdirlog(dir string) (e error) {
140 | _, er := os.Stat(dir)
141 | b := er == nil || os.IsExist(er)
142 | if !b {
143 | if err := os.MkdirAll(dir, 0666); err != nil {
144 | if os.IsPermission(err) {
145 | fmt.Println("create dir error:", err.Error())
146 | e = err
147 | }
148 | }
149 | }
150 | return
151 | }
152 |
153 | func console(s ...interface{}) {
154 | if consoleAppender {
155 | _, file, line, _ := runtime.Caller(2)
156 | short := file
157 | for i := len(file) - 1; i > 0; i-- {
158 | if file[i] == '/' {
159 | short = file[i+1:]
160 | break
161 | }
162 | }
163 | file = short
164 | log.Println(file, strconv.Itoa(line), s)
165 | }
166 | }
167 |
168 | func catchError() {
169 | if err := recover(); err != nil {
170 | log.Println("err", err)
171 | }
172 | }
173 |
174 | func Debug(v ...interface{}) {
175 | if dailyRolling {
176 | fileCheck()
177 | }
178 | defer catchError()
179 | if logObj != nil {
180 | logObj.mu.RLock()
181 | defer logObj.mu.RUnlock()
182 | }
183 |
184 | if logLevel <= DEBUG {
185 | if logObj != nil {
186 | logObj.lg.Output(2, fmt.Sprintln(debug_str, v))
187 | }
188 | console(debug_str, v)
189 | }
190 | }
191 | func Info(v ...interface{}) {
192 | if dailyRolling {
193 | fileCheck()
194 | }
195 | defer catchError()
196 | if logObj != nil {
197 | logObj.mu.RLock()
198 | defer logObj.mu.RUnlock()
199 | }
200 | if logLevel <= INFO {
201 | if logObj != nil {
202 | logObj.lg.Output(2, fmt.Sprintln(info_str, v))
203 | }
204 | console(info_str, v)
205 | }
206 | }
207 | func Warn(v ...interface{}) {
208 | if dailyRolling {
209 | fileCheck()
210 | }
211 | defer catchError()
212 | if logObj != nil {
213 | logObj.mu.RLock()
214 | defer logObj.mu.RUnlock()
215 | }
216 |
217 | if logLevel <= WARN {
218 | if logObj != nil {
219 | logObj.lg.Output(2, fmt.Sprintln(warn_str, v))
220 | }
221 | console(warn_str, v)
222 | }
223 | }
224 | func Error(v ...interface{}) {
225 | if dailyRolling {
226 | fileCheck()
227 | }
228 | defer catchError()
229 | if logObj != nil {
230 | logObj.mu.RLock()
231 | defer logObj.mu.RUnlock()
232 | }
233 | if logLevel <= ERROR {
234 | if logObj != nil {
235 | logObj.lg.Output(2, fmt.Sprintln(error_str, v))
236 | }
237 | console(error_str, v)
238 | }
239 | }
240 | func Fatal(v ...interface{}) {
241 | if dailyRolling {
242 | fileCheck()
243 | }
244 | defer catchError()
245 | if logObj != nil {
246 | logObj.mu.RLock()
247 | defer logObj.mu.RUnlock()
248 | }
249 | if logLevel <= FATAL {
250 | if logObj != nil {
251 | logObj.lg.Output(2, fmt.Sprintln(fatal_str, v))
252 | }
253 | console(fatal_str, v)
254 | }
255 | }
256 |
257 | func (f *_FILE) isMustRename() bool {
258 | if dailyRolling {
259 | t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
260 | if t.After(*f._date) {
261 | return true
262 | }
263 | } else {
264 | if maxFileCount > 1 {
265 | if fileSize(f.dir+"/"+f.filename) >= maxFileSize {
266 | return true
267 | }
268 | }
269 | }
270 | return false
271 | }
272 |
273 | func (f *_FILE) rename() {
274 | if dailyRolling {
275 | fn := f.dir + "/" + f.filename + "." + f._date.Format(DATEFORMAT)
276 | if !isExist(fn) && f.isMustRename() {
277 | if f.logfile != nil {
278 | f.logfile.Close()
279 | }
280 | err := os.Rename(f.dir+"/"+f.filename, fn)
281 | if err != nil {
282 | f.lg.Println("rename err", err.Error())
283 | }
284 | t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
285 | f._date = &t
286 | f.logfile, _ = os.Create(f.dir + "/" + f.filename)
287 | f.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
288 | }
289 | } else {
290 | f.coverNextOne()
291 | }
292 | }
293 |
294 | func (f *_FILE) nextSuffix() int {
295 | return int(f._suffix%int(maxFileCount) + 1)
296 | }
297 |
298 | func (f *_FILE) coverNextOne() {
299 | f._suffix = f.nextSuffix()
300 | if f.logfile != nil {
301 | f.logfile.Close()
302 | }
303 | if isExist(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix))) {
304 | os.Remove(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix)))
305 | }
306 | os.Rename(f.dir+"/"+f.filename, f.dir+"/"+f.filename+"."+strconv.Itoa(int(f._suffix)))
307 | f.logfile, _ = os.Create(f.dir + "/" + f.filename)
308 | f.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
309 | }
310 |
311 | func fileSize(file string) int64 {
312 | fmt.Println("fileSize", file)
313 | f, e := os.Stat(file)
314 | if e != nil {
315 | fmt.Println(e.Error())
316 | return 0
317 | }
318 | return f.Size()
319 | }
320 |
321 | func isExist(path string) bool {
322 | _, err := os.Stat(path)
323 | return err == nil || os.IsExist(err)
324 | }
325 |
326 | func fileMonitor() {
327 | timer := time.NewTicker(1 * time.Second)
328 | for {
329 | select {
330 | case <-timer.C:
331 | fileCheck()
332 | }
333 | }
334 | }
335 |
336 | func fileCheck() {
337 | defer func() {
338 | if err := recover(); err != nil {
339 | log.Println(err)
340 | }
341 | }()
342 | if logObj != nil && logObj.isMustRename() {
343 | logObj.mu.Lock()
344 | defer logObj.mu.Unlock()
345 | logObj.rename()
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/clusterserver/clusterserver.go:
--------------------------------------------------------------------------------
1 | package clusterserver
2 |
3 | import (
4 | "fmt"
5 | "github.com/viphxin/xingo/cluster"
6 | "github.com/viphxin/xingo/fnet"
7 | "github.com/viphxin/xingo/fserver"
8 | "github.com/viphxin/xingo/iface"
9 | "github.com/viphxin/xingo/logger"
10 | "github.com/viphxin/xingo/utils"
11 | "net/http"
12 | "os"
13 | "os/signal"
14 | "reflect"
15 | "strings"
16 | "sync"
17 | "time"
18 | "syscall"
19 | )
20 |
21 | type ClusterServer struct {
22 | Name string
23 | RemoteNodesMgr *cluster.ChildMgr //子节点有
24 | ChildsMgr *cluster.ChildMgr //root节点有
25 | MasterObj *fnet.TcpClient
26 | httpServerMux *http.ServeMux
27 | NetServer iface.Iserver
28 | RootServer iface.Iserver
29 | TelnetServer iface.Iserver
30 | Cconf *cluster.ClusterConf
31 | modules map[string][]interface{} //所有模块统一管理
32 | sync.RWMutex
33 | }
34 |
35 | func DoCSConnectionLost(fconn iface.Iconnection) {
36 | logger.Error("node disconnected from " + utils.GlobalObject.Name)
37 | //子节点掉线
38 | nodename, err := fconn.GetProperty("child")
39 | if err == nil {
40 | GlobalClusterServer.RemoveChild(nodename.(string))
41 | }
42 | }
43 |
44 | func DoCCConnectionLost(fconn iface.Iclient) {
45 | //父节点掉线
46 | rname, err := fconn.GetProperty("remote")
47 | if err == nil {
48 | GlobalClusterServer.RemoveRemote(rname.(string))
49 | logger.Error("remote " + rname.(string) + " disconnected from " + utils.GlobalObject.Name)
50 | }
51 | }
52 |
53 | //reconnected to master
54 | func ReConnectMasterCB(fconn iface.Iclient) {
55 | rpc := cluster.NewChild(utils.GlobalObject.Name, GlobalClusterServer.MasterObj)
56 | response, err := rpc.CallChildForResult("TakeProxy", utils.GlobalObject.Name)
57 | if err == nil {
58 | roots, ok := response.Result["roots"]
59 | if ok {
60 | for _, root := range roots.([]string) {
61 | GlobalClusterServer.ConnectToRemote(root)
62 | }
63 | }
64 | } else {
65 | panic(fmt.Sprintf("reconnected to master error: %s", err))
66 | }
67 | }
68 |
69 | func NewClusterServer(name, path string) *ClusterServer {
70 | logger.SetPrefix(fmt.Sprintf("[%s]", strings.ToUpper(name)))
71 | cconf, err := cluster.NewClusterConf(path)
72 | if err != nil {
73 | panic("cluster conf error!!!")
74 | }
75 |
76 | GlobalClusterServer = &ClusterServer{
77 | Name: name,
78 | Cconf: cconf,
79 | RemoteNodesMgr: cluster.NewChildMgr(),
80 | ChildsMgr: cluster.NewChildMgr(),
81 | modules: make(map[string][]interface{}, 0),
82 | httpServerMux: http.NewServeMux(),
83 | }
84 |
85 | serverconf, ok := GlobalClusterServer.Cconf.Servers[name]
86 | if !ok {
87 | panic(fmt.Sprintf("no server %s in clusterconf!!!", name))
88 | }
89 |
90 | utils.GlobalObject.Name = name
91 | utils.GlobalObject.OnClusterClosed = DoCSConnectionLost
92 | utils.GlobalObject.OnClusterCClosed = DoCCConnectionLost
93 | utils.GlobalObject.RpcCProtoc = cluster.NewRpcClientProtocol()
94 |
95 | if utils.GlobalObject.PoolSize > 0 {
96 | //init rpc worker pool
97 | utils.GlobalObject.RpcCProtoc.InitWorker(int32(utils.GlobalObject.PoolSize))
98 | }
99 | if serverconf.NetPort > 0 {
100 | utils.GlobalObject.Protoc = fnet.NewProtocol()
101 |
102 | }
103 | if serverconf.RootPort > 0 {
104 | utils.GlobalObject.RpcSProtoc = cluster.NewRpcServerProtocol()
105 | }
106 |
107 | if serverconf.Log != "" {
108 | utils.GlobalObject.LogName = serverconf.Log
109 | utils.ReSettingLog()
110 | }
111 |
112 | //telnet debug tool
113 | if serverconf.DebugPort > 0{
114 | if serverconf.Host != ""{
115 | GlobalClusterServer.TelnetServer = fserver.NewTcpServer("telnet_server", "tcp4", serverconf.Host, serverconf.DebugPort, 100, cluster.NewTelnetProtocol())
116 | }else{
117 | GlobalClusterServer.TelnetServer = fserver.NewTcpServer("telnet_server", "tcp4", "127.0.0.1", serverconf.DebugPort, 100, cluster.NewTelnetProtocol())
118 | }
119 | }
120 | return GlobalClusterServer
121 | }
122 |
123 | func (this *ClusterServer) StartClusterServer() {
124 | serverconf, ok := this.Cconf.Servers[utils.GlobalObject.Name]
125 | if !ok {
126 | panic("no server in clusterconf!!!")
127 | }
128 | //自动发现注册modules api
129 | modules, ok := this.modules[serverconf.Module]
130 | if ok {
131 | //api
132 | if serverconf.NetPort > 0 {
133 | for _, m := range modules[0].([]interface{}){
134 | if m != nil{
135 | this.AddRouter(m)
136 | }
137 | }
138 | }
139 | //http
140 | if len(serverconf.Http) > 0 || len(serverconf.Https) > 0{
141 | for _, m := range modules[1].([]interface{}){
142 | if m != nil{
143 | this.AddHttpRouter(m)
144 | }
145 | }
146 | }
147 | //rpc
148 | for _, m := range modules[2].([]interface{}){
149 | if m != nil{
150 | this.AddRpcRouter(m)
151 | }
152 | }
153 | }
154 |
155 | //http server
156 | if len(serverconf.Http) > 0 {
157 | //staticfile handel
158 | if len(serverconf.Http) == 2 {
159 | this.httpServerMux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(serverconf.Http[1].(string)))))
160 | }
161 | httpserver := &http.Server{
162 | Addr: fmt.Sprintf(":%d", int(serverconf.Http[0].(float64))),
163 | Handler: this.httpServerMux,
164 | ReadTimeout: 5 * time.Second,
165 | WriteTimeout: 5 * time.Second,
166 | MaxHeaderBytes: 1 << 20, //1M
167 | }
168 | httpserver.SetKeepAlivesEnabled(true)
169 | go httpserver.ListenAndServe()
170 | logger.Info(fmt.Sprintf("http://%s:%d start", serverconf.Host, int(serverconf.Http[0].(float64))))
171 | } else if len(serverconf.Https) > 2 {
172 | //staticfile handel
173 | if len(serverconf.Https) == 4 {
174 | this.httpServerMux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(serverconf.Https[3].(string)))))
175 | }
176 | httpserver := &http.Server{
177 | Addr: fmt.Sprintf(":%d", int(serverconf.Https[0].(float64))),
178 | Handler: this.httpServerMux,
179 | ReadTimeout: 5 * time.Second,
180 | WriteTimeout: 5 * time.Second,
181 | MaxHeaderBytes: 1 << 20, //1M
182 | }
183 | httpserver.SetKeepAlivesEnabled(true)
184 | go httpserver.ListenAndServeTLS(serverconf.Https[1].(string), serverconf.Https[2].(string))
185 | logger.Info(fmt.Sprintf("http://%s:%d start", serverconf.Host, int(serverconf.Https[0].(float64))))
186 | }
187 | //tcp server
188 | if serverconf.NetPort > 0 {
189 | utils.GlobalObject.TcpPort = serverconf.NetPort
190 | if serverconf.Host != ""{
191 | this.NetServer = fserver.NewTcpServer("xingocluster_net_server", "tcp4", serverconf.Host, serverconf.NetPort,
192 | utils.GlobalObject.MaxConn, utils.GlobalObject.Protoc)
193 | }else{
194 | this.NetServer = fserver.NewTcpServer("xingocluster_net_server", "tcp4", serverconf.Host, serverconf.NetPort,
195 | utils.GlobalObject.MaxConn, utils.GlobalObject.Protoc)
196 | }
197 | this.NetServer.Start()
198 | }
199 | if serverconf.RootPort > 0 {
200 | if serverconf.Host != ""{
201 | this.RootServer = fserver.NewTcpServer("xingocluster_root_server", "tcp4", serverconf.Host, serverconf.RootPort,
202 | utils.GlobalObject.IntraMaxConn, utils.GlobalObject.RpcSProtoc)
203 | }else{
204 | this.RootServer = fserver.NewTcpServer("xingocluster_root_server", "tcp4", serverconf.Host, serverconf.RootPort,
205 | utils.GlobalObject.IntraMaxConn, utils.GlobalObject.RpcSProtoc)
206 | }
207 | this.RootServer.Start()
208 | }
209 | //telnet
210 | if this.TelnetServer != nil{
211 | logger.Info(fmt.Sprintf("telnet tool start: %s:%d.", serverconf.Host, serverconf.DebugPort))
212 | this.TelnetServer.Start()
213 | }
214 |
215 | //master
216 | this.ConnectToMaster()
217 |
218 | logger.Info("xingo cluster start success.")
219 | // close
220 | this.WaitSignal()
221 | this.MasterObj.Stop(true)
222 | if this.RootServer != nil {
223 | this.RootServer.Stop()
224 | }
225 |
226 | if this.NetServer != nil {
227 | this.NetServer.Stop()
228 | }
229 |
230 | if this.TelnetServer != nil{
231 | this.TelnetServer.Stop()
232 | }
233 | logger.Info("xingo cluster stoped.")
234 | }
235 |
236 | func (this *ClusterServer) WaitSignal() {
237 | signal.Notify(utils.GlobalObject.ProcessSignalChan, os.Kill, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT)
238 | sig := <-utils.GlobalObject.ProcessSignalChan
239 | //尝试主动通知master checkalive
240 | rpc := cluster.NewChild(utils.GlobalObject.Name, this.MasterObj)
241 | rpc.CallChildNotForResult("ChildOffLine", utils.GlobalObject.Name)
242 |
243 | logger.Info(fmt.Sprintf("server exit. signal: [%s]", sig))
244 | }
245 |
246 | func (this *ClusterServer) ConnectToMaster() {
247 | master := fnet.NewReConnTcpClient(this.Cconf.Master.Host, this.Cconf.Master.RootPort, utils.GlobalObject.RpcCProtoc, 1024, 60, ReConnectMasterCB)
248 | this.MasterObj = master
249 | master.Start()
250 | //注册到master
251 | rpc := cluster.NewChild(utils.GlobalObject.Name, this.MasterObj)
252 | response, err := rpc.CallChildForResult("TakeProxy", utils.GlobalObject.Name)
253 | if err == nil {
254 | roots, ok := response.Result["roots"]
255 | if ok {
256 | for _, root := range roots.([]string) {
257 | this.ConnectToRemote(root)
258 | }
259 | }
260 | } else {
261 | panic(fmt.Sprintf("connected to master error: %s", err))
262 | }
263 | }
264 |
265 | func (this *ClusterServer) ConnectToRemote(rname string) {
266 | rserverconf, ok := this.Cconf.Servers[rname]
267 | if ok {
268 | //处理master掉线,重新通知的情况
269 | if _, err := this.GetRemote(rname); err != nil {
270 | rserver := fnet.NewTcpClient(rserverconf.Host, rserverconf.RootPort, utils.GlobalObject.RpcCProtoc)
271 | this.RemoteNodesMgr.AddChild(rname, rserver)
272 | rserver.Start()
273 | rserver.SetProperty("remote", rname)
274 | //takeproxy
275 | child, err := this.RemoteNodesMgr.GetChild(rname)
276 | if err == nil {
277 | child.CallChildNotForResult("TakeProxy", utils.GlobalObject.Name)
278 | }
279 | } else {
280 | logger.Info("Remote connection already exist!")
281 | }
282 | } else {
283 | //未找到节点
284 | logger.Error("ConnectToRemote error. " + rname + " node can`t found!!!")
285 | }
286 | }
287 |
288 | func (this *ClusterServer) AddRouter(router interface{}) {
289 | if utils.GlobalObject.Protoc != nil{
290 | //add api ---------------start
291 | utils.GlobalObject.Protoc.AddRpcRouter(router)
292 | //add api ---------------end
293 | }
294 | }
295 |
296 | func (this *ClusterServer) AddRpcRouter(router interface{}) {
297 | //add api ---------------start
298 | utils.GlobalObject.RpcCProtoc.AddRpcRouter(router)
299 | if utils.GlobalObject.RpcSProtoc != nil {
300 | utils.GlobalObject.RpcSProtoc.AddRpcRouter(router)
301 | }
302 | //add api ---------------end
303 | }
304 |
305 | /*
306 | 子节点连上来回调
307 | */
308 | func (this *ClusterServer) AddChild(name string, writer iface.IWriter) {
309 | this.Lock()
310 | defer this.Unlock()
311 |
312 | this.ChildsMgr.AddChild(name, writer)
313 | writer.SetProperty("child", name)
314 | }
315 |
316 | /*
317 | 子节点断开回调
318 | */
319 | func (this *ClusterServer) RemoveChild(name string) {
320 | this.Lock()
321 | defer this.Unlock()
322 |
323 | this.ChildsMgr.RemoveChild(name)
324 | }
325 |
326 | func (this *ClusterServer) RemoveRemote(name string) {
327 | this.Lock()
328 | defer this.Unlock()
329 |
330 | this.RemoteNodesMgr.RemoveChild(name)
331 | }
332 |
333 | func (this *ClusterServer) GetRemote(name string) (*cluster.Child, error) {
334 | this.RLock()
335 | defer this.RUnlock()
336 |
337 | return this.RemoteNodesMgr.GetChild(name)
338 | }
339 |
340 | /*
341 | 注册模块到分布式服务器
342 | */
343 | func (this *ClusterServer) AddModule(mname string, apimodule interface{},httpmodule interface{}, rpcmodule interface{}) {
344 | //this.modules[mname] = []interface{}{module, rpcmodule}
345 | if _,ok := this.modules[mname]; ok{
346 | this.modules[mname][0] = append(this.modules[mname][0].([]interface{}), apimodule)
347 | this.modules[mname][1] = append(this.modules[mname][1].([]interface{}), httpmodule)
348 | this.modules[mname][2] = append(this.modules[mname][2].([]interface{}), rpcmodule)
349 | }else{
350 | this.modules[mname] = []interface{}{[]interface{}{apimodule}, []interface{}{httpmodule}, []interface{}{rpcmodule}}
351 | }
352 | }
353 |
354 | /*
355 | 注册http的api到分布式服务器
356 | */
357 | func (this *ClusterServer) AddHttpRouter(router interface{}) {
358 | value := reflect.ValueOf(router)
359 | tp := value.Type()
360 | for i := 0; i < value.NumMethod(); i += 1 {
361 | name := tp.Method(i).Name
362 | uri := fmt.Sprintf("/%s", strings.ToLower(strings.Replace(name, "Handle", "", 1)))
363 | this.httpServerMux.HandleFunc(uri,
364 | utils.HttpRequestWrap(uri, value.Method(i).Interface().(func(http.ResponseWriter, *http.Request))))
365 | logger.Info("add http url: " + uri)
366 | }
367 | }
368 |
--------------------------------------------------------------------------------
/db/mongo/dboperate.go:
--------------------------------------------------------------------------------
1 | package mongo
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "gopkg.in/mgo.v2"
7 | "gopkg.in/mgo.v2/bson"
8 | "time"
9 | "github.com/viphxin/xingo/logger"
10 | )
11 |
12 | const (
13 | Strong = 1
14 | Monotonic = 2
15 | )
16 |
17 | var (
18 | XINGO_MONGODB_SESSION_NIL_ERR = errors.New("DbOperate session nil.")
19 | XINGO_MONGODB_NOTFOUND_ERR = errors.New("not found!")
20 | XINGO_MONGODB_DBFINDALL_ERR = errors.New("DBFindAll failed,q is nil!")
21 | XINGO_MONGODB_OPENGRIDFILE_ERR = errors.New("OpenGridFile failed!")
22 | XINGO_MONGODB_READGRIDFILE_ERR = errors.New("ReadGridFile failed!")
23 | XINGO_MONGODB_CREATEGRIDFILE_ERR = errors.New("CreateGridFile is nil")
24 | )
25 |
26 | type DbCfg struct {
27 | DbHost string
28 | DbPort int
29 | DbName string
30 | DbUser string
31 | DbPass string
32 | }
33 |
34 | func NewDbCfg(host string, port int, name , user, pass string) *DbCfg{
35 | return &DbCfg{
36 | DbHost: host,
37 | DbPort: port,
38 | DbName: name,
39 | DbUser: user,
40 | DbPass: pass,
41 | }
42 | }
43 |
44 | func (this *DbCfg)String() string{
45 | url := fmt.Sprintf("mongodb://%s:%s@%s:%d/%s",
46 | this.DbUser, this.DbPass, this.DbHost, this.DbPort, this.DbName)
47 | if this.DbUser == "" || this.DbPass == "" {
48 | url = fmt.Sprintf("mongodb://%s:%d/%s", this.DbHost, this.DbPort, this.DbName)
49 | }
50 | return url
51 | }
52 |
53 | type DbOperate struct {
54 | session *mgo.Session
55 | timeout time.Duration
56 | dbcfg *DbCfg
57 | }
58 |
59 | func NewDbOperate(dbcfg *DbCfg, timeout time.Duration) *DbOperate{
60 | return &DbOperate{nil, timeout, dbcfg}
61 | }
62 |
63 | func (db *DbOperate) GetDbSession() *mgo.Session {
64 | return db.session
65 | }
66 |
67 | func (this *DbOperate) SetMode(mode int, refresh bool) {
68 | status := mgo.Monotonic
69 | if mode == Strong {
70 | status = mgo.Strong
71 | } else {
72 | status = mgo.Monotonic
73 | }
74 |
75 | this.session.SetMode(status, refresh)
76 | }
77 |
78 | func (this *DbOperate) OpenDB(set_index_func func(ms *mgo.Session)) error {
79 | logger.Info(fmt.Sprintf("DbOperate mongodb connect url: %s\n", this.dbcfg.String()))
80 |
81 | var err error
82 | this.session, err = mgo.DialWithTimeout(this.dbcfg.String(), this.timeout)
83 | if err != nil {
84 | panic(err.Error())
85 | }
86 |
87 | this.session.SetMode(mgo.Monotonic, true)
88 | //set index
89 | if set_index_func != nil{
90 | set_index_func(this.session)
91 | }
92 | logger.Info(fmt.Sprintf("DbOperate Connect %v mongodb...OK", this.dbcfg.String()))
93 | return nil
94 | }
95 |
96 | func (this *DbOperate) CloseDB() {
97 | if this.session != nil {
98 | this.session.DB("").Logout()
99 | this.session.Close()
100 | this.session = nil
101 | logger.Info("Disconnect mongodb url: ", this.dbcfg.String())
102 | }
103 | }
104 |
105 | func (this *DbOperate) RefreshSession() {
106 | this.session.Refresh()
107 |
108 | }
109 |
110 | func (this *DbOperate) Insert(collection string, doc interface{}) error {
111 | if this.session == nil {
112 | return XINGO_MONGODB_SESSION_NIL_ERR
113 | }
114 |
115 | local_session := this.session.Copy()
116 | defer local_session.Close()
117 |
118 | c := local_session.DB("").C(collection)
119 |
120 | return c.Insert(doc)
121 | }
122 |
123 | func (this *DbOperate) StrongInsert(collection string, doc interface{}) error {
124 | if this.session == nil {
125 | return XINGO_MONGODB_SESSION_NIL_ERR
126 | }
127 |
128 | local_session := this.session.Copy()
129 | defer local_session.Close()
130 |
131 | local_session.SetMode(mgo.Strong, true)
132 |
133 | c := local_session.DB("").C(collection)
134 |
135 | return c.Insert(doc)
136 | }
137 |
138 | func (this *DbOperate) Cover(collection string, cond interface{}, change interface{}) error {
139 | if this.session == nil {
140 | return XINGO_MONGODB_SESSION_NIL_ERR
141 | }
142 |
143 | local_session := this.session.Copy()
144 | defer local_session.Close()
145 |
146 | c := local_session.DB("").C(collection)
147 |
148 | return c.Update(cond, change)
149 | }
150 |
151 | func (this *DbOperate) Update(collection string, cond interface{}, change interface{}) error {
152 | if this.session == nil {
153 | return XINGO_MONGODB_SESSION_NIL_ERR
154 | }
155 |
156 | local_session := this.session.Copy()
157 | defer local_session.Close()
158 |
159 | c := local_session.DB("").C(collection)
160 |
161 | return c.Update(cond, bson.M{"$set": change})
162 | }
163 |
164 | func (this *DbOperate) StrongUpdate(collection string, cond interface{}, change interface{}) error {
165 | if this.session == nil {
166 | return XINGO_MONGODB_SESSION_NIL_ERR
167 | }
168 |
169 | local_session := this.session.Copy()
170 | defer local_session.Close()
171 |
172 | local_session.SetMode(mgo.Strong, true)
173 | c := local_session.DB("").C(collection)
174 |
175 | return c.Update(cond, bson.M{"$set": change})
176 | }
177 |
178 | func (this *DbOperate) UpdateInsert(collection string, cond interface{}, doc interface{}) error {
179 | if this.session == nil {
180 | return XINGO_MONGODB_SESSION_NIL_ERR
181 | }
182 |
183 | local_session := this.session.Copy()
184 | defer local_session.Close()
185 |
186 | c := local_session.DB("").C(collection)
187 | _, err := c.Upsert(cond, bson.M{"$set": doc})
188 | if err != nil {
189 | logger.Error(fmt.Sprintf("UpdateInsert failed collection is:%s. cond is:%v", collection, cond))
190 | }
191 |
192 | return err
193 | }
194 |
195 | func (this *DbOperate) StrongUpdateInsert(collection string, cond interface{}, doc interface{}) error {
196 | if this.session == nil {
197 | return XINGO_MONGODB_SESSION_NIL_ERR
198 | }
199 |
200 | local_session := this.session.Copy()
201 | defer local_session.Close()
202 |
203 | local_session.SetMode(mgo.Strong, true)
204 |
205 | c := local_session.DB("").C(collection)
206 | _, err := c.Upsert(cond, bson.M{"$set": doc})
207 | if err != nil {
208 | logger.Error(fmt.Sprintf("UpdateInsert failed collection is:%s. cond is:%v", collection, cond))
209 | }
210 |
211 | return err
212 | }
213 |
214 | func (this *DbOperate) RemoveOne(collection string, cond_name string, cond_value int64) error {
215 | if this.session == nil {
216 | return XINGO_MONGODB_SESSION_NIL_ERR
217 | }
218 |
219 | local_session := this.session.Copy()
220 | defer local_session.Close()
221 |
222 | c := local_session.DB("").C(collection)
223 | err := c.Remove(bson.M{cond_name: cond_value})
224 | if err != nil && err != mgo.ErrNotFound {
225 | logger.Error(fmt.Sprintf("remove failed from collection:%s. name:%s-value:%d", collection, cond_name, cond_value))
226 | }
227 |
228 | return err
229 |
230 | }
231 |
232 | func (this *DbOperate) RemoveOneByCond(collection string, cond interface{}) error {
233 | if this.session == nil {
234 | return XINGO_MONGODB_SESSION_NIL_ERR
235 | }
236 |
237 | local_session := this.session.Copy()
238 | defer local_session.Close()
239 |
240 | c := local_session.DB("").C(collection)
241 | err := c.Remove(cond)
242 |
243 | if err != nil && err != mgo.ErrNotFound {
244 | logger.Error(fmt.Sprintf("remove failed from collection:%s. cond :%v, err: %v.", collection, cond, err))
245 | }
246 |
247 | return err
248 |
249 | }
250 |
251 | func (this *DbOperate) RemoveAll(collection string, cond interface{}) error {
252 | if this.session == nil {
253 | return XINGO_MONGODB_SESSION_NIL_ERR
254 | }
255 |
256 | local_session := this.session.Copy()
257 | defer local_session.Close()
258 |
259 | c := local_session.DB("").C(collection)
260 | change, err := c.RemoveAll(cond)
261 | if err != nil && err != mgo.ErrNotFound {
262 | logger.Error(fmt.Sprintf("DbOperate.RemoveAll failed : %s, %v", collection, cond))
263 | return err
264 | }
265 | logger.Debug(fmt.Sprintf("DbOperate.RemoveAll: %v, %v", change.Updated, change.Removed))
266 | return nil
267 | }
268 |
269 | func (this *DbOperate) DBFindOne(collection string, cond interface{}, resHandler func(bson.M) error) error {
270 | if this.session == nil {
271 | return XINGO_MONGODB_SESSION_NIL_ERR
272 | }
273 |
274 | local_session := this.session.Copy()
275 | defer local_session.Close()
276 |
277 | c := local_session.DB("").C(collection)
278 | q := c.Find(cond)
279 |
280 | m := make(bson.M)
281 | if err := q.One(m); err != nil {
282 | if mgo.ErrNotFound != err {
283 | logger.Error(fmt.Sprintf("DBFindOne query falied,return error: %v; name: %v.", err, collection))
284 | }
285 | return err
286 | }
287 |
288 | if nil != resHandler {
289 | return resHandler(m)
290 | }
291 |
292 | return nil
293 |
294 | }
295 |
296 | func (this *DbOperate) StrongDBFindOne(collection string, cond interface{}, resHandler func(bson.M) error) error {
297 | if this.session == nil {
298 | return XINGO_MONGODB_SESSION_NIL_ERR
299 | }
300 |
301 | local_session := this.session.Copy()
302 | defer local_session.Close()
303 |
304 | local_session.SetMode(mgo.Strong, true)
305 |
306 | c := local_session.DB("").C(collection)
307 | q := c.Find(cond)
308 |
309 | m := make(bson.M)
310 | if err := q.One(m); err != nil {
311 | if mgo.ErrNotFound != err {
312 | logger.Error(fmt.Sprintf("DBFindOne query falied, return error: %v; name: %v.", err, collection))
313 | }
314 | return err
315 | }
316 |
317 | if nil != resHandler {
318 | return resHandler(m)
319 | }
320 |
321 | return nil
322 |
323 | }
324 |
325 | func (this *DbOperate) DBFindAll(collection string, cond interface{}, resHandler func(bson.M) error) error {
326 | if this.session == nil {
327 | return XINGO_MONGODB_SESSION_NIL_ERR
328 | }
329 |
330 | local_session := this.session.Copy()
331 | defer local_session.Close()
332 |
333 | c := local_session.DB("").C(collection)
334 | q := c.Find(cond)
335 |
336 | if nil == q {
337 | return XINGO_MONGODB_DBFINDALL_ERR
338 | }
339 |
340 | iter := q.Iter()
341 | m := make(bson.M)
342 | for iter.Next(m) == true {
343 | if nil != resHandler {
344 | err := resHandler(m)
345 | if err != nil {
346 | logger.Error(fmt.Sprintf("resHandler error :%v!!!", err))
347 | return err
348 | }
349 | }
350 | }
351 |
352 | return nil
353 |
354 | }
355 |
356 | func (this *DbOperate) StrongDBFindAll(collection string, cond interface{}, resHandler func(bson.M) error) error {
357 | if this.session == nil {
358 | return XINGO_MONGODB_SESSION_NIL_ERR
359 | }
360 |
361 | local_session := this.session.Copy()
362 | defer local_session.Close()
363 |
364 | local_session.SetMode(mgo.Strong, true)
365 |
366 | c := local_session.DB("").C(collection)
367 | q := c.Find(cond)
368 |
369 | logger.Debug(fmt.Sprintf("[DbOperate.DBFindAll] name:%s,query:%v", collection, cond))
370 |
371 | if nil == q {
372 | return XINGO_MONGODB_DBFINDALL_ERR
373 | }
374 | iter := q.Iter()
375 | m := make(bson.M)
376 | for iter.Next(m) == true {
377 | if resHandler != nil {
378 | err := resHandler(m)
379 | if err != nil {
380 | logger.Error(fmt.Sprintf("resHandler error :%v!!!", err))
381 | return err
382 | }
383 | }
384 | }
385 |
386 | return nil
387 | }
388 |
389 | func (this *DbOperate) DBFindAllEx(collection string, cond interface{}, resHandler func(*mgo.Query) error) error {
390 | if this.session == nil {
391 | return XINGO_MONGODB_SESSION_NIL_ERR
392 | }
393 |
394 | local_session := this.session.Copy()
395 | defer local_session.Close()
396 |
397 | c := local_session.DB("").C(collection)
398 | q := c.Find(cond)
399 |
400 | logger.Error(fmt.Sprintf("[DbOperate.DBFindAll] name:%s,query:%v", collection, cond))
401 |
402 | if nil == q {
403 | return XINGO_MONGODB_DBFINDALL_ERR
404 | }
405 | if nil != resHandler {
406 | return resHandler(q)
407 | }
408 | return nil
409 | }
410 |
411 | func (this *DbOperate) StrongDBFindAllEx(collection string, cond interface{}, resHandler func(*mgo.Query) error) error {
412 | if this.session == nil {
413 | return XINGO_MONGODB_SESSION_NIL_ERR
414 | }
415 |
416 | local_session := this.session.Copy()
417 | defer local_session.Close()
418 |
419 | local_session.SetMode(mgo.Strong, true)
420 |
421 | c := local_session.DB("").C(collection)
422 | q := c.Find(cond)
423 |
424 | logger.Debug(fmt.Sprintf("[DbOperate.DBFindAll] name:%s,query:%v", collection, cond))
425 |
426 | if nil == q {
427 | return XINGO_MONGODB_DBFINDALL_ERR
428 | }
429 | if nil != resHandler {
430 | return resHandler(q)
431 | }
432 | return nil
433 | }
434 |
435 | func (this *DbOperate) FindAndModify(collection string, cond interface{}, change mgo.Change, val interface{}) error {
436 | if this.session == nil {
437 | return XINGO_MONGODB_SESSION_NIL_ERR
438 | }
439 |
440 | local_session := this.session.Copy()
441 | defer local_session.Close()
442 |
443 | local_session.SetMode(mgo.Strong, true)
444 | c := local_session.DB("").C(collection)
445 | _, err := c.Find(cond).Apply(change, val)
446 | return err
447 | }
448 |
449 | func (this *DbOperate) FindAll(collection string, cond interface{}, all interface{}) error {
450 | if this.session == nil {
451 | return XINGO_MONGODB_SESSION_NIL_ERR
452 | }
453 |
454 | local_session := this.session.Copy()
455 | defer local_session.Close()
456 |
457 | local_session.SetMode(mgo.Strong, true)
458 | c := local_session.DB("").C(collection)
459 | err := c.Find(cond).All(all)
460 | return err
461 | }
462 |
463 | func (this *DbOperate) StrongBatchInsert(collection string, docs ...interface{}) error {
464 | if this.session == nil {
465 | return XINGO_MONGODB_SESSION_NIL_ERR
466 | }
467 |
468 | local_session := this.session.Copy()
469 | defer local_session.Close()
470 |
471 | local_session.SetMode(mgo.Strong, true)
472 | c := local_session.DB("").C(collection)
473 | return c.Insert(docs...)
474 | }
475 |
476 | func (this *DbOperate) FindOne(collection string, cond interface{}, value interface{}) error {
477 | if this.session == nil {
478 | return XINGO_MONGODB_SESSION_NIL_ERR
479 | }
480 |
481 | local_session := this.session.Copy()
482 | defer local_session.Close()
483 | local_session.SetMode(mgo.Strong, true)
484 | c := local_session.DB("").C(collection)
485 | return c.Find(cond).One(value)
486 | }
487 |
488 | //友情提示,如果存在多个document,会报错,请用DeleteAll
489 | func (this *DbOperate) DeleteOne(collection string, cond interface{}) error {
490 | if this.session == nil {
491 | return XINGO_MONGODB_SESSION_NIL_ERR
492 | }
493 |
494 | local_session := this.session.Copy()
495 | defer local_session.Close()
496 | local_session.SetMode(mgo.Strong, true)
497 | c := local_session.DB("").C(collection)
498 | return c.Remove(cond)
499 | }
500 |
501 | func (this *DbOperate) DeleteAll(collection string, cond interface{}) (int, error) {
502 | if this.session == nil {
503 | return 0, XINGO_MONGODB_SESSION_NIL_ERR
504 | }
505 |
506 | local_session := this.session.Copy()
507 | defer local_session.Close()
508 | local_session.SetMode(mgo.Strong, true)
509 | c := local_session.DB("").C(collection)
510 | changeInfo, err := c.RemoveAll(cond)
511 | if err != nil{
512 | return 0, err
513 | }
514 | return changeInfo.Removed, nil
515 | }
516 |
517 | // gridfs
518 | func (this *DbOperate) OpenGridFile(collection string, filename string) ([]byte, error) {
519 | if this.session == nil {
520 | return nil, XINGO_MONGODB_SESSION_NIL_ERR
521 | }
522 |
523 | local_session := this.session.Copy()
524 | defer local_session.Close()
525 |
526 | gfs := local_session.DB("").GridFS(collection)
527 | // open file only for reading
528 | fsfile, err := gfs.Open(filename)
529 |
530 | if err != nil {
531 | return nil, err
532 | }
533 |
534 | if fsfile == nil {
535 | return nil, XINGO_MONGODB_OPENGRIDFILE_ERR
536 | }
537 | defer fsfile.Close()
538 |
539 | data := make([]byte, fsfile.Size())
540 | _, err = fsfile.Read(data)
541 |
542 | if err != nil {
543 | return nil, XINGO_MONGODB_READGRIDFILE_ERR
544 | }
545 | return data, nil
546 | }
547 |
548 | type gfsDocId struct {
549 | Id interface{} "_id"
550 | }
551 |
552 | func (this *DbOperate) CreateGridFile(collection string, filename string, resHandler func(*mgo.GridFile) error) error {
553 |
554 | var doc gfsDocId
555 | if this.session == nil {
556 | return XINGO_MONGODB_SESSION_NIL_ERR
557 | }
558 |
559 | local_session := this.session.Copy()
560 | defer local_session.Close()
561 |
562 | gfs := local_session.DB("").GridFS(collection)
563 |
564 | file, err := gfs.Create(filename)
565 | if err != nil {
566 | return err
567 | }
568 | if file == nil {
569 | return XINGO_MONGODB_CREATEGRIDFILE_ERR
570 | }
571 | if resHandler != nil{
572 | err = resHandler(file)
573 | if err != nil {
574 | return err
575 | }
576 | }
577 |
578 | newfileId := file.Id()
579 | file.Close()
580 |
581 | query := gfs.Files.Find(bson.M{"filename": filename, "_id": bson.M{"$ne": newfileId}})
582 | iter := query.Iter()
583 | for iter.Next(&doc) {
584 | if e := gfs.RemoveId(doc.Id); e != nil {
585 | err = e
586 | break
587 | }
588 | }
589 |
590 | return err
591 | }
592 |
593 | func (this *DbOperate) GridFileExists(collection string, filename string) (bool, error) {
594 | if this.session == nil {
595 | return false, XINGO_MONGODB_SESSION_NIL_ERR
596 | }
597 |
598 | local_session := this.session.Copy()
599 | defer local_session.Close()
600 |
601 | gfs := local_session.DB("").GridFS(collection)
602 |
603 | query := gfs.Files.Find(bson.M{"filename": filename})
604 | m := make(bson.M)
605 | if err := query.One(m); err != nil {
606 | if mgo.ErrNotFound != err {
607 | return false, err
608 | }
609 | return false, nil
610 | }
611 |
612 | return true, nil
613 | }
614 |
615 | func (this *DbOperate) RemoveGridFile(collection string, filename string) (bool, error) {
616 | if this.session == nil {
617 | return false, XINGO_MONGODB_SESSION_NIL_ERR
618 | }
619 |
620 | local_session := this.session.Copy()
621 | defer local_session.Close()
622 |
623 | gfs := local_session.DB("").GridFS(collection)
624 |
625 | if err := gfs.Remove(filename); err != nil {
626 | if mgo.ErrNotFound != err {
627 | return false, err
628 | }
629 | }
630 |
631 | return true, nil
632 | }
633 |
634 | func (this *DbOperate) BulkInsertDoc(collection string, docs []interface{}) error {
635 | if this.session == nil {
636 | return XINGO_MONGODB_SESSION_NIL_ERR
637 | }
638 |
639 | local_session := this.session.Copy()
640 | defer local_session.Close()
641 |
642 | c := local_session.DB("").C(collection)
643 |
644 | bulk := c.Bulk()
645 |
646 | bulk.Insert(docs...)
647 | _, err := bulk.Run()
648 | return err
649 | }
650 |
651 | func (this *DbOperate) BulkInsert(collection string, pairs []bson.M) error {
652 | if this.session == nil {
653 | return XINGO_MONGODB_SESSION_NIL_ERR
654 | }
655 |
656 | local_session := this.session.Copy()
657 | defer local_session.Close()
658 |
659 | c := local_session.DB("").C(collection)
660 |
661 | bulk := c.Bulk()
662 |
663 | for i := 0; i < len(pairs); i++ {
664 | bulk.Insert(pairs[i])
665 | }
666 | _, err := bulk.Run()
667 | return err
668 | }
669 | func (this *DbOperate) BulkUpdate(collection string, pairs []bson.M) error {
670 | if this.session == nil {
671 | return XINGO_MONGODB_SESSION_NIL_ERR
672 | }
673 |
674 | local_session := this.session.Copy()
675 | defer local_session.Close()
676 |
677 | c := local_session.DB("").C(collection)
678 |
679 | bulk := c.Bulk()
680 |
681 | for i := 0; i < len(pairs); i += 2 {
682 | selector := pairs[i]
683 | update := pairs[i+1]
684 | bulk.Update(selector, update)
685 | }
686 | _, err := bulk.Run()
687 | return err
688 | }
689 |
690 | func (this *DbOperate) GetMaxId(collection string, field string) (int64, error) {
691 | var id int64
692 | var present bool
693 |
694 | fnc := func(mq *mgo.Query) error {
695 | q := mq.Sort("-" + field).Limit(1)
696 | m := make(bson.M)
697 | if err := q.One(m); err != nil {
698 | id = 0
699 | }
700 | id, present = m[field].(int64)
701 | if !present {
702 | id = 0
703 | }
704 | return nil
705 | }
706 | err := this.DBFindAllEx(collection, nil, fnc)
707 | if nil != err {
708 | return 0, nil
709 | }
710 | return id, nil
711 | }
712 |
713 | func (this *DbOperate) WriteGridFile(collection string, filename string, data []byte) error {
714 | logger.Debug(fmt.Sprintf("Write grid file: %v, size %v.", filename, len(data)))
715 |
716 | fnc := func(file *mgo.GridFile) error {
717 | _, err := file.Write(data)
718 | return err
719 | }
720 |
721 | err := this.CreateGridFile(collection, filename, fnc)
722 | if err != nil {
723 | logger.Error("Write grid file fail: ", err)
724 | }
725 | return err
726 | }
727 |
728 | func (this *DbOperate) BulkUpsert(collection string, pairs []bson.M) error {
729 | if this.session == nil {
730 | return XINGO_MONGODB_SESSION_NIL_ERR
731 | }
732 |
733 | local_session := this.session.Copy()
734 | defer local_session.Close()
735 |
736 | c := local_session.DB("").C(collection)
737 |
738 | bulk := c.Bulk()
739 |
740 | for i := 0; i < len(pairs); i += 2 {
741 | selector := pairs[i]
742 | update := pairs[i+1]
743 | bulk.Upsert(selector, update)
744 | }
745 | _, err := bulk.Run()
746 | return err
747 | }
--------------------------------------------------------------------------------