def _requeue_tasks_which_unconfirmed(self): lock_key = f'fsdf_lock__requeue_tasks_which_unconfirmed:{self._queue_name}' with decorators.RedisDistributedLockContextManager( self.redis_db_frame, lock_key, ) as lock: if lock.has_aquire_lock: self._distributed_consumer_statistics.send_heartbeat() current_queue_hearbeat_ids = self._distributed_consumer_statistics.get_queue_heartbeat_ids( without_time=True) current_queue_unacked_msg_queues = self.redis_db_frame.scan( 0, f'unack_{self._queue_name}_*', 100) for current_queue_unacked_msg_queue in current_queue_unacked_msg_queues[ 1]: current_queue_unacked_msg_queue_str = current_queue_unacked_msg_queue.decode( ) if current_queue_unacked_msg_queue_str.split( f'unack_{self._queue_name}_' )[1] not in current_queue_hearbeat_ids: msg_list = self.redis_db_frame.lrange( current_queue_unacked_msg_queue_str, 0, -1) self.logger.warning( f"""{current_queue_unacked_msg_queue_str} 是掉线或关闭消费者的待确认任务, 将 一共 {len(msg_list)} 个消息, 详情是 {msg_list} 推送到正常消费队列 {self._queue_name} 队列中。 """) self.redis_db_frame.lpush(self._queue_name, *msg_list) self.redis_db_frame.delete( current_queue_unacked_msg_queue_str)
def _requeue_tasks_which_unconfirmed(self): ## 防止在多个进程或多个机器中同时做扫描和放入未确认消费的任务。使用个分布式锁。 lock_key = f'fsdf_lock__requeue_tasks_which_unconfirmed_timeout:{self._queue_name}' with decorators.RedisDistributedLockContextManager(self.redis_db_frame, lock_key, ) as lock: if lock.has_aquire_lock: time_max = time.time() - self.UNCONFIRMED_TIMEOUT for value in self.redis_db_frame.zrangebyscore(self._unack_zset_name, 0, time_max): self.logger.warning(f'向 {self._queue_name} 重新放入未消费确认的任务 {value}') self._requeue({'body': json.loads(value)}) self.redis_db_frame.zrem(self._unack_zset_name, value) self.logger.info(f'{self._unack_zset_name} 中有待确认消费任务的数量是' f' {self.redis_db_frame.zcard(self._unack_zset_name)}')
def _requeue_tasks_which_unconfirmed(self): lock_key = f'fsdf_lock__requeue_tasks_which_unconfirmed:{self._queue_name}' with decorators.RedisDistributedLockContextManager( self.redis_db_frame, lock_key, ) as lock: if lock.has_aquire_lock: self._distributed_consumer_statistics.send_heartbeat() current_queue_hearbeat_ids = self._distributed_consumer_statistics.get_queue_heartbeat_ids( without_time=True) xinfo_consumers = self.redis_db_frame_version3.xinfo_consumers( self._queue_name, self.GROUP) # print(current_queue_hearbeat_ids) # print(xinfo_consumers) for xinfo_item in xinfo_consumers: # print(xinfo_item) if xinfo_item['idle'] > 7 * 24 * 3600 * 1000 and xinfo_item[ 'pending'] == 0: self.redis_db_frame_version3.xgroup_delconsumer( self._queue_name, self.GROUP, xinfo_item['name']) if xinfo_item[ 'name'] not in current_queue_hearbeat_ids and xinfo_item[ 'pending'] > 0: # 说明这个消费者掉线断开或者关闭了。 pending_msg_list = self.redis_db_frame_version3.xpending_range( self._queue_name, self.GROUP, '-', '+', 1000, xinfo_item['name']) if pending_msg_list: # min_idle_time 不需要,因为加了分布式锁,所以不需要基于idle最小时间的判断,并且启动了基于心跳的确认消费助手,检测消费者掉线或关闭或断开的准确率100%。 xclaim_task_list = self.redis_db_frame_version3.xclaim( self._queue_name, self.GROUP, self.consumer_identification, force=True, min_idle_time=0 * 1000, message_ids=[ task_item['message_id'] for task_item in pending_msg_list ]) if xclaim_task_list: self.logger.warning( f' {self._queue_name} 的分组 {self.GROUP} 的消费者 {self.consumer_identification} 夺取 断开的消费者 {xinfo_item["name"]}' f' {len(xclaim_task_list)} 个任务,详细是 {xclaim_task_list} ' ) for task in xclaim_task_list: kw = { 'body': json.loads(task[1]['']), 'msg_id': task[0] } self._submit_task(kw)
def _requeue_tasks_which_unconfirmed(self): lock_key = f'fsdf_lock__requeue_tasks_which_unconfirmed:{self._queue_name}' with decorators.RedisDistributedLockContextManager(self.redis_db_frame, lock_key, ) as lock: if lock.has_aquire_lock: self._distributed_consumer_statistics.send_heartbeat() current_queue_hearbeat_ids = self._distributed_consumer_statistics.get_queue_heartbeat_ids(without_time=True) current_queue_unacked_msg_queues = self.redis_db_frame.scan(0, f'{self._queue_name}__unack_id_*', 100) # print(current_queue_unacked_msg_queues) for current_queue_unacked_msg_queue in current_queue_unacked_msg_queues[1]: current_queue_unacked_msg_queue_str = current_queue_unacked_msg_queue.decode() self.logger.info(f'{current_queue_unacked_msg_queue_str} 中有待确认消费任务的数量是' f' {self.redis_db_frame.zcard(current_queue_unacked_msg_queue_str)}') if current_queue_unacked_msg_queue_str.split(f'{self._queue_name}__unack_id_')[1] not in current_queue_hearbeat_ids: self.logger.warning(f'{current_queue_unacked_msg_queue_str} 是过期的') for unacked_task_str in self.redis_db_frame.zrevrange(current_queue_unacked_msg_queue_str, 0, 1000): self.logger.warning(f'从 {current_queue_unacked_msg_queue_str} 向 {self._queue_name} 重新放入未消费确认的任务 {unacked_task_str}') self.redis_db_frame.rpush(self._queue_name, unacked_task_str) self.redis_db_frame.zrem(current_queue_unacked_msg_queue_str, unacked_task_str) else: pass