rabbitmq集群搭建

RabbitMQ 是一个开源的消息代理软件(message broker),它的主要功能是处理消息的收发。它实现了高级消息队列协议(AMQP,Advanced Message Queuing Protocol),用于在分布式系统中传递消息。RabbitMQ 可以在不同的应用之间高效地传递数据,从而解耦发送和接收消息的程序,实现异步通信、消息排队、消息路由等功能。

主要解决的问题

  • 解耦:生产者和消费者无需直接通信,可以通过 RabbitMQ 中间件传递消息,从而降低了系统之间的耦合度。
  • 异步处理:生产者发送消息后,不必等待消费者处理完毕,可以继续执行其他操作,提高系统的并发性和响应速度。
  • 负载均衡:通过消息队列,可以将负载分摊到多个消费者,提高系统的处理能力和稳定性。
  • 流量削峰:在高并发场景下,消息队列可以缓冲瞬间的大量请求,平滑处理负载,避免系统崩溃。
  • 可靠性:通过消息持久化、确认机制等,RabbitMQ 能保证消息不会丢失,确保消息的可靠传递。

底层原理
RabbitMQ 的底层实现基于 Erlang 编程语言,Erlang 以其在构建分布式系统和高可用系统方面的优势而著称。RabbitMQ 的架构主要包括以下几个组件:

  • 生产者(Producer):消息的发送者,将消息发送到 RabbitMQ。
  • 交换器(Exchange):接收生产者发送的消息,并根据路由规则将消息分发到相应的队列。常见的交换器类型有 Direct、Fanout、Topic 和 Headers。
  • 队列(Queue):存储消息的容器,消费者从队列中获取消息进行处理。
  • 消费者(Consumer):消息的接收者,从 RabbitMQ 中的队列获取消息并处理。
  • 绑定(Binding):定义交换器与队列之间的路由规则。

工作原理
1.消息发布:生产者将消息发送到交换器,同时指定路由键(Routing Key)。
2.消息路由:交换器根据绑定规则和路由键将消息路由到一个或多个队列。
3.消息存储:消息到达队列后,根据配置可以持久化存储,以保证消息不会因服务器宕机等问题而丢失。
4.消息消费:消费者订阅队列,RabbitMQ 将队列中的消息按照一定的策略(如轮询等)分发给消费者。
5.消息确认:消费者处理完消息后,可以发送确认信息给 RabbitMQ,
RabbitMQ 收到确认信息后才会将该消息从队列中移除。

RabbitMQ 的高可用性和分布式特性使其在处理大规模、高并发的消息传递场景中表现优异。其灵活的路由机制和可靠的消息传递机制,使其成为许多企业级应用的重要组件。

以下是对 Kafka、RabbitMQ、ActiveMQ、Redis、ZeroMQ、NSQ 和 NATS 这几种开源消息队列系统的优缺点的对比:

Kafka
优点:

高吞吐量,适合处理大规模的数据流。
水平可扩展性,易于增加节点来扩展集群容量。
高容错性,支持数据复制,防止数据丢失。

缺点:

复杂性较高,部署和维护需要较高的技术水平。
依赖 Zookeeper,增加了系统的复杂度。
延迟相对较高,不适合所有实时应用场景。
适用场景:实时数据流处理、大数据分析。

RabbitMQ
优点:

强大的路由功能,支持复杂的消息路由规则。
支持多种消息协议(如 AMQP、MQTT、STOMP)。
消息确认机制,保证消息可靠传递。

缺点:

配置和管理相对复杂。
在高吞吐量场景下性能不如 Kafka。
适用场景:分布式系统消息传递、异步处理、消息路由。

ActiveMQ
优点:

支持多种消息协议(如 AMQP、MQTT、STOMP、OpenWire)。
功能丰富,适合企业级应用。
支持事务性消息传递。

缺点:

性能一般,在高并发场景下表现不如其他消息队列。
资源占用较高,需要较多的内存和CPU资源。
配置复杂,维护成本较高。
适用场景:企业级消息传递、事务性消息传递、异步处理。

Redis
优点:

高性能,低延迟。
支持发布/订阅模式,适合实时消息传递。
简单易用,部署方便。

缺点:

