【RabbitMQ篇】你如何保证全链路数据100%不丢失?

【Redis】如何在redis中防止消息丢失 在项目中,由于网络问题,我们很难保证生产者发送的消息能100%到达消息队列服务器,也就是说有消息丢失的可能性,因 此,生产者就必须具有消息丢失检测和重发机制,也就是我们常说的消息队列的事物机制。不能把可靠性的保证全部交给TCP,TCP只保证了传输层的可靠传输,但是无法保证与应用层的交互是否出错 TCP无法给应用层任何反馈,因此必须在应用层处理差错。 阅读详情

我们都知道,消息从生产端到消费端消费要经过3个步骤:

  1. 生产端发送消息到RabbitMQ;
  2. RabbitMQ发送消息到消费端;
  3. 消费端消费这条消息;
              在这里插入图片描述

  这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不丢失,就是可靠的了。下面来具体分析一下问题以及解决方案。


生产端可靠性投递

生产端可靠性投递,即生产端要确保将消息正确投递到RabbitMQ中。生产端投递的消息丢失的原因有很多,比如消息在网络传输的过程中发生网络故障消息丢失,或者消息投递到RabbitMQ时RabbitMQ挂了,那消息也可能丢失,而我们根本不知道发生了什么。针对以上情况,RabbitMQ本身提供了一些机制。


参考我的文章:RabbitMQ 之消息确认机制(事务+Confirm)

事务消息机制

事务消息机制由于会严重降低性能,所以一般不采用这种方法,我就不介绍了,而采用另一种轻量级的解决方案——confirm消息确认机制。


confirm消息确认机制

什么是confirm消息确认机制?顾名思义,就是生产端投递的消息一旦投递到RabbitMQ后,RabbitMQ就会发送一个确认消息给生产端,让生产端知道我已经收到消息了,否则这条消息就可能已经丢失了,需要生产端重新发送消息了。
              在这里插入图片描述
通过下面这句代码来开启确认模式:

channel.confirmSelect();// 开启发送方确认模式

然后异步监听确认和未确认的消息:

channel.addConfirmListener(new ConfirmListener() {
    //消息正确到达broker
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("已收到消息");
        //做一些其他处理
    }

    //RabbitMQ因为自身内部错误导致消息丢失,就会发送一条nack消息
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("未确认消息,标识:" + deliveryTag);
        //做一些其他处理,比如消息重发等
    }
});

这样就可以让生产端感知到消息是否投递到RabbitMQ中了,当然这样还不够,稍后我会说一下极端情况。


消息持久化

那消息持久化呢?我们知道,RabbitMQ收到消息后将这个消息暂时存在了内存中,那这就会有个问题,如果RabbitMQ挂了,那重启后数据就丢失了,所以相关的数据应该持久化到硬盘中,这样就算RabbitMQ重启后也可以到硬盘中取数据恢复。那如何持久化呢?

message消息到达RabbitMQ后先是到exchange交换机中,然后路由给queue队列,最后发送给消费端。

        在这里插入图片描述
所有需要给exchange、queue和message都进行持久化:

exchange持久化:

//第三个参数true表示这个exchange持久化
channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);

queue持久化:

//第二个参数true表示这个queue持久化
channel.queueDeclare(QUEUE_NAME, true, false, false, null);

message持久化:

//第三个参数MessageProperties.PERSISTENT_TEXT_PLAIN表示这条消息持久化
channel.basicPublish
(
EXCHANGE_NAME, 
ROUTING_KEY,
MessageProperties.PERSISTENT_TEXT_PLAIN, 
message.getBytes(StandardCharsets.UTF_8
));

这样,如果RabbitMQ收到消息后挂了,重启后会自行恢复消息。

到此,RabbitMQ提供的几种机制都介绍完了,但这样还不足以保证消息可靠性投递RabbitMQ中,上面我也提到了会有极端情况,比如RabbitMQ收到消息还没来得及将消息持久化到硬盘时,RabbitMQ挂了,这样消息还是丢失了,或者RabbitMQ在发送确认消息给生产端的过程中,由于网络故障而导致生产端没有收到确认消息,这样生产端就不知道RabbitMQ到底有没有收到消息,就不好做接下来的处理。

        在这里插入图片描述
