diff --git a/Makefile b/Makefile index 0a4bfc0..af08114 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -all: gen build +all: gen game +IMGTIME := $(shell date "+%G%m%d_%H%M%S") +pname = plugin-$(IMGTIME).so gen: protoc -I./protos --go_out=./protos --go-grpc_out=./protos ./protos/*proto @@ -8,11 +10,16 @@ test: go run test/client.go http: go run cmd/http.go -game: + +game:plugin go run cmd/game.go build: go build -o bin/account cmd/http.go go build -o bin/game cmd/game.go go build -o bin/test test/client.go +plugin: + #go build -ldflags -pluginpath="plugin/hot-1" --buildmode=plugin -o bin/plugin.so src/plugin/*.go + go build --buildmode=plugin -o bin/$(pname) src/plugin/*.go + cd bin && rm -rf plugin.so && ln -s $(pname) plugin.so && cd - -.PHONY: all build protos test cert \ No newline at end of file +.PHONY: all build protos test cert plugin \ No newline at end of file diff --git a/cmd/game.go b/cmd/game.go index c97354d..7cea249 100644 --- a/cmd/game.go +++ b/cmd/game.go @@ -14,16 +14,26 @@ func main() { stopChan := make(chan os.Signal) signal.Notify(stopChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) + userChan := make(chan os.Signal) + signal.Notify(userChan, syscall.SIGUSR1, syscall.SIGUSR2) + s := net.NewServer(conf.GlobalConf.GameConf) go func() { err <- s.Start() }() - select { - case e := <- err: - logger.Error("game server error: %v", e) - case <-stopChan: - s.Stop() - logger.Debug("game stop...") + for { + select { + case e := <- err: + logger.Error("game server error: %v", e) + return + case <-stopChan: + s.Stop() + logger.Debug("game stop...") + return + case u := <-userChan: + logger.Debug("userChan .. %v",u.String()) + s.LoadPlugin() + } } } diff --git a/conf/conf.go b/conf/conf.go index 5f456b7..c3bbfea 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -31,18 +31,14 @@ type MongoConf struct { MaxNum int `yaml:"maxnum"` } -type TLS struct { - Status bool `yaml:"status"` - Key string `yaml:"key"` - Pem string `yaml:"pem"` -} - type SConf struct { ID string `yaml:"id"` Name string `yaml:"name"` IP string `yaml:"ip"` Port int `yaml:"port"` DBName string `yaml:"dbname"` + WorkerPoolSize int `yaml:"pool_size"` + PluginPath string `yaml:"plugin_path"` } type LogConsole struct { @@ -81,7 +77,6 @@ type ServerConf struct { WorkerID int64 `yaml:"workerid"` DatacenterID int64 `yaml:"datacenterid"` MongoConf *MongoConf `yaml:"mongo"` - TLS *TLS `yaml:"tls"` AccountConf *SConf `yaml:"server_account"` GameConf *SConf `yaml:"server_game"` RedisConf *RedisConf `yaml:"redis"` diff --git a/conf/conf.yaml b/conf/conf.yaml index c6fa0cd..9e3562d 100644 --- a/conf/conf.yaml +++ b/conf/conf.yaml @@ -22,6 +22,7 @@ server_account: ip: "192.168.0.206" port: 8848 dbname: "account" + pool_size: 1 server_game: id: "1" @@ -29,6 +30,8 @@ server_game: ip: "192.168.0.206" port: 8849 dbname: "game" + pool_size: 1 + plugin_path: "./bin/plugin.so" logconf: TimeFormat: "2006-01-02 15:04:05" diff --git a/plugin/plugin.go b/plugin/plugin.go new file mode 100644 index 0000000..638d61f --- /dev/null +++ b/plugin/plugin.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "pro2d/src/components/net" +) + +func IamPluginA(pkg net.MsgPkg) { + fmt.Println("Hello, I am PluginB!") + fmt.Println(pkg.Head.Length) +} diff --git a/plugin/plumain.go b/plugin/plumain.go new file mode 100644 index 0000000..31a0cac --- /dev/null +++ b/plugin/plumain.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "os" + "plugin" + "pro2d/src/components/net" +) + +func main() { + p, err := plugin.Open("./bin/plugin.so") + if err != nil { + fmt.Println("error open plugin: ", err) + os.Exit(-1) + } + s, err := p.Lookup("IamPluginA") + if err != nil { + fmt.Println("error lookup IamPluginA: ", err) + os.Exit(-1) + } + pkg1 := net.MsgPkg{ + Head: &net.Head{ + Length: 16, + Cmd: 0, + ErrCode: 0, + }, + } + if x, ok := s.(func(net.MsgPkg)); ok { + x(pkg1) + } + +} diff --git a/src/actions/RoleAction.go b/src/actions/RoleAction.go deleted file mode 100644 index 735d356..0000000 --- a/src/actions/RoleAction.go +++ /dev/null @@ -1,54 +0,0 @@ -package actions - -import ( - "github.com/golang/protobuf/proto" - "pro2d/conf" - "pro2d/protos/pb" - "pro2d/src/components/logger" - "pro2d/src/components/net" - "pro2d/src/models" -) - -func HeartRpc(msg *net.MsgPkg) (int32, proto.Message) { - return 0, nil -} - -func CreateRpc(msg *net.MsgPkg) (int32, proto.Message) { - req := pb.CreateReq{} - if err := proto.Unmarshal(msg.Body, &req); err != nil { - logger.Error("CreateRpc err: %v", err) - return 1, nil - } - role := models.RoleExistByUid(req.Uid) - if role != nil { - return 2, nil - } - - roleId := conf.SnowFlack.NextVal() - role = models.NewRole(roleId) - if _, err := role.Create(); err != nil { - logger.Error("CreateRpc role create err: %v", err) - return 3, nil - } - return 0, nil -} - -func LoginRpc(msg *net.MsgPkg) (int32, proto.Message) { - req := pb.LoginReq{} - if err := proto.Unmarshal(msg.Body, &req); err != nil { - logger.Error("loginRpc err: %v", err) - return 1, nil - } - - role := models.RoleExistByUid(req.Uid) - if role == nil { - return 2, nil - } - - return 0, &pb.RoleRsp{ - Role: role.Role, - Hero: nil, - Team: nil, - Equips: nil, - } -} \ No newline at end of file diff --git a/src/actions/protocode.go b/src/actions/protocode.go deleted file mode 100644 index 8018bf8..0000000 --- a/src/actions/protocode.go +++ /dev/null @@ -1,15 +0,0 @@ -package actions - -import ( - "pro2d/protos/pb" - "pro2d/src/components/net" -) - -func init() { - net.ActionMap = make(map[pb.ProtoCode]net.ActionHandler) - - net.ActionMap[pb.ProtoCode_HeartRpc] = HeartRpc - net.ActionMap[pb.ProtoCode_LoginRpc] = LoginRpc - net.ActionMap[pb.ProtoCode_CreateRpc] = CreateRpc - -} \ No newline at end of file diff --git a/src/common/common.go b/src/common/common.go index 41a8345..a4cb375 100644 --- a/src/common/common.go +++ b/src/common/common.go @@ -1,7 +1,14 @@ package common const ( - HEADLEN = 8 + WorkerPoolSize = 10 + MaxTaskPerWorker = 100 + MaxPacketSize = 10 * 1024 * 1024 + MaxMsgChanLen = 20 +) + +const ( + HEADLEN = 16 Pro2DTokenSignedString = "Pro2DSecret" ) diff --git a/src/components/db/redis.go b/src/components/db/redis.go index 0fd0aec..76de78a 100644 --- a/src/components/db/redis.go +++ b/src/components/db/redis.go @@ -2,12 +2,12 @@ package db import ( "github.com/garyburd/redigo/redis" - "pro2d/conf" "time" ) var RedisPool *redis.Pool -func ConnectRedis(conf *conf.ServerConf) error { +//conf *conf.ServerConf +func ConnectRedis(db int, auth, address string) error { RedisPool = &redis.Pool{ //最大活跃连接数,0代表无限 MaxActive: 888, @@ -16,11 +16,11 @@ func ConnectRedis(conf *conf.ServerConf) error { IdleTimeout: time.Second * 100, //定义拨号获得连接的函数 Dial: func() (redis.Conn, error) { - option := []redis.DialOption{redis.DialDatabase(conf.RedisConf.DB)} - if conf.RedisConf.Auth != "" { - option = append(option, redis.DialPassword(conf.RedisConf.Auth)) + option := []redis.DialOption{redis.DialDatabase(db)} + if auth != "" { + option = append(option, redis.DialPassword(auth)) } - return redis.Dial("tcp",conf.RedisConf.Address, option...) + return redis.Dial("tcp",address, option...) }, } return nil diff --git a/src/components/jwt/jwt.go b/src/components/jwt/jwt.go index 0104b26..6662675 100644 --- a/src/components/jwt/jwt.go +++ b/src/components/jwt/jwt.go @@ -3,9 +3,8 @@ package jwt import ( "context" "fmt" - "pro2d/conf" "pro2d/src/common" - "pro2d/src/utils" + "pro2d/src/components/logger" "time" jwt "github.com/dgrijalva/jwt-go" @@ -33,18 +32,18 @@ func ParseToken(tokenStr string) string { token, err := jwt.ParseWithClaims(tokenStr, &clientClaims, func(token *jwt.Token) (interface{}, error) { if token.Header["alg"] != "HS256" { //panic("ErrInvalidAlgorithm") - utils.Sugar.Error("ErrInvalidAlgorithm") + logger.Error("ErrInvalidAlgorithm") return nil, nil } return []byte(common.Pro2DTokenSignedString), nil }) if err != nil { - utils.Sugar.Error("jwt parse error") + logger.Error("jwt parse error") return "" } if !token.Valid { - utils.Sugar.Error("ErrInvalidToken") + logger.Error("ErrInvalidToken") return "" } return clientClaims.Uid @@ -75,7 +74,7 @@ func getTokenFromContext(ctx context.Context) (string, error) { func CheckAuth(ctx context.Context) string { tokenStr, err := getTokenFromContext(ctx) if err != nil { - utils.Sugar.Errorf("get token from context error") + logger.Error("get token from context error") return "" } return ParseToken(tokenStr) @@ -93,5 +92,5 @@ func (c AuthToken) GetRequestMetadata(ctx context.Context, uri ...string) (map[s } func (c AuthToken) RequireTransportSecurity() bool { - return conf.GlobalConf.TLS.Status + return false } \ No newline at end of file diff --git a/src/components/net/conn.go b/src/components/net/conn.go index ffe00e1..584a0ef 100644 --- a/src/components/net/conn.go +++ b/src/components/net/conn.go @@ -67,24 +67,28 @@ func (c *Connection) write() { func (c *Connection) read() { defer c.Quiting() - for { - c.scanner.Split(ParseMsg) + c.scanner.Split(ParseMsg) - for c.scanner.Scan() { - req, err := DecodeMsg(c.scanner.Bytes()) - if err != nil { - return - } - - req.Conn = c - c.Server.OnRecv(req) - } - - if err := c.scanner.Err(); err != nil { - fmt.Printf("scanner.err: %s\n", err.Error()) - c.Quiting() + for c.scanner.Scan() { + req, err := DecodeMsg(c.scanner.Bytes()) + if err != nil { return } + + req.Conn = c + //得到需要处理此条连接的workerID + workerID := c.Id % c.Server.SConf.WorkerPoolSize + //将请求消息发送给任务队列 + c.Server.TaskQueue[workerID] <- req + + //备注,可以在当前协程处理当条请求(如下, 实现很简单,已经删除),也可以丢到协程池里处理任务(如上),还未对比效果。 + //c.Server.OnRecv(req) + } + + if err := c.scanner.Err(); err != nil { + fmt.Printf("scanner.err: %s\n", err.Error()) + c.Quiting() + return } } diff --git a/src/components/net/server.go b/src/components/net/server.go index 59289e6..44dcd71 100644 --- a/src/components/net/server.go +++ b/src/components/net/server.go @@ -4,8 +4,10 @@ import ( "fmt" "github.com/golang/protobuf/proto" "net" + "plugin" "pro2d/conf" "pro2d/protos/pb" + "pro2d/src/common" "pro2d/src/components/db" "pro2d/src/components/etcd" "pro2d/src/components/logger" @@ -21,18 +23,48 @@ type Server struct { Clients *sync.Map EtcdClient *etcd.EtcdClient + TaskQueue []chan*MsgPkg } func NewServer(sConf *conf.SConf) *Server { return &Server{ SConf: sConf, Clients: new(sync.Map), + EtcdClient: new(etcd.EtcdClient), + + TaskQueue: make([]chan *MsgPkg, common.WorkerPoolSize), + } +} + +//StartWorkerPool 启动worker工作池 +func (s *Server) StartWorkerPool() { + //遍历需要启动worker的数量,依此启动 + for i := 0; i < s.SConf.WorkerPoolSize; i++ { + //一个worker被启动 + //给当前worker对应的任务队列开辟空间 + s.TaskQueue[i] = make(chan *MsgPkg, common.MaxTaskPerWorker) + //启动当前Worker,阻塞的等待对应的任务队列是否有消息传递进来 + go s.StartOneWorker(i, s.TaskQueue[i]) + } +} + +//StartOneWorker 启动一个Worker工作流程 +func (s *Server) StartOneWorker(workerID int, taskQueue chan *MsgPkg) { + //不断的等待队列中的消息 + for { + select { + //有消息则取出队列的Request,并执行绑定的业务方法 + case request := <-taskQueue: + _ = workerID + s.DoMsgHandler(request) + } } } -func (s *Server) OnRecv(msg *MsgPkg) { - logger.Debug("cmd: %d, data: %s", msg.Head.Cmd, msg.Body) +func (s *Server) DoMsgHandler(msg *MsgPkg) { + logger.Debug("DoMsgHandler cmd: %d, data: %s", msg.Head.Cmd, msg.Body) if md, ok := ActionMap[pb.ProtoCode(msg.Head.Cmd)]; ok { + logger.Debug("adfadfadfasdfadfadsf") errCode, protomsg := md(msg) rsp, err := proto.Marshal(protomsg) if err != nil { @@ -49,21 +81,47 @@ func (s *Server) OnClose(conn *Connection) { s.Clients.Delete(conn.Id) } +func (s *Server) LoadPlugin() { + //重新加载 + p, err:=plugin.Open(conf.GlobalConf.GameConf.PluginPath) + if err != nil { + logger.Error("load plugin err: %v", err) + return + } + symboll, err := p.Lookup("LoadRpc") + if err != nil { + logger.Error("plugin symbol err: %v", err) + return + } + if x, ok := symboll.(func()); ok { + x() + logger.Debug("load plugin success") + } +} + func (s *Server)Start() error { //mongo 初始化 - db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.AccountConf.DBName) + db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.GameConf.DBName) models.InitGameServerModels() //Etcd 初始化 s.EtcdClient = etcd.NewEtcdClient(conf.GlobalConf.Etcd) s.EtcdClient.PutWithLeasePrefix(conf.GlobalConf.GameConf.Name, conf.GlobalConf.GameConf.ID, fmt.Sprintf("%s:%d", conf.GlobalConf.GameConf.IP, conf.GlobalConf.GameConf.Port), 5) + //初始化plugin + _, err := plugin.Open(conf.GlobalConf.GameConf.PluginPath) + if err != nil { + return err + } + port := fmt.Sprintf(":%d", s.SConf.Port) l, err := net.Listen("tcp", port) if err != nil { return err } + s.StartWorkerPool() + logger.Debug("listen on %s\n", port) id := 0 for { diff --git a/src/plugin/RolePlugin.go b/src/plugin/RolePlugin.go new file mode 100644 index 0000000..face1ce --- /dev/null +++ b/src/plugin/RolePlugin.go @@ -0,0 +1,55 @@ +package main + +import ( + "github.com/golang/protobuf/proto" + "pro2d/conf" + "pro2d/protos/pb" + "pro2d/src/components/logger" + "pro2d/src/components/net" + "pro2d/src/models" +) + +func HeartRpc(msg *net.MsgPkg) (int32, proto.Message) { + return 0, nil +} + +func CreateRpc(msg *net.MsgPkg) (int32, proto.Message) { + req := pb.CreateReq{} + if err := proto.Unmarshal(msg.Body, &req); err != nil { + logger.Error("CreateRpc err: %v", err) + return 1, nil + } + role := models.RoleExistByUid(req.Uid) + if role != nil { + return 2, nil + } + + roleId := conf.SnowFlack.NextVal() + role = models.NewRole(roleId) + if _, err := role.Create(); err != nil { + logger.Error("CreateRpc role create err: %v", err) + return 3, nil + } + return 0, nil +} + +func LoginRpc(msg *net.MsgPkg) (int32, proto.Message) { + logger.Debug("cmd: %v, msg: %s", msg.Head.Cmd, msg.Body) + req := pb.LoginReq{} + if err := proto.Unmarshal(msg.Body, &req); err != nil { + logger.Error("loginRpc err: %v", err) + return 1, nil + } + + role := models.RoleExistByUid(req.Uid) + if role == nil { + return 2, nil + } + + return 0, &pb.RoleRsp{ + Role: role.Role, + Hero: nil, + Team: nil, + Equips: nil, + } +} \ No newline at end of file diff --git a/src/plugin/protocode.go b/src/plugin/protocode.go new file mode 100644 index 0000000..e778830 --- /dev/null +++ b/src/plugin/protocode.go @@ -0,0 +1,20 @@ +package main + +import ( + "pro2d/protos/pb" + "pro2d/src/components/logger" + "pro2d/src/components/net" +) + +func LoadRpc() { + logger.Debug("init protocode...") + net.ActionMap = make(map[pb.ProtoCode]net.ActionHandler) + net.ActionMap[pb.ProtoCode_HeartRpc] = HeartRpc + net.ActionMap[pb.ProtoCode_LoginRpc] = LoginRpc + net.ActionMap[pb.ProtoCode_CreateRpc] = CreateRpc + +} + +func init() { + LoadRpc() +} \ No newline at end of file diff --git a/src/utils/md5.go b/src/utils/md5.go deleted file mode 100644 index ce94156..0000000 --- a/src/utils/md5.go +++ /dev/null @@ -1,12 +0,0 @@ -package utils - -import ( - "crypto/md5" - "encoding/hex" -) - -func Md5V(str string) string { - h := md5.New() - h.Write([]byte(str)) - return hex.EncodeToString(h.Sum(nil)) -} \ No newline at end of file diff --git a/src/utils/utils.go b/src/utils/utils.go index 6e06ab5..a81b4bd 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -1,6 +1,8 @@ package utils import ( + "crypto/md5" + "encoding/hex" "reflect" "strings" ) @@ -44,4 +46,10 @@ func FindIndex(schema interface{}) (string, []string){ } } return strings.ToLower(s.Name()), index +} + +func Md5V(str string) string { + h := md5.New() + h.Write([]byte(str)) + return hex.EncodeToString(h.Sum(nil)) } \ No newline at end of file diff --git a/test/client.go b/test/client.go index 914527d..e6a8999 100644 --- a/test/client.go +++ b/test/client.go @@ -3,7 +3,9 @@ package main import ( "bytes" "encoding/binary" + "github.com/golang/protobuf/proto" "net" + "pro2d/protos/pb" "pro2d/src/components/logger" net2 "pro2d/src/components/net" ) @@ -12,15 +14,20 @@ func main() { head := &net2.Head{ Length: 0, - Cmd: 1, + Cmd: int32(pb.ProtoCode_LoginRpc), ErrCode: 0, PreField: 0, } + loginReq := &pb.LoginReq{ + Uid: "141815055745814528", + Device: "123123", + } + l, _ :=proto.Marshal(loginReq) b := net2.MsgPkg{ Head: head, - Body: []byte("hello world"), + Body: l, } head.Length = int32(16 + len(b.Body)) buf := &bytes.Buffer{} @@ -43,5 +50,4 @@ func main() { return } client.Write(buf.Bytes()) - select {} } \ No newline at end of file diff --git a/tools/csvtostruct_test.go b/tools/csvtostruct_test.go new file mode 100644 index 0000000..cd55938 --- /dev/null +++ b/tools/csvtostruct_test.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "testing" +) + +func TestGenerate_ReadCsvToStruct(t *testing.T) { + gt := NewGenerate("/Users/mac/Documents/project/Pro2D/Pro2DServer/csvdata", "int, string") + //err := gt.ReadCsvToStruct(*readPath) + err := gt.ReadCsvToMemory("/Users/mac/Documents/project/Pro2D/Pro2DServer/tools/") + if err != nil { + fmt.Printf("something err:%v\n", err) + return + } +} diff --git a/tools/main.go b/tools/main.go deleted file mode 100644 index 455faff..0000000 --- a/tools/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "flag" - "fmt" - _ "pro2d/csvdata" -) - -var ( - savePath = flag.String("savePath", "/Users/mac/Documents/project/Pro2D/Pro2DServer/csvdata", "Path to save the makefile") - readPath = flag.String("readPath", "/Users/mac/Documents/project/Pro2D/Pro2DServer/tools/", "The path of reading Excel") - allType = flag.String("allType", "int, string", "Specified field type") -) - -func main() { - flag.Parse() - if *savePath == "" || *readPath == "" || *allType == "" { - fmt.Println("savePath, readPath or allType is nil") - return - } - - gt := NewGenerate(*savePath, *allType) - //err := gt.ReadCsvToStruct(*readPath) - err := gt.ReadCsvToMemory(*readPath) - if err != nil { - fmt.Printf("something err:%v\n", err) - return - } -} \ No newline at end of file diff --git a/tools/protostostruct.go b/tools/protostostruct.go index 2294e0c..724b9e6 100644 --- a/tools/protostostruct.go +++ b/tools/protostostruct.go @@ -14,7 +14,7 @@ var ( ProtoCode = "syntax = \"proto3\";\noption go_package = \"./pb;pb\";\n\npackage protocode;\n\nenum ProtoCode\n{\n UNKNOWN = 0x000;\n %s\n}" ProtoCodeLine = "\t%sRpc = %02x;\n" - GoProtoCodeStr = "package actions\n\nimport (\n\t\"pro2d/components/net\"\n\t\"pro2d/protos/pb\"\n)\n\nfunc init() {\n\tnet.ActionMap = make(map[pb.ProtoCode]net.ActionHandler)\n\n%s\n}" + GoProtoCodeStr = "package main\n\nimport (\n\t\"pro2d/protos/pb\"\n\t\"pro2d/src/components/logger\"\n\t\"pro2d/src/components/net\"\n)\n\nfunc LoadRpc() {\n\tlogger.Debug(\"init protocode...\")\n\tnet.ActionMap = make(map[pb.ProtoCode]net.ActionHandler)\n%s\n}\n\nfunc init() {\n\tLoadRpc()\n}" GoProtoCodeLine = "\tnet.ActionMap[pb.ProtoCode_%sRpc] = %sRpc\n" ) @@ -98,7 +98,7 @@ func ReadProtos(readPath, OutPath string ) error { return fmt.Errorf("WriteNewFile|Write is err:%v", err) } - fw, err = os.OpenFile( OutPath+"actions/protocode.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + fw, err = os.OpenFile( OutPath+"src/plugin/protocode.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return fmt.Errorf("WriteNewFile|OpenFile is err:%v", err) } -- libgit2 0.21.2