def log_error(self, message='message push fail'): self.rds.hincrby("fail_counter", self.app_key) log.error(message) type, value, tb = sys.exc_info() error_message = traceback.format_exception(type, value, tb) log.debug(type) log.error(error_message)
def _send_message(self, message): real_message = simplejson.loads(message['data']) badge = real_message.get('badge', None) sound = real_message.get('sound', None) alert = real_message.get('alert', None) custom = real_message.get('custom', {}) if self.rds.sismember( '%s:%s' % (constants.INVALID_TOKENS, self.app_key), real_message['token']): # the token is invalid,do nothing return try: payload = Payload(sound=sound, badge=badge, alert=alert, custom=custom) except PayloadTooLargeError: # 在内存保留100条缩短后消息,避免批量发送时,每条都要缩短的损耗 if not alert: log.error('push meta data too long to trim, discard') payload = None if isinstance(alert, dict): log.error('payload too long to trim, discard') payload = None log.debug('try to trime large alert') payload = SafePayload(sound=sound, badge=badge, alert=alert, custom=custom) l_payload = len(payload.json()) l_alert = len(alert.encode('unicode_escape')) l_allow = 256 - (l_payload - l_alert) - 3 # 允许提示长度 ec_alert = alert.encode('unicode_escape') t_alert = re.sub(r'([^\\])\\(u|$)[0-9a-f]{0,3}$', r'\1', ec_alert[:l_allow]) alert = t_alert.decode('unicode_escape') + u'...' log.debug('payload is : %s' % alert) payload.alert = alert log.debug('how long dest it after trim %d' % len(payload.json())) payload = payload.as_payload() if not payload: return log.debug('will sent a meesage to token %s', real_message['token']) now = datetime.now() if (now - self.last_sent_time).seconds > 300: log.debug('idle for a long time , reconnect now.') self.reconnect() self.apns.gateway_server.send_notification(real_message['token'], payload) self.last_sent_time = datetime.now() self.rds.hincrby("counter", self.app_key)
def _send_message(self, message): real_message = simplejson.loads(message['data']) badge = real_message.get('badge', None) sound = real_message.get('sound', None) alert = real_message.get('alert', None) custom = real_message.get('custom', {}) if self.rds.sismember('%s:%s' % (constants.INVALID_TOKENS, self.app_key), real_message['token']): # the token is invalid,do nothing return try: payload = Payload(sound=sound, badge=badge, alert=alert, custom=custom) except PayloadTooLargeError: # 在内存保留100条缩短后消息,避免批量发送时,每条都要缩短的损耗 if not alert: log.error('push meta data too long to trim, discard') payload = None if isinstance(alert, dict): log.error('payload too long to trim, discard') payload = None log.debug('try to trime large alert') payload = SafePayload(sound=sound, badge=badge, alert=alert, custom=custom) l_payload = len(payload.json()) l_alert = len(alert.encode('unicode_escape')) l_allow = 256 - (l_payload - l_alert) - 3 # 允许提示长度 ec_alert = alert.encode('unicode_escape') t_alert = re.sub(r'([^\\])\\(u|$)[0-9a-f]{0,3}$', r'\1', ec_alert[:l_allow]) alert = t_alert.decode('unicode_escape') + u'...' log.debug('payload is : %s' % alert) payload.alert = alert log.debug('how long dest it after trim %d' % len(payload.json())) payload = payload.as_payload() if not payload: return log.debug('will sent a meesage to token %s', real_message['token']) now = datetime.now() if (now - self.last_sent_time).seconds > 300: log.debug('idle for a long time , reconnect now.') self.reconnect() self.apns.gateway_server.send_notification(real_message['token'], payload) self.last_sent_time = datetime.now() self.rds.hincrby("counter", self.app_key)
def run(self): """ - 监听redis队列,发送push消息 - 从apns获取feedback service,处理无效token """ log.debug('starting a thread') self.rds = redis.Redis(**self.server_info) if self.job == 'push': self.push() elif self.job == 'feedback': self.feedback() log.debug('leaving a thread')
def feedback(self): """ 从apns获取feedback,处理无效token """ while(self.alive): try: self.reconnect() for (token, fail_time) in self.apns.feedback_server.items(): log.debug('push message fail to send to %s.' % token) # send a empty msg to confirm the token is valid self.client.push(token, enhance=True) except: self.log_error('get feedback fail') time.sleep(10) log.debug('i am leaving feedback')
def handle_error(self, identifier, errorcode): """处理推送错误 """ log.debug('apns sent back an error: %s %d', identifier, errorcode) if errorcode == 8: # add token to invalid token set sent = self.rds.smembers('ENHANCE_SENT:%s' % self.app_key) for s in sent: data = simplejson.loads(s) if data['id'] != identifier: continue token = data['token'] self.rds.sadd( '%s:%s' % (constants.INVALID_TOKENS, self.app_key), token) else: log.debug('not invalid token, ignore error')
def feedback(self): """ 从apns获取feedback,处理无效token """ while (self.alive): try: self.reconnect() for (token, fail_time) in self.apns.feedback_server.items(): log.debug('push message fail to send to %s.' % token) # send a empty msg to confirm the token is valid self.client.push(token, enhance=True) except: self.log_error('get feedback fail') time.sleep(10) log.debug('i am leaving feedback')
def handle_error(self, identifier, errorcode): """处理推送错误 """ log.debug('apns sent back an error: %s %d', identifier, errorcode) if errorcode == 8: # add token to invalid token set sent = self.rds.smembers('ENHANCE_SENT:%s' % self.app_key) for s in sent: data = simplejson.loads(s) if data['id'] != identifier: continue token = data['token'] self.rds.sadd('%s:%s' % (constants.INVALID_TOKENS, self.app_key), token) else: log.debug('not invalid token, ignore error')
def send_message(self, message): """ 发送消息,如果发生异常失败,重新连接再试一次,再失败则丢失 """ log.debug('get a message from channel') log.debug(message) try: if message['type'] != 'message': return self._send_message(message) except SSLError: self.log_error() self.resend(message) except socket.error: self.log_error() self.resend(message) except: self.log_error()
def consume_message(self, channel): # 再订阅消息队列 try: pubsub = self.rds.pubsub() pubsub.subscribe('%s:%s' % (channel, self.app_key)) log.debug('subscribe push job channel successfully') redis_channel = pubsub.listen() for message in redis_channel: self.retry_time = 0 if 'kill' == message['data']: break else: self.send_message(message) except: # 连接redis不上, 睡眠几秒钟重试连接 if self.retry_time <= self.retry_time_max: time.sleep(10) self.retry_time = self.retry_time + 1 log.debug(u'redis cannot connect, retry %d' % self.retry_time) self.consume_message(channel) else: # 这时,需要Email通知管理员了 log.error(u'retry time up, redis gone! help!') log.debug('i am leaving push')
def push_fallback(self, fallback): log.debug('handle fallback messages') old_msg = self.rds.spop('%s:%s' % (fallback, self.app_key)) while (old_msg): log.debug('handle message:%s' % old_msg) try: simplejson.loads(old_msg) self.send_message({'type': 'message', 'data': old_msg}) except: log.debug('message is not a json object') finally: old_msg = self.rds.spop('%s:%s' % (fallback, self.app_key))
def push_fallback(self, fallback): log.debug('handle fallback messages') old_msg = self.rds.spop('%s:%s' % (fallback, self.app_key)) while(old_msg): log.debug('handle message:%s' % old_msg) try: simplejson.loads(old_msg) self.send_message({'type': 'message', 'data': old_msg}) except: log.debug('message is not a json object') finally: old_msg = self.rds.spop('%s:%s' % (fallback, self.app_key))
def resend(self, message): log.debug('resending') self.reconnect() self._send_message(message)
def enhance_push(self): """ 使用增强版协议推送消息 """ self.host = 'localhost' index = self.rds.incr('ENHANCE_THREAD', 1) self.port = 9527 + index - 1 self.rds.hset('ENHANCE_PORT', ':'.join((self.app_key, 'dev' if self.develop else 'pro')), self.port) srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv_sock.bind((self.host, self.port)) srv_sock.listen(5) srv_sock.setblocking(0) self.apns.gateway_server._connect() cli_sock = self.apns.gateway_server._ssl rlist = [srv_sock, cli_sock] wlist = [] xlist = [] while self.alive: rl, wl, xl = select.select(rlist, wlist, xlist, 10) if rl: for r in rl: if r == srv_sock: # connection from client! try: new_sock, addr = srv_sock.accept() rlist.append(new_sock) continue except socket.error: pass elif r == cli_sock: # message from apns, some error eccour! error = self.apns.gateway_server.get_error() if not error: log.debug('apns drop the connection, reconnect!') rlist.remove(r) self.apns.gateway_server._disconnect() self.apns._gateway_connection = None continue else: self.handle_error(error[0], error[1]) else: # message from client buf = '' try: buf = r.recv(4096) except socket.error: rlist.remove(r) r.close() continue if not buf: # client close the socket. rlist.remove(r) r.close() continue try: # 如果还没有连接,或闲置时间过长 now = datetime.now() if not self.apns._gateway_connection: log.debug('无连接,重连') self.apns.gateway_server._connect() cli_sock = self.apns.gateway_server._ssl rlist.append(cli_sock) elif (now - self.last_sent_time).seconds > 300: log.debug('闲置时间过长,重连') self.apns.gateway_server._disconnect() self.apns._gateway_connection = None rlist.remove(cli_sock) self.apns.gateway_server._connect() cli_sock = self.apns.gateway_server._ssl rlist.append(cli_sock) log.debug('推送消息%s' % buf) self.send_enhance_message(buf) self.last_sent_time = now except socket.error: log.debug('send notification fail, reconnect')
def enhance_push(self): """ 使用增强版协议推送消息 """ self.host = 'localhost' index = self.rds.incr('ENHANCE_THREAD', 1) self.port = 9527 + index - 1 self.rds.hset( 'ENHANCE_PORT', ':'.join( (self.app_key, 'dev' if self.develop else 'pro')), self.port) srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv_sock.bind((self.host, self.port)) srv_sock.listen(5) srv_sock.setblocking(0) self.apns.gateway_server._connect() cli_sock = self.apns.gateway_server._ssl rlist = [srv_sock, cli_sock] wlist = [] xlist = [] while self.alive: rl, wl, xl = select.select(rlist, wlist, xlist, 10) if rl: for r in rl: if r == srv_sock: # connection from client! try: new_sock, addr = srv_sock.accept() rlist.append(new_sock) continue except socket.error: pass elif r == cli_sock: # message from apns, some error eccour! error = self.apns.gateway_server.get_error() if not error: log.debug('apns drop the connection, reconnect!') rlist.remove(r) self.apns.gateway_server._disconnect() self.apns._gateway_connection = None continue else: self.handle_error(error[0], error[1]) else: # message from client buf = '' try: buf = r.recv(4096) except socket.error: rlist.remove(r) r.close() continue if not buf: # client close the socket. rlist.remove(r) r.close() continue try: # 如果还没有连接,或闲置时间过长 now = datetime.now() if not self.apns._gateway_connection: log.debug('无连接,重连') self.apns.gateway_server._connect() cli_sock = self.apns.gateway_server._ssl rlist.append(cli_sock) elif (now - self.last_sent_time).seconds > 300: log.debug('闲置时间过长,重连') self.apns.gateway_server._disconnect() self.apns._gateway_connection = None rlist.remove(cli_sock) self.apns.gateway_server._connect() cli_sock = self.apns.gateway_server._ssl rlist.append(cli_sock) log.debug('推送消息%s' % buf) self.send_enhance_message(buf) self.last_sent_time = now except socket.error: log.debug('send notification fail, reconnect')