不支持消息持久化,可能导致消息丢失。
在消息量大时内存消耗高。
适用场景:高性能消息传递、缓存、分布式锁。

ZeroMQ
优点:

高性能,低延迟。
支持多种通信模式(如请求-响应、发布-订阅、推-拉)。
轻量级,无需消息服务器。

缺点:

不支持消息持久化,需自行处理消息的存储和重发。
需要处理网络拓扑和消息序列化,增加开发复杂度。
适用场景:高性能通信、嵌入式系统、分布式计算。

NSQ
优点:

高可用性,去中心化设计,无单点故障。
简单易用,易于部署和维护。
良好的可扩展性,适合大规模消息处理。

缺点:

不支持消息持久化,可能导致消息丢失。
管理工具不够丰富,缺乏一些高级功能。
适用场景:实时消息处理、日志收集、事件驱动架构。

NATS
优点:

高性能,低延迟。
简单易用,适合快速部署。
适合云原生应用,支持 Kubernetes 等容器编排系统。

缺点:

功能相对简单,不适合复杂的消息传递需求。
持久化支持有限,不适合需要严格消息可靠性的场景。
适用场景:微服务通信、实时数据流、物联网(IoT)应用。

通过以上对比,可以根据具体的需求选择适合的消息队列系统。例如,若需要处理大规模数据流,可以选择 Apache Kafka;若需要复杂的消息路由和多协议支持,可以选择 RabbitMQ;若需要高性能和低延迟的消息传递,可以选择 Redis 或 ZeroMQ。

节点IP
rabbitmq01(磁盘节点)192.168.200.80
rabbitmq02(内存节点)192.168.200.81
rabbitmq03(内存节点)192.168.200.82

环境初始化

虚拟机环境centos7.9,使用脚本环境初始化,三个节点分别执行

[root@localhost ~]# cat init.sh
#!/bin/bash

# 定义节点信息
NODES=("192.168.200.80 rabbitmq01" "192.168.200.81 rabbitmq02" "192.168.200.82 rabbitmq03")

# 定义当前节点的密码(默认集群统一密码)
HOST_PASS="000000"

# 时间同步的目标节点
TIME_SERVER=rabbitmq01

# 时间同步的地址段
TIME_SERVER_IP=192.160.200.0/24

# 欢迎界面
cat > /etc/motd <<EOF
 ################################
 #    Welcome  to  mycluster    #
 ################################
EOF

#禁用selinux
sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0

#firewalld
systemctl stop firewalld
systemctl disable firewalld  >> /dev/null 2>&1

#关闭IPtables,清空规则
yum install  iptables-services  -y
if [ 0  -ne  $? ]; then
        echo -e "\033[31mThe installation source configuration errors\033[0m"
        exit 1
fi
systemctl restart iptables
iptables -F
iptables -X
iptables -Z
/usr/sbin/iptables-save
systemctl stop iptables
systemctl disable iptables

#禁用NetworkManager
systemctl stop NetworkManager >> /dev/null 2>&1
systemctl disable NetworkManager >> /dev/null 2>&1
yum remove -y NetworkManager firewalld
systemctl restart network

# 优化ssh连接
sed -i -e 's/#UseDNS yes/UseDNS no/g' -e 's/GSSAPIAuthentication yes/GSSAPIAuthentication no/g' /etc/ssh/sshd_config
systemctl reload sshd

# 修改主机名
for node in "${NODES[@]}"; do
  ip=$(echo "$node" | awk '{print $1}')
  hostname=$(echo "$node" | awk '{print $2}')

  # 获取当前节点的主机名和 IP
  current_ip=$(hostname -I | awk '{print $1}')
  current_hostname=$(hostname)

  # 检查当前节点与要修改的节点信息是否匹配
  if [[ "$current_ip" == "$ip" && "$current_hostname" != "$hostname" ]]; then
    echo "Updating hostname to $hostname on $current_ip..."
    hostnamectl set-hostname "$hostname"

    if [ $? -eq 0 ]; then
      echo "Hostname updated successfully."
    else
      echo "Failed to update hostname."
    fi

    break
  fi
done