所以除了RabbitMQ提供的一些机制外,我们自己也要做一些消息补偿机制,以应对一些极端情况。
接下来我就介绍其中的一种解决方案——消息入库。


消息入库

消息入库,顾名思义就是将要发送的消息保存到数据库中。

首先发送消息前先将消息保存到数据库中,有一个状态字段status=0,表示生产端将消息发送给了RabbitMQ但还没收到确认;在生产端收到确认后将status设为1,表示RabbitMQ已收到消息。这里有可能会出现上面说的两种情况,所以生产端这边开一个定时器,定时检索消息表,将status=0并且超过固定时间后(可能消息刚发出去还没来得及确认这边定时器刚好检索到这条status=0的消息,所以给个时间)还没收到确认的消息取出重发(第二种情况下这里会造成消息重复,消费者端要做幂等性),可能重发还会失败,所以可以做一个最大重发次数,超过就做另外的处理。
     在这里插入图片描述
这样消息就可以可靠性投递到RabbitMQ中了,而生产端也可以感知到了。


消费端消息不丢失

既然已经可以让生产端100%可靠性投递到RabbitMQ了,那接下来就改看看消费端的了,如何让消费端不丢失消息。

默认情况下,以下3种情况会导致消息丢失:

  • 在RabbitMQ将消息发出后,消费端还没接收到消息之前,发生网络故障,消费端与RabbitMQ断开连接,此时消息会丢失;
  • 在RabbitMQ将消息发出后,消费端还没接收到消息之前,消费端挂了,此时消息会丢失;
  • 消费端正确接收到消息,但在处理消息的过程中发生异常或宕机了,消息也会丢失。
         在这里插入图片描述
    其实,上述3中情况导致消息丢失归根结底是因为RabbitMQ的自动ack机制,即默认RabbitMQ在消息发出后就立即将这条消息删除,而不管消费端是否接收到,是否处理完,导致消费端消息丢失时RabbitMQ自己又没有这条消息了。

             在这里插入图片描述
所以就需要将自动ack机制改为手动ack机制。

实现ChannelAwareMessageListener 接口

package com.mq.rabbitmq;
 
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
 
import com.rabbitmq.client.Channel;

@Component
public class ChannleConsumer implements ChannelAwareMessageListener {

	 /**
     * RabbitListener:方法上的注解,声明这个方法是一个消费者方法,需要指定下面的属性:
     * bindings:指定绑定关系,可以有多个。值是@QueueBinding的数组。@QueueBinding包含下面属性:
     * value:这个消费者关联的队列。值是@Queue,代表一个队列
     * exchange:队列所绑定的交换机,值是@Exchange类型
     * key:队列和交换机绑定的RoutingKey,可指定多个
     */
    //监听日志队列
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = QUEUE_LOG, durable = "true"),
            exchange = @Exchange(
                    value = EXCHANGE_NAME,
                    ignoreDeclarationExceptions = "true",
                    type = ExchangeTypes.TOPIC
            ),
            key = {ROUTING_KEY_LOG}))
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		try {
			//消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
			channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			System.out.println("onMessage方法结束");
		} catch (Exception e) {
			//ack返回false,并重新回到队列,api里面解释得很清楚
			channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
			e.printStackTrace();
			System.out.println("onMessage方法异常结束");
		}
	}
 
}

//消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息

channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

//ack返回false,并重新回到队列,api里面解释得很清楚

channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);

//拒绝消息

channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);

这样,当autoAck参数置为false,对于RabbitMQ服务端而言,队列中的消息分成了两个部分:一部分是等待投递给消费端的消息;一部分是已经投递给消费端,但是还没有收到消费端确认信号的消息。如果RabbitMQ一直没有收到消费端的确认信号,并且消费此消息的消费端已经断开连接或宕机(RabbitMQ会自己感知到),则RabbitMQ会安排该消息重新进入队列(放在队列头部),等待投递给下一个消费者,当然也有能还是原来的那个消费端,当然消费端也需要确保幂等性。

