Commit 77f5eec7ee525597a19cda7029bf4a512f3ac8c0

Authored by zhangqijia
1 parent 9a9d092e

plugin 插件热更 接口

Makefile
... ... @@ -11,18 +11,18 @@ test:
11 11 http:
12 12 go run -race cmd/httpserver/http.go cmd/httpserver/AccountAction.go
13 13  
14   -game:
  14 +game:plugin
15 15 go run -race cmd/gameserver/*.go
16 16 build:
17 17 go build -race -o bin/account cmd/http.go
18 18 go build -race -o bin/game cmd/gameserver/*.go
19 19 go build -race -o bin/test cmd/test/client.go
20 20 regame:plugin
21   - lsof -i:8849 | grep "game" | grep -v grep | awk '{print $$2}' | xargs -I {} kill -USR1 {}
  21 + lsof -i:8850 | grep "agent" | grep -v grep | awk '{print $$2}' | xargs -I {} kill -USR1 {}
22 22  
23 23 plugin:
24   - #go build -ldflags -pluginpath="plugin/hot-1" --buildmode=plugin -o bin/plugin.so src/plugin/*.go
25   - go build --buildmode=plugin -o bin/$(pname) src/plugin/*.go
26   - cd bin && rm -rf plugin.so && ln -s $(pname) plugin.so && cd -
  24 + cd bin && rm -rf ./plugin*.so && cd -
  25 + go build -race --buildmode=plugin -o bin/$(pname) cmd/gameserver/plugin/*.go
  26 + cd bin && ln -s $(pname) plugin.so && cd -
27 27  
28 28 .PHONY: all build protos test cert plugin
29 29 \ No newline at end of file
... ...
cmd/gameserver/agent.go
1 1 package main
2 2  
3 3 import (
4   - "fmt"
5 4 "github.com/golang/protobuf/proto"
6 5 "math"
7 6 "pro2d/common"
8 7 "pro2d/common/components"
9 8 "pro2d/models"
10   - "pro2d/pb"
11 9 "pro2d/utils"
12 10 "pro2d/utils/logger"
13 11 "sync/atomic"
... ... @@ -45,24 +43,30 @@ func (c *Agent) OnConnection(conn components.IConnection) {
45 43  
46 44 func (c *Agent) OnMessage(msg components.IMessage) {
47 45 atomic.StoreInt64(&c.lastHeartCheckTime, utils.Timex())
48   - if md, ok := components.ActionMap[pb.ProtoCode(msg.GetHeader().GetMsgID())]; ok {
49   - logger.Debug("protocode handler: %d", msg.GetHeader().GetMsgID())
50   - errCode, protomsg := md(msg)
51   - rsp, err := proto.Marshal(protomsg)
52   - fmt.Printf("errCode: %d, protomsg:%v\n", errCode, protomsg)
53   - if err != nil {
54   - conn := c.Server.GetIConnection(msg.GetSessId())
55   - if conn != nil {
56   - conn.Send(-100, msg.GetHeader().GetMsgID(), nil)
57   - }
58   - return
59   - }
  46 + md := c.Server.GetPlugin().GetAction(msg.GetHeader().GetMsgID())
  47 + if md == nil {
  48 + logger.Debug("cmd: %d, md is nil", msg.GetHeader().GetMsgID())
  49 + return
  50 + }
  51 +
  52 + logger.Debug("protocode handler: %d", msg.GetHeader().GetMsgID())
  53 + //fmt.Printf("errCode: %d, protomsg:%v\n", errCode, protomsg)
  54 +
  55 + f := md.(func (conn components.IConnection, msg components.IMessage) (int32, interface{}))
  56 + errCode, protomsg := f(c, msg)
  57 + rsp, err := proto.Marshal(protomsg.(proto.Message))
  58 + if err != nil {
60 59 conn := c.Server.GetIConnection(msg.GetSessId())
61 60 if conn != nil {
62   - conn.Send(errCode, msg.GetHeader().GetMsgID(), rsp)
  61 + conn.Send(-100, msg.GetHeader().GetMsgID(), nil)
63 62 }
64 63 return
65 64 }
  65 + conn := c.Server.GetIConnection(msg.GetSessId())
  66 + if conn != nil {
  67 + conn.Send(errCode, msg.GetHeader().GetMsgID(), rsp)
  68 + }
  69 + return
66 70 logger.Error("protocode not handler: %d", msg.GetHeader().GetMsgID())
67 71 }
68 72  
... ...
cmd/gameserver/game.go
... ... @@ -10,7 +10,7 @@ import (
10 10 "pro2d/common/components"
11 11 "pro2d/conf"
12 12 "pro2d/models"
13   - _ "pro2d/plugin"
  13 + //_ "pro2d/cmd/gameserver/plugin"
14 14 "pro2d/utils/db"
15 15 "pro2d/utils/etcd"
16 16 "pro2d/utils/logger"
... ... @@ -27,13 +27,20 @@ type GameServer struct {
27 27  
28 28 func NewGameServer(sconf *conf.SConf) (*GameServer, error) {
29 29 s := &GameServer{
30   - IServer: components.NewServer(sconf.Port, conf.GlobalConf.GameConf.PluginPath, components.NewPBSplitter()),
31 30 Agents: new(sync.Map),
32 31 }
33   - s.SetConnectionCallback(s.OnConnection)
34   - s.SetMessageCallback(s.OnMessage)
35   - s.SetCloseCallback(s.OnClose)
36   - s.SetTimerCallback(s.OnTimer)
  32 +
  33 + options := []components.Option{
  34 + components.WithPlugin(components.NewPlugin(sconf.PluginPath)),
  35 + components.WithSplitter(components.NewPBSplitter()),
  36 + components.WithConnCbk(s.OnConnection),
  37 + components.WithMsgCbk(s.OnMessage),
  38 + components.WithCloseCbk(s.OnClose),
  39 + components.WithTimerCbk(s.OnTimer),
  40 + }
  41 +
  42 + iserver := components.NewServer(sconf.Port, options...)
  43 + s.IServer = iserver
37 44  
38 45 //mongo 初始化
39 46 db.MongoDatabase = db.MongoClient.Database(sconf.DBName)
... ... @@ -120,7 +127,10 @@ func main() {
120 127 return
121 128 case u := <-userChan:
122 129 logger.Debug("userChan .. %v",u.String())
123   - //s.LoadPlugin()
  130 + e := s.IServer.GetPlugin().LoadPlugin()
  131 + if e != nil {
  132 + logger.Error("err: ", e.Error())
  133 + }
124 134 }
125 135 }
126 136 }
... ...
plugin/RolePlugin.go renamed to cmd/gameserver/plugin/RolePlugin.go
1   -package plugin
  1 +package main
2 2  
3 3 import (
4 4 "github.com/golang/protobuf/proto"
... ... @@ -9,12 +9,12 @@ import (
9 9 "pro2d/utils/logger"
10 10 )
11 11  
12   -func HeartRpc(msg components.IMessage) (int32, proto.Message) {
  12 +func HeartRpc(msg components.IMessage) (int32, interface{}) {
13 13 //msg.Conn.SetLastHeartCheckTime()
14 14 return 0, nil
15 15 }
16 16  
17   -func CreateRpc(msg components.IMessage) (int32, proto.Message) {
  17 +func CreateRpc(msg components.IMessage) (int32, interface{}) {
18 18 req := pb.CreateReq{}
19 19 if err := proto.Unmarshal(msg.GetData(), &req); err != nil {
20 20 logger.Error("CreateRpc err: %v", err)
... ... @@ -34,8 +34,8 @@ func CreateRpc(msg components.IMessage) (int32, proto.Message) {
34 34 return 0, nil
35 35 }
36 36  
37   -func LoginRpc(msg components.IMessage) (int32, proto.Message) {
38   - //logger.Debug("cmd: %v, msg: %s", msg.PBHead.Cmd, msg.Body)
  37 +func LoginRpc(msg components.IMessage) (int32, interface{}) {
  38 + //logger.Debug("11111111cmd: %v, msg: %s", msg.GetHeader().GetMsgID(), msg.GetData())
39 39 req := pb.LoginReq{}
40 40 if err := proto.Unmarshal(msg.GetData(), &req); err != nil {
41 41 logger.Error("loginRpc err: %v", err)
... ... @@ -48,7 +48,6 @@ func LoginRpc(msg components.IMessage) (int32, proto.Message) {
48 48 }
49 49 role.SetProperty("Device", req.Device)
50 50  
51   -
52 51 return 0, &pb.RoleRsp{
53 52 Role: role.Role,
54 53 Hero: role.GetAllHero(),
... ...
cmd/gameserver/plugin/protocode.go 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +package main
  2 +
  3 +import (
  4 + "pro2d/pb"
  5 + "pro2d/utils/logger"
  6 +)
  7 +
  8 +func GetActionMap() map[interface{}]interface{} {
  9 + logger.Debug("init protocode...")
  10 + am := make(map[interface{}]interface{})
  11 + am[pb.ProtoCode_HeartReq] = HeartRpc
  12 + am[pb.ProtoCode_LoginReq] = LoginRpc
  13 + am[pb.ProtoCode_CreateReq] = CreateRpc
  14 +
  15 + return am
  16 +}
0 17 \ No newline at end of file
... ...
cmd/httpserver/http.go
... ... @@ -23,7 +23,7 @@ func NewAccountServer(version string, port ...string) *AccountServer {
23 23 return &AccountServer{IHttp: components.NewHttpServer(version, port...)}
24 24 }
25 25  
26   -func (s *AccountServer) Start() error {
  26 +func (s *AccountServer) Init() error {
27 27 //mongo 初始化
28 28 db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.AccountConf.DBName)
29 29 models.InitAccountServerModels()
... ... @@ -35,6 +35,13 @@ func (s *AccountServer) Start() error {
35 35 return err
36 36 }
37 37 s.EtcdClient.PutWithLeasePrefix(conf.GlobalConf.AccountConf.Name, conf.GlobalConf.AccountConf.ID, fmt.Sprintf("%s:%d", conf.GlobalConf.AccountConf.IP, conf.GlobalConf.AccountConf.Port), 5)
  38 + return nil
  39 +}
  40 +
  41 +func (s *AccountServer) Start() error {
  42 + if err := s.Init(); err != nil {
  43 + return err
  44 + }
38 45  
39 46 return s.IHttp.Start()
40 47 }
... ...
common/components/conn.go
... ... @@ -74,6 +74,47 @@ func (c *Connection) SetTimerCallback(cb TimerCallback) {
74 74 c.timerCallback = cb
75 75 }
76 76  
  77 +func (c *Connection) Start() {
  78 + go c.write()
  79 + go c.read()
  80 + go c.listen()
  81 +
  82 + c.Status = 1
  83 + c.connectionCallback(c)
  84 + c.handleTimeOut()
  85 +}
  86 +
  87 +func (c *Connection) Stop() {
  88 + logger.Debug("ID: %d close", c.Id)
  89 + closed := atomic.LoadUint32(&c.Status)
  90 + if closed == 0 {
  91 + return
  92 + }
  93 + atomic.StoreUint32(&c.Status, 0)
  94 +
  95 + close(c.WBuffer)
  96 + close(c.Quit)
  97 + c.Conn.Close()
  98 + c.closeCallback(c)
  99 +}
  100 +
  101 +func (c *Connection) Send(errCode int32, cmd uint32, data []byte) error{
  102 + buf, err := c.Server.GetSplitter().Pack(cmd, data, errCode, 0)
  103 + if err != nil {
  104 + return err
  105 + }
  106 +
  107 + sendTimeout := time.NewTimer(5 * time.Millisecond)
  108 + defer sendTimeout.Stop()
  109 + // 发送超时
  110 + select {
  111 + case <-sendTimeout.C:
  112 + return fmt.Errorf("send buff msg timeout")
  113 + case c.WBuffer <- buf:
  114 + return nil
  115 + }
  116 +}
  117 +
77 118 func (c *Connection) defaultConnectionCallback(conn IConnection) {
78 119 }
79 120  
... ... @@ -124,6 +165,7 @@ func (c *Connection) read() {
124 165 }
125 166 }
126 167  
  168 +//此设计目的是为了让网络数据与定时器处理都在一条协程里处理。不想加锁。。。
127 169 func (c *Connection) listen(){
128 170 defer c.Stop()
129 171  
... ... @@ -139,7 +181,6 @@ func (c *Connection) listen(){
139 181 }
140 182 }
141 183  
142   -
143 184 func (c *Connection) handleTimeOut() {
144 185 c.timerFunc <- func() {
145 186 c.timerCallback(c)
... ... @@ -150,45 +191,3 @@ func (c *Connection) handleTimeOut() {
150 191 func (c *Connection) Quitting() {
151 192 c.Quit <- c
152 193 }
153   -
154   -func (c *Connection) Start() {
155   - go c.write()
156   - go c.read()
157   - go c.listen()
158   -
159   - c.Status = 1
160   - c.connectionCallback(c)
161   - c.handleTimeOut()
162   -}
163   -
164   -func (c *Connection) Stop() {
165   - logger.Debug("ID: %d close", c.Id)
166   - closed := atomic.LoadUint32(&c.Status)
167   - if closed == 0 {
168   - return
169   - }
170   - atomic.StoreUint32(&c.Status, 0)
171   -
172   - close(c.WBuffer)
173   - close(c.Quit)
174   - c.Conn.Close()
175   - c.closeCallback(c)
176   -}
177   -
178   -func (c *Connection) Send(errCode int32, cmd uint32, data []byte) error{
179   - buf, err := c.Server.GetSplitter().Pack(cmd, data, errCode, 0)
180   - if err != nil {
181   - return err
182   - }
183   -
184   - sendTimeout := time.NewTimer(5 * time.Millisecond)
185   - defer sendTimeout.Stop()
186   - // 发送超时
187   - select {
188   - case <-sendTimeout.C:
189   - return fmt.Errorf("send buff msg timeout")
190   - case c.WBuffer <- buf:
191   - return nil
192   - }
193   -}
194   -
... ...
common/components/icompontents.go
... ... @@ -54,6 +54,7 @@ type IServer interface {
54 54  
55 55 GetSplitter() ISplitter
56 56 GetIConnection(id int) IConnection
  57 + GetPlugin() IPlugin
57 58  
58 59 SetConnectionCallback(ConnectionCallback)
59 60 SetMessageCallback(MessageCallback)
... ... @@ -66,4 +67,11 @@ type IHttp interface {
66 67 Start() error
67 68 Stop()
68 69 BindHandler(interface{})
  70 +}
  71 +
  72 +type ActionHandler func (conn IConnection, msg IMessage) (int32, interface{})
  73 +//用于热更逻辑的插件接口
  74 +type IPlugin interface {
  75 + LoadPlugin() error
  76 + GetAction(uint32) interface{}
69 77 }
70 78 \ No newline at end of file
... ...
common/components/plugin.go 0 → 100644
... ... @@ -0,0 +1,62 @@
  1 +package components
  2 +
  3 +import (
  4 + "plugin"
  5 + "pro2d/pb"
  6 + "pro2d/utils/logger"
  7 + "sync"
  8 +)
  9 +
  10 +type ActionMap sync.Map//map[pb.ProtoCode]ActionHandler
  11 +
  12 +type PluginOption func(*Plugin)
  13 +
  14 +type Plugin struct {
  15 + pluginPath string
  16 +
  17 + Actions sync.Map
  18 +}
  19 +
  20 +func NewPlugin(path string, options ...PluginOption) IPlugin{
  21 + p := &Plugin{
  22 + pluginPath: path,
  23 + }
  24 + for _, option := range options {
  25 + option(p)
  26 + }
  27 + return p
  28 +}
  29 +
  30 +func (p *Plugin) LoadPlugin() error {
  31 + plu, err := plugin.Open(p.pluginPath)
  32 +
  33 + if err != nil {
  34 + return err
  35 + }
  36 + logger.Debug("func LoadPlugin open success...")
  37 +
  38 + f, err := plu.Lookup("GetActionMap")
  39 + if err != nil {
  40 + return err
  41 + }
  42 + logger.Debug("func LoadPlugin Lookup success...")
  43 +
  44 + if x, ok := f.(func()map[interface{}]interface{}); ok {
  45 + logger.Debug("func LoadPlugin GetActionMap success...")
  46 + am := x()
  47 + for k, v := range am {
  48 + p.Actions.Delete(k)
  49 + p.Actions.Store(k, v)
  50 + }
  51 + }
  52 +
  53 + return nil
  54 +}
  55 +
  56 +func (p *Plugin) GetAction(cmd uint32) interface{} {
  57 + f, ok := p.Actions.Load(pb.ProtoCode(cmd))
  58 + if !ok {
  59 + return nil
  60 + }
  61 + return f
  62 +}
... ...
common/components/server.go
... ... @@ -2,39 +2,74 @@ package components
2 2  
3 3 import (
4 4 "fmt"
5   - "github.com/golang/protobuf/proto"
6 5 "net"
7   - "plugin"
8   - "pro2d/pb"
9 6 "pro2d/utils/logger"
10 7 "sync"
11 8 )
12 9  
13   -type ActionHandler func (msg IMessage) (int32, proto.Message)
14   -var ActionMap map[pb.ProtoCode]ActionHandler
  10 +type Option func(*Server)
  11 +
  12 +func WithPlugin(iPlugin IPlugin) Option {
  13 + return func(server *Server) {
  14 + server.plugins = iPlugin
  15 + }
  16 +}
  17 +
  18 +func WithSplitter(splitter ISplitter) Option {
  19 + return func(server *Server) {
  20 + server.splitter = splitter
  21 + }
  22 +}
  23 +
  24 +func WithConnCbk(cb ConnectionCallback) Option {
  25 + return func(server *Server) {
  26 + server.connectionCallback = cb
  27 + }
  28 +}
  29 +
  30 +func WithMsgCbk(cb MessageCallback) Option {
  31 + return func(server *Server) {
  32 + server.messageCallback = cb
  33 + }
  34 +}
  35 +
  36 +func WithCloseCbk(cb CloseCallback) Option {
  37 + return func(server *Server) {
  38 + server.closeCallback = cb
  39 + }
  40 +}
  41 +
  42 +func WithTimerCbk(cb TimerCallback) Option {
  43 + return func(server *Server) {
  44 + server.timerCallback = cb
  45 + }
  46 +}
  47 +
15 48  
16 49 type Server struct {
17   - IServer
  50 + PluginPath string
  51 + plugins IPlugin
  52 + splitter ISplitter
  53 + actionHandlers sync.Map
18 54  
19 55 connectionCallback ConnectionCallback
20 56 messageCallback MessageCallback
21 57 closeCallback CloseCallback
22 58 timerCallback TimerCallback
23 59  
24   - splitter ISplitter
25   -
26 60 port int
27   - PluginPath string
28 61 Clients *sync.Map
29 62 }
30 63  
31   -func NewServer(port int, pluginPath string, splitter ISplitter) *Server {
  64 +func NewServer(port int, options ...Option) IServer {
32 65 s := &Server{
33   - splitter: splitter,
34 66 port: port,
35   - PluginPath: pluginPath,
36 67 Clients: new(sync.Map),
37 68 }
  69 + for _, option := range options {
  70 + option(s)
  71 + }
  72 +
38 73 return s
39 74 }
40 75  
... ... @@ -50,6 +85,10 @@ func (s *Server) GetIConnection(id int) IConnection {
50 85 return c.(IConnection)
51 86 }
52 87  
  88 +func (s *Server) GetPlugin() IPlugin {
  89 + return s.plugins
  90 +}
  91 +
53 92 func (s *Server) SetConnectionCallback(cb ConnectionCallback) {
54 93 s.connectionCallback = cb
55 94 }
... ... @@ -82,23 +121,11 @@ func (s *Server) removeConnection(conn IConnection) {
82 121 s.Clients.Delete(conn.GetID())
83 122 }
84 123  
85   -func (s *Server) LoadPlugin() {
86   - //重新加载
87   - _, err:=plugin.Open(s.PluginPath)
88   - if err != nil {
89   - logger.Error("load plugin err: %v, %s", err, s.PluginPath)
90   - return
  124 +func (s *Server) Start() error {
  125 + if err := s.plugins.LoadPlugin(); err != nil {
  126 + return err
91 127 }
92   - logger.Debug("load plugin success")
93   -}
94 128  
95   -
96   -func (s *Server) Start() error {
97   - //初始化plugin
98   - //_, err = plugin.Open(conf.GlobalConf.GameConf.PluginPath)
99   - //if err != nil {
100   - // return err
101   - //}
102 129 port := fmt.Sprintf(":%d", s.port)
103 130 l, err := net.Listen("tcp", port)
104 131 if err != nil {
... ...
plugin/protocode.go deleted
... ... @@ -1,17 +0,0 @@
1   -package plugin
2   -
3   -import (
4   - "pro2d/common/components"
5   - "pro2d/pb"
6   - "pro2d/utils/logger"
7   -)
8   -
9   -func init() {
10   - logger.Debug("init protocode...")
11   - components.ActionMap = make(map[pb.ProtoCode]components.ActionHandler)
12   -
13   - components.ActionMap[pb.ProtoCode_HeartReq] = HeartRpc
14   - components.ActionMap[pb.ProtoCode_LoginReq] = LoginRpc
15   - components.ActionMap[pb.ProtoCode_CreateReq] = CreateRpc
16   -
17   -}
tools/protostostruct.go
... ... @@ -15,8 +15,8 @@ var (
15 15 ProtoCodeLineReq = "\t%sReq = %d;\n"
16 16 ProtoCodeLineRsp = "\t%sRsp = %d;\n"
17 17  
18   - GoProtoCodeStr = "package main\n\nimport (\n\t\"pro2d/pb\"\n\t\"pro2d/src/components/logger\"\n\t\"pro2d/src/components/net\"\n)\n\nfunc init() {\n\tlogger.Debug(\"init protocode...\")\n\tnet.ActionMap = make(map[pb.ProtoCode]net.ActionHandler)\n\n%s\n}\n"
19   - GoProtoCodeLine = "\tnet.ActionMap[pb.ProtoCode_%sReq] = %sRpc\n"
  18 + GoProtoCodeStr = "package main\n\nimport (\n\t\"pro2d/pb\"\n\t\"pro2d/utils/logger\"\n)\n\nfunc GetActionMap() map[interface{}]interface{} {\n\tlogger.Debug(\"init protocode...\")\n\tam := make(map[interface{}]interface{})\n%s\n\treturn am\n}"
  19 + GoProtoCodeLine = "\tam[pb.ProtoCode_%sReq] = %sRpc\n"
20 20 )
21 21  
22 22 func ProtoToCode(readPath, filename string) (string, string) {
... ... @@ -106,7 +106,7 @@ func ReadProtos(readPath, OutPath string ) error {
106 106 return fmt.Errorf("WriteNewFile|Write is err:%v", err)
107 107 }
108 108  
109   - fw, err = os.OpenFile( OutPath+"src/plugin/protocode.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
  109 + fw, err = os.OpenFile( OutPath+"cmd/gameserver/plugin/protocode.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
110 110 if err != nil {
111 111 return fmt.Errorf("WriteNewFile|OpenFile is err:%v", err)
112 112 }
... ...