# 遍历节点信息并添加到 hosts 文件
for node in "${NODES[@]}"; do
  ip=$(echo "$node" | awk '{print $1}')
  hostname=$(echo "$node" | awk '{print $2}')

  # 检查 hosts 文件中是否已存在相应的解析
  if grep -q "$ip $hostname" /etc/hosts; then
    echo "Host entry for $hostname already exists in /etc/hosts."
  else
    # 添加节点的解析条目到 hosts 文件
    sudo sh -c "echo '$ip $hostname' >> /etc/hosts"
    echo "Added host entry for $hostname in /etc/hosts."
  fi
done

if [[ ! -s ~/.ssh/id_rsa.pub ]]; then
    ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa -q -b 2048
fi

# 检查并安装 expect 工具
if ! which expect &> /dev/null; then
    echo "expect 工具未安装,正在安装 expect..."
    sudo yum install -y expect
fi

# 遍历所有节点
for node in "${NODES[@]}"; do
    ip=$(echo "$node" | awk '{print $1}')
    hostname=$(echo "$node" | awk '{print $2}')

    expect -c "
        set timeout -1
        spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $hostname
        expect {
            \"*password:*\" { send -- \"$HOST_PASS\r\"; exp_continue }
            \"*(yes/no)*\" { send -- \"yes\r\"; exp_continue }
            eof { exit 1 }
        }
    "
done

# 时间同步
if [[ $name == $TIME_SERVER ]]; then
    # 配置当前节点为时间同步源
    sed -i '3,6s/^/#/g' /etc/chrony.conf
    sed -i "7s/^/server $TIME_SERVER iburst/g" /etc/chrony.conf
    echo "allow $TIME_SERVER_IP" >> /etc/chrony.conf
    echo "local stratum 10" >> /etc/chrony.conf
else
    # 配置当前节点同步到目标节点
    sed -i '3,6s/^/#/g' /etc/chrony.conf
    sed -i "7s/^/server $TIME_SERVER iburst/g" /etc/chrony.conf
fi

# 重启并启用 chrony 服务
systemctl restart chronyd
systemctl enable chronyd

echo "###############################################################"
echo "#################      集群初始化成功     #####################"
echo "###############################################################"

配置集群

配置离线源,安装软件包

