// hanleTCPConn handle a long live tcp connection. func handleTCPConn(conn net.Conn, rc chan *bufio.Reader) { addr := conn.RemoteAddr().String() log.Debug("<%s> handleTcpConn routine start", addr) rd := newBufioReader(rc, conn) if args, err := parseCmd(rd); err == nil { // return buffer bufio.Reader putBufioReader(rc, rd) switch args[0] { case "sub": SubscribeTCPHandle(conn, args[1:]) break default: conn.Write(ParamReply) log.Warn("<%s> unknown cmd \"%s\"", addr, args[0]) break } } else { // return buffer bufio.Reader putBufioReader(rc, rd) log.Error("<%s> parseCmd() error(%v)", addr, err) } // close the connection if err := conn.Close(); err != nil { log.Error("<%s> conn.Close() error(%v)", addr, err) } log.Debug("<%s> handleTcpConn routine stop", addr) return }
// DelMulti implements the Storage DelMulti method. func (s *RedisStorage) clean() { for { info := <-s.delCH conn := s.getConn(info.Key) if conn == nil { log.Warn("get redis connection nil") continue } for _, mid := range info.MIds { if err := conn.Send("ZREMRANGEBYSCORE", info.Key, mid, mid); err != nil { log.Error("conn.Send(\"ZREMRANGEBYSCORE\", \"%s\", %d, %d) error(%v)", info.Key, mid, mid, err) conn.Close() continue } } if err := conn.Flush(); err != nil { log.Error("conn.Flush() error(%v)", err) conn.Close() continue } for _, _ = range info.MIds { _, err := conn.Receive() if err != nil { log.Error("conn.Receive() error(%v)", err) conn.Close() continue } } conn.Close() } }
// HandleWrite start a goroutine get msg from chan, then send to the conn. func (c *Connection) HandleWrite(key string) { go func() { var ( n int err error ) log.Debug("user_key: \"%s\" HandleWrite goroutine start", key) for { msg, ok := <-c.Buf if !ok { log.Debug("user_key: \"%s\" HandleWrite goroutine stop", key) return } if c.Proto == WebsocketProto { // raw n, err = c.Conn.Write(msg) } else if c.Proto == TCPProto { // redis protocol msg = []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(msg), string(msg))) n, err = c.Conn.Write(msg) } else { log.Error("unknown connection protocol: %d", c.Proto) panic(ErrConnProto) } // update stat if err != nil { log.Error("user_key: \"%s\" conn.Write() error(%v)", key, err) MsgStat.IncrFailed(1) } else { log.Debug("user_key: \"%s\" write \r\n========%s(%d)========", key, string(msg), n) MsgStat.IncrSucceed(1) } } }() }
// watchNode watch a named node for leader selection when failover func watchCometNode(conn *zk.Conn, node, fpath string, retry, ping time.Duration, ch chan *CometNodeEvent) { fpath = path.Join(fpath, node) for { nodes, watch, err := myzk.GetNodesW(conn, fpath) if err == myzk.ErrNodeNotExist { log.Warn("zk don't have node \"%s\"", fpath) break } else if err == myzk.ErrNoChild { log.Warn("zk don't have any children in \"%s\", retry in %d second", fpath, waitNodeDelay) time.Sleep(waitNodeDelaySecond) continue } else if err != nil { log.Error("zk path: \"%s\" getNodes error(%v), retry in %d second", fpath, err, waitNodeDelay) time.Sleep(waitNodeDelaySecond) continue } // leader selection sort.Strings(nodes) if info, err := registerCometNode(conn, nodes[0], fpath, retry, ping, true); err != nil { log.Error("zk path: \"%s\" registerCometNode error(%v)", fpath, err) time.Sleep(waitNodeDelaySecond) continue } else { // update node info ch <- &CometNodeEvent{Event: eventNodeUpdate, Key: node, Value: info} } // blocking receive event event := <-watch log.Info("zk path: \"%s\" receive a event: (%v)", fpath, event) } // WARN, if no persistence node and comet rpc not config log.Warn("zk path: \"%s\" never watch again till recreate", fpath) }
// NewMySQLStorage initialize mysql pool and consistency hash ring. func NewMySQLStorage() *MySQLStorage { dbPool := make(map[string]*sql.DB) ring := ketama.NewRing(ketamaBase) for n, source := range Conf.MySQLSource { nw := strings.Split(n, mysqlSourceSpliter) if len(nw) != 2 { err := errors.New("node config error, it's nodeN:W") log.Error("strings.Split(\"%s\", :) failed (%v)", n, err) panic(err) } w, err := strconv.Atoi(nw[1]) if err != nil { log.Error("strconv.Atoi(\"%s\") failed (%v)", nw[1], err) panic(err) } db, err := sql.Open("mysql", source) if err != nil { log.Error("sql.Open(\"mysql\", %s) failed (%v)", source, err) panic(err) } dbPool[nw[0]] = db ring.AddNode(nw[0], w) } ring.Bake() s := &MySQLStorage{pool: dbPool, ring: ring} go s.clean() return s }
// PushMsg implements the Channel PushMsg method. func (c *SeqChannel) PushMsg(key string, m *myrpc.Message, expire uint) (err error) { client := myrpc.MessageRPC.Get() if client == nil { return ErrMessageRPC } c.mutex.Lock() // private message need persistence // if message expired no need persistence, only send online message // rewrite message id //m.MsgId = c.timeID.ID() m.MsgId = id.Get() if m.GroupId != myrpc.PublicGroupId && expire > 0 { args := &myrpc.MessageSavePrivateArgs{Key: key, Msg: m.Msg, MsgId: m.MsgId, Expire: expire} ret := 0 if err = client.Call(myrpc.MessageServiceSavePrivate, args, &ret); err != nil { c.mutex.Unlock() log.Error("%s(\"%s\", \"%v\", &ret) error(%v)", myrpc.MessageServiceSavePrivate, key, args, err) return } } // push message if err = c.writeMsg(key, m); err != nil { c.mutex.Unlock() log.Error("c.WriteMsg(\"%s\", m) error(%v)", key, err) return } c.mutex.Unlock() return }
// GetPrivate implements the Storage GetPrivate method. func (s *MySQLStorage) GetPrivate(key string, mid int64) ([]*myrpc.Message, error) { db := s.getConn(key) if db == nil { return nil, ErrNoMySQLConn } now := time.Now().Unix() rows, err := db.Query(getPrivateMsgSQL, key, mid) if err != nil { log.Error("db.Query(\"%s\",\"%s\",%d,now) failed (%v)", getPrivateMsgSQL, key, mid, err) return nil, err } msgs := []*myrpc.Message{} for rows.Next() { expire := int64(0) cmid := int64(0) msg := []byte{} if err := rows.Scan(&cmid, &expire, &msg); err != nil { log.Error("rows.Scan() failed (%v)", err) return nil, err } if now > expire { log.Warn("user_key: \"%s\" mid: %d expired", key, cmid) continue } msgs = append(msgs, &myrpc.Message{MsgId: cmid, GroupId: myrpc.PrivateGroupId, Msg: json.RawMessage(msg)}) } return msgs, nil }
// RegisterTmp create a ephemeral node, and watch it, if node droped then send a SIGQUIT to self. func RegisterTemp(conn *zk.Conn, fpath string, data []byte) error { tpath, err := conn.Create(path.Join(fpath)+"/", data, zk.FlagEphemeral|zk.FlagSequence, zk.WorldACL(zk.PermAll)) if err != nil { log.Error("conn.Create(\"%s\", \"%s\", zk.FlagEphemeral|zk.FlagSequence) error(%v)", fpath, string(data), err) return err } log.Debug("create a zookeeper node:%s", tpath) // watch self go func() { for { log.Info("zk path: \"%s\" set a watch", tpath) exist, _, watch, err := conn.ExistsW(tpath) if err != nil { log.Error("zk.ExistsW(\"%s\") error(%v)", tpath, err) log.Warn("zk path: \"%s\" set watch failed, kill itself", tpath) killSelf() return } if !exist { log.Warn("zk path: \"%s\" not exist, kill itself", tpath) killSelf() return } event := <-watch log.Info("zk path: \"%s\" receive a event %v", tpath, event) } }() return nil }
// parseCmd parse the tcp request command. func parseCmd(rd *bufio.Reader) ([]string, error) { // get argument number argNum, err := parseCmdSize(rd, '*') if err != nil { log.Error("tcp:cmd format error when find '*' (%v)", err) return nil, err } if argNum < minCmdNum || argNum > maxCmdNum { log.Error("tcp:cmd argument number length error") return nil, ErrProtocol } args := make([]string, 0, argNum) for i := 0; i < argNum; i++ { // get argument length cmdLen, err := parseCmdSize(rd, '$') if err != nil { log.Error("tcp:parseCmdSize(rd, '$') error(%v)", err) return nil, err } // get argument data d, err := parseCmdData(rd, cmdLen) if err != nil { log.Error("tcp:parseCmdData() error(%v)", err) return nil, err } // append args args = append(args, string(d)) } return args, nil }
// watchMessageRoot watch the message root path. func watchMessageRoot(conn *zk.Conn, fpath string, ch chan *MessageNodeEvent) error { for { nodes, watch, err := myzk.GetNodesW(conn, fpath) if err == myzk.ErrNodeNotExist { log.Warn("zk don't have node \"%s\", retry in %d second", fpath, waitNodeDelay) time.Sleep(waitNodeDelaySecond) continue } else if err == myzk.ErrNoChild { log.Warn("zk don't have any children in \"%s\", retry in %d second", fpath, waitNodeDelay) // all child died, kick all the nodes for _, client := range MessageRPC.Clients { log.Debug("node: \"%s\" send del node event", client.Addr) ch <- &MessageNodeEvent{Event: eventNodeDel, Key: &WeightRpc{Addr: client.Addr, Weight: client.Weight}} } time.Sleep(waitNodeDelaySecond) continue } else if err != nil { log.Error("getNodes error(%v), retry in %d second", err, waitNodeDelay) time.Sleep(waitNodeDelaySecond) continue } nodesMap := map[string]bool{} // handle new add nodes for _, node := range nodes { data, _, err := conn.Get(path.Join(fpath, node)) if err != nil { log.Error("zk.Get(\"%s\") error(%v)", path.Join(fpath, node), err) continue } // parse message node info nodeInfo := &MessageNodeInfo{} if err := json.Unmarshal(data, nodeInfo); err != nil { log.Error("json.Unmarshal(\"%s\", nodeInfo) error(%v)", string(data), err) continue } for _, addr := range nodeInfo.Rpc { // if not exists in old map then trigger a add event if _, ok := MessageRPC.Clients[addr]; !ok { ch <- &MessageNodeEvent{Event: eventNodeAdd, Key: &WeightRpc{Addr: addr, Weight: nodeInfo.Weight}} } nodesMap[addr] = true } } // handle delete nodes for _, client := range MessageRPC.Clients { if _, ok := nodesMap[client.Addr]; !ok { ch <- &MessageNodeEvent{Event: eventNodeDel, Key: client} } } // blocking wait node changed event := <-watch log.Info("zk path: \"%s\" receive a event %v", fpath, event) } }
// GetOfflineMsg get offline mesage http handler. func GetOfflineMsg0(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { http.Error(w, "Method Not Allowed", 405) return } params := r.URL.Query() key := params.Get("key") midStr := params.Get("mid") callback := params.Get("callback") res := map[string]interface{}{"ret": OK, "msg": "ok"} defer retWrite(w, r, res, callback, time.Now()) if key == "" || midStr == "" { res["ret"] = ParamErr return } mid, err := strconv.ParseInt(midStr, 10, 64) if err != nil { res["ret"] = ParamErr log.Error("strconv.ParseInt(\"%s\", 10, 64) error(%v)", midStr, err) return } // RPC get offline messages reply := &myrpc.MessageGetResp{} args := &myrpc.MessageGetPrivateArgs{MsgId: mid, Key: key} client := myrpc.MessageRPC.Get() if client == nil { res["ret"] = InternalErr return } if err := client.Call(myrpc.MessageServiceGetPrivate, args, reply); err != nil { log.Error("myrpc.MessageRPC.Call(\"%s\", \"%v\", reply) error(%v)", myrpc.MessageServiceGetPrivate, args, err) res["ret"] = InternalErr return } omsgs := []string{} opmsgs := []string{} for _, msg := range reply.Msgs { omsg, err := msg.OldBytes() if err != nil { res["ret"] = InternalErr return } omsgs = append(omsgs, string(omsg)) } if len(omsgs) == 0 { return } res["data"] = map[string]interface{}{"msgs": omsgs, "pmsgs": opmsgs} return }
// registerCometNode get infomation of comet node func registerCometNode(conn *zk.Conn, node, fpath string, retry, ping time.Duration, startPing bool) (info *CometNodeInfo, err error) { // get current node info from zookeeper fpath = path.Join(fpath, node) data, _, err := conn.Get(fpath) if err != nil { log.Error("zk.Get(\"%s\") error(%v)", fpath, err) return } info = &CometNodeInfo{} if err = json.Unmarshal(data, info); err != nil { log.Error("json.Unmarshal(\"%s\", nodeData) error(%v)", string(data), err) return } if len(info.RpcAddr) == 0 { log.Error("zk nodes: \"%s\" don't have rpc addr", fpath) err = ErrCometRPC return } // get old node info for finding the old rpc connection oldInfo := cometNodeInfoMap[node] // init comet rpc clients := make(map[string]*WeightRpc, len(info.RpcAddr)) for _, addr := range info.RpcAddr { var ( r *rpc.Client ) if oldInfo != nil && oldInfo.Rpc != nil { if wr, ok := oldInfo.Rpc.Clients[addr]; ok && wr.Client != nil { // reuse the rpc connection must let old client = nil, avoid reclose rpc. oldInfo.Rpc.Clients[addr].Client = nil r = wr.Client } } if r == nil { if r, err = rpc.Dial("tcp", addr); err != nil { log.Error("rpc.Dial(\"%s\") error(%v)", addr, err) return } log.Debug("node:%s addr:%s rpc reconnect", node, addr) } clients[addr] = &WeightRpc{Weight: 1, Addr: addr, Client: r} } // comet rpc use rand load balance lb, err := NewRandLB(clients, cometService, retry, ping, startPing) if err != nil { log.Error("NewRandLR() error(%v)", err) return } info.Rpc = lb log.Info("zk path: \"%s\" register nodes: \"%s\"", fpath, node) return }
func httpListen(mux *http.ServeMux, bind string) { server := &http.Server{Handler: mux, ReadTimeout: Conf.HttpServerTimeout, WriteTimeout: Conf.HttpServerTimeout} server.SetKeepAlivesEnabled(false) l, err := net.Listen("tcp", bind) if err != nil { log.Error("net.Listen(\"tcp\", \"%s\") error(%v)", bind, err) panic(err) } if err := server.Serve(l); err != nil { log.Error("server.Serve() error(%v)", err) panic(err) } }
func rpcListen(bind string) { l, err := net.Listen("tcp", bind) if err != nil { log.Error("net.Listen(\"tcp\", \"%s\") error(%v)", bind, err) panic(err) } defer func() { if err := l.Close(); err != nil { log.Error("listener.Close() error(%v)", err) } }() rpc.Accept(l) }
// PushPrivate handle for push private message. func PushPrivate(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method Not Allowed", 405) return } body := "" res := map[string]interface{}{"ret": OK} defer retPWrite(w, r, res, &body, time.Now()) // param bodyBytes, err := ioutil.ReadAll(r.Body) if err != nil { res["ret"] = InternalErr log.Error("ioutil.ReadAll() failed (%v)", err) return } body = string(bodyBytes) params := r.URL.Query() key := params.Get("key") expire, err := strconv.ParseUint(params.Get("expire"), 10, 32) if err != nil { res["ret"] = ParamErr log.Error("strconv.ParseUint(\"%s\", 10, 32) error(%v)", params.Get("expire"), err) return } node := myrpc.GetComet(key) if node == nil || node.Rpc == nil { res["ret"] = NotFoundServer return } client := node.Rpc.Get() if client == nil { res["ret"] = NotFoundServer return } rm := json.RawMessage(bodyBytes) msg, err := rm.MarshalJSON() if err != nil { res["ret"] = ParamErr log.Error("json.RawMessage(\"%s\").MarshalJSON() error(%v)", body, err) return } args := &myrpc.CometPushPrivateArgs{Msg: json.RawMessage(msg), Expire: uint(expire), Key: key} ret := 0 if err := client.Call(myrpc.CometServicePushPrivate, args, &ret); err != nil { log.Error("client.Call(\"%s\", \"%s\", &ret) error(%v)", myrpc.CometServicePushPrivate, args.Key, err) res["ret"] = InternalErr return } return }
// retPWrite marshal the result and write to client(post). func retPWrite(w http.ResponseWriter, r *http.Request, res map[string]interface{}, body *string, start time.Time) { data, err := json.Marshal(res) if err != nil { log.Error("json.Marshal(\"%v\") error(%v)", res, err) return } dataStr := string(data) if n, err := w.Write([]byte(dataStr)); err != nil { log.Error("w.Write(\"%s\") error(%v)", dataStr, err) } else { log.Debug("w.Write(\"%s\") write %d bytes", dataStr, n) } log.Info("req: \"%s\", post: \"%s\", res:\"%s\", ip:\"%s\", time:\"%fs\"", r.URL.String(), *body, dataStr, r.RemoteAddr, time.Now().Sub(start).Seconds()) }
// NewRedis initialize the redis pool and consistency hash ring. func NewRedisStorage() *RedisStorage { redisPool := map[string]*redis.Pool{} ring := ketama.NewRing(ketamaBase) reg := regexp.MustCompile("(.+)@(.+)#(.+)|(.+)@(.+)") for n, addr := range Conf.RedisSource { nw := strings.Split(n, ":") if len(nw) != 2 { err := errors.New("node config error, it's nodeN:W") log.Error("strings.Split(\"%s\", :) failed (%v)", n, err) panic(err) } w, err := strconv.Atoi(nw[1]) if err != nil { log.Error("strconv.Atoi(\"%s\") failed (%v)", nw[1], err) panic(err) } // get protocol and addr pw := reg.FindStringSubmatch(addr) if len(pw) < 3 { log.Error("strings.regexp(\"%s\", \"%s\") failed (%v)", addr, pw) panic(fmt.Sprintf("config redis.source node:\"%s\" format error", addr)) } tmpProto := pw[1] tmpAddr := pw[2] // WARN: closures use redisPool[nw[0]] = &redis.Pool{ MaxIdle: Conf.RedisMaxIdle, MaxActive: Conf.RedisMaxActive, IdleTimeout: Conf.RedisIdleTimeout, Dial: func() (redis.Conn, error) { conn, err := redis.Dial(tmpProto, tmpAddr) if err != nil { log.Error("redis.Dial(\"%s\", \"%s\") error(%v)", tmpProto, tmpAddr, err) return nil, err } if len(pw) > 3 { conn.Do("AUTH", pw[3]) } return conn, err }, } // add node to ketama hash ring.AddNode(nw[0], w) } ring.Bake() s := &RedisStorage{pool: redisPool, ring: ring, delCH: make(chan *RedisDelMessage, 10240)} go s.clean() return s }
// parseCmdData get the sub request protocol cmd data not included \r\n. func parseCmdData(rd *bufio.Reader, cmdLen int) ([]byte, error) { d, err := rd.ReadBytes('\n') if err != nil { log.Error("tcp:rd.ReadBytes('\\n') error(%v)", err) return nil, err } dl := len(d) // check last \r\n if dl != cmdLen+2 || d[dl-2] != '\r' { log.Error("tcp:\"%v\"(%d) number format error, length error or no \\r", d, dl) return nil, ErrProtocol } // skip last \r\n return d[0:cmdLen], nil }
func rpcListen(bind string) { l, err := net.Listen("tcp", bind) if err != nil { log.Error("net.Listen(\"tcp\", \"%s\") error(%v)", bind, err) panic(err) } // if process exit, then close the rpc bind defer func() { log.Info("rpc addr: \"%s\" close", bind) if err := l.Close(); err != nil { log.Error("listener.Close() error(%v)", err) } }() rpc.Accept(l) }
// New expored a method for creating new channel. func (c *CometRPC) New(args *myrpc.CometNewArgs, ret *int) error { if args == nil || args.Key == "" { return myrpc.ErrParam } // create a new channel for the user ch, _, err := UserChannel.New(args.Key) if err != nil { log.Error("UserChannel.New(\"%s\") error(%v)", args.Key, err) return err } if err = ch.AddToken(args.Key, args.Token); err != nil { log.Error("ch.AddToken(\"%s\", \"%s\") error(%v)", args.Key, args.Token) return err } return nil }
// GetPrivate implements the Storage GetPrivate method. func (s *RedisStorage) GetPrivate(key string, mid int64) ([]*myrpc.Message, error) { conn := s.getConn(key) if conn == nil { return nil, RedisNoConnErr } defer conn.Close() values, err := redis.Values(conn.Do("ZRANGEBYSCORE", key, fmt.Sprintf("(%d", mid), "+inf", "WITHSCORES")) if err != nil { log.Error("conn.Do(\"ZRANGEBYSCORE\", \"%s\", \"%d\", \"+inf\", \"WITHSCORES\") error(%v)", key, mid, err) return nil, err } msgs := make([]*myrpc.Message, 0, len(values)) delMsgs := []int64{} now := time.Now().Unix() for len(values) > 0 { cmid := int64(0) b := []byte{} values, err = redis.Scan(values, &b, &cmid) if err != nil { log.Error("redis.Scan() error(%v)", err) return nil, err } rm := &RedisPrivateMessage{} if err = json.Unmarshal(b, rm); err != nil { log.Error("json.Unmarshal(\"%s\", rm) error(%v)", string(b), err) delMsgs = append(delMsgs, cmid) continue } // check expire if rm.Expire < now { log.Warn("user_key: \"%s\" msg: %d expired", key, cmid) delMsgs = append(delMsgs, cmid) continue } m := &myrpc.Message{MsgId: cmid, Msg: rm.Msg, GroupId: myrpc.PrivateGroupId} msgs = append(msgs, m) } // delete unmarshal failed and expired message if len(delMsgs) > 0 { select { case s.delCH <- &RedisDelMessage{Key: key, MIds: delMsgs}: default: log.Warn("user_key: \"%s\" send del messages failed, channel full", key) } } return msgs, nil }
// Migrate migrate portion of connections which don't belong to this comet. func (l *ChannelList) Migrate(nw map[string]int) (err error) { migrate := false // check new/update node for k, v := range nw { weight, ok := nodeWeightMap[k] // not found or weight change if !ok || weight != v { migrate = true break } } // check del node if !migrate { for k, _ := range nodeWeightMap { // node deleted if _, ok := nw[k]; !ok { migrate = true break } } } if !migrate { return } // init ketama ring := ketama.NewRing(ketama.Base) for node, weight := range nw { ring.AddNode(node, weight) } ring.Bake() // atomic update nodeWeightMap = nw CometRing = ring // get all the channel lock channels := []Channel{} for i, c := range l.Channels { c.Lock() for k, v := range c.Data { hn := ring.Hash(k) if hn != Conf.ZookeeperCometNode { channels = append(channels, v) delete(c.Data, k) log.Debug("migrate delete channel key \"%s\"", k) } } c.Unlock() log.Debug("migrate channel bucket:%d finished", i) } // close all the migrate channels log.Info("close all the migrate channels") for _, channel := range channels { if err := channel.Close(); err != nil { log.Error("channel.Close() error(%v)", err) continue } } log.Info("close all the migrate channels finished") return }
// jsonRes format the output func jsonRes(res map[string]interface{}) []byte { byteJson, err := json.MarshalIndent(res, "", " ") if err != nil { log.Error("json.MarshalIndent(\"%v\", \"\", \" \") error(%v)", res, err) return nil } return byteJson }
// configuration info func ConfigInfo() []byte { byteJson, err := json.MarshalIndent(Conf, "", " ") if err != nil { log.Error("json.MarshalIndent(\"%v\", \"\", \" \") error(%v)", Conf, err) return nil } return byteJson }
func statListen(bind string) { httpServeMux := http.NewServeMux() httpServeMux.HandleFunc("/stat", StatHandle) if err := http.ListenAndServe(bind, httpServeMux); err != nil { log.Error("http.ListenAdServe(\"%s\") error(%v)", bind, err) panic(err) } }
// Close expored a method for closing new channel. func (c *CometRPC) Close(key string, ret *int) error { if key == "" { return myrpc.ErrParam } // close the channle for the user ch, err := UserChannel.Delete(key) if err != nil { log.Error("UserChannel.Delete(\"%s\") error(%v)", key, err) return err } // ignore channel close error, only log a warnning if err := ch.Close(); err != nil { log.Error("ch.Close() error(%v)", err) return err } return nil }
// Bytes get a message reply bytes. func (m *Message) Bytes() ([]byte, error) { byteJson, err := json.Marshal(m) if err != nil { log.Error("json.Marshal(%v) error(%v)", m, err) return nil, err } return byteJson, nil }
// DelPrivate implements the Storage DelPrivate method. func (s *MySQLStorage) DelPrivate(key string) error { db := s.getConn(key) if db == nil { return ErrNoMySQLConn } res, err := db.Exec(delPrivateMsgSQL, key) if err != nil { log.Error("db.Exec(\"%s\", \"%s\") error(%v)", delPrivateMsgSQL, key, err) return err } rows, err := res.RowsAffected() if err != nil { log.Error("res.RowsAffected() error(%v)", err) return err } log.Info("user_key: \"%s\" clean message num: %d", rows) return nil }
// InitConfig get a new Config struct. func InitConfig(file string) (*Config, error) { cf := &Config{ // base Addr: "localhost:6969", Key: "Terry-Mao", Heartbeat: 30, } c := goconf.New() if err := c.Parse(file); err != nil { log.Error("goconf.Parse(\"%s\") failed (%s)", file, err.Error()) return nil, err } if err := c.Unmarshal(cf); err != nil { log.Error("goconf.Unmarshal() failed (%s)", err.Error()) return nil, err } return cf, nil }
// PushPrivate expored a method for publishing a user private message for the channel. // if it`s going failed then it`ll return an error func (c *CometRPC) PushPrivate(args *myrpc.CometPushPrivateArgs, ret *int) error { if args == nil || args.Key == "" { return myrpc.ErrParam } // get a user channel ch, _, err := UserChannel.New(args.Key) if err != nil { log.Error("UserChannel.New(\"%s\") error(%v)", args.Key, err) return err } // use the channel push message m := &myrpc.Message{Msg: args.Msg} if err = ch.PushMsg(args.Key, m, args.Expire); err != nil { log.Error("ch.PushMsg(\"%s\", \"%v\") error(%v)", args.Key, m, err) return err } return nil }