kombu 运行超长时间任务导致RabbitMQ消费者断开

LabVIew串口收发的一些问题 LabView串口收发实验总结 阅读详情

在排查异步任务没有执行的问题时,发现RabbitMQ管理端消息队列消费者异常断开,页面显示 … no consumers … 如下图所示
在这里插入图片描述
文章会引入测试代码模拟超长时间的异步任务,以及分析消费者为啥断开,最后给出解决方案

测试代码

异步任务执行使用的时 kombu的示例代码,参考 https://docs.celeryq.dev/projects/kombu/en/stable/userguide/examples.html
在这里插入图片描述

  1. queues.py
from __future__ import annotations

from kombu import Exchange, Queue

task_exchange = Exchange('kombu_big_tasks', type='direct')
task_queues = [Queue('hipri', task_exchange, routing_key='hipri'),
               Queue('midpri', task_exchange, routing_key='midpri'),
               Queue('lopri', task_exchange, routing_key='lopri')]

queues.py 定义了3个队列,它们绑定到同一个交换机,使用与队列名相同的routing_key。通过不同的队列名区分优先级(hipri高优先级、midpri中优先级、lopri低优先级)

  1. worker.py
from __future__ import annotations

import os

os.environ['KOMBU_LOG_CONNECTION'] = 'True'
os.environ['KOMBU_LOG_CHANNEL'] = 'True'

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

from kombu.log import get_logger
from kombu.mixins import ConsumerMixin
from kombu.utils.functional import reprcall

from queues import task_queues

logger = get_logger(__name__)


class Worker(ConsumerMixin):

    def __init__(self, connection):
        self.connection = connection

    def get_consumers(self, Consumer, channel):
        return [Consumer(queues=task_queues,
                         accept=['pickle', 'json'],
                         callbacks=[self.process_task],
                         prefetch_count=1)]

    def process_task(self, body, message):
        fun = body['fun']
        args = body['args']
        kwargs = body['kwargs']
        logger.info('Got task: %s', reprcall(fun.__name__, args, kwargs))
        try:
            fun(*args, **kwargs)
        except Exception as exc:
            logger.error('task raised exception: %r', exc)
        message.ack()


if __name__ == '__main__':
    from kombu import Connection
    from kombu.utils.debug import setup_logging

    # setup root logger
    setup_logging(loglevel=logging.DEBUG)

    with Connection('amqp://guest:guest@localhost:5672//', heartbeat=60) as conn:
        try:
            worker = Worker(conn)
            worker.run()
        except KeyboardInterrupt:
            print('bye bye')

work.py 主要用来执行异步任务,作为RabbitMQ 的消费者

  1. tasks.py
from __future__ import annotations

import time


def hello_task(who='world'):
    print(f"execute hello task")
    time.sleep(360)
    print(f'Hello {who}')

tasks.py 声明需要执行的任务的方法,由work.py 消费者调度。在这里引入time模块来模拟执行超长时间任务的情况

  1. client.py
from __future__ import annotations

from kombu.pools import producers

from queues import task_exchange

priority_to_routing_key = {
    'high': 'hipri',
    'mid': 'midpri',
    'low': 'lopri',
}


def send_as_task(connection, fun, args=(), kwargs={}, priority='mid'):
    payload = {'fun': fun, 'args': args, 'kwargs': kwargs}
    routing_key = priority_to_routing_key[priority]

    with producers[connection].acquire(block=True) as producer:
        producer.publish(payload,
                         serializer='pickle',
                         compression='bzip2',
                         exchange=task_exchange,
                         declare=[task_exchange],
                         routing_key=routing_key)


if __name__ == '__main__':
    from kombu import Connection

    from tasks import hello_task

    connection = Connection('amqp://guest:guest@localhost:5672//')
    send_as_task(connection, fun=hello_task, args=('Kombu',), kwargs={},
                 priority='high')

client.py 发送消息到RabbitMQ,属于生产者。消息体里面包含方法、参数