[root@rabbitmq01 ~]# ls
anaconda-ks.cfg  init.sh  rabbitmq-repo.tar.gz
[root@rabbitmq01 ~]# tar -xf rabbitmq-repo.tar.gz -C /opt/
[root@rabbitmq01 ~]# mkdir /etc/yum.repos.d/repo.bak
[root@rabbitmq01 ~]# mv /etc/yum.repos.d/CentOS-* /etc/yum.repos.d/repo.bak/
[root@rabbitmq01 ~]# mkdir /opt/centos
[root@rabbitmq01 ~]# mount /dev/sr0 /opt/centos/
mount: /dev/sr0 is write-protected, mounting read-only
[root@rabbitmq01 ~]# cat /etc/yum.repos.d/rabbitmq.repo
[rabbitmq]
name=rabbitmq
baseurl=file:///opt/rabbitmq-repo
gpgcheck=0
enabled=1
[centos]
name=centos
baseurl=file:///opt/centos
gpgcheck=0
enabled=1
[root@rabbitmq01 ~]# yum clean all;yum repolist
Loaded plugins: fastestmirror
Cleaning repos: centos rabbitmq
Cleaning up list of fastest mirrors
Other repos take up 176 M of disk space (use --verbose for details)
Loaded plugins: fastestmirror
Determining fastest mirrors
centos                                                                                           | 3.6 kB  00:00:00
rabbitmq                                                                                         | 2.9 kB  00:00:00
(1/3): centos/group_gz                                                                           | 153 kB  00:00:00
(2/3): rabbitmq/primary_db                                                                       |  35 kB  00:00:00
(3/3): centos/primary_db                                                                         | 3.3 MB  00:00:00
repo id                                                  repo name                                                status
centos                                                   centos                                                   4,070
rabbitmq                                                 rabbitmq                                                    26
repolist: 4,096
[root@rabbitmq01 ~]#
[root@rabbitmq01 ~]# yum install -y vsftpd
[root@rabbitmq01 ~]# echo "anon_root=/opt" >> /etc/vsftpd/vsftpd.conf
[root@rabbitmq01 ~]# systemctl enable --now vsftpd
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd.service to /usr/lib/systemd/system/vsftpd.service.
[root@rabbitmq01 ~]#
[root@rabbitmq02 ~]# mkdir /etc/yum.repos.d/repo.bak
[root@rabbitmq02 ~]# mv /etc/yum.repos.d/CentOS-* /etc/yum.repos.d/repo.bak/
[root@rabbitmq02 ~]# cat /etc/yum.repos.d/rabbitmq.repo abbitmq.repo
[rabbitmq]
name=rabbitmq
baseurl=ftp://rabbitmq01/rabbitmq-repo
gpgcheck=0
enabled=1
[centos]
name=centos
baseurl=ftp://rabbitmq01/centos
gpgcheck=0
enabled=1
[root@rabbitmq02 ~]# yum clean all;yum repolist
Loaded plugins: fastestmirror
Cleaning repos: centos rabbitmq
Cleaning up list of fastest mirrors
Other repos take up 176 M of disk space (use --verbose for details)
Loaded plugins: fastestmirror
Determining fastest mirrors
centos                                                                                           | 3.6 kB  00:00:00
rabbitmq                                                                                         | 2.9 kB  00:00:00
(1/3): centos/group_gz                                                                           | 153 kB  00:00:00
(2/3): rabbitmq/primary_db                                                                       |  35 kB  00:00:00
(3/3): centos/primary_db                                                                         | 3.3 MB  00:00:00
repo id                                                  repo name                                                status
centos                                                   centos                                                   4,070
rabbitmq                                                 rabbitmq                                                    26
repolist: 4,096
[root@rabbitmq02 ~]#
[root@rabbitmq03 ~]# mkdir /etc/yum.repos.d/repo.bak
[root@rabbitmq03 ~]# mv /etc/yum.repos.d/CentOS-* /etc/yum.repos.d/repo.bak/
[root@rabbitmq03 ~]# cat /etc/yum.repos.d/rabbitmq.repo
[rabbitmq]
name=rabbitmq
baseurl=ftp://rabbitmq01/rabbitmq-repo
gpgcheck=0
enabled=1
[centos]
name=centos
baseurl=ftp://rabbitmq01/centos
gpgcheck=0
enabled=1
[root@rabbitmq03 ~]# yum clean all;yum repolist
Loaded plugins: fastestmirror
Cleaning repos: centos rabbitmq
Cleaning up list of fastest mirrors
Other repos take up 176 M of disk space (use --verbose for details)
Loaded plugins: fastestmirror
Determining fastest mirrors
centos                                                                                           | 3.6 kB  00:00:00
rabbitmq                                                                                         | 2.9 kB  00:00:00
(1/3): rabbitmq/primary_db                                                                       |  35 kB  00:00:00
(2/3): centos/group_gz                                                                           | 153 kB  00:00:00
(3/3): centos/primary_db                                                                         | 3.3 MB  00:00:00
repo id                                                  repo name                                                status
centos                                                   centos                                                   4,070
rabbitmq                                                 rabbitmq                                                    26
repolist: 4,096
[root@rabbitmq03 ~]#
[root@rabbitmq01 ~]# yum install -y rabbitmq-server
[root@rabbitmq02 ~]# yum install -y rabbitmq-server
[root@rabbitmq03 ~]# yum install -y rabbitmq-server

启动并开机自动

[root@rabbitmq01 ~]# systemctl enable --now rabbitmq-server
Created symlink from /etc/systemd/system/multi-user.target.wants/rabbitmq-server.service to /usr/lib/systemd/system/rabbitmq-server.service.
[root@rabbitmq01 ~]#

[root@rabbitmq02 ~]# systemctl enable --now rabbitmq-server
Created symlink from /etc/systemd/system/multi-user.target.wants/rabbitmq-server.service to /usr/lib/systemd/system/rabbitmq-server.service.
[root@rabbitmq02 ~]#

[root@rabbitmq03 ~]# systemctl enable --now rabbitmq-server
Created symlink from /etc/systemd/system/multi-user.target.wants/rabbitmq-server.service to /usr/lib/systemd/system/rabbitmq-server.service.
[root@rabbitmq03 ~]#

