Commit 5d9cf01cbdc139472b319451621762875f5a3bbf

Authored by zhangqijia
1 parent cad2b7f3

plugin 热更

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