// 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 }
// PushPrivates expored a method for publishing a user multiple private message for the channel. // because of it`s going asynchronously in this method, so it won`t return an error to caller. func (c *CometRPC) PushPrivates(args *myrpc.CometPushPrivatesArgs, rw *myrpc.CometPushPrivatesResp) error { if args == nil { return myrpc.ErrParam } bucketMap := make(map[*ChannelBucket]*batchChannel, Conf.ChannelBucket) for _, key := range args.Keys { // get channel ch, bp, err := UserChannel.New(key) if err != nil { log.Error("UserChannel.New(\"%s\") error(%v)", key, err) // log failed keys. rw.FKeys = append(rw.FKeys, key) continue } if bucket, ok := bucketMap[bp]; !ok { bucketMap[bp] = &batchChannel{ Keys: []string{key}, Chs: map[string]Channel{key: ch}, } } else { // ignore duplicate key if _, ok := bucket.Chs[key]; !ok { bucket.Chs[key] = ch bucket.Keys = append(bucket.Keys, key) } } } // every bucket start a goroutine, return till all bucket gorouint finish wg := &sync.WaitGroup{} wg.Add(len(bucketMap)) // stored every gorouint failed keys fKeysList := make([][]string, len(bucketMap)) ti := 0 for tb, tm := range bucketMap { go func(b *ChannelBucket, m *batchChannel, i int) { defer wg.Done() c := myrpc.MessageRPC.Get() if c == nil { // static slice is thread-safe // log all keys fKeysList[i] = m.Keys log.Debug("fkeys len:%d", len(m.Keys)) return } b.Lock() defer b.Unlock() timeId := id.Get() // msg := &myrpc.Message{Msg: args.Msg, MsgId: timeId} // private message need persistence // if message expired no need persistence, only send online message // rewrite message id resp := &myrpc.MessageSavePrivatesResp{} if args.Expire > 0 { args := &myrpc.MessageSavePrivatesArgs{Keys: m.Keys, Msg: args.Msg, MsgId: timeId, Expire: args.Expire} if err := c.Call(myrpc.MessageServiceSavePrivates, args, resp); err != nil { log.Error("%s(\"%v\", \"%v\", &ret) error(%v)", myrpc.MessageServiceSavePrivates, m.Keys, args, err) // static slice is thread-safe fKeysList[i] = m.Keys log.Debug("fkeys len:%d", len(m.Keys)) return } fKeysList[i] = resp.FKeys log.Debug("fkeys len:%d", len(resp.FKeys)) } // delete the failed keys for _, fk := range resp.FKeys { delete(m.Chs, fk) } // get all channels from batchChannel chs. // for key, ch := range m.Chs { // if err := ch.WriteMsg(key, msg); err != nil { // // ignore online push error, cause offline msg succeed // log.Error("ch.WriteMsg(\"%s\", \"%s\") error(%v)", key, string(msg.Msg), err) // continue // } // } }(tb, tm, ti) ti++ } wg.Wait() // merge all failed keys for _, k := range fKeysList { rw.FKeys = append(rw.FKeys, k...) } return nil }