好了,到此从生产端到RabbitMQ再到消费端的全链路,就可以保证数据的不丢失。

Clion编译stm32cube初始化工程时non constant or forward reference address expression for section .ARM.extab报错 相信应该有同学在看了稚晖君关于clion开发stm32的文章之后也想在自己PC上实现“优雅の嵌入式开发”遇到和我类似的问题。他的文章是22年写的,我个人猜测应当是版本更新导致的不适配。意思就是说 READONLY 只能在gcc11以后的版本中使用,gcc10及以前的版本将无法正确识别,也就有如下错误。或者就可以将gcc升级至最新的版本。记得在clion工具链中修改c,cpp编译器的位置。可以将文本括号中的 READONLY 全部删除。当时由于没有注意到这行注释导致在这里卡了好久。 阅读详情

相关推荐

从ADRV9002到ADRV9003:手把手教你移植FPGA驱动,避开那些官方没说的坑

本文详细介绍了从ADRV9002到ADRV9003的FPGA驱动移植实战指南,重点解析了两者在硬件架构上的关键差异,如通道配置和DPD功能的移除。通过提供完整的移植步骤、调试技巧和性能优化建议,帮助工程师避开官方文档未提及的常见陷阱,实现高效驱动开发。

weixin_28738397的博客 174

面试官:引入RabbitMQ后,你如何保证全链路数据100%不丢失?

来源:blog.youkuaiyun.com/hsz2568952354/article/details/86559470生产端可靠性投递事务消息机制confirm消息确认机制消息持久化消息入库消费端消息不丢失我们都知道,消息从生产端到消费端消费要经过3个步骤:生产端发送消息到RabbitMQ;RabbitMQ发送消息到消费端;消费端消费这条消息;这3个步骤中的每一步都有可能导致消息...

HollisChuang's Blog 531

单波束与多波束声呐:从原理到应用场景的全面解析

本文全面解析了单波束与多波束声呐的工作原理、核心特点及应用场景。单波束声呐如精准的狙击手,适合高精度需求;多波束声呐则像高效的散弹枪,适用于快速测绘和大面积覆盖。文章还提供了选型指南和前沿发展趋势,帮助用户根据预算和需求选择最适合的声呐技术。

weixin_30872157的博客 220

RabbitMQ如何保证数据不丢失

第三个是开启消费者确认机制为auto,由spring确认消息处理成功后完成ack,当然也需要设置一定的重试次数,我们当时设置了3次,如果重试3次还没有收到消息,就将失败后的消息投递到异常交换机,交由人工处理。MYSQL和Redis的数据双写一致性采用RabbitMQ实现同步的,这里面就要求了消息的高可用性,我们要保证消息的不丢失。下面是一个简化的代码示例,展示了如何使用Spring Boot和RabbitMQ来保证MySQL和Redis的数据双写一致性。6. RabbitMQ配置类。

weixin_46432131的博客 622

基于Simulink的直流电机双闭环调速系统抗扰动仿真与参数优化

本文详细介绍了基于Simulink的直流电机双闭环调速系统仿真与参数优化方法。通过构建转速外环与电流内环的仿真模型,阐述了利用工程设计法整定PI参数的核心步骤,并重点探讨了如何优化系统参数以增强其抗负载突变等扰动的能力,实现快速、平稳的调速性能。

weixin_29061997的博客 192

FMCW雷达基本原理(通俗易懂)篇2

FMCW雷达(Frequency Modulated Continuous Wave Radar)是一种使用频率调制连续波的雷达系统。它使用一个发射器发射一个连续波,但是这个连续波的频率会在一定的范围内不断变化。当这个连续波遇到一个目标物体并被反射回来时,接收器会检测到这个反射波的频率变化,并计算出目标物体的距离和速度等信息。FMCW雷达的原理可以理解为:1.发射器发射一个连续波,这个波的频率会从一个固定的起始频率开始,逐渐变化至一个固定的终止频率。

研二小学鸡的博客 7687

RocketMQ如何保证消息的可靠性?

