diff --git a/actions/HttpAction.go b/actions/HttpAction.go deleted file mode 100644 index e623070..0000000 --- a/actions/HttpAction.go +++ /dev/null @@ -1,127 +0,0 @@ -package actions - -import ( - "fmt" - "github.com/gin-gonic/gin" - "net/http" - "pro2d/components/db" - "pro2d/components/etcd" - "pro2d/conf" - "pro2d/models" - "pro2d/protos/pb" - "pro2d/utils" - "reflect" - "strings" -) - -type HttpAction struct { - version string - port []string - EtcdClient *etcd.EtcdClient -} - -func NewHttpAction(version string, port ...string) *HttpAction { - return &HttpAction{version: version, port: port} -} - -func Pong (c *gin.Context) { - c.JSON(200, gin.H{ - "message": "pong", - }) -} - -func HandlerFuncObj(tvl, obj reflect.Value) gin.HandlerFunc { - apiFun := func(c *gin.Context) interface{} { return c } - return func(c *gin.Context) { - tvl.Call([]reflect.Value{obj, reflect.ValueOf(apiFun(c))}) - } -} - -func GetRoutePath(objName, objFunc string) string { - return strings.ToLower(objName + "/" + objFunc) -} - -func PubRsp(c *gin.Context, code int, data interface{}) { - c.JSON(http.StatusOK, gin.H{"code": code, "data": data}) -} - -func (h *HttpAction) Register(c *gin.Context) { - var register pb.Register - if err := c.ShouldBindJSON(®ister); err != nil { - PubRsp(c, -1, err.Error()) - return - } - - account := models.NewAccount(register.Phone) - if err := account.Load(); err == nil { - PubRsp(c, -2, "account exist: " + register.Phone) - return - } - - account.Uid = conf.SnowFlack.NextValStr() - account.Password = utils.Md5V(register.Password) - if _, err := account.Create(); err != nil{ - PubRsp(c, -3, "account register err: " + err.Error()) - return - } - account.Password = register.Password - PubRsp(c, 0, account.Account) -} - -func (h *HttpAction) Login(c *gin.Context) { - var login pb.Account - if err := c.ShouldBindJSON(&login); err != nil { - PubRsp(c, -1, err.Error()) - return - } - account := models.NewAccount(login.Phone) - if err := account.Load(); err != nil { - PubRsp(c, -2, err.Error()) - return - } - - if utils.Md5V(login.Password) != account.Password { - PubRsp(c, -3, "password error") - return - } - - var gs []*pb.ServiceInfo - for k, v := range h.EtcdClient.GetByPrefix(conf.GlobalConf.GameConf.Name) { - gs = append(gs, &pb.ServiceInfo{ - Id: k, - Name: conf.GlobalConf.GameConf.Name, - Address: v, - }) - } - rsp := &pb.LoginRsp{ - Uid: account.Uid, - GameService: gs, - } - PubRsp(c, 0, rsp) -} - -func (h *HttpAction) Start() error { - //mongo 初始化 - db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.AccountConf.DBName) - models.InitAccountServerModels() - - //Etcd 初始化 - h.EtcdClient = etcd.NewEtcdClient(conf.GlobalConf.Etcd) - h.EtcdClient.PutWithLeasePrefix(conf.GlobalConf.AccountConf.Name, conf.GlobalConf.AccountConf.ID, fmt.Sprintf("%s:%d", conf.GlobalConf.AccountConf.IP, conf.GlobalConf.AccountConf.Port), 5) - - //gin初始化 - r := gin.Default() - r.GET("/ping", Pong) - typ := reflect.TypeOf(h) - val := reflect.ValueOf(h) - //t := reflect.Indirect(val).Type() - //objectName := t.Name() - - numOfMethod := val.NumMethod() - for i := 0; i < numOfMethod; i++ { - method := typ.Method(i) - r.GET(GetRoutePath(h.version, method.Name), HandlerFuncObj(method.Func, val)) - r.POST(GetRoutePath(h.version, method.Name), HandlerFuncObj(method.Func, val)) - } - return r.Run(h.port...) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") -} \ No newline at end of file diff --git a/actions/RoleAction.go b/actions/RoleAction.go deleted file mode 100644 index 50e4af1..0000000 --- a/actions/RoleAction.go +++ /dev/null @@ -1,54 +0,0 @@ -package actions - -import ( - "github.com/golang/protobuf/proto" - "pro2d/components/net" - "pro2d/conf" - "pro2d/models" - "pro2d/protos/pb" - "pro2d/utils" -) - -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 { - utils.Sugar.Errorf("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 { - utils.Sugar.Errorf("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 { - utils.Sugar.Errorf("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/actions/protocode.go b/actions/protocode.go deleted file mode 100644 index f160b1f..0000000 --- a/actions/protocode.go +++ /dev/null @@ -1,15 +0,0 @@ -package actions - -import ( - "pro2d/components/net" - "pro2d/protos/pb" -) - -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/cmd/game.go b/cmd/game.go index 7db89e4..c97354d 100644 --- a/cmd/game.go +++ b/cmd/game.go @@ -3,9 +3,9 @@ package main import ( "os" "os/signal" - "pro2d/components/net" "pro2d/conf" - "pro2d/utils" + "pro2d/src/components/logger" + "pro2d/src/components/net" "syscall" ) @@ -21,8 +21,9 @@ func main() { select { case e := <- err: - utils.Sugar.Errorf("game server error: %v", e) + logger.Error("game server error: %v", e) case <-stopChan: s.Stop() + logger.Debug("game stop...") } } diff --git a/cmd/http.go b/cmd/http.go index c38e30d..c800c19 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -3,8 +3,9 @@ package main import ( "os" "os/signal" - "pro2d/actions" - "pro2d/utils" + _ "pro2d/conf" + "pro2d/src/actions" + "pro2d/src/components/logger" "syscall" ) @@ -20,8 +21,8 @@ func main() { select { case e := <- err: - utils.Sugar.Errorf("game server error: %v", e) + logger.Error("game server error: %v", e) case <-stopChan: - utils.Sugar.Debugf("game stop") + logger.Debug("game stop") } } \ No newline at end of file diff --git a/common/common.go b/common/common.go deleted file mode 100644 index 41a8345..0000000 --- a/common/common.go +++ /dev/null @@ -1,7 +0,0 @@ -package common - -const ( - HEADLEN = 8 - - Pro2DTokenSignedString = "Pro2DSecret" -) diff --git a/components/db/mongo.go b/components/db/mongo.go deleted file mode 100644 index 4c56db2..0000000 --- a/components/db/mongo.go +++ /dev/null @@ -1,226 +0,0 @@ -package db - -import ( - "context" - "fmt" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/mongo/readpref" - "go.mongodb.org/mongo-driver/x/bsonx" - "pro2d/utils" - "sort" - "strconv" - "time" -) - -var ( - MongoClient *mongo.Client - MongoDatabase *mongo.Database -) - -//初始化 -func Connect(user, password, host string,port int, MaxNum int, timeOut int) error { - var uri string - if user!= "" { - //uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?w=majority", conf.User, conf.Password, conf.Host, conf.Port, conf.DBName) - uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/?w=majority", user, password, host, port) - }else { - //uri = fmt.Sprintf("mongodb://%s:%d/%s?w=majority", conf.Host, conf.Port, conf.DBName) - uri = fmt.Sprintf("mongodb://%s:%d/?w=majority", host, port) - } - // 设置连接超时时间 - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeOut)) - defer cancel() - // 通过传进来的uri连接相关的配置 - o := options.Client().ApplyURI(uri) - // 设置最大连接数 - 默认是100 ,不设置就是最大 max 64 - o.SetMaxPoolSize(uint64(MaxNum)) - // 发起链接 - var err error - MongoClient , err = mongo.Connect(ctx, o) - if err != nil { - return err - } - // 判断服务是不是可用 - if err = MongoClient.Ping(context.Background(), readpref.Primary()); err != nil { - return err - } - - //MongoDatabase = MongoClient.Database(dbname) - return nil -} - -func CreateCollection(collection string) error { - colls, _ := MongoDatabase.ListCollectionNames(context.TODO(), bson.D{}) - pos := sort.SearchStrings(colls, collection) - if pos != len(colls) { - if collection == colls[pos] { - return MongoDatabase.CreateCollection(context.TODO(), collection) - } - } - return MongoDatabase.CreateCollection(context.TODO(), collection) -} - -func SetUnique(collection string, key string) (string, error) { - return MongoDatabase.Collection(collection).Indexes().CreateOne( - context.TODO(), - mongo.IndexModel{ - Keys : bsonx.Doc{{key, bsonx.Int32(1)}}, - Options: options.Index().SetUnique(true), - }, - ) -} - -type MgoColl struct { - collection *mongo.Collection - - pri interface{} - schema interface{} -} - -func GetBsonD(key string, value interface{}) interface{} { - return bson.D{ {key, value}} -} -func GetBsonM(key string, value interface{}) interface{} { - return bson.M{key: value} -} - -func NewMongoColl(key string, schema interface{}) *MgoColl { - return &MgoColl{ - collection: MongoDatabase.Collection(utils.GetCollName(schema)), - pri: GetBsonM(utils.GetPriKey(schema), key), - schema: schema, - } -} - -func FindOne(pri interface{}, schema interface{}) error { - r := MongoDatabase.Collection(utils.GetCollName(schema)).FindOne(context.TODO(), pri) - return r.Decode(schema) -} - -// 查询单个 -func (m *MgoColl) FindOneKV(key string, value interface{}) *mongo.SingleResult { - //collection. - filter := bson.D{ {key, value}} - singleResult := m.collection.FindOne(context.TODO(), filter) - return singleResult -} - -//查询集合里有多少数据 -func (m *MgoColl) CollectionCount() (string, int64) { - size, _ := m.collection.EstimatedDocumentCount(context.TODO()) - return m.collection.Name(), size -} - -//按选项查询集合 Skip 跳过 Limit 读取数量 sort 1 ,-1 . 1 为最初时间读取 , -1 为最新时间读取 -func (m *MgoColl) CollectionDocuments(Skip, Limit int64, sort int) *mongo.Cursor { - SORT := bson.D{ - {"_id", sort}} //filter := bson.D{ {key,value}} - filter := bson.D{ - {}} - findOptions := options.Find().SetSort(SORT).SetLimit(Limit).SetSkip(Skip) - //findOptions.SetLimit(i) - temp, _ := m.collection.Find(context.Background(), filter, findOptions) - return temp -} - -//获取集合创建时间和编号 -func (m *MgoColl) ParsingId(result string) (time.Time, uint64) { - temp1 := result[:8] - timestamp, _ := strconv.ParseInt(temp1, 16, 64) - dateTime := time.Unix(timestamp, 0) //这是截获情报时间 时间格式 2019-04-24 09:23:39 +0800 CST - temp2 := result[18:] - count, _ := strconv.ParseUint(temp2, 16, 64) //截获情报的编号 - return dateTime, count -} - -//删除文章和查询文章 -func (m *MgoColl) DeleteAndFind(key string, value interface{}) (int64, *mongo.SingleResult) { - filter := bson.D{ - {key, value}} - singleResult := m.collection.FindOne(context.TODO(), filter) - DeleteResult, err := m.collection.DeleteOne(context.TODO(), filter, nil) - if err != nil { - fmt.Println("删除时出现错误,你删不掉的~") - } - return DeleteResult.DeletedCount, singleResult -} - -//删除文章 -func (m *MgoColl) Delete(key string, value interface{}) int64 { - filter := bson.D{ {key, value}} - count, err := m.collection.DeleteOne(context.TODO(), filter, nil) - if err != nil { - fmt.Println(err) - } - return count.DeletedCount - -} - -//删除多个 -func (m *MgoColl) DeleteMany(key string, value interface{}) int64 { - filter := bson.D{ {key, value}} - - count, err := m.collection.DeleteMany(context.TODO(), filter) - if err != nil { - fmt.Println(err) - } - return count.DeletedCount -} - -//索引 -func (m *MgoColl) SetUnique(key string){ - m.collection.Indexes().CreateOne( - context.Background(), - mongo.IndexModel{ - Keys : bsonx.Doc{{key, bsonx.Int32(1)}}, - Options: options.Index().SetUnique(true), - }, - ) -} - -//更新&保存 -func (m *MgoColl) FindOneAndUpdate(filter interface{}, update interface{})*mongo.SingleResult { - //filter := bson.M{"name": "x", "array.name": "b"} - //update := bson.M{"array.$[item].detail": "test"} - - res := m.collection.FindOneAndUpdate(context.Background(), - filter, - bson.M{"$set": update}) - if res.Err() != nil { - return nil - } - return res -} - -func (m *MgoColl) UpdateOne(filter interface{}, update interface{})*mongo.UpdateResult { - res, err := m.collection.UpdateOne(context.TODO(), filter, update) - if err != nil { - return nil - } - - return res -} - - -func (m *MgoColl) Load() error{ - r := m.collection.FindOne(context.TODO(), m.pri) - err := r.Decode(m.schema) - if err != nil { - return err - } - return nil -} - -func (m *MgoColl) Create() (*mongo.InsertOneResult, error){ - return m.collection.InsertOne(context.TODO(), m.schema) -} - -func (m *MgoColl) Update(update interface{}) { - m.FindOneAndUpdate(m.pri, update) -} - -func (m *MgoColl)Save() { - m.FindOneAndUpdate(m.pri, m.schema) -} \ No newline at end of file diff --git a/components/db/redis.go b/components/db/redis.go deleted file mode 100644 index 6fbd124..0000000 --- a/components/db/redis.go +++ /dev/null @@ -1,54 +0,0 @@ -package db -// -//import ( -// "fmt" -// "github.com/garyburd/redigo/redis" -// "pro2d/conf" -// "pro2d/utils" -// "time" -//) -// -//type RedisPool struct { -// RedisPool *redis.Pool -//} -// -//func (rp *RedisPool)Connect(conf *conf.ServerConf) error { -// rp.RedisPool = &redis.Pool{ -// //最大活跃连接数,0代表无限 -// MaxActive: 888, -// MaxIdle: 20, -// //闲置连接的超时时间 -// 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)) -// } -// return redis.Dial("tcp",conf.RedisConf.Address, option...) -// }, -// } -// return nil -//} -// -//func (rp *RedisPool)Close() { -// rp.RedisPool.Close() -//} -// -//func (rp *RedisPool) Insert() error { -// conn := rp.RedisPool.Get() -// defer conn.Close() -// reply, err := conn.Do("HKEYS", fmt.Sprintf("account:%s", "123123")) -// if err != nil { -// return err -// } -// -// utils.Sugar.Debugf("%v", reply) -// reply, err = conn.Do("HMSET", fmt.Sprintf("account:%s", "1231231"), "phone", "1231231", "passwd", "2131231") -// if err != nil { -// utils.Sugar.Errorf("%v", err) -// return err -// } -// utils.Sugar.Debugf("%v", reply) -// return nil -//} diff --git a/components/etcd/etcd.go b/components/etcd/etcd.go deleted file mode 100644 index 9df28e0..0000000 --- a/components/etcd/etcd.go +++ /dev/null @@ -1,107 +0,0 @@ -package etcd - -import ( - "context" - "fmt" - clientv3 "go.etcd.io/etcd/client/v3" - "pro2d/conf" - "pro2d/utils" - "time" -) - -type EtcdClient struct { - etcd *clientv3.Client -} - -func NewEtcdClient(conf *conf.Etcd) *EtcdClient { - cli, err := clientv3.New(clientv3.Config{ - Endpoints: conf.Endpoints, - DialTimeout: time.Duration(conf.DialTimeout) * time.Second, - Logger: utils.Logger, - }) - if err != nil { - utils.Sugar.Errorf("etcd init err: %v", err) - return nil - } - return &EtcdClient{ - etcd: cli, - } -} - -func (e *EtcdClient)PutWithPrefix(prefix, key, val string) { - _, err := e.etcd.Put(context.TODO(), fmt.Sprintf("/%s/%s/", prefix, key), val) - if err != nil { - utils.Sugar.Errorf("PutWithPrefix err: %v", err) - return - } -} - -func (e *EtcdClient)PutWithLeasePrefix(prefix, key, val string, ttl int64) error { - lease := clientv3.NewLease(e.etcd) - leaseResp, err := lease.Grant(context.TODO(), ttl) - if err != nil { - utils.Sugar.Errorf("PutWithLeasePrefix 设置租约时间失败:%v\n", err) - return err - } - - _, err = e.etcd.Put(context.TODO(), fmt.Sprintf("/%s/%s/", prefix, key), val, clientv3.WithLease(leaseResp.ID)) - if err != nil { - utils.Sugar.Errorf("PutWithLeasePrefix err: %v", err) - return err - } - - keepRespChan, err := lease.KeepAlive(context.TODO(), leaseResp.ID) - if err != nil { - utils.Sugar.Errorf("keepalive err: %v", err) - return err - } - go func() { - for { - select { - case _ = <-keepRespChan: - if keepRespChan == nil { - fmt.Println("租约已经失效") - goto END - } else { //每秒会续租一次,所以就会受到一次应答 - //fmt.Println("收到自动续租应答:", keepResp.ID) - } - } - } - END: - }() - return nil -} - -func (e *EtcdClient)Get(key string) map[string]string { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - resp, err := e.etcd.Get(ctx, fmt.Sprintf("/%s/", key)) - cancel() - if err != nil { - utils.Sugar.Errorf("etcd get key: %s, err: %v", key, err) - return nil - } - m := make(map[string]string) - for _, v := range resp.Kvs { - m[string(v.Key)] = string(v.Value) - } - return m -} - -func (e *EtcdClient)GetByPrefix(prefix string) map[string]string { - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - resp, err := e.etcd.Get(ctx, fmt.Sprintf("/%s/", prefix), clientv3.WithPrefix()) - cancel() - if err != nil { - utils.Sugar.Errorf("etcd get prefix: %s, err: %v", prefix, err) - return nil - } - m := make(map[string]string) - for _, v := range resp.Kvs { - m[string(v.Key)] = string(v.Value) - } - return m -} - -func (e *EtcdClient)Close() { - e.etcd.Close() -} \ No newline at end of file diff --git a/components/etcd/etcd_test.go b/components/etcd/etcd_test.go deleted file mode 100644 index d08ef92..0000000 --- a/components/etcd/etcd_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package etcd - -import ( - "context" - "fmt" - "go.etcd.io/etcd/api/v3/mvccpb" - clientv3 "go.etcd.io/etcd/client/v3" - "pro2d/conf" - "pro2d/utils" - "testing" -) - -func TestEtcdClient_GetByPrefix(t *testing.T) { - etcd := NewEtcdClient(conf.GlobalConf.Etcd) - gameInfo := etcd.GetByPrefix(conf.GlobalConf.AccountConf.Name) - for k, v := range gameInfo { - utils.Sugar.Debugf("game info key: %v val: %v", k, v) - } - - rch := etcd.etcd.Watch(context.Background(), fmt.Sprintf("/%s/", conf.GlobalConf.AccountConf.Name), clientv3.WithPrefix()) - go func() { - for wresp := range rch { - for _, ev := range wresp.Events { - switch ev.Type { - case mvccpb.PUT: //修改或者新增 - utils.Sugar.Debugf("account put key: %s val: %s", ev.Kv.Key, ev.Kv.Value) - case mvccpb.DELETE: //删除 - utils.Sugar.Debugf("account delete key: %s val: %s", ev.Kv.Key, ev.Kv.Value) - } - } - } - }() - - game := etcd.etcd.Watch(context.Background(), fmt.Sprintf("/%s/", conf.GlobalConf.GameConf.Name), clientv3.WithPrefix()) - for wresp := range game { - for _, ev := range wresp.Events { - switch ev.Type { - case mvccpb.PUT: //修改或者新增 - utils.Sugar.Debugf("game put key: %s val: %s", ev.Kv.Key, ev.Kv.Value) - case mvccpb.DELETE: //删除 - utils.Sugar.Debugf("game delete key: %s val: %s", ev.Kv.Key, ev.Kv.Value) - } - } - } - -} diff --git a/components/jwt/jwt.go b/components/jwt/jwt.go deleted file mode 100644 index 1d2743e..0000000 --- a/components/jwt/jwt.go +++ /dev/null @@ -1,97 +0,0 @@ -package jwt - -import ( - "context" - "fmt" - "pro2d/common" - "pro2d/conf" - "pro2d/utils" - "time" - - jwt "github.com/dgrijalva/jwt-go" - "google.golang.org/grpc/metadata" -) - -func CreateToken(uid string) (tokenString string) { - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "iss": "pro2d-app-server", - "aud": "pro2d-app-server", - "nbf": time.Now().Unix(), - "exp": time.Now().Add(time.Hour).Unix(), - "sub": "pro2d", - "uid": uid, - }) - tokenString, err := token.SignedString([]byte(common.Pro2DTokenSignedString)) - if err != nil { - panic(err) - } - return tokenString -} - -func ParseToken(tokenStr string) string { - var clientClaims Claims - token, err := jwt.ParseWithClaims(tokenStr, &clientClaims, func(token *jwt.Token) (interface{}, error) { - if token.Header["alg"] != "HS256" { - //panic("ErrInvalidAlgorithm") - utils.Sugar.Error("ErrInvalidAlgorithm") - return nil, nil - } - return []byte(common.Pro2DTokenSignedString), nil - }) - if err != nil { - utils.Sugar.Error("jwt parse error") - return "" - } - - if !token.Valid { - utils.Sugar.Error("ErrInvalidToken") - return "" - } - return clientClaims.Uid -} - - -// Claims defines the struct containing the token claims. -type Claims struct { - jwt.StandardClaims - Uid string -} - -// 从 context 的 metadata 中,取出 token -func getTokenFromContext(ctx context.Context) (string, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "", fmt.Errorf("ErrNoMetadataInContext") - } - // md 的类型是 type MD map[string][]string - token, ok := md["authorization"] - if !ok || len(token) == 0 { - return "", fmt.Errorf("ErrNoAuthorizationInMetadata") - } - // 因此,token 是一个字符串数组,我们只用了 token[0] - return token[0], nil -} - -func CheckAuth(ctx context.Context) string { - tokenStr, err := getTokenFromContext(ctx) - if err != nil { - utils.Sugar.Errorf("get token from context error") - return "" - } - return ParseToken(tokenStr) -} - -// AuthToken 自定义认证 客户端使用 -type AuthToken struct { - Token string -} - -func (c AuthToken) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - return map[string]string{ - "authorization": c.Token, - }, nil -} - -func (c AuthToken) RequireTransportSecurity() bool { - return conf.GlobalConf.TLS.Status -} \ No newline at end of file diff --git a/components/jwt/jwt_test.go b/components/jwt/jwt_test.go deleted file mode 100644 index ecc2eb7..0000000 --- a/components/jwt/jwt_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package jwt - -import ( - "fmt" - "pro2d/protos/pb" - "testing" -) - -func TestCreateToken(t *testing.T) { - account := &pb.Account{ - Phone: "17683852936", - Password: "123456", - Uid: "12312", - } - token := CreateToken(account.Uid) - ac := ParseToken(token) - fmt.Println("token: ", token, "\nac: ", ac) -} diff --git a/components/net/conn.go b/components/net/conn.go deleted file mode 100644 index 44cb9d9..0000000 --- a/components/net/conn.go +++ /dev/null @@ -1,123 +0,0 @@ -package net - -import ( - "bufio" - "fmt" - "net" - "pro2d/common" - "pro2d/utils" -) - -type Head struct { - Length int32 - Cmd int32 - ErrCode int32 - PreField int32 -} - - -type Connection struct { - net.Conn - Id int - Server *Server - - scanner *bufio.Scanner - writer *bufio.Writer - - WBuffer chan []byte - RBuffer chan *MsgPkg - - Quit chan *Connection -} - -type MsgPkg struct { - Head *Head - Body []byte - Conn *Connection -} - -func NewConn(id int, conn net.Conn, s *Server) *Connection { - return &Connection{ - Id: id, - Conn: conn, - Server: s, - - scanner: bufio.NewScanner(conn), - writer: bufio.NewWriter(conn), - WBuffer: make(chan []byte), - RBuffer: make(chan *MsgPkg), - Quit: make(chan *Connection), - } -} - -func (c *Connection) write() { - defer c.Quiting() - - for msg := range c.WBuffer { - if _, err := c.writer.Write(msg); err != nil { - fmt.Println("write fail err: " + err.Error()) - return - } - if err := c.writer.Flush(); err != nil { - fmt.Println("write Flush fail err: " + err.Error()) - return - } - } -} - -func (c *Connection) read() { - defer c.Quiting() - for { - 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() - return - } - } -} - -func (c *Connection) Start() { - go c.write() - go c.read() -} - -func (c *Connection) Stop() { - close(c.RBuffer) - close(c.WBuffer) - c.Conn.Close() -} - -func (c *Connection) Quiting() { - c.Server.OnClose(c) -} - -func (c *Connection) SendMsgByCode(errCode int32, cmd int32, data []byte){ - h := &Head{ - Length: int32(common.HEADLEN + len(data)), - Cmd: cmd, - ErrCode: errCode, - PreField: 0, - } - pkg := &MsgPkg{ - Head: h, - Body: data, - } - buf, err := EncodeMsg(pkg) - if err != nil { - utils.Sugar.Errorf("SendMsg error: %v", err) - return - } - c.WBuffer <- buf -} \ No newline at end of file diff --git a/components/net/msg.go b/components/net/msg.go deleted file mode 100644 index 131ae58..0000000 --- a/components/net/msg.go +++ /dev/null @@ -1,57 +0,0 @@ -package net - -import ( - "bytes" - "encoding/binary" - "fmt" - "pro2d/common" -) - -func ParseMsg (data []byte, atEOF bool) (advance int, token []byte, err error) { - // 表示我们已经扫描到结尾了 - if atEOF && len(data) == 0 { - return 0, nil, nil - } - if !atEOF && len(data) >= common.HEADLEN { //4字节数据包长度 4字节指令 - length := int32(0) - binary.Read(bytes.NewReader(data[0:4]), binary.BigEndian, &length) - if length <= 0 { - return 0, nil, fmt.Errorf("length is 0") - } - if int(length) <= len(data) { - return int(length) , data[:int(length)], nil - } - return 0 , nil, nil - } - if atEOF { - return len(data), data, nil - } - return 0, nil, nil -} - - -func DecodeMsg(data []byte) (*MsgPkg, error) { - h := &Head{} - err := binary.Read(bytes.NewReader(data), binary.BigEndian, h) - if err != nil { - return nil, err - } - return &MsgPkg{ - Head: h, - Body: data[common.HEADLEN:], - },nil -} - -func EncodeMsg(pkg *MsgPkg) ([]byte, error) { - buf := &bytes.Buffer{} - err := binary.Write(buf, binary.BigEndian, pkg.Head) - if err != nil { - return nil, err - } - - err = binary.Write(buf, binary.BigEndian, pkg.Body) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} diff --git a/components/net/server.go b/components/net/server.go deleted file mode 100644 index 4b4cb5a..0000000 --- a/components/net/server.go +++ /dev/null @@ -1,84 +0,0 @@ -package net - -import ( - "fmt" - "github.com/golang/protobuf/proto" - "net" - "pro2d/components/db" - "pro2d/components/etcd" - "pro2d/conf" - "pro2d/models" - "pro2d/protos/pb" - "pro2d/utils" - "sync" -) - -type ActionHandler func (msg *MsgPkg) (int32, proto.Message) -var ActionMap map[pb.ProtoCode]ActionHandler - -type Server struct { - SConf *conf.SConf - Clients *sync.Map - EtcdClient *etcd.EtcdClient - -} - -func NewServer(sConf *conf.SConf) *Server { - return &Server{ - SConf: sConf, - Clients: new(sync.Map), - } -} - -func (s *Server) OnRecv(msg *MsgPkg) { - utils.Sugar.Debugf("cmd: %d, data: %s", msg.Head.Cmd, msg.Body) - if md, ok := ActionMap[pb.ProtoCode(msg.Head.Cmd)]; ok { - errCode, protomsg := md(msg) - rsp, err := proto.Marshal(protomsg) - if err != nil { - msg.Conn.SendMsgByCode(-100, msg.Head.Cmd, nil) - return - } - msg.Conn.SendMsgByCode(errCode, msg.Head.Cmd, rsp) - return - } - utils.Sugar.Errorf("protocode not handler: %d", msg.Head.Cmd) -} - -func (s *Server) OnClose(conn *Connection) { - s.Clients.Delete(conn.Id) -} - -func (s *Server)Start() error { - //mongo 初始化 - db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.AccountConf.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) - - port := fmt.Sprintf(":%d", s.SConf.Port) - l, err := net.Listen("tcp", port) - if err != nil { - return err - } - - utils.Sugar.Debugf("listen on %s\n", port) - id := 0 - for { - conn, err := l.Accept() - if err != nil { - return err - } - - id++ - client := NewConn(id, conn, s) - s.Clients.Store(id, client) - go client.Start() - } -} - -func (s *Server)Stop() { -} - diff --git a/conf/RedisKeys.go b/conf/RedisKeys.go deleted file mode 100644 index 3d8b4a9..0000000 --- a/conf/RedisKeys.go +++ /dev/null @@ -1,2 +0,0 @@ -package conf - diff --git a/conf/conf.go b/conf/conf.go index a7a02e6..5f456b7 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -1,12 +1,14 @@ package conf import ( + "encoding/json" "fmt" - lumberjack "gopkg.in/natefinch/lumberjack.v2" "gopkg.in/yaml.v3" "io/ioutil" - "pro2d/components/db" - "pro2d/utils" + "pro2d/src/components/db" + "pro2d/src/components/logger" + "pro2d/src/utils" + "strings" ) type RedisConf struct { @@ -36,11 +38,41 @@ type TLS struct { } type SConf struct { - ID string `yaml:"id"` - Name string `yaml:"name"` - IP string `yaml:"ip"` - Port int `yaml:"port"` - DBName string `yaml:"dbname"` + ID string `yaml:"id"` + Name string `yaml:"name"` + IP string `yaml:"ip"` + Port int `yaml:"port"` + DBName string `yaml:"dbname"` +} + +type LogConsole struct { + Level string `yaml:"level" json:"level"` + Color bool `yaml:"color" json:"color"` +} + +type LogFile struct { + Level string `yaml:"level" json:"level"` + Daily bool `yaml:"daily" json:"daily"` + Maxlines int `yaml:"maxlines" json:"maxlines"` + Maxsize int `yaml:"maxsize" json:"maxsize"` + Maxdays int `yaml:"maxdays" json:"maxdays"` + Append bool `yaml:"append" json:"append"` + Permit string `yaml:"permit" json:"permit"` +} + +type LogConn struct { + Net string `yaml:"net" json:"net"` + Addr string `yaml:"addr" json:"addr"` + Level string `yaml:"level" json:"level"` + Reconnect bool `yaml:"reconnect" json:"reconnect"` + ReconnectOnMsg bool `yaml:"reconnectOnMsg" json:"reconnectOnMsg"` +} + +type LogConf struct { + TimeFormat string `yaml:"TimeFormat" json:"TimeFormat"` + LogConsole *LogConsole `yaml:"Console" json:"Console"` + LogFile *LogFile `yaml:"File" json:"File"` + LogConn *LogConn `yaml:"Conn" json:"Conn"` } type ServerConf struct { @@ -53,7 +85,7 @@ type ServerConf struct { AccountConf *SConf `yaml:"server_account"` GameConf *SConf `yaml:"server_game"` RedisConf *RedisConf `yaml:"redis"` - LogConf *lumberjack.Logger `json:"logconf"` + LogConf *LogConf `yaml:"logconf" json:"logconf"` Etcd *Etcd `yaml:"etcd"` } @@ -74,15 +106,24 @@ func init() { return } + c, err := json.Marshal(&GlobalConf.LogConf) + if err != nil { + fmt.Errorf("log conf %v", err) + return + } //初始化日志 - utils.InitLogger(GlobalConf.LogConf) + err = logger.SetLogger(string(c), strings.ToLower(GlobalConf.GameConf.Name)) + if err != nil { + fmt.Errorf("log conf %v", err) + return + } //初始化雪花算法 SnowFlack = utils.NewSnowflake(GlobalConf.WorkerID, GlobalConf.DatacenterID) err = db.Connect(GlobalConf.MongoConf.User, GlobalConf.MongoConf.Password, GlobalConf.MongoConf.Host, GlobalConf.MongoConf.Port, GlobalConf.MongoConf.MaxNum, GlobalConf.MongoConf.TimeOut) if err != nil { - utils.Sugar.Errorf("connect db err: %v", err) + logger.Error("connect db err: %v", err) } } \ No newline at end of file diff --git a/conf/conf.yaml b/conf/conf.yaml index 57c1ae2..c6fa0cd 100644 --- a/conf/conf.yaml +++ b/conf/conf.yaml @@ -16,11 +16,6 @@ etcd: endpoints: - "192.168.0.206:2379" -tls: - status: true - key: "keys/server.key" - pem: "keys/server.pem" - server_account: id: "1" name: "account" @@ -36,8 +31,21 @@ server_game: dbname: "game" logconf: - filename: "./pro2d.log" # ⽇志⽂件路径 - maxsize: 1024 # 1M=1024KB=1024000byte - maxbackups: 5 # 最多保留5个备份 - maxage: 30 # days - compress: true # 是否压缩 disabled by default \ No newline at end of file + TimeFormat: "2006-01-02 15:04:05" + Console: + level: "TRAC" + color: true + File: + level: "TRAC" + daily: true + maxlines: 1000000 + maxsize: 1 + maxdays: -1 + append: true + permit: "0660" +# Conn: +# net: "tcp" +# addr: "127.0.0.1" +# level: "TRAC" +# reconnect: false +# reconnectOnMsg: false \ No newline at end of file diff --git a/go.mod b/go.mod index 9c9e95f..61b3bba 100644 --- a/go.mod +++ b/go.mod @@ -5,16 +5,15 @@ go 1.17 require ( github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/garyburd/redigo v1.6.3 github.com/gin-gonic/gin v1.7.7 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/protobuf v1.5.2 go.etcd.io/etcd/api/v3 v3.5.2 go.etcd.io/etcd/client/v3 v3.5.2 go.mongodb.org/mongo-driver v1.8.3 - go.uber.org/zap v1.17.0 google.golang.org/grpc v1.38.0 google.golang.org/protobuf v1.27.1 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) @@ -44,12 +43,14 @@ require ( go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.17.0 // indirect golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect golang.org/x/text v0.3.5 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 0eebcc9..e7cf565 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/garyburd/redigo v1.6.3 h1:HCeeRluvAgMusMomi1+6Y5dmFOdYV/JzoRrrbFlkGIc= +github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= diff --git a/keys/ca.crt b/keys/ca.crt deleted file mode 100644 index 231a8ec..0000000 --- a/keys/ca.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDiDCCAnACCQDvizTzfA5snjANBgkqhkiG9w0BAQsFADCBhTELMAkGA1UEBhMC -Q04xETAPBgNVBAgMCFpoZUppYW5nMREwDwYDVQQHDAhaaGVKaWFuZzEPMA0GA1UE -CgwGR2xvYmFsMRcwFQYDVQQDDA5wcm8yZCBTZWN1cml0eTEWMBQGA1UECwwNSVQg -RGVwYXJ0bWVudDEOMAwGA1UEAwwFcHJvMmQwHhcNMjIwMjIyMDYwNzIzWhcNMzIw -MjIwMDYwNzIzWjCBhTELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZUppYW5nMREw -DwYDVQQHDAhaaGVKaWFuZzEPMA0GA1UECgwGR2xvYmFsMRcwFQYDVQQDDA5wcm8y -ZCBTZWN1cml0eTEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDEOMAwGA1UEAwwFcHJv -MmQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+eik5acnrHwEz+Zwb -j7R1eCb9VrJymCii7PtCeeqCZaRBuUYUwKSkEdv367HRH00CW2tYCMMsoBqBewxO -tBwa+1rpCbUvqWdmipMTjE4vmA5Kb50HS3/VxTlnICPb0P0CO2kArrktEPg3W7c5 -Xwmbe8BvYtdEV/BkLUG0+NQbXfXgkKBEs6t1FOqtJAubURann3wAH9pLIDRUcj5B -QzM9b+8qvTjLLj4/uaac4b7X6bfVyaeX8cWOXLHDYEXwIdlRXYz4l+gSVO/EKIgA -5QfwLJTWuxnzcM/klOPsIamQtOYIwEkc1KiCNPZ2CAkzXFspKweR1IwsDM8N/hUU -BWxZAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFzY2wB6phXffFwAcGQZx9FYY9S5 -3L0Xm4mji50+e6UA+N9MjO/4SXNpjt6qMQ3zwSUalapmr8uh9DWLsjRv6HRvgoIm -9tkx8UQkjOoFKcee2+Z780BjsR7SI1HS3VLKUOjm8avKazFGGxjsOtayxzGytAT2 -DK1ubsqSbiK7hFFJqU3cUPP7D3pJOAKaBnLq8MA63vSGTsz2sQUR2Y5DKMXpIhEQ -zlSQvMzsQXv0yll3DhPv76yV6ZKQzCHCoqaPBNU+9QhrWFqIP2QXLR5smeFqOGQM -ngBFwwv9ysSMmcpanMePiuuvXykZiPJpknxdAxry6+A8+/KQ/07hFAHarbI= ------END CERTIFICATE----- diff --git a/keys/ca.key b/keys/ca.key deleted file mode 100644 index 2993d6f..0000000 --- a/keys/ca.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC+eik5acnrHwEz -+Zwbj7R1eCb9VrJymCii7PtCeeqCZaRBuUYUwKSkEdv367HRH00CW2tYCMMsoBqB -ewxOtBwa+1rpCbUvqWdmipMTjE4vmA5Kb50HS3/VxTlnICPb0P0CO2kArrktEPg3 -W7c5Xwmbe8BvYtdEV/BkLUG0+NQbXfXgkKBEs6t1FOqtJAubURann3wAH9pLIDRU -cj5BQzM9b+8qvTjLLj4/uaac4b7X6bfVyaeX8cWOXLHDYEXwIdlRXYz4l+gSVO/E -KIgA5QfwLJTWuxnzcM/klOPsIamQtOYIwEkc1KiCNPZ2CAkzXFspKweR1IwsDM8N -/hUUBWxZAgMBAAECggEATl/Jkpwavyn0vsQYHacVo7gaoucHaet93PwRrpqniZv0 -6C4pzeQuWmwWzH4onll4wF2JX6HLXRNLlLdiqwelAN0n3PdnnALiTuj593MlwKOa -Tbp7LEM+iGEsa2hoGMx3LnHvlJ5QB2ESIQUV8P9P3rAe0DYlSTO98BpHDQXNkKx0 -UPo85k5IgnjueeHdxn8lokQsKXN5R8bS9GduRaEyMR/SHrT3T6YmRKr666N/AQas -7dDe9qGwTpUFympP3PEX/VHQsF9x3/ng6wbRsiijKsUUeGpbslulKi5kHw3j+5Eo -YkeHjH/iMySd41m4oszM4QmCYr5t49AAQl2bYW3b4QKBgQDxFW297lVS8O+z/ANR -yVKI7iejiLKdpc0wlZiFWkAKCve+SCGJ2GZ907NKzLq1O6/byhs7XCu229oLvIFm -/e1sCxdHR/bEf3CTlgoQYXiya0jCgF2GUM/hZpvWZHL0ECZfhwPsks4NWhdKEPyD -9XWpjfC0qlVh18xAF7Zyrw4sTQKBgQDKQyp/wJub0UZ9FmaLORpxr7rO5pROEFov -SiwoJ8gYLo7eU0QT6ipVD/vOTE2+5FdYuk41euYZLOzR0N72kqddUlVAtq9bly92 -sjtvM8iPSGU5cN0D+Up9KT0ZU3zIXH1mVFHsNmm62uN4B3s9Rs+0JvLTI6OwqLAW -LtyqK09WPQKBgEOFP+YpASaoqknbdEaMvxvwr5Nirrvueuh3jW8T1sm5Rqe5ZgNI -Y3QsPZPegRBPNjK1iSj36JpfOtN8qTViOwO+m3dwVVG1a586L3llAzvdRlSLRSZg -LciwR0clfPiUKVsp4lR4zVL5/3nUBhUjQyAIy/idmCo+GUt+GLBIDQ0lAoGABnmT -+Lb/xEM00HhRHA/d6tnHTyxOfxlC6dSkCT0MyMlkxXVA7qpGZKa4VuhWbM/+g9ai -/k9K4m9vvV/EY3xaY0BpfkLa5kG2wUP/ZxXvS7bzlp0oViI64jrZu9/SVM+xK/9z -B+7N/69WLNeAeHu11nyQtXWkndkome0yHzh3t7kCgYBM/U2XmSx0LYHqe+699NXy -4ey3B4IQHb55Fw8LNiaI96ylQG43kNQpZUELjaPBTpQ1a76uL0bRDhjEpZDwjyK0 -gJUWilI8DYTvMa/fdpuVtc5qJErwfNmhRIWiWWryE1OrD1dprQYZzeAy/0+5HDrb -lhQhMGXOJbhiQdKPMcwGbw== ------END PRIVATE KEY----- diff --git a/keys/ca.srl b/keys/ca.srl deleted file mode 100644 index a71a80f..0000000 --- a/keys/ca.srl +++ /dev/null @@ -1 +0,0 @@ -D4E715D41B6F9424 diff --git a/keys/openssl.cnf b/keys/openssl.cnf deleted file mode 100644 index eea897a..0000000 --- a/keys/openssl.cnf +++ /dev/null @@ -1,357 +0,0 @@ -# -# OpenSSL example configuration file. -# This is mostly being used for generation of certificate requests. -# - -# Note that you can include other files from the main configuration -# file using the .include directive. -#.include filename - -# This definition stops the following lines choking if HOME isn't -# defined. -HOME = . -RANDFILE = $ENV::HOME/.rnd - -# Extra OBJECT IDENTIFIER info: -#oid_file = $ENV::HOME/.oid -oid_section = new_oids - -# To use this configuration file with the "-extfile" option of the -# "openssl x509" utility, name here the section containing the -# X.509v3 extensions to use: -# extensions = -# (Alternatively, use a configuration file that has only -# X.509v3 extensions in its main [= default] section.) - -[ new_oids ] - -# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. -# Add a simple OID like this: -# testoid1=1.2.3.4 -# Or use config file substitution like this: -# testoid2=${testoid1}.5.6 - -# Policies used by the TSA examples. -tsa_policy1 = 1.2.3.4.1 -tsa_policy2 = 1.2.3.4.5.6 -tsa_policy3 = 1.2.3.4.5.7 - -#################################################################### -[ ca ] -default_ca = CA_default # The default ca section - -#################################################################### -[ CA_default ] - -dir = ./demoCA # Where everything is kept -certs = $dir/certs # Where the issued certs are kept -crl_dir = $dir/crl # Where the issued crl are kept -database = $dir/index.txt # database index file. -#unique_subject = no # Set to 'no' to allow creation of - # several certs with same subject. -new_certs_dir = $dir/newcerts # default place for new certs. - -certificate = $dir/cacert.pem # The CA certificate -serial = $dir/serial # The current serial number -crlnumber = $dir/crlnumber # the current crl number - # must be commented out to leave a V1 CRL -crl = $dir/crl.pem # The current CRL -private_key = $dir/private/cakey.pem# The private key -RANDFILE = $dir/private/.rand # private random number file - -x509_extensions = usr_cert # The extensions to add to the cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -# Extension copying option: use with caution. -copy_extensions = copy - -# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs -# so this is commented out by default to leave a V1 CRL. -# crlnumber must also be commented out to leave a V1 CRL. -# crl_extensions = crl_ext - -default_days = 365 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = default # use public key default MD -preserve = no # keep passed DN ordering - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_match - -# For the CA policy -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 2048 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extensions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgits Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment -subjectAltName = @alt_names - -[alt_names] -DNS.1 = localhost -DNS.1 = pro2d - -[ v3_ca ] - - -# Extensions for a typical CA - - -# PKIX recommendation. - -subjectKeyIdentifier=hash - -authorityKeyIdentifier=keyid:always,issuer - -basicConstraints = critical,CA:true - -# Key usage: this is typical for a CA certificate. However since it will -# prevent it being used as an test self-signed certificate it is best -# left out by default. -# keyUsage = cRLSign, keyCertSign - -# Some might want this also -# nsCertType = sslCA, emailCA - -# Include email address in subject alt name: another PKIX recommendation -# subjectAltName=email:copy -# Copy issuer details -# issuerAltName=issuer:copy - -# DER hex encoding of an extension: beware experts only! -# obj=DER:02:03 -# Where 'obj' is a standard or added object -# You can even override a supported extension: -# basicConstraints= critical, DER:30:03:01:01:FF - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ proxy_cert_ext ] -# These extensions should be added when creating a proxy certificate - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This really needs to be in place for it to be a proxy certificate. -proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo - -#################################################################### -[ tsa ] - -default_tsa = tsa_config1 # the default TSA section - -[ tsa_config1 ] - -# These are used by the TSA reply generation only. -dir = ./demoCA # TSA root directory -serial = $dir/tsaserial # The current serial number (mandatory) -crypto_device = builtin # OpenSSL engine to use for signing -signer_cert = $dir/tsacert.pem # The TSA signing certificate - # (optional) -certs = $dir/cacert.pem # Certificate chain to include in reply - # (optional) -signer_key = $dir/private/tsakey.pem # The TSA private key (optional) -signer_digest = sha256 # Signing digest to use. (Optional) -default_policy = tsa_policy1 # Policy if request did not specify it - # (optional) -other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) -digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) -accuracy = secs:1, millisecs:500, microsecs:100 # (optional) -clock_precision_digits = 0 # number of digits after dot. (optional) -ordering = yes # Is ordering defined for timestamps? - # (optional, default: no) -tsa_name = yes # Must the TSA name be included in the reply? - # (optional, default: no) -ess_cert_id_chain = no # Must the ESS cert id chain be included? - # (optional, default: no) -ess_cert_id_alg = sha1 # algorithm to compute certificate - # identifier (optional, default: sha1) diff --git a/keys/server.csr b/keys/server.csr deleted file mode 100644 index f4ff1d3..0000000 --- a/keys/server.csr +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICzjCCAbYCAQAwTjELMAkGA1UEBhMCQ04xCzAJBgNVBAsMAklUMQ8wDQYDVQQK -DAZHbG9iYWwxDjAMBgNVBAMMBXBybzJkMREwDwYDVQQHDAhaaGVKaWFuZzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANd6QZzSWnpQN74TzcN8WoUYwutx -mSQB2x8h17VBlVJepFpog1ujWoZUl1xQ7lgPr2DhMHM1qNkroahdI/AZGj2juuC2 -1vg2WZ4Wa/8vsICGPvTTqEd7VSpY66ybk0kd7rWFp7naXLBGFVf7mi3RPDW9Y3mO -cyTR7Iwtm1iBhMDS5uczUOzrZ63yd0FA62iizKqckfwXVsYFUJqdG0uUUt88whnt -qAPRKrkuU+Y6I+jo+C6gf7i7RTLz6aI01QYLRMENmJI5NqFcJ4cNKWsLIWY3sDz0 -XoyueWl3tHrjiX4TmZM28OZAHl/rKd0lQpQEB0UiHe7At/8xZHLDol6ip4MCAwEA -AaA7MDkGCSqGSIb3DQEJDjEsMCowCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEAYD -VR0RBAkwB4IFcHJvMmQwDQYJKoZIhvcNAQELBQADggEBAKytMdGU/yLmC5uUUdWd -0dnqloVaCiyPCjWBsv44H2jiVq2UT5nQeiTWJ2hAt6RIsIUyymrY6Flg6ZpCfKaa -yqYNDBzDwGAJAWTHicNyQT/Uxb5rn+6R4qfyBOkFGaPlF9dxCgKRTqaSX5WmWFE6 -FzsAiwYcc8fb+ioljnN3NJ7MZLz0n6RU52PCwYDbgC941t3yFa5R1wHgGoK1/93B -2/+IUNWaS8XRGfRe3SUZ2rSTuCgr8J9jfsvsx3qga3KWTpyAxOe3vexKpnhO9Xw0 -wDVRApMMmlPVrLrKMNGSCXNaBT0JdTpFn9CJFheJs9jqv+q77T8qpqln9leMgtvF -ql8= ------END CERTIFICATE REQUEST----- diff --git a/keys/server.key b/keys/server.key deleted file mode 100644 index 7c918fa..0000000 --- a/keys/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDXekGc0lp6UDe+ -E83DfFqFGMLrcZkkAdsfIde1QZVSXqRaaINbo1qGVJdcUO5YD69g4TBzNajZK6Go -XSPwGRo9o7rgttb4NlmeFmv/L7CAhj7006hHe1UqWOusm5NJHe61hae52lywRhVX -+5ot0Tw1vWN5jnMk0eyMLZtYgYTA0ubnM1Ds62et8ndBQOtoosyqnJH8F1bGBVCa -nRtLlFLfPMIZ7agD0Sq5LlPmOiPo6PguoH+4u0Uy8+miNNUGC0TBDZiSOTahXCeH -DSlrCyFmN7A89F6Mrnlpd7R644l+E5mTNvDmQB5f6yndJUKUBAdFIh3uwLf/MWRy -w6JeoqeDAgMBAAECggEAecQ5zdBFlvc7+OsiDUV5tdsfU4PXgbSWykoKpwBPzMN0 -5y5GhQOUBXNKMb3+Yr9CYWIASirZpxfz+7vesjDNVmXvFkvcwVmdXSvYjdW6TXcP -nrT5VKPKpeqoC9vC6L/EhMnp1aojVO3V4+ln4FpsRwYmb6vjP7xti22+as6OYZQC -ygC0f0hCWO4t67BrH7lmA+l5KHmfQtl0t7iJ3yXLXz1EMtbVKwoOuNxBnfxTdPuE -a2ke5G8S8nN5ZCe2FpaN+BxQQ4NpRnc0aK5O02EibA5Ix2ItLnXs+MluNC91veVS -WRAbEbvFBwZx624u0NEe5oAIFhS2m4VeKVUmqfdxwQKBgQD/fuE13vXtSV30I4Sj -lZJfr9r+T/gGA6wIna0RhfaXnKDS9SjzFgF6MS2QSRKYzjPCzfGQIxq368NLrEdu -lCLNQF9Z06lVNeuJT84VPYm4P/wheIG7LhNmBHsnkTzYnupFrlB22TnmStDSfuGp -dhlyAUGN91pg/mP/nO1ZrTPpowKBgQDX5ycPD8dOUz2ACoKCGEf37hbcfqBmKd7a -JGYOWaBXPOGZ26KboWoClrStHow2VCnqEx9pewA6+4m0cv4JXiCXa1/uoY9XxHRI -Tz3BSycqAOFTGhz9Z+Nq8Rc9PIFYBHzjyVgrsXiO86TKK33uE7GAqXZGB+MeDOf0 -FSb8a8vooQKBgDkR715oKkjRnZH+KQ+dRm/nSSSLWlyFj3TxO4pxgQ6GpwnYR0hd -PwE7YPEc0XGehcNa2z2WCc7Rc/NATUhvAIMWgPYAqI9nFvC6Cc+Gym+Eo14am+fi -t+SO1a+V6qB8htn/wOt7REqjpZePTfrbbX2guDLs8Jw/1rhvJjlkzfa/AoGBAJ+/ -RbQsPZDjoE6b+CKgKqf0v2+YNcBB3MVVRzn48N17i4VW8ILstM6Did3KC36rWXP7 -gDOAshPyR9p/dx2hSsYeyZV8bt5G2q8iCpR5sdmvWwks+iQ5eRiImGRT33Qrpei4 -8ocpwgUrm1OHSJ8ebSjAumVospBqhjmgaP8+F1rhAoGBAIaDv/yhFtEQaldOkK8X -YQACUmt3YflHxWmdlmulVqAOCDH8nV3YYHGFIEatBx2w0OBvxoLtw5HTvEq1pFcF -4cL8ulNQeozLTsGfNeLgIe7NOb6T54QZFVg1+dgePtBIsab59sZE3817j5zmkr0A -DywcIXZFRNuOpQgL2I4JF7PX ------END PRIVATE KEY----- diff --git a/keys/server.pem b/keys/server.pem deleted file mode 100644 index 13b643b..0000000 --- a/keys/server.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIJANTnFdQbb5QkMA0GCSqGSIb3DQEBBQUAMIGFMQswCQYD -VQQGEwJDTjERMA8GA1UECAwIWmhlSmlhbmcxETAPBgNVBAcMCFpoZUppYW5nMQ8w -DQYDVQQKDAZHbG9iYWwxFzAVBgNVBAMMDnBybzJkIFNlY3VyaXR5MRYwFAYDVQQL -DA1JVCBEZXBhcnRtZW50MQ4wDAYDVQQDDAVwcm8yZDAeFw0yMjAyMjIwNjA3MjNa -Fw0yMzAyMjIwNjA3MjNaME4xCzAJBgNVBAYTAkNOMQswCQYDVQQLDAJJVDEPMA0G -A1UECgwGR2xvYmFsMQ4wDAYDVQQDDAVwcm8yZDERMA8GA1UEBwwIWmhlSmlhbmcw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXekGc0lp6UDe+E83DfFqF -GMLrcZkkAdsfIde1QZVSXqRaaINbo1qGVJdcUO5YD69g4TBzNajZK6GoXSPwGRo9 -o7rgttb4NlmeFmv/L7CAhj7006hHe1UqWOusm5NJHe61hae52lywRhVX+5ot0Tw1 -vWN5jnMk0eyMLZtYgYTA0ubnM1Ds62et8ndBQOtoosyqnJH8F1bGBVCanRtLlFLf -PMIZ7agD0Sq5LlPmOiPo6PguoH+4u0Uy8+miNNUGC0TBDZiSOTahXCeHDSlrCyFm -N7A89F6Mrnlpd7R644l+E5mTNvDmQB5f6yndJUKUBAdFIh3uwLf/MWRyw6JeoqeD -AgMBAAGjLDAqMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBAGA1UdEQQJMAeCBXBy -bzJkMA0GCSqGSIb3DQEBBQUAA4IBAQBmCfF8okV2lCH7KBA4gKjKh7dCKdLiTBWP -q5qHNt2dYOmZAi3CFlgvAqgg30/Ql9pILN+0XJTL7TnJRhvvCZ/eObtCJbmZD/jP -SzmowtCyydPtj/DGmxY+UQZk4KqtRuDGD5LQRU2VYzHJvrf9yEse2uIf+uUetx7b -r2dklvP+H33rMB1k3hswHg2/EmhJxtfvQCLQX8+Ofur/wW8uYKFj3hTabzYfcew3 -Uw1/5a+rLHBLAA1SYoviwnoNgiVBxkWkfEH7tsheFapVULltz3vll013Q69RBXVw -K7QloFM0LgoJKM+X65ymUGPGL3F4WvewSOiWyFLQdW43wRlUUNkq ------END CERTIFICATE----- diff --git a/models/account.go b/models/account.go deleted file mode 100644 index 027c1ff..0000000 --- a/models/account.go +++ /dev/null @@ -1,31 +0,0 @@ -package models - -import ( - "pro2d/components/db" - "pro2d/protos/pb" -) - -type AccountModel struct { - *db.MgoColl - *pb.Account -} - -func AccountExistByPhone(phone string) (bool, *AccountModel){ - m := NewAccount(phone) - if err := m.Load(); err != nil { - return false, m - } - return true, m -} - -func NewAccount(phone string) *AccountModel { - ac := &pb.Account{ - Phone: phone, - } - account := &AccountModel{ - MgoColl: db.NewMongoColl(phone, ac), - Account: ac, - } - - return account -} \ No newline at end of file diff --git a/models/equip.go b/models/equip.go deleted file mode 100644 index 368319d..0000000 --- a/models/equip.go +++ /dev/null @@ -1,24 +0,0 @@ -package models - -import ( - "pro2d/components/db" - "pro2d/protos/pb" - "strconv" -) - -type EquipModels struct { - *db.MgoColl - Equip *pb.Equipment -} - -func NewEquip(id int64) *EquipModels{ - data := &pb.Equipment{ - Id: id, - } - m := &EquipModels { - MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), - Equip: data, - } - - return m -} \ No newline at end of file diff --git a/models/hero.go b/models/hero.go deleted file mode 100644 index 4e15e62..0000000 --- a/models/hero.go +++ /dev/null @@ -1,32 +0,0 @@ -package models - -import ( - "pro2d/components/db" - "pro2d/protos/pb" - "strconv" -) - -type HeroModel struct { - *db.MgoColl - Hero *pb.Hero -} -type HeroMap map[string]*HeroModel - -func GetHeros(hm HeroMap) map[string]*pb.Hero { - h := make(map[string]*pb.Hero) - for k, v := range hm { - h[k] = v.Hero - } - return h -} - -func NewHero(id int64) *HeroModel { - h := &pb.Hero{ - Id: id, - } - m := &HeroModel{ - MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), h), - Hero: h, - } - return m -} diff --git a/models/init.go b/models/init.go deleted file mode 100644 index db72f6b..0000000 --- a/models/init.go +++ /dev/null @@ -1,39 +0,0 @@ -package models - -import ( - "pro2d/components/db" - "pro2d/protos/pb" - "pro2d/utils" -) - -func InitDoc(schema ...interface{}) { - for _, s := range schema { - coll, keys := utils.FindIndex(s) - for _, index := range keys { - db.CreateCollection(coll) - - utils.Sugar.Debugf("InitDoc collect: %v, createIndex: %s", coll, index) - res, err := db.SetUnique(coll, index) - if err != nil { - utils.Sugar.Errorf("InitDoc unique: %s, err: %v", res, err) - continue - } - } - } -} - -func InitAccountServerModels() { - var schema []interface{} = []interface{}{ - pb.Account{}, - } - InitDoc(schema...) -} - -func InitGameServerModels() { - var schema []interface{} = []interface{}{ - pb.Hero{}, - pb.Role{}, - pb.Team{}, - } - InitDoc(schema...) -} \ No newline at end of file diff --git a/models/init_test.go b/models/init_test.go deleted file mode 100644 index ac108ba..0000000 --- a/models/init_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package models - -import ( - "context" - "pro2d/components/db" - _ "pro2d/conf" - "testing" -) - - -func TestInitModels(t *testing.T) { - - db.MongoDatabase = db.MongoClient.Database("account") - InitAccountServerModels() - db.MongoClient.Disconnect(context.TODO()) - - db.MongoDatabase = db.MongoClient.Database("game") - InitGameServerModels() - db.MongoClient.Disconnect(context.TODO()) -} \ No newline at end of file diff --git a/models/prop.go b/models/prop.go deleted file mode 100644 index a78e679..0000000 --- a/models/prop.go +++ /dev/null @@ -1,24 +0,0 @@ -package models - -import ( - "pro2d/components/db" - "pro2d/protos/pb" - "strconv" -) - -type PropModels struct { - *db.MgoColl - Prop *pb.Prop -} - -func NewProp(id int64) *PropModels{ - data := &pb.Prop{ - Id: id, - } - m := &PropModels{ - MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), - Prop: data, - } - - return m -} \ No newline at end of file diff --git a/models/role.go b/models/role.go deleted file mode 100644 index cb33bdb..0000000 --- a/models/role.go +++ /dev/null @@ -1,55 +0,0 @@ -package models - -import ( - "fmt" - "pro2d/components/db" - "pro2d/protos/pb" - "pro2d/utils" - "strconv" -) - -type RoleModel struct { - *db.MgoColl - Role *pb.Role - Heros HeroMap - Teams *TeamModel - Equip *pb.Equipment - Prop *pb.Prop -} - -func RoleExistByUid(uid string) *RoleModel{ - data := &pb.Role{Uid: uid} - - if err := db.FindOne(db.GetBsonM("uid", uid), data); err != nil { - utils.Sugar.Errorf("Role exist err: %v", err) - return nil - } - - return &RoleModel{ - MgoColl: db.NewMongoColl(strconv.Itoa(int(data.Id)), data), - Role: data, - } -} - -func NewRole(id int64) *RoleModel { - data := &pb.Role{Id: id} - m := &RoleModel{ - MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), - Role: data, - Heros: make(HeroMap), - } - return m -} - -func (m *RoleModel) LoadAll() { -} - -func (m *RoleModel) LoadHero() { -} - -func (m *RoleModel) AddHero(hero *pb.Hero) { - h := NewHero(hero.Id) - h.Hero = hero - h.Create() - m.Heros[fmt.Sprintf("%d%d", m.Role.Id, h.Hero.Id)] = h -} \ No newline at end of file diff --git a/models/role_test.go b/models/role_test.go deleted file mode 100644 index ce774bd..0000000 --- a/models/role_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package models - -import ( - "fmt" - "pro2d/components/db" - "pro2d/conf" - "pro2d/protos/pb" - "pro2d/utils" - "testing" -) - -func TestNewRole(t *testing.T) { - db.MongoDatabase = db.MongoClient.Database("game") - - var uid = conf.SnowFlack.NextValStr() - ok, role := RoleExistByUid(uid) - if ok { - //uid存在 , 更新角色 - role.Role.Device = "222222" - role.AddHero(&pb.Hero{ - Id: 1, - RoleId: role.Role.Id, - Type: 0, - Level: 0, - ReinCount: 0, - ReinPoint: 0, - Equipments: "", - }) - role.Save() - }else { - //uid不存在,创建角色 - role = NewRole(1) - role.Role.Uid = uid - role.Role.Device = "111111" - role.Role.Level = 0 - i, err := role.Create() - fmt.Println(i, err) - } - print(role) -} - -func TestRoleIndex(t *testing.T) { - coll, keys := utils.FindIndex(pb.Role{}) - for _, index := range keys { - utils.Sugar.Debugf("coll: %s, key: %s", coll, index) - } -} \ No newline at end of file diff --git a/models/team.go b/models/team.go deleted file mode 100644 index 13b119d..0000000 --- a/models/team.go +++ /dev/null @@ -1,24 +0,0 @@ -package models - -import ( - "pro2d/components/db" - "pro2d/protos/pb" - "strconv" -) - -type TeamModel struct { - *db.MgoColl - Team *pb.Team -} - -func NewTeam(id int64) *TeamModel{ - data := &pb.Team{ - Id: id, - } - m := &TeamModel{ - MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), - Team: data, - } - - return m -} \ No newline at end of file diff --git a/src/actions/HttpAction.go b/src/actions/HttpAction.go new file mode 100644 index 0000000..828b95b --- /dev/null +++ b/src/actions/HttpAction.go @@ -0,0 +1,127 @@ +package actions + +import ( + "fmt" + "github.com/gin-gonic/gin" + "net/http" + "pro2d/conf" + "pro2d/protos/pb" + "pro2d/src/components/db" + "pro2d/src/components/etcd" + "pro2d/src/models" + "pro2d/src/utils" + "reflect" + "strings" +) + +type HttpAction struct { + version string + port []string + EtcdClient *etcd.EtcdClient +} + +func NewHttpAction(version string, port ...string) *HttpAction { + return &HttpAction{version: version, port: port} +} + +func Pong (c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) +} + +func HandlerFuncObj(tvl, obj reflect.Value) gin.HandlerFunc { + apiFun := func(c *gin.Context) interface{} { return c } + return func(c *gin.Context) { + tvl.Call([]reflect.Value{obj, reflect.ValueOf(apiFun(c))}) + } +} + +func GetRoutePath(objName, objFunc string) string { + return strings.ToLower(objName + "/" + objFunc) +} + +func PubRsp(c *gin.Context, code int, data interface{}) { + c.JSON(http.StatusOK, gin.H{"code": code, "data": data}) +} + +func (h *HttpAction) Register(c *gin.Context) { + var register pb.Register + if err := c.ShouldBindJSON(®ister); err != nil { + PubRsp(c, -1, err.Error()) + return + } + + account := models.NewAccount(register.Phone) + if err := account.Load(); err == nil { + PubRsp(c, -2, "account exist: " + register.Phone) + return + } + + account.Uid = conf.SnowFlack.NextValStr() + account.Password = utils.Md5V(register.Password) + if _, err := account.Create(); err != nil{ + PubRsp(c, -3, "account register err: " + err.Error()) + return + } + account.Password = register.Password + PubRsp(c, 0, account.Account) +} + +func (h *HttpAction) Login(c *gin.Context) { + var login pb.Account + if err := c.ShouldBindJSON(&login); err != nil { + PubRsp(c, -1, err.Error()) + return + } + account := models.NewAccount(login.Phone) + if err := account.Load(); err != nil { + PubRsp(c, -2, err.Error()) + return + } + + if utils.Md5V(login.Password) != account.Password { + PubRsp(c, -3, "password error") + return + } + + var gs []*pb.ServiceInfo + for k, v := range h.EtcdClient.GetByPrefix(conf.GlobalConf.GameConf.Name) { + gs = append(gs, &pb.ServiceInfo{ + Id: k, + Name: conf.GlobalConf.GameConf.Name, + Address: v, + }) + } + rsp := &pb.LoginRsp{ + Uid: account.Uid, + GameService: gs, + } + PubRsp(c, 0, rsp) +} + +func (h *HttpAction) Start() error { + //mongo 初始化 + db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.AccountConf.DBName) + models.InitAccountServerModels() + + //Etcd 初始化 + h.EtcdClient = etcd.NewEtcdClient(conf.GlobalConf.Etcd) + h.EtcdClient.PutWithLeasePrefix(conf.GlobalConf.AccountConf.Name, conf.GlobalConf.AccountConf.ID, fmt.Sprintf("%s:%d", conf.GlobalConf.AccountConf.IP, conf.GlobalConf.AccountConf.Port), 5) + + //gin初始化 + r := gin.Default() + r.GET("/ping", Pong) + typ := reflect.TypeOf(h) + val := reflect.ValueOf(h) + //t := reflect.Indirect(val).Type() + //objectName := t.Name() + + numOfMethod := val.NumMethod() + for i := 0; i < numOfMethod; i++ { + method := typ.Method(i) + r.GET(GetRoutePath(h.version, method.Name), HandlerFuncObj(method.Func, val)) + r.POST(GetRoutePath(h.version, method.Name), HandlerFuncObj(method.Func, val)) + } + return r.Run(h.port...) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080") +} \ No newline at end of file diff --git a/src/actions/RoleAction.go b/src/actions/RoleAction.go new file mode 100644 index 0000000..735d356 --- /dev/null +++ b/src/actions/RoleAction.go @@ -0,0 +1,54 @@ +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 new file mode 100644 index 0000000..8018bf8 --- /dev/null +++ b/src/actions/protocode.go @@ -0,0 +1,15 @@ +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 new file mode 100644 index 0000000..41a8345 --- /dev/null +++ b/src/common/common.go @@ -0,0 +1,7 @@ +package common + +const ( + HEADLEN = 8 + + Pro2DTokenSignedString = "Pro2DSecret" +) diff --git a/src/components/db/mongo.go b/src/components/db/mongo.go new file mode 100644 index 0000000..94c26e6 --- /dev/null +++ b/src/components/db/mongo.go @@ -0,0 +1,226 @@ +package db + +import ( + "context" + "fmt" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" + "go.mongodb.org/mongo-driver/x/bsonx" + "pro2d/src/utils" + "sort" + "strconv" + "time" +) + +var ( + MongoClient *mongo.Client + MongoDatabase *mongo.Database +) + +//初始化 +func Connect(user, password, host string,port int, MaxNum int, timeOut int) error { + var uri string + if user!= "" { + //uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?w=majority", conf.User, conf.Password, conf.Host, conf.Port, conf.DBName) + uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/?w=majority", user, password, host, port) + }else { + //uri = fmt.Sprintf("mongodb://%s:%d/%s?w=majority", conf.Host, conf.Port, conf.DBName) + uri = fmt.Sprintf("mongodb://%s:%d/?w=majority", host, port) + } + // 设置连接超时时间 + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeOut)) + defer cancel() + // 通过传进来的uri连接相关的配置 + o := options.Client().ApplyURI(uri) + // 设置最大连接数 - 默认是100 ,不设置就是最大 max 64 + o.SetMaxPoolSize(uint64(MaxNum)) + // 发起链接 + var err error + MongoClient, err = mongo.Connect(ctx, o) + if err != nil { + return err + } + // 判断服务是不是可用 + if err = MongoClient.Ping(context.Background(), readpref.Primary()); err != nil { + return err + } + + //MongoDatabase = MongoClient.Database(dbname) + return nil +} + +func CreateCollection(collection string) error { + colls, _ := MongoDatabase.ListCollectionNames(context.TODO(), bson.D{}) + pos := sort.SearchStrings(colls, collection) + if pos != len(colls) { + if collection == colls[pos] { + return MongoDatabase.CreateCollection(context.TODO(), collection) + } + } + return MongoDatabase.CreateCollection(context.TODO(), collection) +} + +func SetUnique(collection string, key string) (string, error) { + return MongoDatabase.Collection(collection).Indexes().CreateOne( + context.TODO(), + mongo.IndexModel{ + Keys : bsonx.Doc{{key, bsonx.Int32(1)}}, + Options: options.Index().SetUnique(true), + }, + ) +} + +type MgoColl struct { + collection *mongo.Collection + + pri interface{} + schema interface{} +} + +func GetBsonD(key string, value interface{}) interface{} { + return bson.D{ {key, value}} +} +func GetBsonM(key string, value interface{}) interface{} { + return bson.M{key: value} +} + +func NewMongoColl(key string, schema interface{}) *MgoColl { + return &MgoColl{ + collection: MongoDatabase.Collection(utils.GetCollName(schema)), + pri: GetBsonM(utils.GetPriKey(schema), key), + schema: schema, + } +} + +func FindOne(pri interface{}, schema interface{}) error { + r := MongoDatabase.Collection(utils.GetCollName(schema)).FindOne(context.TODO(), pri) + return r.Decode(schema) +} + +// 查询单个 +func (m *MgoColl) FindOneKV(key string, value interface{}) *mongo.SingleResult { + //collection. + filter := bson.D{ {key, value}} + singleResult := m.collection.FindOne(context.TODO(), filter) + return singleResult +} + +//查询集合里有多少数据 +func (m *MgoColl) CollectionCount() (string, int64) { + size, _ := m.collection.EstimatedDocumentCount(context.TODO()) + return m.collection.Name(), size +} + +//按选项查询集合 Skip 跳过 Limit 读取数量 sort 1 ,-1 . 1 为最初时间读取 , -1 为最新时间读取 +func (m *MgoColl) CollectionDocuments(Skip, Limit int64, sort int) *mongo.Cursor { + SORT := bson.D{ + {"_id", sort}} //filter := bson.D{ {key,value}} + filter := bson.D{ + {}} + findOptions := options.Find().SetSort(SORT).SetLimit(Limit).SetSkip(Skip) + //findOptions.SetLimit(i) + temp, _ := m.collection.Find(context.Background(), filter, findOptions) + return temp +} + +//获取集合创建时间和编号 +func (m *MgoColl) ParsingId(result string) (time.Time, uint64) { + temp1 := result[:8] + timestamp, _ := strconv.ParseInt(temp1, 16, 64) + dateTime := time.Unix(timestamp, 0) //这是截获情报时间 时间格式 2019-04-24 09:23:39 +0800 CST + temp2 := result[18:] + count, _ := strconv.ParseUint(temp2, 16, 64) //截获情报的编号 + return dateTime, count +} + +//删除文章和查询文章 +func (m *MgoColl) DeleteAndFind(key string, value interface{}) (int64, *mongo.SingleResult) { + filter := bson.D{ + {key, value}} + singleResult := m.collection.FindOne(context.TODO(), filter) + DeleteResult, err := m.collection.DeleteOne(context.TODO(), filter, nil) + if err != nil { + fmt.Println("删除时出现错误,你删不掉的~") + } + return DeleteResult.DeletedCount, singleResult +} + +//删除文章 +func (m *MgoColl) Delete(key string, value interface{}) int64 { + filter := bson.D{ {key, value}} + count, err := m.collection.DeleteOne(context.TODO(), filter, nil) + if err != nil { + fmt.Println(err) + } + return count.DeletedCount + +} + +//删除多个 +func (m *MgoColl) DeleteMany(key string, value interface{}) int64 { + filter := bson.D{ {key, value}} + + count, err := m.collection.DeleteMany(context.TODO(), filter) + if err != nil { + fmt.Println(err) + } + return count.DeletedCount +} + +//索引 +func (m *MgoColl) SetUnique(key string){ + m.collection.Indexes().CreateOne( + context.Background(), + mongo.IndexModel{ + Keys : bsonx.Doc{{key, bsonx.Int32(1)}}, + Options: options.Index().SetUnique(true), + }, + ) +} + +//更新&保存 +func (m *MgoColl) FindOneAndUpdate(filter interface{}, update interface{})*mongo.SingleResult { + //filter := bson.M{"name": "x", "array.name": "b"} + //update := bson.M{"array.$[item].detail": "test"} + + res := m.collection.FindOneAndUpdate(context.Background(), + filter, + bson.M{"$set": update}) + if res.Err() != nil { + return nil + } + return res +} + +func (m *MgoColl) UpdateOne(filter interface{}, update interface{})*mongo.UpdateResult { + res, err := m.collection.UpdateOne(context.TODO(), filter, update) + if err != nil { + return nil + } + + return res +} + + +func (m *MgoColl) Load() error{ + r := m.collection.FindOne(context.TODO(), m.pri) + err := r.Decode(m.schema) + if err != nil { + return err + } + return nil +} + +func (m *MgoColl) Create() (*mongo.InsertOneResult, error){ + return m.collection.InsertOne(context.TODO(), m.schema) +} + +func (m *MgoColl) Update(update interface{}) { + m.FindOneAndUpdate(m.pri, update) +} + +func (m *MgoColl)Save() { + m.FindOneAndUpdate(m.pri, m.schema) +} \ No newline at end of file diff --git a/src/components/db/redis.go b/src/components/db/redis.go new file mode 100644 index 0000000..0fd0aec --- /dev/null +++ b/src/components/db/redis.go @@ -0,0 +1,43 @@ +package db + +import ( + "github.com/garyburd/redigo/redis" + "pro2d/conf" + "time" +) +var RedisPool *redis.Pool + +func ConnectRedis(conf *conf.ServerConf) error { + RedisPool = &redis.Pool{ + //最大活跃连接数,0代表无限 + MaxActive: 888, + MaxIdle: 20, + //闲置连接的超时时间 + 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)) + } + return redis.Dial("tcp",conf.RedisConf.Address, option...) + }, + } + return nil +} + +func CloseRedis() { + RedisPool.Close() +} + +func HKEYS(args ...interface{}) (reply interface{}, err error) { + conn := RedisPool.Get() + defer conn.Close() + return conn.Do("HKEYS", args) +} + +func HMSET(args ...interface{}) (reply interface{}, err error) { + conn := RedisPool.Get() + defer conn.Close() + return conn.Do("HMSET", args) +} \ No newline at end of file diff --git a/src/components/etcd/etcd.go b/src/components/etcd/etcd.go new file mode 100644 index 0000000..3fff95e --- /dev/null +++ b/src/components/etcd/etcd.go @@ -0,0 +1,106 @@ +package etcd + +import ( + "context" + "fmt" + clientv3 "go.etcd.io/etcd/client/v3" + "pro2d/conf" + "pro2d/src/components/logger" + "time" +) + +type EtcdClient struct { + etcd *clientv3.Client +} + +func NewEtcdClient(conf *conf.Etcd) *EtcdClient { + cli, err := clientv3.New(clientv3.Config{ + Endpoints: conf.Endpoints, + DialTimeout: time.Duration(conf.DialTimeout) * time.Second, + }) + if err != nil { + logger.Error("etcd init err: %v", err) + return nil + } + return &EtcdClient{ + etcd: cli, + } +} + +func (e *EtcdClient)PutWithPrefix(prefix, key, val string) { + _, err := e.etcd.Put(context.TODO(), fmt.Sprintf("/%s/%s/", prefix, key), val) + if err != nil { + logger.Error("PutWithPrefix err: %v", err) + return + } +} + +func (e *EtcdClient)PutWithLeasePrefix(prefix, key, val string, ttl int64) error { + lease := clientv3.NewLease(e.etcd) + leaseResp, err := lease.Grant(context.TODO(), ttl) + if err != nil { + logger.Error("PutWithLeasePrefix 设置租约时间失败:%v\n", err) + return err + } + + _, err = e.etcd.Put(context.TODO(), fmt.Sprintf("/%s/%s/", prefix, key), val, clientv3.WithLease(leaseResp.ID)) + if err != nil { + logger.Error("PutWithLeasePrefix err: %v", err) + return err + } + + keepRespChan, err := lease.KeepAlive(context.TODO(), leaseResp.ID) + if err != nil { + logger.Error("keepalive err: %v", err) + return err + } + go func() { + for { + select { + case _ = <-keepRespChan: + if keepRespChan == nil { + fmt.Println("租约已经失效") + goto END + } else { //每秒会续租一次,所以就会受到一次应答 + //fmt.Println("收到自动续租应答:", keepResp.ID) + } + } + } + END: + }() + return nil +} + +func (e *EtcdClient)Get(key string) map[string]string { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + resp, err := e.etcd.Get(ctx, fmt.Sprintf("/%s/", key)) + cancel() + if err != nil { + logger.Error("etcd get key: %s, err: %v", key, err) + return nil + } + m := make(map[string]string) + for _, v := range resp.Kvs { + m[string(v.Key)] = string(v.Value) + } + return m +} + +func (e *EtcdClient)GetByPrefix(prefix string) map[string]string { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + resp, err := e.etcd.Get(ctx, fmt.Sprintf("/%s/", prefix), clientv3.WithPrefix()) + cancel() + if err != nil { + logger.Error("etcd get prefix: %s, err: %v", prefix, err) + return nil + } + m := make(map[string]string) + for _, v := range resp.Kvs { + m[string(v.Key)] = string(v.Value) + } + return m +} + +func (e *EtcdClient)Close() { + e.etcd.Close() +} \ No newline at end of file diff --git a/src/components/etcd/etcd_test.go b/src/components/etcd/etcd_test.go new file mode 100644 index 0000000..adbb3b5 --- /dev/null +++ b/src/components/etcd/etcd_test.go @@ -0,0 +1,46 @@ +package etcd + +import ( + "context" + "fmt" + "go.etcd.io/etcd/api/v3/mvccpb" + clientv3 "go.etcd.io/etcd/client/v3" + "pro2d/conf" + "pro2d/src/components/logger" + "testing" +) + +func TestEtcdClient_GetByPrefix(t *testing.T) { + etcd := NewEtcdClient(conf.GlobalConf.Etcd) + gameInfo := etcd.GetByPrefix(conf.GlobalConf.AccountConf.Name) + for k, v := range gameInfo { + logger.Debug("game info key: %v val: %v", k, v) + } + + rch := etcd.etcd.Watch(context.Background(), fmt.Sprintf("/%s/", conf.GlobalConf.AccountConf.Name), clientv3.WithPrefix()) + go func() { + for wresp := range rch { + for _, ev := range wresp.Events { + switch ev.Type { + case mvccpb.PUT: //修改或者新增 + logger.Debug("account put key: %s val: %s", ev.Kv.Key, ev.Kv.Value) + case mvccpb.DELETE: //删除 + logger.Debug("account delete key: %s val: %s", ev.Kv.Key, ev.Kv.Value) + } + } + } + }() + + game := etcd.etcd.Watch(context.Background(), fmt.Sprintf("/%s/", conf.GlobalConf.GameConf.Name), clientv3.WithPrefix()) + for wresp := range game { + for _, ev := range wresp.Events { + switch ev.Type { + case mvccpb.PUT: //修改或者新增 + logger.Debug("game put key: %s val: %s", ev.Kv.Key, ev.Kv.Value) + case mvccpb.DELETE: //删除 + logger.Debug("game delete key: %s val: %s", ev.Kv.Key, ev.Kv.Value) + } + } + } + +} diff --git a/src/components/jwt/jwt.go b/src/components/jwt/jwt.go new file mode 100644 index 0000000..0104b26 --- /dev/null +++ b/src/components/jwt/jwt.go @@ -0,0 +1,97 @@ +package jwt + +import ( + "context" + "fmt" + "pro2d/conf" + "pro2d/src/common" + "pro2d/src/utils" + "time" + + jwt "github.com/dgrijalva/jwt-go" + "google.golang.org/grpc/metadata" +) + +func CreateToken(uid string) (tokenString string) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "iss": "pro2d-app-server", + "aud": "pro2d-app-server", + "nbf": time.Now().Unix(), + "exp": time.Now().Add(time.Hour).Unix(), + "sub": "pro2d", + "uid": uid, + }) + tokenString, err := token.SignedString([]byte(common.Pro2DTokenSignedString)) + if err != nil { + panic(err) + } + return tokenString +} + +func ParseToken(tokenStr string) string { + var clientClaims Claims + token, err := jwt.ParseWithClaims(tokenStr, &clientClaims, func(token *jwt.Token) (interface{}, error) { + if token.Header["alg"] != "HS256" { + //panic("ErrInvalidAlgorithm") + utils.Sugar.Error("ErrInvalidAlgorithm") + return nil, nil + } + return []byte(common.Pro2DTokenSignedString), nil + }) + if err != nil { + utils.Sugar.Error("jwt parse error") + return "" + } + + if !token.Valid { + utils.Sugar.Error("ErrInvalidToken") + return "" + } + return clientClaims.Uid +} + + +// Claims defines the struct containing the token claims. +type Claims struct { + jwt.StandardClaims + Uid string +} + +// 从 context 的 metadata 中,取出 token +func getTokenFromContext(ctx context.Context) (string, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return "", fmt.Errorf("ErrNoMetadataInContext") + } + // md 的类型是 type MD map[string][]string + token, ok := md["authorization"] + if !ok || len(token) == 0 { + return "", fmt.Errorf("ErrNoAuthorizationInMetadata") + } + // 因此,token 是一个字符串数组,我们只用了 token[0] + return token[0], nil +} + +func CheckAuth(ctx context.Context) string { + tokenStr, err := getTokenFromContext(ctx) + if err != nil { + utils.Sugar.Errorf("get token from context error") + return "" + } + return ParseToken(tokenStr) +} + +// AuthToken 自定义认证 客户端使用 +type AuthToken struct { + Token string +} + +func (c AuthToken) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { + return map[string]string{ + "authorization": c.Token, + }, nil +} + +func (c AuthToken) RequireTransportSecurity() bool { + return conf.GlobalConf.TLS.Status +} \ No newline at end of file diff --git a/src/components/jwt/jwt_test.go b/src/components/jwt/jwt_test.go new file mode 100644 index 0000000..ecc2eb7 --- /dev/null +++ b/src/components/jwt/jwt_test.go @@ -0,0 +1,18 @@ +package jwt + +import ( + "fmt" + "pro2d/protos/pb" + "testing" +) + +func TestCreateToken(t *testing.T) { + account := &pb.Account{ + Phone: "17683852936", + Password: "123456", + Uid: "12312", + } + token := CreateToken(account.Uid) + ac := ParseToken(token) + fmt.Println("token: ", token, "\nac: ", ac) +} diff --git a/src/components/logger/README.md b/src/components/logger/README.md new file mode 100644 index 0000000..43d62bd --- /dev/null +++ b/src/components/logger/README.md @@ -0,0 +1,143 @@ +# logger +convenient log package + +# 1. 使用说明 +```go + import "github.com/wonderivan/logger" + + // 配置logger,如果不配置时默认为控制台输出,等级为DEBG + logger.SetLogger(`{"Console": {"level": "DEBG"}`) + // 配置说明见下文 + + // 设置完成后,即可在控制台和日志文件app.log中看到如下输出 + logger.Trace("this is Trace") + logger.Debug("this is Debug") + logger.Info("this is Info") + logger.Warn("this is Warn") + logger.Error("this is Error") + logger.Crit("this is Critical") + logger.Alert("this is Alert") + logger.Emer("this is Emergency") +``` +输出结果: + +![](images/output1.png) + +# 2. 日志等级 + +当前日志输出等级共8种,从0-7对应的等级由高到底,当配置为某个输出等级时,只有大于等于该等级的日志才会输出。不同的输出适配器支持不同的日志等级配置: + +| 等级 | 配置 | 释义 | 控制台颜色 | +| ---- | ---- | ------------------------------------------------ | ---------- | +| 0 | EMER | 系统级紧急,比如磁盘出错,内存异常,网络不可用等 | 红色底 | +| 1 | ALRT | 系统级警告,比如数据库访问异常,配置文件出错等 | 紫色 | +| 2 | CRIT | 系统级危险,比如权限出错,访问异常等 | 蓝色 | +| 3 | EROR | 用户级错误 | 红色 | +| 4 | WARN | 用户级警告 | 黄色 | +| 5 | INFO | 用户级重要 | 天蓝色 | +| 6 | DEBG | 用户级调试 | 绿色 | +| 7 | TRAC | 用户级基本输出 | 绿色 | + + +# 3. 配置说明 +logger当前支持控制台、文件、网络3种方式适配器输出,可以通过各自的参数进行设置,该logger支持多个方式同时输出,如果未配置某项适配器时,则不初始化也不会输出到该适配器。 + +通过调用logger.SetLogger(config string)方法设置参数,config支持json配置,也支持指定内容为json配置的文件路径,例如: +```go + // 通过配置参数直接配置 + logger.SetLogger(`{"Console": {"level": "DEBG"}}`) + // 通过配置文件配置 + logger.SetLogger("/home/log.json") + +``` + +```json +{ + "TimeFormat":"2006-01-02 15:04:05", // 输出日志开头时间格式 + "Console": { // 控制台日志配置 + "level": "TRAC", // 控制台日志输出等级 + "color": true // 控制台日志颜色开关 + }, + "File": { // 文件日志配置 + "filename": "app.log", // 初始日志文件名 + "level": "TRAC", // 日志文件日志输出等级 + "daily": true, // 跨天后是否创建新日志文件,当append=true时有效 + "maxlines": 1000000, // 日志文件最大行数,当append=true时有效 + "maxsize": 1, // 日志文件最大大小,当append=true时有效 + "maxdays": -1, // 日志文件有效期 + "append": true, // 是否支持日志追加 + "permit": "0660" // 新创建的日志文件权限属性 + }, + "Conn": { // 网络日志配置 + "net":"tcp", // 日志传输模式 + "addr":"10.1.55.10:1024", // 日志接收服务器 + "level": "Warn", // 网络日志输出等级 + "reconnect":true, // 网络断开后是否重连 + "reconnectOnMsg":false, // 发送完每条消息后是否断开网络 + } +} +``` + +- 时间格式 + +| 时间类型 | 时间格式 | +| ------------ | ----------------------------------------- | +| ANSIC | "Mon Jan _2 15:04:05 2006" | +| UnixDate | "Mon Jan _2 15:04:05 MST 2006" | +| RubyDate | "Mon Jan 02 15:04:05 -0700 2006" | +| RFC822 | "02 Jan 06 15:04 MST" | +| RFC822Z | "02 Jan 06 15:04 -0700" | +| RFC850 | "Monday, 02-Jan-06 15:04:05 MST" | +| RFC1123 | "Mon, 02 Jan 2006 15:04:05 MST" | +| RFC1123Z | "Mon, 02 Jan 2006 15:04:05 -0700" | +| RFC3339 | "2006-01-02T15:04:05Z07:00" | +| RFC3339Nano | "2006-01-02T15:04:05.999999999Z07:00" | +| Kitchen | "3:04PM" | +| Stamp | "Jan _2 15:04:05" | +| StampMilli | "Jan _2 15:04:05.000" | +| StampMicro | "Jan _2 15:04:05.000000" | +| StampNano | "Jan _2 15:04:05.000000000" | +| RFC3339Nano1 | "2006-01-02 15:04:05.999999999 -0700 MST" | +| DEFAULT | "2006-01-02 15:04:05" | + +- 时间格式打印: +``` +========RFC1123Z time format======== +Thu, 02 Aug 2018 18:48:04 +0800 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC1123Z +========Stamp time format======== +Aug 2 18:48:04 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug Stamp +========StampMilli time format======== +Aug 2 18:48:04.489 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug StampMilli +========StampNano time format======== +Aug 2 18:48:04.490002155 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug StampNano +========RubyDate time format======== +Thu Aug 02 18:48:04 +0800 2018 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RubyDate +========RFC822 time format======== +02 Aug 18 18:48 CST [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC822 +========RFC822Z time format======== +02 Aug 18 18:48 +0800 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC822Z +========RFC1123 time format======== +Thu, 02 Aug 2018 18:48:04 CST [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC1123 +========RFC3339 time format======== +2018-08-02T18:48:04+08:00 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC3339 +========RFC3339Nano time format======== +2018-08-02T18:48:04.490377325+08:00 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC3339Nano +========ANSIC time format======== +Thu Aug 2 18:48:04 2018 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug ANSIC +========UnixDate time format======== +Thu Aug 2 18:48:04 CST 2018 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug UnixDate +========RFC850 time format======== +Thursday, 02-Aug-18 18:48:04 CST [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC850 +========Kitchen time format======== +6:48PM [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug Kitchen +========StampMicro time format======== +Aug 2 18:48:04.490662 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug StampMicro +``` + +# 4. 其他 + +1. logger默认是控制台输出,输出等级为DEBG,默认是支持颜色区分的。 +2. 日志文件append为true时,当写入的日志文件发生跨天(daily为true)或超过最大限制时,会创建一个新文件,原有文件格式被重命名为: ****.xxxx-xx-xx.xxx.xxx 格式,例如:当向app.log写入日志时,触发了创建新文件操作,则将app.log重命名为 app.2018-01-01.001.log, 如果此时app.2018-01-01.001.log已经存在,则将刚才的app.log重命名为 app.2018-01-01.002.log,以此类推。 +3. logger package默认初始化了全局的defaultLogger,直接调用logger包的Debug方法时,会默认调用defaultLogger.Debug,所以普通调用时,仅需要import logger即可使用。 +4. 网络配置中的reconnectOnMsg为每条消息都重连一次网络日志中心,适用于写日志频率极低的情况下的服务调用,避免长时间连接,占用资源。但强烈不建议普通使用时设置为true,这将会导致调用方反复的网络重连,极大增加资源消耗和延迟。 +5. conn网络输出适配器经过ELK集成环境的测试验证,通过该方式发送的日志,能够正常通过Elecsearch和Kibana检索和分析 \ No newline at end of file diff --git a/src/components/logger/conn.go b/src/components/logger/conn.go new file mode 100644 index 0000000..0c26efa --- /dev/null +++ b/src/components/logger/conn.go @@ -0,0 +1,115 @@ +package logger + +import ( + "encoding/json" + "fmt" + "io" + "net" + "strings" + "sync" + "time" +) + +type connLogger struct { + sync.Mutex + innerWriter io.WriteCloser + Net string `json:"net"` + Addr string `json:"addr"` + Level string `json:"level"` + LogLevel int + illNetFlag bool //网络异常标记 +} + +func (c *connLogger) Init(jsonConfig string, appName string) error { + if len(jsonConfig) == 0 { + return nil + } + //fmt.Printf("consoleWriter Init:%s\n", jsonConfig) + err := json.Unmarshal([]byte(jsonConfig), c) + if err != nil { + return err + } + if l, ok := LevelMap[c.Level]; ok { + c.LogLevel = l + } + if c.innerWriter != nil { + c.innerWriter.Close() + c.innerWriter = nil + } + + go func() { + for { + c.connect() + time.Sleep(10*time.Millisecond) + } + }() + + return nil +} + +func (c *connLogger) LogWrite(when time.Time, msgText interface{}, level int) (err error) { + if level > c.LogLevel { + return nil + } + + msg, ok := msgText.(*loginfo) + if !ok { + return + } + + if c.innerWriter != nil { + err = c.println(when, msg) + //网络异常,通知处理网络的go程自动重连 + if err != nil { + c.innerWriter.Close() + c.innerWriter = nil + } + } + + return +} + +func (c *connLogger) Destroy() { + if c.innerWriter != nil { + c.innerWriter.Close() + } +} + +func (c *connLogger) connect() error { + if c.innerWriter != nil { + return nil + } + addrs := strings.Split(c.Addr, ";") + for _, addr := range addrs { + conn, err := net.DialTimeout(c.Net, addr, 1 * time.Second) + if err != nil { + fmt.Printf("net.Dial error:%v\n", err) + //continue + return err + } + + if tcpConn, ok := conn.(*net.TCPConn); ok { + tcpConn.SetKeepAlive(true) + } + c.innerWriter = conn + return nil + } + return fmt.Errorf("hava no valid logs service addr:%v", c.Addr) +} + +func (c *connLogger) println(when time.Time, msg *loginfo) error { + c.Lock() + defer c.Unlock() + ss, err := json.Marshal(msg) + if err != nil { + return err + } + _, err = c.innerWriter.Write(append(ss, '\n')) + + //返回err,解决日志系统网络异常后的自动重连 + return err +} + +func init() { + Register(AdapterConn, &connLogger{LogLevel: LevelTrace}) +} diff --git a/src/components/logger/console.go b/src/components/logger/console.go new file mode 100644 index 0000000..691d308 --- /dev/null +++ b/src/components/logger/console.go @@ -0,0 +1,92 @@ +package logger + +import ( + "encoding/json" + //"fmt" + "os" + "runtime" + "sync" + "time" +) + +type brush func(string) string + +func newBrush(color string) brush { + pre := "\033[" + reset := "\033[0m" + return func(text string) string { + return pre + color + "m" + text + reset + } +} + +//鉴于终端的通常使用习惯,一般白色和黑色字体是不可行的,所以30,37不可用, +var colors = []brush{ + newBrush("1;41"), // Emergency 红色底 + newBrush("1;35"), // Alert 紫色 + newBrush("1;34"), // Critical 蓝色 + newBrush("1;31"), // Error 红色 + newBrush("1;33"), // Warn 黄色 + newBrush("1;36"), // Informational 天蓝色 + newBrush("1;32"), // Debug 绿色 + newBrush("1;32"), // Trace 绿色 +} + +type consoleLogger struct { + sync.Mutex + Level string `json:"level"` + Colorful bool `json:"color"` + LogLevel int +} + +func (c *consoleLogger) Init(jsonConfig string, appName string) error { + if len(jsonConfig) == 0 { + return nil + } + if jsonConfig != "{}" { + //fmt.Fprintf(os.Stdout, "consoleLogger Init:%s\n", jsonConfig) + } + + err := json.Unmarshal([]byte(jsonConfig), c) + if runtime.GOOS == "windows" { + c.Colorful = false + } + + if l, ok := LevelMap[c.Level]; ok { + c.LogLevel = l + return nil + } + + return err +} + +func (c *consoleLogger) LogWrite(when time.Time, msgText interface{}, level int) error { + if level > c.LogLevel { + return nil + } + msg, ok := msgText.(string) + if !ok { + return nil + } + if c.Colorful { + msg = colors[level](msg) + } + c.printlnConsole(when, msg) + return nil +} + +func (c *consoleLogger) Destroy() { + +} + +func (c *consoleLogger) printlnConsole(when time.Time, msg string) { + c.Lock() + defer c.Unlock() + os.Stdout.Write(append([]byte(msg), '\n')) +} + +func init() { + Register(AdapterConsole, &consoleLogger{ + LogLevel: LevelDebug, + Colorful: runtime.GOOS != "windows", + }) +} diff --git a/src/components/logger/file.go b/src/components/logger/file.go new file mode 100644 index 0000000..e2a94e9 --- /dev/null +++ b/src/components/logger/file.go @@ -0,0 +1,288 @@ +package logger + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" +) + +type fileLogger struct { + sync.RWMutex + fileWriter *os.File + + Filename string //`json:"filename"` + Append bool `json:"append"` + MaxLines int `json:"maxlines"` + MaxSize int `json:"maxsize"` + Daily bool `json:"daily"` + MaxDays int64 `json:"maxdays"` + Level string `json:"level"` + PermitMask string `json:"permit"` + + LogLevel int + maxSizeCurSize int + maxLinesCurLines int + dailyOpenDate int + dailyOpenTime time.Time + fileNameOnly, suffix string +} + +// Init file logger with json config. +// jsonConfig like: +// { +// "filename":"log/app.log", +// "maxlines":10000, +// "maxsize":1024, +// "daily":true, +// "maxdays":15, +// "rotate":true, +// "permit":"0600" +// } +func (f *fileLogger) Init(jsonConfig string, appName string) error { + //fmt.Printf("fileLogger Init:%s\n", jsonConfig) + if len(jsonConfig) == 0 { + return nil + } + err := json.Unmarshal([]byte(jsonConfig), f) + if err != nil { + return err + } + f.Filename = appName + if len(f.Filename) == 0 { + return errors.New("jsonconfig must have filename") + } + f.suffix = filepath.Ext(f.Filename) + f.fileNameOnly = strings.TrimSuffix(f.Filename, f.suffix) + f.MaxSize *= 1024 * 1024 // 将单位转换成MB + if f.suffix == "" { + f.suffix = ".log" + f.Filename += f.suffix + } + if l, ok := LevelMap[f.Level]; ok { + f.LogLevel = l + } + err = f.newFile() + return err +} + +func (f *fileLogger) needCreateFresh(size int, day int) bool { + return (f.MaxLines > 0 && f.maxLinesCurLines >= f.MaxLines) || + (f.MaxSize > 0 && f.maxSizeCurSize+size >= f.MaxSize) || + (f.Daily && day != f.dailyOpenDate) + +} + +// WriteMsg write logger message into file. +func (f *fileLogger) LogWrite(when time.Time, msgText interface{}, level int) error { + msg, ok := msgText.(string) + if !ok { + return nil + } + if level > f.LogLevel { + return nil + } + + day := when.Day() + msg += "\n" + if f.Append { + f.RLock() + if f.needCreateFresh(len(msg), day) { + f.RUnlock() + f.Lock() + if f.needCreateFresh(len(msg), day) { + if err := f.createFreshFile(when); err != nil { + fmt.Fprintf(os.Stdout, "createFreshFile(%q): %s\n", f.Filename, err) + } + } + f.Unlock() + } else { + f.RUnlock() + } + } + + f.Lock() + _, err := f.fileWriter.Write([]byte(msg)) + if err == nil { + f.maxLinesCurLines++ + f.maxSizeCurSize += len(msg) + } + f.Unlock() + return err +} + +func (f *fileLogger) createLogFile() (*os.File, error) { + // Open the log file + perm, err := strconv.ParseInt(f.PermitMask, 8, 64) + if err != nil { + return nil, err + } + fd, err := os.OpenFile(f.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm)) + if err == nil { + // Make sure file perm is user set perm cause of `os.OpenFile` will obey umask + os.Chmod(f.Filename, os.FileMode(perm)) + } + return fd, err +} + +func (f *fileLogger) newFile() error { + file, err := f.createLogFile() + if err != nil { + return err + } + if f.fileWriter != nil { + f.fileWriter.Close() + } + f.fileWriter = file + + fInfo, err := file.Stat() + if err != nil { + return fmt.Errorf("get stat err: %s", err) + } + f.maxSizeCurSize = int(fInfo.Size()) + f.dailyOpenTime = time.Now() + f.dailyOpenDate = f.dailyOpenTime.Day() + f.maxLinesCurLines = 0 + if f.maxSizeCurSize > 0 { + count, err := f.lines() + if err != nil { + return err + } + f.maxLinesCurLines = count + } + return nil +} + +func (f *fileLogger) lines() (int, error) { + fd, err := os.Open(f.Filename) + if err != nil { + return 0, err + } + defer fd.Close() + + buf := make([]byte, 32768) // 32k + count := 0 + lineSep := []byte{'\n'} + + for { + c, err := fd.Read(buf) + if err != nil && err != io.EOF { + return count, err + } + + count += bytes.Count(buf[:c], lineSep) + + if err == io.EOF { + break + } + } + + return count, nil +} + +// new file name like xx.2013-01-01.001.log +func (f *fileLogger) createFreshFile(logTime time.Time) error { + // file exists + // Find the next available number + num := 1 + fName := "" + rotatePerm, err := strconv.ParseInt(f.PermitMask, 8, 64) + if err != nil { + return err + } + + _, err = os.Lstat(f.Filename) + if err != nil { + // 初始日志文件不存在,无需创建新文件 + goto RESTART_LOGGER + } + // 日期变了, 说明跨天,重命名时需要保存为昨天的日期 + if f.dailyOpenDate != logTime.Day() { + for ; err == nil && num <= 999; num++ { + fName = f.fileNameOnly + fmt.Sprintf(".%s.%03d%s", f.dailyOpenTime.Format("2006-01-02"), num, f.suffix) + _, err = os.Lstat(fName) + } + } else { //如果仅仅是文件大小或行数达到了限制,仅仅变更后缀序号即可 + for ; err == nil && num <= 999; num++ { + fName = f.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, f.suffix) + _, err = os.Lstat(fName) + } + } + + if err == nil { + return fmt.Errorf("Cannot find free log number to rename %s", f.Filename) + } + f.fileWriter.Close() + + // 当创建新文件标记为true时 + // 当日志文件超过最大限制行 + // 当日志文件超过最大限制字节 + // 当日志文件隔天更新标记为true时 + // 将旧文件重命名,然后创建新文件 + err = os.Rename(f.Filename, fName) + if err != nil { + fmt.Fprintf(os.Stdout, "os.Rename %s to %s err:%s\n", f.Filename, fName, err.Error()) + goto RESTART_LOGGER + } + + err = os.Chmod(fName, os.FileMode(rotatePerm)) + +RESTART_LOGGER: + + startLoggerErr := f.newFile() + go f.deleteOldLog() + + if startLoggerErr != nil { + return fmt.Errorf("Rotate StartLogger: %s", startLoggerErr) + } + if err != nil { + return fmt.Errorf("Rotate: %s", err) + } + return nil +} + +func (f *fileLogger) deleteOldLog() { + dir := filepath.Dir(f.Filename) + filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stdout, "Unable to delete old log '%s', error: %v\n", path, r) + } + }() + + if info == nil { + return + } + + if f.MaxDays != -1 && !info.IsDir() && info.ModTime().Add(24*time.Hour*time.Duration(f.MaxDays)).Before(time.Now()) { + if strings.HasPrefix(filepath.Base(path), filepath.Base(f.fileNameOnly)) && + strings.HasSuffix(filepath.Base(path), f.suffix) { + os.Remove(path) + } + } + return + }) +} + +func (f *fileLogger) Destroy() { + f.fileWriter.Close() +} + +func init() { + Register(AdapterFile, &fileLogger{ + Daily: true, + MaxDays: 7, + Append: true, + LogLevel: LevelDebug, + PermitMask: "0777", + MaxLines: 10, + MaxSize: 10 * 1024 * 1024, + }) +} diff --git a/src/components/logger/log.go b/src/components/logger/log.go new file mode 100644 index 0000000..79f1b15 --- /dev/null +++ b/src/components/logger/log.go @@ -0,0 +1,472 @@ +package logger + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "runtime" + "strings" + "sync" + "time" +) + +// 默认日志输出 +var defaultLogger *LocalLogger +var appName = "app" + +// 日志等级,从0-7,日优先级由高到低 +const ( + LevelEmergency = iota // 系统级紧急,比如磁盘出错,内存异常,网络不可用等 + LevelAlert // 系统级警告,比如数据库访问异常,配置文件出错等 + LevelCritical // 系统级危险,比如权限出错,访问异常等 + LevelError // 用户级错误 + LevelWarning // 用户级警告 + LevelInformational // 用户级信息 + LevelDebug // 用户级调试 + LevelTrace // 用户级基本输出 +) + +// 日志等级和描述映射关系 +var LevelMap = map[string]int{ + "EMER": LevelEmergency, + "ALRT": LevelAlert, + "CRIT": LevelCritical, + "EROR": LevelError, + "WARN": LevelWarning, + "INFO": LevelInformational, + "DEBG": LevelDebug, + "TRAC": LevelTrace, +} + +// 注册实现的适配器, 当前支持控制台,文件和网络输出 +var adapters = make(map[string]Logger) + +// 日志记录等级字段 +var levelPrefix = [LevelTrace + 1]string{ + "EMER", + "ALRT", + "CRIT", + "EROR", + "WARN", + "INFO", + "DEBG", + "TRAC", +} + +const ( + logTimeDefaultFormat = "2006-01-02 15:04:05" // 日志输出默认格式 + AdapterConsole = "console" // 控制台输出配置项 + AdapterFile = "file" // 文件输出配置项 + AdapterConn = "conn" // 网络输出配置项 +) + +// log provider interface +type Logger interface { + Init(config string, appName string) error + LogWrite(when time.Time, msg interface{}, level int) error + Destroy() +} + +// 日志输出适配器注册,log需要实现Init,LogWrite,Destroy方法 +func Register(name string, log Logger) { + if log == nil { + panic("logs: Register provide is nil") + } + if _, ok := adapters[name]; ok { + panic("logs: Register called twice for provider " + name) + } + adapters[name] = log +} + +type loginfo struct { + Time string + Level string + Path string + Name string + Content string +} + +type nameLogger struct { + Logger + name string + config string +} + +type LocalLogger struct { + lock sync.RWMutex + init bool + outputs []*nameLogger + appName string + callDepth int + timeFormat string + usePath string +} + +func NewLogger(depth ...int) *LocalLogger { + dep := append(depth, 2)[0] + l := new(LocalLogger) + // appName用于记录网络传输时标记的程序发送方, + // 通过环境变量APPSN进行设置,默认为NONE,此时无法通过网络日志检索区分不同服务发送方 + appSn := os.Getenv("APPSN") + if appSn == "" { + appSn = "NONE" + } + l.appName = "[" + appSn + "]" + l.callDepth = dep + l.SetLogger(AdapterConsole) + l.timeFormat = logTimeDefaultFormat + return l +} + +//配置文件 +type logConfig struct { + TimeFormat string `json:"TimeFormat"` + Console *consoleLogger `json:"Console,omitempty"` + File *fileLogger `json:"File,omitempty"` + Conn *connLogger `json:"Conn,omitempty"` +} + +func init() { + defaultLogger = NewLogger(3) +} + +func (this *LocalLogger) SetLogger(adapterName string, configs ...string) error { + this.lock.Lock() + defer this.lock.Unlock() + + if !this.init { + this.outputs = []*nameLogger{} + this.init = true + } + + config := append(configs, "{}")[0] + var num int = -1 + var i int + var l *nameLogger + for i, l = range this.outputs { + if l.name == adapterName { + if l.config == config { + //配置没有变动,不重新设置 + return fmt.Errorf("you have set same config for this adaptername %s", adapterName) + } + l.Logger.Destroy() + num = i + break + } + } + logger, ok := adapters[adapterName] + if !ok { + return fmt.Errorf("unknown adaptername %s (forgotten Register?)", adapterName) + } + + err := logger.Init(config, appName) + if err != nil { + fmt.Fprintf(os.Stdout, "logger Init <%s> err:%v, %s output ignore!\n", + adapterName, err, adapterName) + return err + } + if num >= 0 { + this.outputs[i] = &nameLogger{name: adapterName, Logger: logger, config: config} + return nil + } + this.outputs = append(this.outputs, &nameLogger{name: adapterName, Logger: logger, config: config}) + return nil +} + +func (this *LocalLogger) DelLogger(adapterName string) error { + this.lock.Lock() + defer this.lock.Unlock() + outputs := []*nameLogger{} + for _, lg := range this.outputs { + if lg.name == adapterName { + lg.Destroy() + } else { + outputs = append(outputs, lg) + } + } + if len(outputs) == len(this.outputs) { + return fmt.Errorf("logs: unknown adaptername %s (forgotten Register?)", adapterName) + } + this.outputs = outputs + return nil +} + +// 设置日志起始路径 +func (this *LocalLogger) SetLogPathTrim(trimPath string) { + this.usePath = trimPath +} + +func (this *LocalLogger) writeToLoggers(when time.Time, msg *loginfo, level int) { + this.lock.RLock() + defer this.lock.RUnlock() + for _, l := range this.outputs { + if l.name == AdapterConn { + //网络日志,使用json格式发送,此处使用结构体,用于类似ElasticSearch功能检索 + err := l.LogWrite(when, msg, level) + if err != nil { + fmt.Fprintf(os.Stdout, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err) + } + continue + } + + msgStr := when.Format(this.timeFormat) + " [" + msg.Level + "] " + "[" + msg.Path + "] " + msg.Content + err := l.LogWrite(when, msgStr, level) + if err != nil { + fmt.Fprintf(os.Stdout, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err) + } + } +} + +func (this *LocalLogger) writeMsg(logLevel int, msg string, v ...interface{}) error { + if !this.init { + this.SetLogger(AdapterConsole) + } + msgSt := new(loginfo) + src := "" + if len(v) > 0 { + msg = fmt.Sprintf(msg, v...) + } + when := time.Now() + _, file, lineno, ok := runtime.Caller(this.callDepth) + var strim string = "src/" + if this.usePath != "" { + strim = this.usePath + } + if ok { + + src = strings.Replace( + fmt.Sprintf("%s:%d", stringTrim(file, strim), lineno), "%2e", ".", -1) + } + + msgSt.Level = levelPrefix[logLevel] + msgSt.Path = src + msgSt.Content = msg + msgSt.Name = this.appName + msgSt.Time = when.Format(this.timeFormat) + this.writeToLoggers(when, msgSt, logLevel) + + return nil +} + +func (this *LocalLogger) Fatal(format string, args ...interface{}) { + this.Emer("###Exec Panic:"+format, args...) + os.Exit(1) +} + +func (this *LocalLogger) Panic(format string, args ...interface{}) { + this.Emer("###Exec Panic:"+format, args...) + panic(fmt.Sprintf(format, args...)) +} + +// Emer Log EMERGENCY level message. +func (this *LocalLogger) Emer(format string, v ...interface{}) { + this.writeMsg(LevelEmergency, format, v...) +} + +// Alert Log ALERT level message. +func (this *LocalLogger) Alert(format string, v ...interface{}) { + this.writeMsg(LevelAlert, format, v...) +} + +// Crit Log CRITICAL level message. +func (this *LocalLogger) Crit(format string, v ...interface{}) { + this.writeMsg(LevelCritical, format, v...) +} + +// Error Log ERROR level message. +func (this *LocalLogger) Error(format string, v ...interface{}) { + this.writeMsg(LevelError, format, v...) +} + +// Warn Log WARNING level message. +func (this *LocalLogger) Warn(format string, v ...interface{}) { + this.writeMsg(LevelWarning, format, v...) +} + +// Info Log INFO level message. +func (this *LocalLogger) Info(format string, v ...interface{}) { + this.writeMsg(LevelInformational, format, v...) +} + +// Debug Log DEBUG level message. +func (this *LocalLogger) Debug(format string, v ...interface{}) { + this.writeMsg(LevelDebug, format, v...) +} + +// Trace Log TRAC level message. +func (this *LocalLogger) Trace(format string, v ...interface{}) { + this.writeMsg(LevelTrace, format, v...) +} + +func (this *LocalLogger) Close() { + + for _, l := range this.outputs { + l.Destroy() + } + this.outputs = nil + +} + +func (this *LocalLogger) Reset() { + this.lock.Lock() + defer this.lock.Unlock() + for _, l := range this.outputs { + l.Destroy() + } + this.outputs = nil +} + +func (this *LocalLogger) SetCallDepth(depth int) { + this.callDepth = depth +} + +// GetlocalLogger returns the defaultLogger +func GetlocalLogger() *LocalLogger { + return defaultLogger +} + +// Reset will remove all the adapter +func Reset() { + defaultLogger.Reset() +} + +func SetLogPathTrim(trimPath string) { + defaultLogger.SetLogPathTrim(trimPath) +} + +// param 可以是log配置文件名,也可以是log配置内容,默认DEBUG输出到控制台 +func SetLogger(param ...string) error { + if 0 == len(param) { + //默认只输出到控制台 + defaultLogger.SetLogger(AdapterConsole) + return nil + } + + c := param[0] + if len(param) > 1 { + appName = param[1] + } + conf := new(logConfig) + err := json.Unmarshal([]byte(c), conf) + if err != nil { //不是json,就认为是配置文件,如果都不是,打印日志,然后退出 + // Open the configuration file + fd, err := os.Open(c) + if err != nil { + fmt.Fprintf(os.Stdout, "Could not open %s for configure: %s\n", c, err) + os.Exit(1) + return err + } + + contents, err := ioutil.ReadAll(fd) + if err != nil { + fmt.Fprintf(os.Stdout, "Could not read %s: %s\n", c, err) + os.Exit(1) + return err + } + err = json.Unmarshal(contents, conf) + if err != nil { + fmt.Fprintf(os.Stdout, "Could not Unmarshal %s: %s\n", contents, err) + os.Exit(1) + return err + } + } + if conf.TimeFormat != "" { + defaultLogger.timeFormat = conf.TimeFormat + } + if conf.Console != nil { + console, _ := json.Marshal(conf.Console) + defaultLogger.SetLogger(AdapterConsole, string(console)) + } + if conf.File != nil { + file, _ := json.Marshal(conf.File) + defaultLogger.SetLogger(AdapterFile, string(file)) + } + if conf.Conn != nil { + conn, _ := json.Marshal(conf.Conn) + defaultLogger.SetLogger(AdapterConn, string(conn)) + } + return nil +} + +// Painc logs a message at emergency level and panic. +func Painc(f interface{}, v ...interface{}) { + defaultLogger.Panic(formatLog(f, v...)) +} + +// Fatal logs a message at emergency level and exit. +func Fatal(f interface{}, v ...interface{}) { + defaultLogger.Fatal(formatLog(f, v...)) +} + +// Emer logs a message at emergency level. +func Emer(f interface{}, v ...interface{}) { + defaultLogger.Emer(formatLog(f, v...)) +} + +// Alert logs a message at alert level. +func Alert(f interface{}, v ...interface{}) { + defaultLogger.Alert(formatLog(f, v...)) +} + +// Crit logs a message at critical level. +func Crit(f interface{}, v ...interface{}) { + defaultLogger.Crit(formatLog(f, v...)) +} + +// Error logs a message at error level. +func Error(f interface{}, v ...interface{}) { + defaultLogger.Error(formatLog(f, v...)) +} + +// Warn logs a message at warning level. +func Warn(f interface{}, v ...interface{}) { + defaultLogger.Warn(formatLog(f, v...)) +} + +// Info logs a message at info level. +func Info(f interface{}, v ...interface{}) { + defaultLogger.Info(formatLog(f, v...)) +} + +// Notice logs a message at debug level. +func Debug(f interface{}, v ...interface{}) { + defaultLogger.Debug(formatLog(f, v...)) +} + +// Trace logs a message at trace level. +func Trace(f interface{}, v ...interface{}) { + defaultLogger.Trace(formatLog(f, v...)) +} + +func formatLog(f interface{}, v ...interface{}) string { + var msg string + switch f.(type) { + case string: + msg = f.(string) + if len(v) == 0 { + return msg + } + if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") { + //format string + } else { + //do not contain format char + msg += strings.Repeat(" %v", len(v)) + } + default: + msg = fmt.Sprint(f) + if len(v) == 0 { + return msg + } + msg += strings.Repeat(" %v", len(v)) + } + return fmt.Sprintf(msg, v...) +} + +func stringTrim(s string, cut string) string { + ss := strings.SplitN(s, cut, 2) + if 1 == len(ss) { + return ss[0] + } + return ss[1] +} diff --git a/src/components/net/conn.go b/src/components/net/conn.go new file mode 100644 index 0000000..ffe00e1 --- /dev/null +++ b/src/components/net/conn.go @@ -0,0 +1,123 @@ +package net + +import ( + "bufio" + "fmt" + "net" + "pro2d/src/common" + "pro2d/src/components/logger" +) + +type Head struct { + Length int32 + Cmd int32 + ErrCode int32 + PreField int32 +} + + +type Connection struct { + net.Conn + Id int + Server *Server + + scanner *bufio.Scanner + writer *bufio.Writer + + WBuffer chan []byte + RBuffer chan *MsgPkg + + Quit chan *Connection +} + +type MsgPkg struct { + Head *Head + Body []byte + Conn *Connection +} + +func NewConn(id int, conn net.Conn, s *Server) *Connection { + return &Connection{ + Id: id, + Conn: conn, + Server: s, + + scanner: bufio.NewScanner(conn), + writer: bufio.NewWriter(conn), + WBuffer: make(chan []byte), + RBuffer: make(chan *MsgPkg), + Quit: make(chan *Connection), + } +} + +func (c *Connection) write() { + defer c.Quiting() + + for msg := range c.WBuffer { + if _, err := c.writer.Write(msg); err != nil { + fmt.Println("write fail err: " + err.Error()) + return + } + if err := c.writer.Flush(); err != nil { + fmt.Println("write Flush fail err: " + err.Error()) + return + } + } +} + +func (c *Connection) read() { + defer c.Quiting() + for { + 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() + return + } + } +} + +func (c *Connection) Start() { + go c.write() + go c.read() +} + +func (c *Connection) Stop() { + close(c.RBuffer) + close(c.WBuffer) + c.Conn.Close() +} + +func (c *Connection) Quiting() { + c.Server.OnClose(c) +} + +func (c *Connection) SendMsgByCode(errCode int32, cmd int32, data []byte){ + h := &Head{ + Length: int32(common.HEADLEN + len(data)), + Cmd: cmd, + ErrCode: errCode, + PreField: 0, + } + pkg := &MsgPkg{ + Head: h, + Body: data, + } + buf, err := EncodeMsg(pkg) + if err != nil { + logger.Error("SendMsg error: %v", err) + return + } + c.WBuffer <- buf +} \ No newline at end of file diff --git a/src/components/net/msg.go b/src/components/net/msg.go new file mode 100644 index 0000000..2c5d330 --- /dev/null +++ b/src/components/net/msg.go @@ -0,0 +1,57 @@ +package net + +import ( + "bytes" + "encoding/binary" + "fmt" + "pro2d/src/common" +) + +func ParseMsg (data []byte, atEOF bool) (advance int, token []byte, err error) { + // 表示我们已经扫描到结尾了 + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if !atEOF && len(data) >= common.HEADLEN { //4字节数据包长度 4字节指令 + length := int32(0) + binary.Read(bytes.NewReader(data[0:4]), binary.BigEndian, &length) + if length <= 0 { + return 0, nil, fmt.Errorf("length is 0") + } + if int(length) <= len(data) { + return int(length) , data[:int(length)], nil + } + return 0 , nil, nil + } + if atEOF { + return len(data), data, nil + } + return 0, nil, nil +} + + +func DecodeMsg(data []byte) (*MsgPkg, error) { + h := &Head{} + err := binary.Read(bytes.NewReader(data), binary.BigEndian, h) + if err != nil { + return nil, err + } + return &MsgPkg{ + Head: h, + Body: data[common.HEADLEN:], + },nil +} + +func EncodeMsg(pkg *MsgPkg) ([]byte, error) { + buf := &bytes.Buffer{} + err := binary.Write(buf, binary.BigEndian, pkg.Head) + if err != nil { + return nil, err + } + + err = binary.Write(buf, binary.BigEndian, pkg.Body) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/src/components/net/server.go b/src/components/net/server.go new file mode 100644 index 0000000..59289e6 --- /dev/null +++ b/src/components/net/server.go @@ -0,0 +1,84 @@ +package net + +import ( + "fmt" + "github.com/golang/protobuf/proto" + "net" + "pro2d/conf" + "pro2d/protos/pb" + "pro2d/src/components/db" + "pro2d/src/components/etcd" + "pro2d/src/components/logger" + "pro2d/src/models" + "sync" +) + +type ActionHandler func (msg *MsgPkg) (int32, proto.Message) +var ActionMap map[pb.ProtoCode]ActionHandler + +type Server struct { + SConf *conf.SConf + Clients *sync.Map + EtcdClient *etcd.EtcdClient + +} + +func NewServer(sConf *conf.SConf) *Server { + return &Server{ + SConf: sConf, + Clients: new(sync.Map), + } +} + +func (s *Server) OnRecv(msg *MsgPkg) { + logger.Debug("cmd: %d, data: %s", msg.Head.Cmd, msg.Body) + if md, ok := ActionMap[pb.ProtoCode(msg.Head.Cmd)]; ok { + errCode, protomsg := md(msg) + rsp, err := proto.Marshal(protomsg) + if err != nil { + msg.Conn.SendMsgByCode(-100, msg.Head.Cmd, nil) + return + } + msg.Conn.SendMsgByCode(errCode, msg.Head.Cmd, rsp) + return + } + logger.Error("protocode not handler: %d", msg.Head.Cmd) +} + +func (s *Server) OnClose(conn *Connection) { + s.Clients.Delete(conn.Id) +} + +func (s *Server)Start() error { + //mongo 初始化 + db.MongoDatabase = db.MongoClient.Database(conf.GlobalConf.AccountConf.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) + + port := fmt.Sprintf(":%d", s.SConf.Port) + l, err := net.Listen("tcp", port) + if err != nil { + return err + } + + logger.Debug("listen on %s\n", port) + id := 0 + for { + conn, err := l.Accept() + if err != nil { + return err + } + + id++ + client := NewConn(id, conn, s) + s.Clients.Store(id, client) + go client.Start() + } +} + +func (s *Server)Stop() { +} + diff --git a/src/models/account.go b/src/models/account.go new file mode 100644 index 0000000..c337784 --- /dev/null +++ b/src/models/account.go @@ -0,0 +1,31 @@ +package models + +import ( + "pro2d/protos/pb" + "pro2d/src/components/db" +) + +type AccountModel struct { + *db.MgoColl + *pb.Account +} + +func AccountExistByPhone(phone string) (bool, *AccountModel){ + m := NewAccount(phone) + if err := m.Load(); err != nil { + return false, m + } + return true, m +} + +func NewAccount(phone string) *AccountModel { + ac := &pb.Account{ + Phone: phone, + } + account := &AccountModel{ + MgoColl: db.NewMongoColl(phone, ac), + Account: ac, + } + + return account +} \ No newline at end of file diff --git a/src/models/equip.go b/src/models/equip.go new file mode 100644 index 0000000..790e3a1 --- /dev/null +++ b/src/models/equip.go @@ -0,0 +1,24 @@ +package models + +import ( + "pro2d/protos/pb" + "pro2d/src/components/db" + "strconv" +) + +type EquipModels struct { + *db.MgoColl + Equip *pb.Equipment +} + +func NewEquip(id int64) *EquipModels { + data := &pb.Equipment{ + Id: id, + } + m := &EquipModels{ + MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), + Equip: data, + } + + return m +} \ No newline at end of file diff --git a/src/models/hero.go b/src/models/hero.go new file mode 100644 index 0000000..7ce56a2 --- /dev/null +++ b/src/models/hero.go @@ -0,0 +1,32 @@ +package models + +import ( + "pro2d/protos/pb" + "pro2d/src/components/db" + "strconv" +) + +type HeroModel struct { + *db.MgoColl + Hero *pb.Hero +} +type HeroMap map[string]*HeroModel + +func GetHeros(hm HeroMap) map[string]*pb.Hero { + h := make(map[string]*pb.Hero) + for k, v := range hm { + h[k] = v.Hero + } + return h +} + +func NewHero(id int64) *HeroModel { + h := &pb.Hero{ + Id: id, + } + m := &HeroModel{ + MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), h), + Hero: h, + } + return m +} diff --git a/src/models/init.go b/src/models/init.go new file mode 100644 index 0000000..dded13b --- /dev/null +++ b/src/models/init.go @@ -0,0 +1,40 @@ +package models + +import ( + "pro2d/protos/pb" + "pro2d/src/components/db" + "pro2d/src/components/logger" + "pro2d/src/utils" +) + +func InitDoc(schema ...interface{}) { + for _, s := range schema { + coll, keys := utils.FindIndex(s) + for _, index := range keys { + db.CreateCollection(coll) + + logger.Debug("InitDoc collect: %v, createIndex: %s", coll, index) + res, err := db.SetUnique(coll, index) + if err != nil { + logger.Error("InitDoc unique: %s, err: %v", res, err) + continue + } + } + } +} + +func InitAccountServerModels() { + var schema []interface{} = []interface{}{ + pb.Account{}, + } + InitDoc(schema...) +} + +func InitGameServerModels() { + var schema []interface{} = []interface{}{ + pb.Hero{}, + pb.Role{}, + pb.Team{}, + } + InitDoc(schema...) +} \ No newline at end of file diff --git a/src/models/init_test.go b/src/models/init_test.go new file mode 100644 index 0000000..f50fa63 --- /dev/null +++ b/src/models/init_test.go @@ -0,0 +1,20 @@ +package models + +import ( + "context" + _ "pro2d/conf" + "pro2d/src/components/db" + "testing" +) + + +func TestInitModels(t *testing.T) { + + db.MongoDatabase = db.MongoClient.Database("account") + InitAccountServerModels() + db.MongoClient.Disconnect(context.TODO()) + + db.MongoDatabase = db.MongoClient.Database("game") + InitGameServerModels() + db.MongoClient.Disconnect(context.TODO()) +} \ No newline at end of file diff --git a/src/models/prop.go b/src/models/prop.go new file mode 100644 index 0000000..ce3901c --- /dev/null +++ b/src/models/prop.go @@ -0,0 +1,24 @@ +package models + +import ( + "pro2d/protos/pb" + "pro2d/src/components/db" + "strconv" +) + +type PropModels struct { + *db.MgoColl + Prop *pb.Prop +} + +func NewProp(id int64) *PropModels { + data := &pb.Prop{ + Id: id, + } + m := &PropModels{ + MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), + Prop: data, + } + + return m +} \ No newline at end of file diff --git a/src/models/role.go b/src/models/role.go new file mode 100644 index 0000000..8d518ea --- /dev/null +++ b/src/models/role.go @@ -0,0 +1,55 @@ +package models + +import ( + "fmt" + "pro2d/protos/pb" + "pro2d/src/components/db" + "pro2d/src/components/logger" + "strconv" +) + +type RoleModel struct { + *db.MgoColl + Role *pb.Role + Heros HeroMap + Teams *TeamModel + Equip *pb.Equipment + Prop *pb.Prop +} + +func RoleExistByUid(uid string) *RoleModel { + data := &pb.Role{Uid: uid} + + if err := db.FindOne(db.GetBsonM("uid", uid), data); err != nil { + logger.Error("Role exist err: %v", err) + return nil + } + + return &RoleModel{ + MgoColl: db.NewMongoColl(strconv.Itoa(int(data.Id)), data), + Role: data, + } +} + +func NewRole(id int64) *RoleModel { + data := &pb.Role{Id: id} + m := &RoleModel{ + MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), + Role: data, + Heros: make(HeroMap), + } + return m +} + +func (m *RoleModel) LoadAll() { +} + +func (m *RoleModel) LoadHero() { +} + +func (m *RoleModel) AddHero(hero *pb.Hero) { + h := NewHero(hero.Id) + h.Hero = hero + h.Create() + m.Heros[fmt.Sprintf("%d%d", m.Role.Id, h.Hero.Id)] = h +} \ No newline at end of file diff --git a/src/models/role_test.go b/src/models/role_test.go new file mode 100644 index 0000000..09639ba --- /dev/null +++ b/src/models/role_test.go @@ -0,0 +1,48 @@ +package models + +import ( + "fmt" + "pro2d/conf" + "pro2d/protos/pb" + "pro2d/src/components/db" + "pro2d/src/components/logger" + "pro2d/src/utils" + "testing" +) + +func TestNewRole(t *testing.T) { + db.MongoDatabase = db.MongoClient.Database("game") + + var uid = conf.SnowFlack.NextValStr() + role := RoleExistByUid(uid) + if role != nil { + //uid存在 , 更新角色 + role.Role.Device = "222222" + role.AddHero(&pb.Hero{ + Id: 1, + RoleId: role.Role.Id, + Type: 0, + Level: 0, + ReinCount: 0, + ReinPoint: 0, + Equipments: "", + }) + role.Save() + }else { + //uid不存在,创建角色 + role = NewRole(1) + role.Role.Uid = uid + role.Role.Device = "111111" + role.Role.Level = 0 + i, err := role.Create() + fmt.Println(i, err) + } + print(role) +} + +func TestRoleIndex(t *testing.T) { + coll, keys := utils.FindIndex(pb.Role{}) + for _, index := range keys { + logger.Debug("coll: %s, key: %s", coll, index) + } +} \ No newline at end of file diff --git a/src/models/team.go b/src/models/team.go new file mode 100644 index 0000000..0cd93a4 --- /dev/null +++ b/src/models/team.go @@ -0,0 +1,24 @@ +package models + +import ( + "pro2d/protos/pb" + "pro2d/src/components/db" + "strconv" +) + +type TeamModel struct { + *db.MgoColl + Team *pb.Team +} + +func NewTeam(id int64) *TeamModel { + data := &pb.Team{ + Id: id, + } + m := &TeamModel{ + MgoColl: db.NewMongoColl(strconv.Itoa(int(id)), data), + Team: data, + } + + return m +} \ No newline at end of file diff --git a/src/utils/md5.go b/src/utils/md5.go new file mode 100644 index 0000000..ce94156 --- /dev/null +++ b/src/utils/md5.go @@ -0,0 +1,12 @@ +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/snowflake.go b/src/utils/snowflake.go new file mode 100644 index 0000000..3b94ba5 --- /dev/null +++ b/src/utils/snowflake.go @@ -0,0 +1,76 @@ +package utils + +import ( + "fmt" + "github.com/golang/glog" + "sync" + "time" +) + +type Snowflake struct { + *sync.Mutex // 锁 + timestamp int64 // 时间戳 ,毫秒 + workerid int64 // 工作节点 + datacenterid int64 // 数据中心机房id + sequence int64 // 序列号 +} +const ( + epoch = int64(1577808000000) // 设置起始时间(时间戳/毫秒):2020-01-01 00:00:00,有效期69年 + timestampBits = uint(41) // 时间戳占用位数 + datacenteridBits = uint(2) // 数据中心id所占位数 + workeridBits = uint(7) // 机器id所占位数 + sequenceBits = uint(12) // 序列所占的位数 + timestampMax = int64(-1 ^ (-1 << timestampBits)) // 时间戳最大值 + datacenteridMax = int64(-1 ^ (-1 << datacenteridBits)) // 支持的最大数据中心id数量 + workeridMax = int64(-1 ^ (-1 << workeridBits)) // 支持的最大机器id数量 + sequenceMask = int64(-1 ^ (-1 << sequenceBits)) // 支持的最大序列id数量 + workeridShift = sequenceBits // 机器id左移位数 + datacenteridShift = sequenceBits + workeridBits // 数据中心id左移位数 + timestampShift = sequenceBits + workeridBits + datacenteridBits // 时间戳左移位数 +) + + + +func NewSnowflake(workerid, datacenterid int64) *Snowflake { + return &Snowflake{ + Mutex: new(sync.Mutex), + timestamp: time.Now().UnixNano() / 1000000, + workerid: workerid, + datacenterid: datacenterid, + sequence: 0, + } +} + +func (s *Snowflake) NextValStr() string { + return fmt.Sprintf("%d", s.NextVal()) +} + + +func (s *Snowflake) NextVal() int64 { + s.Lock() + now := time.Now().UnixNano() / 1000000 // 转毫秒 + if s.timestamp == now { + // 当同一时间戳(精度:毫秒)下多次生成id会增加序列号 + s.sequence = (s.sequence + 1) & sequenceMask + if s.sequence == 0 { + // 如果当前序列超出12bit长度,则需要等待下一毫秒 + // 下一毫秒将使用sequence:0 + for now <= s.timestamp { + now = time.Now().UnixNano() / 1000000 + } + } + } else { + // 不同时间戳(精度:毫秒)下直接使用序列号:0 + s.sequence = 0 + } + t := now - epoch + if t > timestampMax { + s.Unlock() + glog.Errorf("epoch must be between 0 and %d", timestampMax-1) + return 0 + } + s.timestamp = now + r := int64((t)< timestampMax { - s.Unlock() - glog.Errorf("epoch must be between 0 and %d", timestampMax-1) - return 0 - } - s.timestamp = now - r := int64((t)<