func (ns *numbers) init(path string) { ns.tables = make(map[string]*table) client := etcdclient.GetClient() defer func() { client.Close() }() resp, err := client.Get(path, false, false) if err != nil { log.Critical(err) return } // 解码xlsx xlsx_bin, err := base64.StdEncoding.DecodeString(resp.Node.Value) if err != nil { log.Critical(err) return } // 读取xlsx xlsx_reader, err := xlsx.OpenBinary(xlsx_bin) if err != nil { log.Critical(err) return } ns.parse(xlsx_reader.Sheets) }
func (s *server) init_machine_id() { client := <-s.client_pool defer func() { s.client_pool <- client }() for { // get the key resp, err := client.Get(context.Background(), UUID_KEY, nil) if err != nil { log.Critical(err) os.Exit(-1) } // get prevValue & prevIndex prevValue, err := strconv.Atoi(resp.Node.Value) if err != nil { log.Critical(err) os.Exit(-1) } prevIndex := resp.Node.ModifiedIndex // CompareAndSwap resp, err = client.Set(context.Background(), UUID_KEY, fmt.Sprint(prevValue+1), &etcd.SetOptions{PrevIndex: prevIndex}) if err != nil { cas_delay() continue } // record serial number of this service, already shifted s.machine_id = (uint64(prevValue+1) & MACHINE_ID_MASK) << 12 return } }
// 密钥交换 func P_get_seed_req(sess *Session, reader *packet.Packet) []byte { tbl, _ := PKT_seed_info(reader) // KEY1 X1, E1 := dh.DHExchange() KEY1 := dh.DHKey(X1, big.NewInt(int64(tbl.F_client_send_seed))) // KEY2 X2, E2 := dh.DHExchange() KEY2 := dh.DHKey(X2, big.NewInt(int64(tbl.F_client_receive_seed))) ret := seed_info{int32(E1.Int64()), int32(E2.Int64())} // 服务器加密种子是客户端解密种子 encoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY2))) if err != nil { log.Critical(err) return nil } decoder, err := rc4.NewCipher([]byte(fmt.Sprintf("%v%v", SALT, KEY1))) if err != nil { log.Critical(err) return nil } sess.Encoder = encoder sess.Decoder = decoder sess.Flag |= SESS_KEYEXCG return packet.Pack(Code["get_seed_ack"], ret, nil) }
//---------------------------------------------------------- 产生GridFS文件 func SaveFile(filename string, buf []byte) bool { ms := _global_ms.Copy() defer ms.Close() gridfs := ms.DB("").GridFS("fs") // 首先删除同名文件 err := gridfs.Remove(filename) if err != nil { log.Critical("gridfs", filename, err) return false } // 产生新文件 file, err := gridfs.Create(filename) if err != nil { log.Critical("gridfs", filename, err) return false } n, err := file.Write(buf) if err != nil { log.Critical("gridfs", filename, n, err) return false } err = file.Close() if err != nil { log.Critical("gridfs", filename, err) return false } log.Info("gridfs", filename, "saved to GridFS!!") return true }
func (arch *Archiver) init() { arch.pending = make(chan []byte) arch.stop = make(chan bool) cfg := nsq.NewConfig() consumer, err := nsq.NewConsumer(TOPIC, CHANNEL, cfg) if err != nil { log.Critical(err) os.Exit(-1) } // message process consumer.AddHandler(nsq.HandlerFunc(func(msg *nsq.Message) error { arch.pending <- msg.Body return nil })) // read environtment variable addresses := []string{DEFAULT_NSQLOOKUPD} if env := os.Getenv(ENV_NSQLOOKUPD); env != "" { addresses = strings.Split(env, ";") } // connect to nsqlookupd log.Trace("connect to nsqlookupds ip:", addresses) if err := consumer.ConnectToNSQLookupds(addresses); err != nil { log.Critical(err) return } log.Info("nsqlookupd connected") go arch.archive_task() }
// get next value of a key, like auto-increment in mysql func (s *server) Next(ctx context.Context, in *pb.Snowflake_Key) (*pb.Snowflake_Value, error) { client := <-s.client_pool defer func() { s.client_pool <- client }() key := PATH + in.Name for { // get the key resp, err := client.Get(context.Background(), key, nil) if err != nil { log.Critical(err) return nil, errors.New("Key not exists, need to create first") } // get prevValue & prevIndex prevValue, err := strconv.Atoi(resp.Node.Value) if err != nil { log.Critical(err) return nil, errors.New("marlformed value") } prevIndex := resp.Node.ModifiedIndex // CompareAndSwap resp, err = client.Set(context.Background(), key, fmt.Sprint(prevValue+1), &etcd.SetOptions{PrevIndex: prevIndex}) if err != nil { cas_delay() continue } return &pb.Snowflake_Value{int64(prevValue + 1)}, nil } }
// get next value of a key, like auto-increment in mysql func (s *server) Next(ctx context.Context, in *pb.Snowflake_Key) (*pb.Snowflake_Value, error) { client := s.client_pool.Get().(*etcd.Client) defer func() { s.client_pool.Put(client) }() key := PATH + in.Name for i := 0; i < RETRY_MAX; i++ { // get the key resp, err := client.Get(key, false, false) if err != nil { log.Critical(err) return nil, errors.New("Key not exists, need to create first") } // get prevValue & prevIndex prevValue, err := strconv.Atoi(resp.Node.Value) if err != nil { log.Critical(err) return nil, errors.New("marlformed value") } prevIndex := resp.Node.ModifiedIndex // CAS resp, err = client.CompareAndSwap(key, fmt.Sprint(prevValue+1), 0, resp.Node.Value, prevIndex) if err != nil { log.Error(err) <-time.After(RETRY_DELAY) continue } return &pb.Snowflake_Value{int64(prevValue + 1)}, nil } return nil, errors.New("etcd server busy") }
func (s *server) restore() { // restore data from db file db := s.open_db() defer db.Close() count := 0 db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BOLTDB_BUCKET)) b.ForEach(func(k, v []byte) error { var msg []Chat_Message err := msgpack.Unmarshal(v, &msg) if err != nil { log.Critical("chat data corrupted:", err) os.Exit(-1) } id, err := strconv.ParseUint(string(k), 0, 64) if err != nil { log.Critical("chat data corrupted:", err) os.Exit(-1) } ep := NewEndPoint() ep.inbox = msg s.eps[id] = ep count++ return nil }) return nil }) log.Infof("restored %v chats", count) }
func (s *server) dump(db *bolt.DB, changes map[uint64]bool) { db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BOLTDB_BUCKET)) for k := range changes { ep := s.read_ep(k) if ep == nil { log.Errorf("cannot find endpoint %v", k) continue } // serialization and save bin, err := msgpack.Marshal(ep.Read()) if err != nil { log.Critical("cannot marshal:", err) continue } err = b.Put([]byte(fmt.Sprint(k)), bin) if err != nil { log.Critical(err) continue } } return nil }) }
// 玩家登陆过程 func P_user_login_req(sess *Session, reader *packet.Packet) []byte { // TODO: 登陆鉴权 sess.UserId = 1 // TODO: 选择登陆服务器 sess.GSID = DEFAULT_GSID // 选服 cli, err := sp.GetServiceWithId(sp.SERVICE_GAME, sess.GSID) if err != nil { log.Critical(err) return nil } // type assertion game_cli, ok := cli.(spp.GameServiceClient) if !ok { log.Critical("cannot do type assertion on: %v", sess.GSID) return nil } // 开启到游戏服的流 // TODO: 处理context,设定超时 stream, err := game_cli.Stream(context.Background()) if err != nil { log.Critical(err) return nil } sess.Stream = stream // 在game注册 // TODO: 新用户的创建由game处理 sess.Stream.Send(&spp.Game_Frame{Type: spp.Game_Register, UserId: sess.UserId}) // 读取GAME返回消息 fetcher_task := func(sess *Session) { for { in, err := sess.Stream.Recv() if err == io.EOF { // 流关闭 log.Trace(err) return } if err != nil { log.Error(err) return } sess.MQ <- *in } } go fetcher_task(sess) return packet.Pack(Code["user_login_ack"], user_snapshot{F_uid: sess.UserId}, nil) }
func (s *db) Set(tbl string, uid int32, data interface{}) error { bin, err := msgpack.Marshal(data) if err != nil { log.Critical(err) return err } _, err = s.redis_client.Cmd("SET", Key(tbl, uid), bin).Str() if err != nil { log.Critical(err) return err } return nil }
func (s *db) Get(tbl string, uid int32, data interface{}) error { raw, err := s.redis_client.Cmd("GET", Key(tbl, uid)).Bytes() if err != nil { log.Critical(err) return err } // unpack message from msgpack format err = msgpack.Unmarshal(raw, &data) if err != nil { log.Critical(err) return err } return nil }
// 玩家登陆过程 func P_user_login_req(sess *Session, reader *packet.Packet) []byte { // TODO: 登陆鉴权 sess.UserId = 1 // TODO: 选择登陆服务器 sess.GSID = DEFAULT_GSID // 选服 conn := sp.GetServiceWithId(sp.DEFAULT_SERVICE_PATH+"/game", sess.GSID) if conn == nil { log.Critical("cannot get game service:", sess.GSID) return nil } cli := pb.NewGameServiceClient(conn) // 开启到游戏服的流 // TODO: 处理context,设定超时 stream, err := cli.Stream(context.Background()) if err != nil { log.Critical(err) return nil } sess.Stream = stream // 在game注册 // TODO: 新用户的创建由game处理 sess.Stream.Send(&pb.Game_Frame{Type: pb.Game_Register, UserId: sess.UserId}) // 读取GAME返回消息 fetcher_task := func(sess *Session) { for { in, err := sess.Stream.Recv() if err == io.EOF { // 流关闭 log.Trace(err) return } if err != nil { log.Error(err) return } select { case sess.MQ <- *in: case <-sess.Die: } } } go fetcher_task(sess) return packet.Pack(Code["user_login_succeed_ack"], S_user_snapshot{F_uid: sess.UserId}, nil) }
// stream receiver func (s *server) recv(stream GameService_StreamServer) chan *Game_Frame { ch := make(chan *Game_Frame, 1) go func() { for { in, err := stream.Recv() if err == io.EOF { // client closed close(ch) return } if err != nil { log.Critical(err) close(ch) return } select { case ch <- in: case <-time.After(RECV_TIMEOUT): log.Warning("recv deliver timeout") close(ch) return } } }() return ch }
// stream receiver func (s *server) recv(stream GameService_StreamServer, sess_die chan struct{}) chan *Game_Frame { ch := make(chan *Game_Frame, 1) go func() { defer func() { close(ch) }() for { in, err := stream.Recv() if err == io.EOF { // client closed return } if err != nil { log.Critical(err) return } select { case ch <- in: case <-sess_die: return } } }() return ch }
func (s *server) init() { // get an unique value for consumer channel of nsq s.machines = []string{DEFAULT_ETCD} if env := os.Getenv("ETCD_HOST"); env != "" { s.machines = strings.Split(env, ";") } s.client_pool = make(chan *etcd.Client, CLIENT_MAX) // init client pool for i := 0; i < CLIENT_MAX; i++ { s.client_pool <- etcd.NewClient(s.machines) } // check if user specified machine id is set if env := os.Getenv(ENV_MACHINE_ID); env != "" { if id, err := strconv.Atoi(env); err == nil { s.machine_id = (uint64(id) & MACHINE_ID_MASK) << 12 log.Info("machine id specified:", id) } else { log.Critical(err) os.Exit(-1) } } else { s.init_machine_id() } }
func (s *server) dump(db *bolt.DB, changes map[uint64]bool) { for k := range changes { // marshal var rs *RankSet s.lock_read(func() { rs = s.ranks[k] }) if rs == nil { log.Warning("empty rankset:", k) continue } // serialization and save bin, err := rs.Marshal() if err != nil { log.Critical("cannot marshal:", err) os.Exit(-1) } db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BOLTDB_BUCKET)) err := b.Put([]byte(fmt.Sprint(k)), bin) return err }) } }
// publish to nsqd (localhost nsqd is suggested!) func Publish(r *RedoRecord) { // pack message pack, err := msgpack.Marshal(r) if err != nil { log.Critical(err) return } // post to nsqd resp, err := http.Post(_pub_addr, MIME, bytes.NewReader(pack)) if err != nil { log.Critical(err) return } defer resp.Body.Close() }
func (s *server) init() { s.dirty_words = make(map[string]bool) dict_path, dirty_words_path := s.data_path() // 载入字典 log.Trace("Loading Dictionary...") s.segmenter.LoadDictionary(dict_path) log.Trace("Dictionary Loaded") // 读取脏词库 log.Trace(SERVICE, "Loading Dirty Words...") f, err := os.Open(dirty_words_path) if err != nil { log.Critical(err) os.Exit(-1) } defer f.Close() // 逐行扫描 scanner := bufio.NewScanner(f) scanner.Split(bufio.ScanLines) for scanner.Scan() { word := strings.ToUpper(strings.TrimSpace(scanner.Text())) // 均处理为大写 if word != "" { s.dirty_words[word] = true } } log.Trace("Dirty Words Loaded") }
func Key(tbl string, uid int32) string { if tbl == "" || uid == 0 { log.Critical("Key error: %+v: %+v", tbl, uid) return "" } return fmt.Sprintf("%s:%s", tbl, uid) }
// watcher for data change in etcd directory func (p *service_pool) watcher() { client := p.client_pool.Get().(*etcd.Client) defer func() { p.client_pool.Put(client) }() for { ch := make(chan *etcd.Response, 10) go func() { for { if resp, ok := <-ch; ok { if resp.Node.Dir { continue } key, value := resp.Node.Key, resp.Node.Value if value == "" { log.Tracef("node delete: %v", key) p.remove_service(key) } else { log.Tracef("node add: %v %v", key, value) p.add_service(key, value) } } else { return } } }() _, err := client.Watch(DEFAULT_SERVICE_PATH, 0, true, ch, nil) if err != nil { log.Critical(err) } <-time.After(RETRY_DELAY) } }
func init() { mongo_host := DEFAULT_MONGODB_URL if env := os.Getenv(ENV_MONGODB_URL); env != "" { mongo_host = env } // dial mongodb sess, err := mgo.Dial(mongo_host) if err != nil { log.Critical("mongodb: cannot connect to", mongo_host, err) os.Exit(-1) } // set default session mode to strong for saving player's data sess.SetMode(mgo.Strong, true) // set a high timout sess.SetSocketTimeout(DEFAULT_MGO_TIMEOUT * time.Second) // infinite wait cursor sess.SetCursorTimeout(0) _global_ms = sess // value valve := DEFAULT_DBOPS_VALVE _valve_dbops = make(chan bool, valve) _valve_dbops_high = make(chan bool, valve) // high priority caller _high_prio = make(map[string]bool) _high_prio[`agent/AI.LoginProc`] = true _high_prio[`agent/net.P_user_login_req`] = true _high_prio[`main._flush`] = true }
// 玩家登陆过程 func P_user_login_req(sess *Session, reader *packet.Packet) []byte { // TODO: 登陆鉴权 // 简单鉴权可以在agent直接完成,通常公司都存在一个用户中心服务器用于鉴权 sess.UserId = 1 // TODO: 选择GAME服务器 // 选服策略依据业务进行,比如小服可以固定选取某台,大服可以采用HASH或一致性HASH sess.GSID = DEFAULT_GSID // 连接到已选定GAME服务器 conn := sp.GetServiceWithId(sp.DEFAULT_SERVICE_PATH+"/game", sess.GSID) if conn == nil { log.Critical("cannot get game service:", sess.GSID) return nil } cli := pb.NewGameServiceClient(conn) // 开启到游戏服的流 ctx := metadata.NewContext(context.Background(), metadata.New(map[string]string{"userid": fmt.Sprint(sess.UserId)})) stream, err := cli.Stream(ctx) if err != nil { log.Critical(err) return nil } sess.Stream = stream // 读取GAME返回消息的goroutine fetcher_task := func(sess *Session) { for { in, err := sess.Stream.Recv() if err == io.EOF { // 流关闭 log.Trace(err) return } if err != nil { log.Error(err) return } select { case sess.MQ <- *in: case <-sess.Die: } } } go fetcher_task(sess) return packet.Pack(Code["user_login_succeed_ack"], S_user_snapshot{F_uid: sess.UserId}, nil) }
func (arch *Archiver) archive_task() { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGTERM) timer := time.After(REDO_ROTATE_INTERVAL) sync_ticker := time.NewTicker(SYNC_INTERVAL) db := arch.new_redolog() key := make([]byte, 8) for { select { case <-sync_ticker.C: n := len(arch.pending) if n == 0 { continue } db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BOLTDB_BUCKET)) for i := 0; i < n; i++ { id, err := b.NextSequence() if err != nil { log.Critical(err) continue } binary.BigEndian.PutUint64(key, uint64(id)) if err = b.Put(key, <-arch.pending); err != nil { log.Critical(err) continue } } return nil }) case <-timer: db.Close() // rotate redolog db = arch.new_redolog() timer = time.After(REDO_ROTATE_INTERVAL) case <-sig: db.Close() log.Info("SIGTERM") os.Exit(0) } } }
func (s *server) init() { // 载入IP表 log.Trace("Loading GEOIP City...") reader, err := maxminddb.Open(s.data_path()) if err != nil { log.Critical(err) os.Exit(-1) } s.mmdb = reader log.Trace("GEOIP City Load Complete.") }
func init() { addr := DEFAULT_STATSD_HOST if env := os.Getenv(ENV_STATSD); env != "" { addr = env } s, err := g2s.Dial("udp", addr) if err != nil { log.Critical(err) os.Exit(-1) } _statter = s }
func (s *server) init() { // snappy if env := os.Getenv(ENV_SNAPPY); env != "" { s.enable_snappy = true } // read redis host redis_host := DEFAULT_REDIS_HOST if env := os.Getenv(ENV_REDIS_HOST); env != "" { redis_host = env } // start connection to redis cluster client, err := cluster.NewCluster(redis_host) if err != nil { log.Critical(err) os.Exit(-1) } s.redis_client = client // read mongodb host mongodb_url := DEFAULT_MONGODB_URL if env := os.Getenv(ENV_MONGODB_URL); env != "" { mongodb_url = env } // start connection to mongodb sess, err := mgo.Dial(mongodb_url) if err != nil { log.Critical(err) os.Exit(-1) } // database is provided in url s.db = sess.DB("") // wait chan s.wait = make(chan string, BUFSIZ) go s.loader_task() }
func (s *server) init_machine_id() { client := s.client_pool.Get().(*etcd.Client) defer func() { s.client_pool.Put(client) }() for i := 0; i < RETRY_MAX; i++ { // get the key resp, err := client.Get(UUID_KEY, false, false) if err != nil { log.Critical(err) os.Exit(-1) } // get prevValue & prevIndex prevValue, err := strconv.Atoi(resp.Node.Value) if err != nil { log.Critical(err) os.Exit(-1) } prevIndex := resp.Node.ModifiedIndex // CAS resp, err = client.CompareAndSwap(UUID_KEY, fmt.Sprint(prevValue+1), 0, resp.Node.Value, prevIndex) if err != nil { log.Error(err) <-time.After(RETRY_DELAY) continue } // record serial number of this service, already shifted s.machine_id = (uint64(prevValue+1) & MACHINE_ID_MASK) << 12 return } // failed to get machine id, exit os.Exit(-1) }
func (ns *numbers) init(path string) { ns.tables = make(map[string]*table) kapi := etcdclient.KeysAPI() resp, err := kapi.Get(context.Background(), path, nil) if err != nil { log.Error(err) return } // 解码xlsx xlsx_bin, err := base64.StdEncoding.DecodeString(resp.Node.Value) if err != nil { log.Critical(err) return } // 读取xlsx xlsx_reader, err := xlsx.OpenBinary(xlsx_bin) if err != nil { log.Critical(err) return } ns.parse(xlsx_reader.Sheets) }
func (s *server) restore() { // restore data from db file db := s.open_db() defer db.Close() db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BOLTDB_BUCKET)) b.ForEach(func(k, v []byte) error { rs := NewRankSet() err := rs.Unmarshal(v) if err != nil { log.Critical("rank data corrupted:", err) os.Exit(-1) } id, err := strconv.ParseUint(string(k), 0, 64) if err != nil { log.Critical("chat data corrupted:", err) os.Exit(-1) } s.ranks[id] = rs return nil }) return nil }) }