消息的发送方式有哪几种?存储消息的可靠性面临哪些挑战?消费消息的确认机制是怎样的?本文通过分析消息流转的整个过程,从消息发送、消息存储和消息消费三个阶段介绍RocketMQ是如何保证消息的可靠性的。 分布式系统中一个重要的前提假设是所有的网络传输都是不可靠的,在网络传输不可靠的情况下,保证消息的可靠传输,除了进行重试投递别无他法。常用的绝大多数消息队列RocketMQ、RabbitMQ等在消息传输上都只能保证至少传输成功一次,也即(At least once),而不能保证只传输成功一次(Exactl.

liuxing201122013的专栏 444

人工智能_大模型095_Web版003_deepseek R1本地安装_私有化部署_支持RAG_调用HTTPAPI做自己的AI应用_安装RagFlow以及使用_---人工智能工作笔记0240

这里的一定要注意,是资源不够用了,可以点击ragflow-server左边的小绿点,查看 结果。注意去把内存改成10G把至少,我这里改成了10G,因为电脑一共就16G,再试试吧.可以看日志,就显示的是没有连接上mysql,结果我一看,mysql,根本没启动.然后下面选择general就可以了,然后点击确定,然后再去点击。手动去启动,然后再去试试,注意,内存这里给8G太小了还是.调成8G了,然后硬盘大了大了一些换成50G了,然后。点击系统模型设置可以看到,选择聊天模型就可以了,

添柴程序猿的专栏 447

RabbitMQ 保证消息不丢失的几种手段

我们通过分析消息从生产者发送消息到消费者消费消息的全过程,得出了消息可能丢失的几种场景,并给出了相应的解决方案,如果需要保证消息在整条链路中不丢失,那就需要生产端、mq自身与消费端共同去保障。生产端:对生产的消息进行状态标记,开启confirm机制,依据mq的响应来更新消息状态,使用定时任务重新投递超时的消息,多次投递失败进行报警。mq自身:开启持久化,并在落盘后再进行ack。如果是镜像部署模式,需要在同步到多个副本之后再进行ack。

zhiyikeji的博客 6164

基于Simscape的双PID比例调速阀与比例溢流阀液压控制系统建模与仿真

本文提出了一种基于Simscape的双PID液压控制系统建模方法,通过比例调速阀和比例溢流阀实现液压缸速度与压力的解耦控制。系统采用Simscape Fluids物理建模,精确模拟液压元件非线性特性。文章详细介绍了液压被控对象建模过程,包括动力单元、比例阀组合建模、执行机构及传感器配置。同时设计了双PID控制器架构,分别控制速度环和压力环,并给出参数整定建议。该方法通过物理网络直接模拟流体流动和压力动态,相比传统传递函数法具有更高精度,为液压系统仿真提供了有效解决方案。

AI的内部世界的博客 284

安装 miniconda linux

安装 miniconda linux

jacke121的专栏 1万+

如何保证RabbitMQ全链路数据100%不丢失

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Ma...

指尖凉的博客 8050

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

RabbitMQ - 保证全链路数据100%不丢失

我们都知道,消息从生产端到消费端消费要经过3个步骤: 生产端发送消息到RabbitMQ; RabbitMQ发送消息到消费端; 消费端消费这条消息; 这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不丢失,就是可靠的了。下面来具体分析一下问题以及解决方案。 1、生产端可靠性投递 生产端

。 6万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

快手二面:引入RabbitMQ后,你如何保证全链路数据100%不丢失?

我们都知道,消息从生产端到消费端消费要经过3个步骤:生产端发送消息到RabbitMQ;RabbitMQ发送消息到消费端;消费端消费这条消息;这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不丢失,就是可靠的了。下面来具体分析一下问题以及解决方案。

sjkflw121150的博客 672

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

RabbitMQ,怎么保证全链路数据100%不丢失

RabbitMQ,怎么保证全链路数据100%不丢失 一个消息从生产端到消费端消费要经过3个步骤: ​ - 1、生产端发送消息到MQ - 2、MQ发送消息到消费端 - 3、消费端消费这个消息 这三步都会可能导致消息的丢失,那我们就要制定一些措施来保证这些消息的可靠性,防止丢失了我们也不知道的情况。 生产端可靠性传递 生产端可靠性投递,即生产端要确保将消息正确投递到RabbitMQ中。生产端投递的消息丢失的原因有很多,比如消息在网络传输的过程中发生网络故障消息丢失,或者消息投递到RabbitMQ时

css1223mjf的专栏 610

如何保证RabbitMQ全链路数据100%不丢失?

点击上方终端研发部,选择设为星标每天9:30点,干货准时奉上!上一篇:接口性能优化技巧,有点硬...正在学RabbitMQ,特此记录一下,这里就不讲RabbitMQ基础了,直接进入主题。我们都知道,消息从生产端到消费端消费要经过3个步骤:生产端发送消息到RabbitMQ;RabbitMQ发送消息到消费端;消费端消费这条消息;这3个步骤中的每一步都有可能导致消息丢失,消...

这个时代,作为程序员可能要学习小程序 322

RabbitMQ后,你如何保证全链路数据100%不丢失

我们都知道,消息从生产端到消费端消费要经过3个步骤: 生产端发送消息到RabbitMQ; RabbitMQ发送消息到消费端; 消费端消费这条消息 这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不丢失,就是可靠的了。下面来具体分析一下问题以及解决方案。 1生产端可靠性投递 生产端可靠性投递,即生产

weixin_37624195的博客 170

如何保证Mq消息不丢失

mq: rabbitmq, rocketmq, kafka

959 4920

rabbitmq如何保证消息不丢失_如何保证RabbitMQ全链路数据100%不丢失

我们都知道,消息从生产端到消费端消费要经过3个步骤:生产端发送消息到RabbitMQ;RabbitMQ发送消息到消费端;消费端消费这条消息;这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不...

weixin_39963523的博客 236

引入RabbitMQ后,如何保证全链路数据100%不丢失

我们都知道,消息从生产端到消费端消费要经过3个步骤: 生产端发送消息到RabbitMQ; RabbitMQ发送消息到消费端; 消费端消费这条消息; 这3个步骤中的每一步都有可能导致消息丢失,消息丢失不可怕,可怕的是丢失了我们还不知道,所以要有一些措施来保证系统的可靠性。这里的可靠并不是一定就100%不丢失了,磁盘损坏,机房爆炸等等都能导致数据丢失,当然这种都是极小概率发生,能做到99.999999%消息不丢失,就是可靠的了。下面来具体分析一下问题以及解决方案。 生产端可靠

m0_37294522的博客 156

【MQ】如何保证消息中间件全链路数据100%不丢失(1)

目录 1、背景引入 2、前情回顾 3、目前已有的技术方案 4、数据100%不丢失了吗? 5、按需制定技术方案 6、需要研究的技术细节 1、背景引入 这篇文章,我们来聊聊在线上生产环境使用消息中间件技术的时候,从前到后的全链路到底如何保证数据不能丢失。 这个问题,在互联网公司面试的时候高频出现,而且也是非常现实的生产环境问题。 如果你的简历中写了自己熟悉MQ技术(RabbitMQ...

凡是过往,皆为序章 513

如何保证消息中间件全链路数据100%不丢失(1)【石杉的架构笔记】

**互联网面试必杀:如何保证消息中间件全链路数据100%不丢失(1)【石杉的架构笔记】 欢迎关注个人公众号:石杉的架构笔记(ID:shishan100) 周一至周五早8点半!精品技术文章准时送上! 1、背景引入 这篇文章,我们来聊聊在线上生产环境使用消息中间件技术的时候,从前到后的全链路到底如何保证数据不能丢失。 这个问题,在互联网公司面试的时候高频出现,而且也是非常现实的生产环境问题。 如果你的...

mhHao的博客 517
上一篇: 流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls)
下一篇: RabbitMQ 之消息确认机制(事务+Confirm)
_Romeo
_Romeo 优质创作者: 后端开发技术领域 优质创作者: 后端开发技术领域
博客等级 码龄8年 2万+粉丝 104原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

_Romeo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值