配置界面访问

[root@rabbitmq01 ~]# rabbitmq-plugins enable rabbitmq_management
The following plugins have been enabled:
  mochiweb
  webmachine
  rabbitmq_web_dispatch
  amqp_client
  rabbitmq_management_agent
  rabbitmq_management
Plugin configuration has changed. Restart RabbitMQ for changes to take effect.

重启后界面查看

[root@rabbitmq01 ~]# systemctl restart rabbitmq-server
[root@rabbitmq01 ~]# ss -tln | grep 15672
LISTEN     0      128          *:15672                    *:*
[root@rabbitmq01 ~]#

可以看到15672端口已开放,打开浏览器,输入rabbitmq1节点的ip加端口15672(http://IP:15672)访问RabbitMQ监控界面,使用用户名:密码guest:guest登录
在这里插入图片描述
在这里插入图片描述

配置集群节点通信

[root@rabbitmq01 ~]# cat /var/lib/rabbitmq/.erlang.cookie
XQDUOWPJABMYRFCNXQKV
[root@rabbitmq01 ~]# chmod 777 /var/lib/rabbitmq/.erlang.cookie
[root@rabbitmq01 ~]# scp /var/lib/rabbitmq/.erlang.cookie root@rabbitmq02:/var/lib/rabbitmq/
.erlang.cookie                                                                        100%   20    12.8KB/s   00:00
[root@rabbitmq01 ~]# scp /var/lib/rabbitmq/.erlang.cookie root@rabbitmq03:/var/lib/rabbitmq/
.erlang.cookie                                                                        100%   20    12.5KB/s   00:00
[root@rabbitmq01 ~]#
[root@rabbitmq02 ~]# chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
[root@rabbitmq03 ~]# chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie

重启rabbitmq服务

[root@rabbitmq02 ~]#systemctl restart rabbitmq-server
[root@rabbitmq03 ~]#systemctl restart rabbitmq-server

注;经过测试只需要远程把erlang.cookie同步,不做授权和更改文件属性也能成功搭建的,但是做了这些可能后续有服务会用到,就还是做了

配置节点加入集群

默认rabbitmq启动后是磁盘节点,在这个cluster命令下,rabbitmq02和rabbitmq03是内存节点,rabbitmq1是磁盘节点。
如果要使rabbitmq02、rabbitmq03都是磁盘节点,去掉–ram参数即可。
如果想要更改节点类型,可以使用命令rabbitmqctl change_cluster_node_type disc(ram),前提是必须停掉rabbit应用。

[root@rabbitmq02 ~]# rabbitmqctl stop_app
Stopping node rabbit@rabbitmq02 ...
...done.
[root@rabbitmq02 ~]# rabbitmqctl join_cluster --ram rabbit@rabbitmq01
Clustering node rabbit@rabbitmq02 with rabbit@rabbitmq01 ...
...done.
[root@rabbitmq02 ~]# rabbitmqctl start_app
Starting node rabbit@rabbitmq02 ...
...done.
[root@rabbitmq02 ~]#
[root@rabbitmq03 ~]# rabbitmqctl stop_app
Stopping node rabbit@rabbitmq03 ...
...done.
[root@rabbitmq03 ~]# rabbitmqctl join_cluster --ram rabbit@rabbitmq01
Clustering node rabbit@rabbitmq03 with rabbit@rabbitmq01 ...
...done.
[root@rabbitmq03 ~]# rabbitmqctl start_app
Starting node rabbit@rabbitmq03 ...
...done.
[root@rabbitmq03 ~]#

在rabbitmq02和rabbitmq03节点上启用rabbitmq_management

[root@rabbitmq02 ~]# rabbitmq-plugins enable rabbitmq_management
The following plugins have been enabled:
  mochiweb
  webmachine
  rabbitmq_web_dispatch
  amqp_client
  rabbitmq_management_agent
  rabbitmq_management
Plugin configuration has changed. Restart RabbitMQ for changes to take effect.
[root@rabbitmq02 ~]# systemctl restart rabbitmq-server
[root@rabbitmq02 ~]#
[root@rabbitmq03 ~]# rabbitmq-plugins enable rabbitmq_management
The following plugins have been enabled:
  mochiweb
  webmachine
  rabbitmq_web_dispatch
  amqp_client
  rabbitmq_management_agent
  rabbitmq_management
Plugin configuration has changed. Restart RabbitMQ for changes to take effect.
[root@rabbitmq03 ~]# systemctl restart rabbitmq-server

界面验证
在这里插入图片描述

在这里插入图片描述

常用命令

查看集群状态

[root@rabbitmq01 ~]# rabbitmqctl cluster_status
Cluster status of node rabbit@rabbitmq01 ...
[{nodes,[{disc,[rabbit@rabbitmq01]},
         {ram,[rabbit@rabbitmq03,rabbit@rabbitmq02]}]},
 {running_nodes,[rabbit@rabbitmq02,rabbit@rabbitmq03,rabbit@rabbitmq01]},
 {cluster_name,<<"rabbit@rabbitmq01">>},
 {partitions,[]}]
...done.

新增用户admin,并设置密码为admin,命令如下:

# rabbitmqctl add_user admin admin

删除admin用户,命令如下:

# rabbitmqctl delete_user admin

修改admin用户的密码为admin123,命令如下:

# rabbitmqctl change_password admin admin123

设置角色命令:

# rabbitmqctl set_user_tags admin administrator monitoring policymaker management

设置用户权限命令:

# rabbitmqctl set_permissions -p VHostPath admin ConfP WriteP ReadP

查询所有权限命令:

# rabbitmqctl list_permissions [-p  VHostPath]

指定用户权限命令:

# rabbitmqctl list_user_permissions admin

清除用户权限命令:

# rabbitmqctl clear_permissions [-p VHostPath] admin

集群搭建避坑

1

[root@rabbitmq02 ~]# rabbitmqctl stop_app
Stopping node rabbit@localhost ...
Error: unable to connect to node rabbit@rabbitmq02: nodedown

DIAGNOSTICS
===========

attempted to contact: [rabbit@rabbitmq02]

rabbit@rabbitmq02:
  * connected to epmd (port 4369) on rabbitmq02
  * epmd reports node 'rabbit' running on port 25672
  * TCP connection succeeded but Erlang distribution failed
  * suggestion: hostname mismatch?
  * suggestion: is the cookie set correctly?

current node details:
- node name: rabbitmqctl1883@rabbitmq02
- home dir: /var/lib/rabbitmq
- cookie hash: tRBtsDxkfPswwGSJJst8qw==

遇见这个问题,rabbit@localhost,如果在配置了主机名,主机解析,关闭了防火墙、selinux等情况下,而且验证了erlang.cookie三个节点是一致的,就重启服务再试一下,因为有时候你配置完毕后特别是erlang.cookie,是需要重启服务加载一下的,如果确保了这些操作都做完了,再试试最后一招

在使用rabbitmqctl stop_app停止服务时,使用 -n rabbit@rabbitmq02参数,强行指定

rabbitmqctl -n rabbit@rabbitmq02 stop_app
rabbitmqctl -n rabbit@rabbitmq02 join_cluster --ram rabbit@rabbitmq01
rabbitmqctl -n rabbit@rabbitmq02 start_app

2

[root@rabbitmq02 ~]# rabbitmqctl stop_app
Stopping node rabbit@rabbitmq02 ...
Error: unable to connect to node rabbit@rabbitmq02: nodedown

DIAGNOSTICS
===========

attempted to contact: [rabbit@rabbitmq02]

rabbit@rabbitmq02:
  * connected to epmd (port 4369) on rabbitmq02
  * epmd reports node 'rabbit' running on port 25672
  * TCP connection succeeded but Erlang distribution failed
  * suggestion: hostname mismatch?
  * suggestion: is the cookie set correctly?

current node details:
- node name: rabbitmqctl1883@rabbitmq02
- home dir: /var/lib/rabbitmq
- cookie hash: tRBtsDxkfPswwGSJJst8qw==

如果报错rabbit@rabbitmq02,这个连接不上,再基于上述操作对erlang.cookie完成后重启一下即可,就能正常使用命令配置集群了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值