问题分析

  1. 执行 worker.py,会发现管理端会产生1个exchange以及3个queue
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    运行的日志输出
[Kombu connection:0x1eb707e80d0] establishing connection...
2025-07-19 15:55:05 - kombu.connection - DEBUG - [Kombu connection:0x1eb707e80d0] establishing connection...
2025-07-19 15:55:05 - amqp - DEBUG - Start from server, version: 0.9, properties: {'capabilities': {'publisher_confirms': True, 'exchange_exchange_bindings': True, 'basic.nack': True, 'consumer_cancel_notify': True, 'connection.blocked': True, 'consumer_priorities': True, 'authentication_failure_close': True, 'per_consumer_qos': True, 'direct_reply_to': True}, 'cluster_name': 'rabbit@Win10-2022QOGYT', 'copyright': 'Copyright (c) 2007-2025 Broadcom Inc and/or its subsidiaries', 'information': 'Licensed under the MPL 2.0. Website: https://rabbitmq.com', 'platform': 'Erlang/OTP 27.3.4.2', 'product': 'RabbitMQ', 'version': '4.1.2'}, mechanisms: [b'ANONYMOUS', b'PLAIN', b'AMQPLAIN'], locales: ['en_US']
[Kombu connection:0x1eb707e80d0] connection established: <Connection: amqp://guest:**@127.0.0.1:5672// at 0x1eb707e80d0>
2025-07-19 15:55:05 - kombu.connection - DEBUG - [Kombu connection:0x1eb707e80d0] connection established: <Connection: amqp://guest:**@127.0.0.1:5672// at 0x1eb707e80d0>
2025-07-19 15:55:05 - kombu.mixins - INFO - Connected to amqp://guest:**@127.0.0.1:5672//
[Kombu connection:0x1eb707e80d0] create channel
2025-07-19 15:55:05 - kombu.connection - DEBUG - [Kombu connection:0x1eb707e80d0] create channel
2025-07-19 15:55:05 - amqp - DEBUG - using channel_id: 1
2025-07-19 15:55:05 - amqp - DEBUG - Channel open
[Kombu channel:1] exchange_declare(exchange='kombu_big_tasks', type='direct', durable=True, auto_delete=False, arguments=None, nowait=False, passive=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] exchange_declare(exchange='kombu_big_tasks', type='direct', durable=True, auto_delete=False, arguments=None, nowait=False, passive=False)
[Kombu channel:1] prepare_queue_arguments({}, expires=None, message_ttl=None, max_length=None, max_length_bytes=None, max_priority=None)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] prepare_queue_arguments({}, expires=None, message_ttl=None, max_length=None, max_length_bytes=None, max_priority=None)
[Kombu channel:1] queue_declare(queue='hipri', passive=False, durable=True, exclusive=False, auto_delete=False, arguments={}, nowait=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] queue_declare(queue='hipri', passive=False, durable=True, exclusive=False, auto_delete=False, arguments={}, nowait=False)
[Kombu channel:1] queue_bind(queue='hipri', exchange='kombu_big_tasks', routing_key='hipri', arguments=None, nowait=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] queue_bind(queue='hipri', exchange='kombu_big_tasks', routing_key='hipri', arguments=None, nowait=False)
[Kombu channel:1] exchange_declare(exchange='kombu_big_tasks', type='direct', durable=True, auto_delete=False, arguments=None, nowait=False, passive=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] exchange_declare(exchange='kombu_big_tasks', type='direct', durable=True, auto_delete=False, arguments=None, nowait=False, passive=False)
[Kombu channel:1] prepare_queue_arguments({}, expires=None, message_ttl=None, max_length=None, max_length_bytes=None, max_priority=None)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] prepare_queue_arguments({}, expires=None, message_ttl=None, max_length=None, max_length_bytes=None, max_priority=None)
[Kombu channel:1] queue_declare(queue='midpri', passive=False, durable=True, exclusive=False, auto_delete=False, arguments={}, nowait=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] queue_declare(queue='midpri', passive=False, durable=True, exclusive=False, auto_delete=False, arguments={}, nowait=False)
[Kombu channel:1] queue_bind(queue='midpri', exchange='kombu_big_tasks', routing_key='midpri', arguments=None, nowait=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] queue_bind(queue='midpri', exchange='kombu_big_tasks', routing_key='midpri', arguments=None, nowait=False)
[Kombu channel:1] exchange_declare(exchange='kombu_big_tasks', type='direct', durable=True, auto_delete=False, arguments=None, nowait=False, passive=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] exchange_declare(exchange='kombu_big_tasks', type='direct', durable=True, auto_delete=False, arguments=None, nowait=False, passive=False)
[Kombu channel:1] prepare_queue_arguments({}, expires=None, message_ttl=None, max_length=None, max_length_bytes=None, max_priority=None)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] prepare_queue_arguments({}, expires=None, message_ttl=None, max_length=None, max_length_bytes=None, max_priority=None)
[Kombu channel:1] queue_declare(queue='lopri', passive=False, durable=True, exclusive=False, auto_delete=False, arguments={}, nowait=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] queue_declare(queue='lopri', passive=False, durable=True, exclusive=False, auto_delete=False, arguments={}, nowait=False)
[Kombu channel:1] queue_bind(queue='lopri', exchange='kombu_big_tasks', routing_key='lopri', arguments=None, nowait=False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] queue_bind(queue='lopri', exchange='kombu_big_tasks', routing_key='lopri', arguments=None, nowait=False)
[Kombu channel:1] basic_qos(0, 1, False)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] basic_qos(0, 1, False)
[Kombu channel:1] basic_consume(queue='hipri', no_ack=False, consumer_tag='None1', callback=<bound method Consumer._receive_callback of <Consumer: [<Queue hipri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> hipri bound to chan:1>, <Queue midpri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> midpri bound to chan:1>, <Queue lopri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> lopri bound to chan:1>]>>, nowait=True, arguments=None, on_cancel=None)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] basic_consume(queue='hipri', no_ack=False, consumer_tag='None1', callback=<bound method Consumer._receive_callback of <Consumer: [<Queue hipri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> hipri bound to chan:1>, <Queue midpri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> midpri bound to chan:1>, <Queue lopri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> lopri bound to chan:1>]>>, nowait=True, arguments=None, on_cancel=None)
[Kombu channel:1] basic_consume(queue='midpri', no_ack=False, consumer_tag='None2', callback=<bound method Consumer._receive_callback of <Consumer: [<Queue hipri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> hipri bound to chan:1>, <Queue midpri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> midpri bound to chan:1>, <Queue lopri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> lopri bound to chan:1>]>>, nowait=True, arguments=None, on_cancel=None)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] basic_consume(queue='midpri', no_ack=False, consumer_tag='None2', callback=<bound method Consumer._receive_callback of <Consumer: [<Queue hipri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> hipri bound to chan:1>, <Queue midpri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> midpri bound to chan:1>, <Queue lopri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> lopri bound to chan:1>]>>, nowait=True, arguments=None, on_cancel=None)
[Kombu channel:1] basic_consume(queue='lopri', no_ack=False, consumer_tag='None3', callback=<bound method Consumer._receive_callback of <Consumer: [<Queue hipri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> hipri bound to chan:1>, <Queue midpri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> midpri bound to chan:1>, <Queue lopri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> lopri bound to chan:1>]>>, nowait=False, arguments=None, on_cancel=None)
2025-07-19 15:55:05 - kombu.channel - DEBUG - [Kombu channel:1] basic_consume(queue='lopri', no_ack=False, consumer_tag='None3', callback=<bound method Consumer._receive_callback of <Consumer: [<Queue hipri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> hipri bound to chan:1>, <Queue midpri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> midpri bound to chan:1>, <Queue lopri -> <Exchange kombu_big_tasks(direct) bound to chan:1> -> lopri bound to chan:1>]>>, nowait=False, arguments=None, on_cancel=None)
2025-07-19 15:55:06 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:06 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : Prev sent/recv: None/None, now - 17/15, monotonic - 1966022.093, last_heartbeat_sent - 1966022.093, heartbeat int. - 60 for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:07 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:07 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : Prev sent/recv: 17/15, now - 17/15, monotonic - 1966023.093, last_heartbeat_sent - 1966022.093, heartbeat int. - 60 for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:08 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:08 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : Prev sent/recv: 17/15, now - 17/15, monotonic - 1966024.093, last_heartbeat_sent - 1966022.093, heartbeat int. - 60 for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:09 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:09 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : Prev sent/recv: 17/15, now - 17/15, monotonic - 1966025.093, last_heartbeat_sent - 1966022.093, heartbeat int. - 60 for connection e2f2a870a0e6451187c0fc3aef3d55bf
2025-07-19 15:55:10 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection e2f2a870a0e6451187c0fc3aef3d55bf

