func NewpushRawDatabaseCache(c *DatabaseConfig, dbreader pushRawDatabaseReader, dbwriter pushRawDatabaseWriter) (*pushRawDatabaseCache, error) { cacheSize := 1024 flushPeriod := 600 leastDirty := 128 if c != nil { cacheSize = c.CacheSize flushPeriod = int(c.EverySec) leastDirty = c.LeastDirty } cdb := new(pushRawDatabaseCache) cdb.dbreader = dbreader cdb.dbwriter = dbwriter pspflusher := &pspFlusher{cdb: cdb} dpflusher := &dpFlusher{cdb: cdb} cdb.pspCache = cache.New(cacheSize, leastDirty, time.Duration(flushPeriod)*time.Second, pspflusher) cdb.dpCache = cache.New(cacheSize, leastDirty, time.Duration(flushPeriod)*time.Second, dpflusher) // We will flush them manually cdb.srvSub2Dp = cache.New(cacheSize, -1, time.Duration(0)*time.Second, nil) cdb.srv2Psp = cache.New(cacheSize, -1, time.Duration(0)*time.Second, nil) return cdb, nil }
func (self *apnsPushService) pushWorker(psp *PushServiceProvider, reqChan chan *pushRequest) { resultChan := make(chan *apnsResult, 100) manager := newAPNSConnManager(psp, resultChan) pool := connpool.NewPool(maxNrConn, maxNrConn, manager) defer pool.Close() workerid := fmt.Sprintf("workder-%v-%v", time.Now().Unix(), rand.Int63()) dpCache := cache.New(256, -1, 0*time.Second, nil) go feedbackChecker(psp, dpCache, self.errChan) // XXX use a tree structure would be faster and more stable. reqMap := make(map[uint32]*pushRequest, 1024) for { select { case req := <-reqChan: if req == nil { fmt.Printf("[%v][%v] I was told to stop (req == nil)\n", time.Now(), workerid) return } for i, _ := range req.devtokens { mid := req.getId(i) reqMap[mid] = req } for _, dp := range req.dpList { if key, ok := dp.FixedData["devtoken"]; ok { dpCache.Set(key, dp) } } go self.multiPush(req, pool) go clearRequest(req, resultChan) case res := <-resultChan: if res == nil { fmt.Printf("[%v][%v] I was told to stop (res == nil)\n", time.Now(), workerid) return } if req, ok := reqMap[res.msgId]; ok { delete(reqMap, res.msgId) req.resChan <- res } else if res.err != nil { self.errChan <- res.err } } } }
func (self *apnsPushService) pushWorker(psp *PushServiceProvider, reqChan chan *pushRequest) { resChan := make(chan *apnsResult) reqIdMap := make(map[uint32]*pushRequest) dpCache := cache.New(1024, -1, 0*time.Second, nil) //dropedDp := make([]string, 0, 1024) var connErr error connErr = nil connErrReported := false conn, err := self.connectAPNS(psp) if err != nil { connErr = err } else { go self.resultCollector(psp, resChan, conn) } var nextid uint32 nextid = 5 tmpErrReconnect := fmt.Errorf("Reconnect") for { select { case req := <-reqChan: // closed by another goroutine // close the connection and exit if req == nil { if connErr == nil { conn.Close() } return } dp := req.dp notif := req.notif mid := nextid nextid++ messageId := fmt.Sprintf("apns:%v-%v", psp.Name(), mid) if devtoken, ok := dp.FixedData["devtoken"]; ok { dpCache.Set(devtoken, dp) } req.msgIdChan <- messageId // Connection dropped by remote server. // Reconnect it. if connErr == tmpErrReconnect { conn, err = self.connectAPNS(psp) if err != nil { connErr = err } else { connErr = nil go self.resultCollector(psp, resChan, conn) } } if connErr != nil { if connErrReported { // we have already reported the connection error. start again. if conn != nil { conn.Close() } // try to recover by re-connecting the server conn, err = self.connectAPNS(psp) if err != nil { // we failed again. connErr = err result := new(PushResult) result.Content = notif result.Provider = psp result.Destination = dp result.MsgId = messageId result.Err = connErr req.resChan <- result close(req.resChan) connErrReported = true continue } else { go self.resultCollector(psp, resChan, conn) } } else { // report this error connErrReported = true result := new(PushResult) result.Content = notif result.Provider = psp result.Destination = dp result.MsgId = messageId result.Err = connErr req.resChan <- result close(req.resChan) continue } } err := self.singlePush(psp, dp, notif, mid, conn) if err != nil { // encountered some difficulty on sending the data. result := new(PushResult) result.Content = notif result.Provider = psp result.Destination = dp result.MsgId = messageId result.Err = err req.resChan <- result close(req.resChan) } else { // wait the result from APNs reqIdMap[mid] = req } unsubed := self.feedbackReceiver(psp) for _, unsubDev := range unsubed { dpif := dpCache.Delete(unsubDev) if dpif == nil { continue } dp, ok := dpif.(*DeliveryPoint) if !ok { continue } err := NewUnsubscribeUpdate(psp, dp) self.errChan <- err } case apnsres := <-resChan: // Connection Closed by remote server. // Recover it. if apnsres.err == io.EOF { conn.Close() // Notify all waiting goroutines for msgid, req := range reqIdMap { result := new(PushResult) result.Content = req.notif result.Provider = psp result.Destination = req.dp result.MsgId = fmt.Sprintf("apns:%v-%v", psp.Name(), msgid) result.Err = fmt.Errorf("Connection Closed by Remote Server") req.resChan <- result close(req.resChan) } reqIdMap = make(map[uint32]*pushRequest) connErr = tmpErrReconnect continue } if cerr, ok := apnsres.err.(*ConnectionError); ok { connErr = cerr } if req, ok := reqIdMap[apnsres.msgId]; ok { result := new(PushResult) result.Content = req.notif result.Provider = psp result.Destination = req.dp result.MsgId = fmt.Sprintf("apns:%v-%v", psp.Name(), apnsres.msgId) if apnsres.err != nil { result.Err = apnsres.err req.resChan <- result close(req.resChan) continue } switch apnsres.status { case 0: result.Err = nil case 1: result.Err = NewBadDeliveryPointWithDetails(req.dp, "Processing Error") case 2: result.Err = NewBadDeliveryPointWithDetails(req.dp, "Missing Device Token") case 3: result.Err = NewBadNotificationWithDetails("Missing topic") case 4: result.Err = NewBadNotificationWithDetails("Missing payload") case 5: result.Err = NewBadNotificationWithDetails("Invalid token size") case 6: result.Err = NewBadNotificationWithDetails("Invalid topic size") case 7: result.Err = NewBadNotificationWithDetails("Invalid payload size") case 8: // result.Err = NewBadDeliveryPointWithDetails(req.dp, "Invalid Token") // This token is invalid, we should unsubscribe this device. result.Err = NewUnsubscribeUpdate(psp, req.dp) default: result.Err = fmt.Errorf("Unknown Error: %d", apnsres.status) } delete(reqIdMap, apnsres.msgId) go func() { select { case req.resChan <- result: case <-time.After((maxWaitTime + 1) * time.Second): } close(req.resChan) }() } } } }