worker.py 开启了 debug级别的日志,方便排查问题。在没有执行超长时间任务之前,消费者发送心跳到RabbitMQ服务端,服务端感知消费者存在不会断开消费者的连接。
在这里插入图片描述
RabbitMQ 服务端的日志如下

2025-07-19 15:55:05.840000+08:00 [info] <0.1487.0> accepting AMQP connection 127.0.0.1:59923 -> 127.0.0.1:5672
2025-07-19 15:55:05.844000+08:00 [info] <0.1487.0> connection 127.0.0.1:59923 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'

本地是window系统启动的RabbitMQ,服务端的默认日志路径为
C:\Users%USERNAME%\AppData\Roaming\RabbitMQ\log
在这里插入图片描述
2. 执行 client.py,消息发送给服务端,worker.py 接收到消息之后,开始执行任务,任务开始执行后,心跳的运行日志没了,心跳发送被超长时间任务阻塞了,如下图所示
在这里插入图片描述
管理端这边也可以查询到有一个任务正在执行没有应答
在这里插入图片描述
等待一段时间之后,服务端没有接收到心跳,主动断开连接,服务端日志如下

2025-07-19 15:55:05.840000+08:00 [info] <0.1487.0> accepting AMQP connection 127.0.0.1:59923 -> 127.0.0.1:5672
2025-07-19 15:55:05.844000+08:00 [info] <0.1487.0> connection 127.0.0.1:59923 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:09:23.336000+08:00 [info] <0.1982.0> accepting AMQP connection 127.0.0.1:60967 -> 127.0.0.1:5672
2025-07-19 16:09:23.341000+08:00 [info] <0.1982.0> connection 127.0.0.1:60967 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:09:23.350000+08:00 [warning] <0.1982.0> closing AMQP connection <0.1982.0> (127.0.0.1:60967 -> 127.0.0.1:5672, vhost: '/', user: 'guest', duration: '14ms'):
2025-07-19 16:09:23.350000+08:00 [warning] <0.1982.0> client unexpectedly closed TCP connection
2025-07-19 16:12:05.968000+08:00 [error] <0.1487.0> closing AMQP connection <0.1487.0> (127.0.0.1:59923 -> 127.0.0.1:5672, duration: '17M, 0s'):
2025-07-19 16:12:05.968000+08:00 [error] <0.1487.0> missed heartbeats from client, timeout: 60s

服务端日志分析如下:
2025-07-19 15:55:05.840000 worker.py 运行,消费者连接上RabbitMQ,日志标识 <0.1487.0> 通道标识 59923
2025-07-19 16:09:23.336000 client.py 运行,生产者连接上RabbitMQ,日志标识 <0.1982.0> 通道标识60967, 投递消息任务到RabbitMQ
2025-07-19 16:09:23.341000 消息发送成功后,生产者断开连接 client unexpectedly closed TCP connection 日志标识 <0.1982.0>
2025-07-19 16:12:05.968000 服务端没有收到消费者的心跳 <0.1487.0> missed heartbeats from client, timeout: 60s, 断开连接 closing AMQP connection <0.1487.0>

服务端断开连接之后,消费者还在执行任务,任务执行完毕之后,进行应答,由于连接已经断开,worker.py(消费者)会产生错误,之后就会重连,日志如下

execute hello task
Hello Kombu
[Kombu channel:1] close()
2025-07-19 16:27:23 - kombu.channel - DEBUG - [Kombu channel:1] close()
2025-07-19 16:27:23 - amqp - DEBUG - Closed channel #1
[Kombu connection:0x1eb7089e950] closed
2025-07-19 16:27:23 - kombu.connection - DEBUG - [Kombu connection:0x1eb7089e950] closed
2025-07-19 16:27:23 - kombu.mixins - WARNING - Connection to broker lost, trying to re-establish connection...
Traceback (most recent call last):
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\mixins.py", line 174, in run
    for _ in self.consume(limit=None, **kwargs):
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\mixins.py", line 190, in consume
    with self.consumer_context(**kwargs) as (conn, channel, consumers):
  File "E:\Anaconda\Lib\contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\mixins.py", line 183, in consumer_context
    with self.Consumer() as (connection, channel, consumers):
  File "E:\Anaconda\Lib\contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\mixins.py", line 232, in Consumer
    with self._consume_from(*self.get_consumers(cls, channel)) as c:
  File "E:\Anaconda\Lib\contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\utils\compat.py", line 135, in nested
    reraise(exc[0], exc[1], exc[2])
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\exceptions.py", line 35, in reraise
    raise value
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\utils\compat.py", line 118, in nested
    vars.append(enter())
                ^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\messaging.py", line 464, in __enter__
    self.consume()
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\messaging.py", line 514, in consume
    self._basic_consume(T, no_ack=no_ack, nowait=False)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\messaging.py", line 641, in _basic_consume
    queue.consume(tag, self._receive_callback,
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\entity.py", line 750, in consume
    return self.channel.basic_consume(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\utils\debug.py", line 69, in __wrapped
    return meth(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\channel.py", line 1574, in basic_consume
    p = self.send_method(
        ^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\abstract_channel.py", line 79, in send_method
    return self.wait(wait, returns_tuple=returns_tuple)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\abstract_channel.py", line 99, in wait
    self.connection.drain_events(timeout=timeout)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\connection.py", line 526, in drain_events
    while not self.blocking_read(timeout):
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\connection.py", line 532, in blocking_read
    return self.on_inbound_frame(frame)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\method_framing.py", line 77, in on_frame
    callback(channel, msg.frame_method, msg.frame_args, msg)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\connection.py", line 538, in on_inbound_method
    return self.channels[channel_id].dispatch_method(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\abstract_channel.py", line 156, in dispatch_method
    listener(*args)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\channel.py", line 1629, in _on_basic_deliver
    fun(msg)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\messaging.py", line 668, in _receive_callback
    return on_m(message) if on_m else self.receive(decoded, message)
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\messaging.py", line 634, in receive
    [callback(body, message) for callback in callbacks]
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\messaging.py", line 634, in <listcomp>
    [callback(body, message) for callback in callbacks]
     ^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\worker.py", line 45, in process_task
    message.ack()
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\kombu\message.py", line 126, in ack
    self.channel.basic_ack(self.delivery_tag, multiple=multiple)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\channel.py", line 1407, in basic_ack
    return self.send_method(
           ^^^^^^^^^^^^^^^^^
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\abstract_channel.py", line 70, in send_method
    conn.frame_writer(1, self.channel_id, sig, args, content)
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\method_framing.py", line 186, in write_frame
    write(buffer_store.view[:offset])
  File "F:\Github\python\kombu_worker\.venv\Lib\site-packages\amqp\transport.py", line 350, in write
    self._write(s)
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
[Kombu connection:0x1eb708c1c50] establishing connection...
2025-07-19 16:27:23 - kombu.connection - DEBUG - [Kombu connection:0x1eb708c1c50] establishing connection...

这个超长时间的任务会循环的重复运行。消费者运行超长时间任务,阻塞心跳 -> 服务端断开连接,任务重新回到队列 -> 消费者应答失败,重新连接服务端 -> 消费者运行超长时间任务,阻塞心跳,如图所示
在这里插入图片描述
服务端的日志表现如下

2025-07-19 15:55:05.840000+08:00 [info] <0.1487.0> accepting AMQP connection 127.0.0.1:59923 -> 127.0.0.1:5672
2025-07-19 15:55:05.844000+08:00 [info] <0.1487.0> connection 127.0.0.1:59923 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:09:23.336000+08:00 [info] <0.1982.0> accepting AMQP connection 127.0.0.1:60967 -> 127.0.0.1:5672
2025-07-19 16:09:23.341000+08:00 [info] <0.1982.0> connection 127.0.0.1:60967 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:09:23.350000+08:00 [warning] <0.1982.0> closing AMQP connection <0.1982.0> (127.0.0.1:60967 -> 127.0.0.1:5672, vhost: '/', user: 'guest', duration: '14ms'):
2025-07-19 16:09:23.350000+08:00 [warning] <0.1982.0> client unexpectedly closed TCP connection
2025-07-19 16:12:05.968000+08:00 [error] <0.1487.0> closing AMQP connection <0.1487.0> (127.0.0.1:59923 -> 127.0.0.1:5672, duration: '17M, 0s'):
2025-07-19 16:12:05.968000+08:00 [error] <0.1487.0> missed heartbeats from client, timeout: 60s
2025-07-19 16:15:23.418000+08:00 [info] <0.2184.0> accepting AMQP connection 127.0.0.1:61211 -> 127.0.0.1:5672
2025-07-19 16:15:23.425000+08:00 [info] <0.2184.0> connection 127.0.0.1:61211 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:18:23.455000+08:00 [error] <0.2184.0> closing AMQP connection <0.2184.0> (127.0.0.1:61211 -> 127.0.0.1:5672, duration: '3M, 0s'):
2025-07-19 16:18:23.455000+08:00 [error] <0.2184.0> missed heartbeats from client, timeout: 60s
2025-07-19 16:21:23.448000+08:00 [info] <0.2248.0> accepting AMQP connection 127.0.0.1:61377 -> 127.0.0.1:5672
2025-07-19 16:21:23.453000+08:00 [info] <0.2248.0> connection 127.0.0.1:61377 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:24:23.484000+08:00 [error] <0.2248.0> closing AMQP connection <0.2248.0> (127.0.0.1:61377 -> 127.0.0.1:5672, duration: '3M, 0s'):
2025-07-19 16:24:23.484000+08:00 [error] <0.2248.0> missed heartbeats from client, timeout: 60s
2025-07-19 16:27:23.474000+08:00 [info] <0.2312.0> accepting AMQP connection 127.0.0.1:61524 -> 127.0.0.1:5672
2025-07-19 16:27:23.481000+08:00 [info] <0.2312.0> connection 127.0.0.1:61524 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:30:23.502000+08:00 [error] <0.2312.0> closing AMQP connection <0.2312.0> (127.0.0.1:61524 -> 127.0.0.1:5672, duration: '3M, 0s'):
2025-07-19 16:30:23.502000+08:00 [error] <0.2312.0> missed heartbeats from client, timeout: 60s
2025-07-19 16:33:23.507000+08:00 [info] <0.2489.0> accepting AMQP connection 127.0.0.1:61749 -> 127.0.0.1:5672
2025-07-19 16:33:23.512000+08:00 [info] <0.2489.0> connection 127.0.0.1:61749 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'
2025-07-19 16:36:23.543000+08:00 [error] <0.2489.0> closing AMQP connection <0.2489.0> (127.0.0.1:61749 -> 127.0.0.1:5672, duration: '3M, 0s'):
2025-07-19 16:36:23.543000+08:00 [error] <0.2489.0> missed heartbeats from client, timeout: 60s
2025-07-19 16:39:23.533000+08:00 [info] <0.2732.0> accepting AMQP connection 127.0.0.1:62055 -> 127.0.0.1:5672
2025-07-19 16:39:23.538000+08:00 [info] <0.2732.0> connection 127.0.0.1:62055 -> 127.0.0.1:5672: user 'guest' authenticated and granted access to vhost '/'

管理端可以查看到消费者断开的间隙,消费端正在运行超长时间任务,阻塞了心跳
在这里插入图片描述
在这里插入图片描述

解决方法

使用异步线程运行超长时间任务 防止心跳阻塞

修该 worker.py,增加线程池,异步执行任务

from __future__ import annotations

import os

os.environ['KOMBU_LOG_CONNECTION'] = 'True'
os.environ['KOMBU_LOG_CHANNEL'] = 'True'

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

from kombu.log import get_logger
from kombu.mixins import ConsumerMixin
from kombu.utils.functional import reprcall
from queues import task_queues
from concurrent.futures import ThreadPoolExecutor

logger = get_logger(__name__)


class Worker(ConsumerMixin):

    def __init__(self, connection):
        self.connection = connection
        self.thread_pool = ThreadPoolExecutor(max_workers=4, thread_name_prefix='queue_worker')

    def get_consumers(self, Consumer, channel):
        return [Consumer(queues=task_queues,
                         accept=['pickle', 'json'],
                         callbacks=[self.on_message],
                         prefetch_count=1)]

    def on_message(self, body, message):
        self.thread_pool.submit(self.process_task, body, message)

    def process_task(self, body, message):
        fun = body['fun']
        args = body['args']
        kwargs = body['kwargs']
        logger.info('Got task: %s', reprcall(fun.__name__, args, kwargs))
        try:
            fun(*args, **kwargs)
        except Exception as exc:
            logger.error('task raised exception: %r', exc)
        message.ack()


if __name__ == '__main__':
    from kombu import Connection
    from kombu.utils.debug import setup_logging

    # setup root logger
    setup_logging(loglevel=logging.DEBUG)

    with Connection('amqp://guest:guest@localhost:5672//', heartbeat=60) as conn:
        try:
            worker = Worker(conn)
            worker.run()
        except KeyboardInterrupt:
            print('bye bye')

在运行任务的时候,不会阻塞心跳了,worker.py日志如下

2025-07-19 16:56:19 - kombu.channel - DEBUG - [Kombu channel:1] message_to_python(<amqp.basic_message.Message object at 0x000001B436F16C30>)
2025-07-19 16:56:19 - __main__ - INFO - Got task: hello_task('Kombu')
execute hello task
2025-07-19 16:56:20 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection 7fb334777d214ef1ac01025683428e30
2025-07-19 16:56:20 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : Prev sent/recv: None/None, now - 17/18, monotonic - 1969696.078, last_heartbeat_sent - 1969696.078, heartbeat int. - 60 for connection 7fb334777d214ef1ac01025683428e30
2025-07-19 16:56:21 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection 7fb334777d214ef1ac01025683428e30
2025-07-19 16:56:21 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : Prev sent/recv: 17/18, now - 17/18, monotonic - 1969697.078, last_heartbeat_sent - 1969696.078, heartbeat int. - 60 for connection 7fb334777d214ef1ac01025683428e30
2025-07-19 16:56:22 - amqp.connection.Connection.heartbeat_tick - DEBUG - heartbeat_tick : for connection 7fb334777d214ef1ac01025683428e30

可以看到管理端的任务运行完了
在这里插入图片描述

实测对比:DeepSeek、豆包、千问三大AI模型生成测试用例谁更强?(附完整代码) 本文通过实测对比了DeepSeek、豆包、千问三大AI模型在生成测试用例方面的表现。文章提供了完整的Python代码实现,详细分析了各模型在用例完整性、准确性、边界覆盖及幻觉控制等维度的差异,并分享了工程化集成与多模型融合等优化策略,旨在帮助测试团队选择合适工具以提升工作效率。 阅读详情

相关推荐

RK3576+IMX415 MIPI摄像头零拷贝显示系统

MIPI CSI-2是嵌入式视觉系统中主流的高速图像传感器接口标准,其核心价值在于低延迟、高带宽与硬件级DMA直通能力。基于V4L2子系统与DRM/KMS驱动栈,可构建从传感器RAW数据到帧缓冲区的零拷贝通路,显著降低CPU负载并提升实时性。该技术广泛应用于工业检测、边缘AI推理前数据接入及多路同步采集等场景。本文以Rockchip RK3576 SoC和索尼IMX415传感器为典型组合,详解MIPI物理层适配、内核驱动集成、GStreamer零拷贝管道搭建及systemd服务化部署,覆盖从硬件信号完整性到

weixin_36047538的博客 216

一文教你如何解决RabbitMQ队列无消费者

一、基础知识 Spring 通过发布事件的方式,可以通知观察者(即事件监听器)消费者的一些行为,消费者相关的事件如下所示: AsyncConsumerStartedEvent:An event that is published whenever a new consumer is started. AsyncConsumerStoppedEvent:An event that is pu...

写Bug的小白的博客 1万+

WPF数据可视化:LiveCharts自定义X轴与Y轴的5个实战技巧(附完整代码)

本文详细介绍了在WPF应用中使用LiveCharts进行数据可视化时,自定义X轴与Y轴的5个核心实战技巧。内容涵盖精确控制刻度步距、深度定制视觉样式、实现多轴布局对比、优化显示空间以及构建响应式动态轴,并提供了完整的C#和XAML代码示例,帮助开发者创建专业且用户体验出色的图表。

moss5的博客 690

记录一次联调过程中RabbitMq下面无法找到消费者

这两天做系统优化处理,要给客端在某个节点发送im消息(Im底层不用关注,公司基础技术平台提供的通用接口),功能都开发完了,自测也通过了,昨天开始与App联调,一切都是好好的。但是在联调过程中出现了插曲。通过RabbitMq的管理页面Publish消息,在客户端的程序收不到有效的消息,尝试直接向队列中Publish消息,发现消息存在于queue中,处于Ready状态,并未进行消费,于是百思不得其解,在管理页面发现Consumers没有消费者,咦,哪里的问题,代码没动过,之前还是好的,尝试下重启机器。重试完了还

Lizhj的博客 1825

Windows系统监控实战:Grafana+Prometheus+node_exporter保姆级配置指南(含常见问题解决)

本文提供了一份详尽的Windows系统监控实战指南,详细讲解了如何从零部署Grafana、Prometheus和node_exporter(实际为windows_exporter)监控栈。内容涵盖核心组件认知、分步配置解析、高级生产环境考量,并重点针对配置过程中常见的“No Data”问题提供了系统性的排查手册,旨在帮助用户快速搭建稳定可靠的系统资源监控平台。

weixin_29197835的博客 1008

Kalibr标定避坑指南:如何用AprilGrid板快速完成双目IMU标定

本文详细介绍了使用AprilGrid板进行Kalibr双目IMU联合标定的实战技巧,包括标定工具选型、数据采集优化、标定流程及结果验证。通过AprilGrid板的唯一标识性和抗遮挡优势,结合IMU静止标定和运动激励设计,显著提升标定精度。文章还提供了常见报错处理和高级优化技巧,帮助开发者高效完成传感器标定。

sunny的博客 282

rabbitmq发送消息成功无消费者消费

由于是外部项目,不是自己写的,出现了这个问题;试了各种办法都没用,自己搭建demo都正常,肯定是项目代码哪边配置或者代码逻辑出了问题,进入mq后台管理发现 没有消费者注册。最后在对比排除各种情况后发现是spring.rabbitmq.listener.simple.auto-startup的值设置为了false,容器启动时不注册listener,改为true后接收到消息并消费。 ...

timeless的博客 4162

17、扩展景深成像的瞳孔函数工程技术解析

本文详细解析了扩展景深成像的关键技术,包括基于焦距堆栈的数字处理方法、立方相位掩模的光学设计、对数非球面透镜的应用以及旋转点扩散函数的深度编码功能。通过实验验证和实际应用分析,展示了这些技术在显微镜成像、工业检测和医疗影像中的广泛用途及优势。同时探讨了未来发展方向,如智能算法优化、新型光学材料和多模态成像技术,为相关领域的技术创新提供了参考。

y2z3a4b5c的博客 168

RabbitMQ消费者不消费问题的一种处理办法

问题 现象:消费者接收不到MQ的消费数据,MQ管理后台数据阻塞。 排查 发现阻塞的队列(queue)找不到消费者(consumer)服务器。报错:… no consumers … 解决 删除队列,点击删除按钮(Delete) 结果 队列(queue)找到消费者(consumer)服务器,大功告成。 ...

翟鳳璽的博客 1万+

如何懂固高卡以及滚筒轴+平移轴配合运动?

🏆本文收录于专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家!持续更新中,up!up!up!!

**My Coding Family** 842

DeepSeek 赋能智慧消防:以 AI 之力筑牢城市安全 “防火墙”

在数字化浪潮下,智慧消防系统亟需先进技术提升效能。DeepSeek 凭借 Transformer 架构与混合专家架构等核心技术,具备强大的多模态数据处理与智能决策能力,且支持本地化部署,保障数据安全。其在智慧消防领域的应用成效显著,从实现火灾风险动态感知与早期精准预警,到优化智能接警调度、设备健康管理,再到辅助应急指挥与疏散,有效降低火灾风险与事故损失。未来,DeepSeek 有望在智慧消防领域持续拓展,同时也需应对技术、数据等多方面挑战。

邓邓子的博客 1690

重启rabbitmq后,队列中没有消费者的原因

问题: 上次生产服务器出问题,导致上面的rabbitmq主节点和一系列应用服务宕机。运维先重启应用服务(消费者)后再启动MQ主节点后发现,主节点队列上堆积的消息没有被消费,点进队列一看,没有消费者! 场景复现: 在开发环境搭建mq集群,模拟生产场景。 场景重现: 启动消费者应用,可以发现日志报错信息如下: 解释一下意思就是:消费者发现没有要监听的 queue 时,默认会进行三次重试监听 queue,三次都失败后就无法重试了(三次时间很短,默认10...

桔子大大 7936

【人工智能】架构演进:基于本体论与 LLM Agent 的供应链备件决策闭环实践

本文探讨了如何将本体论(Ontology)与大语言模型(LLM)Agent结合,构建供应链备件管理的智能决策系统。针对LLM在垂直领域存在的幻觉和逻辑缺陷问题,提出通过本体论建立结构化知识框架,形成四层架构:知识建模、动态感知、深度分析和决策闭环。系统利用图数据库实例化领域本体,融合多源数据实现实时感知,结合图谱推理和时序预测进行确定性分析,最终通过工具调用完成决策执行。技术选型推荐Gemini 1.5 Pro、Neo4j和LangChain等工具,实现从自动化到智能化的升级。该架构为工业领域提供了可解释、

奶爸的编程之路,也就一周冷个三天 168

openstack学习之路-集群(rabbitmq)

openstack搭建集群笔记-rabbitmq

weixin_58567821的博客 1576

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

RabbitMQ 常见问题与故障排查

在使用 RabbitMQ 的过程中,可能会遇到各种问题。以下是对常见问题及其解决方案的详细描述,以及性能问题的排查方法和日志分析技巧。

StaticKing的博客 5572

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

解决一次rabbitmq的消费者消费不了消息的情况

1.问题描述 通过rabbitmq队列,消费方出现不能消费消息的情况,经过细查rabbitmq的配置没有问题,然而消费方不能执行逻辑。 交换机配置exchange: 交换机绑定的队列: 同样也发现properties文件中的配置是正确的: spring.rabbitmq.port = 5672 spring.rabbitmq.username = admin spring.rabbitmq.password = spring.rabbitmq.virtual-host = /dev spring.r

m0_67391121的博客 3269

Rabbitmq direct模式保证一个队列只对应一个消费者

RabbitMQ提供了四种Exchange:fanout,direct,topic,header性能排序:fanout > direct >> topic。比例大约为11:10:6一 、消息分发 官网的示例中介绍了默认情况下rabbitMqRabbitMQ会一个一个的发送信息给下一个消费者(consumer),而不考虑每个任务的时长等等,且是一次性分配,并非一个一个分配。平均的每个消费者将会获得相等数量的消息。这样分发消息的方式叫做round-robin。二、消息应答-保证消息被正确接受并处理 默认情况下消

我的博客 1767

【问题系列】消费者与MQ连接断开问题解决方案(一)

当使用RabbitMQ作为中间件,而消费者为服务时,可能会出现以下情况:在长时间没有消息传递后,消费者与RabbitMQ之间出现连接断开,导致无法处理新消息。解决这一问题的方法是重启Python消费者服务,之后连接恢复正常。

颜淡慕潇 2万+

RabbitMQ 工作队列

工作队列 2.1模型 生产者将消息放入消息队列,有两个以上的消费者进行消费。 为什么会有工作队列 上文介绍了simple队列,一一对应的;而且生产者发送消息很快,不费时间;消费者需要处理业务,花费时间较多;消息队列容易挤压大量消息; 2.2增加消费者C1,C2 public class RecvC1 { private final static String QUEUE_NAME = "...

wsdfym的博客 347

RabbitMQ异常重启,部分消费队列不消费问题

RabbitMQ版本: RabbitMQ 3.6.8, Erlang 19.0.4 jar包版本: spring-boot-starter-amqp 2.0.4.RELEASE 问题背景: 之前线上服务曾经出现MQ重启之后,服务不消费队列消息,其具体表现为Queue没有Consumer。但是当时没有拿到日志,并且本着对RabbitMQ这类 中间件的信任,也并没有去做过多的深入研究,觉得是极其偶然事件。但是今天上午线上某个服务的RabbitMQ由于不知名原因重启,导致出现同样现象。 该服务目前一共监听4个

lisheng19870305的专栏 5920

RabbitMQ 注册consumer

订阅方式其实是向queue注册consumer,通过rpc向queue server发送注册consumer的消息,rabbitMQ Server在收到消息后,根据消息的内容类型判断这是一个订阅消息,这样当MQ 中queue有消息时,会自动把消息通过该socket(长连接)通道发送出去...

farmwang的专栏 2150

rabbitmq-理论-工作队列

在第一篇教程中,我们已经写了一个从已知队列中发送和获取消息的程序。在这篇教程中,我们将创建一个工作队列(Work Queue),它会发送一些耗时的任务给多个工作者(Worker)。工作队列(又称:任务队列——Task Queues)是为了避免等待一些占用大量资源、时间的操作。当我们把任务(Task)当作消息发送到队列中,一个运行在后台的工作者(worker)进程就会取出任务然后处理。当你运行多个工...

zhuchunyan_aijia的博客 957

PHP 解决RabbitMQ消费者自动掉线问题

在cmd下运行php使用rabbitmq获取服务器消息消费时,碰到队列在运行几分钟之后卡死的状况, 之后再怎么向服务器发送消息,消费者都没有反应, 抓了一次数据包来查看,下图是消费者消费消息的,端口55155: 后来知道零值表示对等​​方(消费者)建议完全禁用心跳。 代码中默认是0: 后来重新抓了一次包,端口60351,队列里没有消息的时候, 后来看到它在进行心跳包检测: 最后主动断开了和消费者的连接 启动消费者的时候,在rabbitmq后台看到: ...

Make 4295

rabbitMQ自动断开问题解决

背景 负责的自定义查询系统,使用了rabbitMQ,来实现异步查询。 即web端查询,先提交到消息队列,同时发起轮询请求拉取查询结果; 多进程消费者从消息队列中取消息,触发查询引擎查询; 查询引擎响应后,产出文件到服务端;此时web端轮询请求,获取到响应,完成本次查询。 问题 系统运行过程中,性能很差,偶发卡顿现象;需要人为干预,杀死查询process才能保证系统正常。 定位过程 (1)以为是系统用户,看到不出结果,反复提交相同sql; (2)sql查询处,加了查询态不允许提交的前端限制,仍然有重复sql提

m0_67402096的博客 8735

celery连接rabbitmq报错“consumer: Cannot connect to amqp://******* vhost ‘test_vhosts‘ is down”

consumer: Cannot connect to amqp://test:**@172.17.0.1:5672/test_vhosts: Connection.open: (541) INTERNAL_ERROR - access to vhost '*' refused for user '*': vhost '*' is down

ling的专栏 1587

rabbitMq 全量同步数据 出现consumer和mq断开

有个需求,需要从其他系统全量同步供应商数据(大概有40万的信息,每个信息有大量的数据,需要保存到多个表中),第一次同步发下触发了GC,同时发现consumer从mq断开。 然后开始排查,第一步怀疑是oom,创建对象过多,cup高负荷运行,导致内存oom, 针对这个问题,使用了克隆clone方式,实体类实现 添加克隆后,启动全量同步,发现还是oom,然后又排查是否还有其他大对象生成,检查代码后,发现每个消息都会生成一个ArrayList,而且还没有默认容量,这样会导致扩容,扩容会导致消化大量的内存。解决方式

ouyang_x的博客 777
上一篇: mysql not in 查询引发的bug问题记录
下一篇: Go 编程语言指南 练习题目分享
Chengdu.S
博客等级 码龄9年 282粉丝 86原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chengdu.S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值