Prometheus配置consul服务发现

consul是使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全,同时保证server-leader的选举能够正确的进行。

产品具有以下特点:

服务发现

Consul 的客户端可以注册一个服务,例如 api 或 mysql,其他客户端可以使用 Consul 来发现给定服务的提供者。

健康检查

Consul 可以根据给定的信息,对服务的状态进行检查,并获取服务的健康状态。

Key/Value存储

通过HTTP API的方式实现Key/Value存储,可用于动态配置、功能标记、协商等多种场景。

多数据中心支持

支持多数据中心的分布式架构。

Consul角色

  • client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群.

  • server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯. 每个数据中心的 server 数量推荐为 3 个或是 5 个.

2、consul二进制安装

wget  https://releases.hashicorp.com/consul/1.14.1/consul_1.14.1_linux_amd64.zip
unzip consul_1.14.1_linux_amd64.zip
mv consul  /usr/local
2.2 启动Consul
./consul agent  -server  -bootstrap  -bind=192.168.100.221    -client=192.168.100.221  -data-dir=data  -ui  -node=192.168.100.221 &

2.3 consul启动参数介绍

-server :定义agent运行在server模式
-bootstrap-expect :在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群,该标记不能和bootstrap共用
-data-dir:提供一个目录用来存放agent的状态,所有的agent允许都需要该目录,该目录必须是稳定的,系统重启后都继续存在
-node:节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名
-bind:该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0
-ui:启动web界面
-config-dir:配置文件目录,里面所有以.json结尾的文件都会被加载
-rejoin:使consul忽略先前的离开,在再次启动后仍旧尝试加入集群中。
-client:consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0

通过system启动consul

vim /etc/consul/consul.json 
​
{
  "datacenter": "Consul",
  "data_dir": "/opt/consul/data",
  "log_level": "INFO",
  "enable_debug": false,
  "bind_addr": "0.0.0.0",
  "client_addr": "0.0.0.0",
  "server": true,
  "bootstrap_expect": 1,
  "ui_config": {
    "enabled": true
  }
}
​
vim /etc/systemd/system/consul.service
​
[Unit]
Description=Consul Service Discovery Agent
Documentation=https://www.consul.io/
After=network-online.target
Wants=network-online.target
​
​
[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/local/bin/consul agent -server -ui -config-dir /etc/consul
ExecReload=/bin/kill -s SIGHUP $MAINPID
ExecStop=/bin/kill -INT $MAINPID
​
​
ExecReload=/bin/kill -HUP
KillSignal=SIGINT
TimeoutStopSec=5
Restart=on-failure
SyslogIdentifier=consul
​
​
[Install]
WantedBy=multi-user.target
​
​
systemctl daemon-reload  && systemctl restart consul

通过配置文件的方式注册 node-exporter 服务到 Consul

#多个node-exporter写在一个文件中
​
{
  "services": [
    {
      "name": "node_exporter_20",
      "tags": ["node20"],
      "address": "10.10.0.20",
      "port": 9100,
      "check": [
       {
        "http": "http://10.10.0.20:9100/metrics",
        "interval": "10s"
        }
      ]
    },
    {
      "name": "node_exporter_21",
      "tags": ["node21"],
      "address": "10.10.0.21",
      "port": 9100,
      "check": [
        {
        "http": "http://10.10.0.21:9100/metrics",
        "interval": "10s"
      }
    ]
   }
  ]
}
​

一个node-exporter写一个json文件

{  
  "service": {  
    "name": "node_exporter66",  
    "tags": ["node66"],  
    "address": "10.10.0.66",  
    "port": 9100,  
    "check": {  
      "http": "http://10.10.0.66:9100/metrics",  
      "interval": "10s"  
    }  
  }  
}
​

Docker 安装 使用 Docker 启动 Consul 单节点服务,直接获取最新版官方镜像 consul:latest 命令如下:

 docker run --name consul -d -p 8500:8500 consul
2.3 访问

打开浏览器,访问http://<cousul_ip>:8500,可看到Consul已经正常启动,目前只有默认的consul服务。

babad0fff00dfab28b68c559cd529286

Consul API 注册 node-exporter 服务到 Consul

通过 API 注册node-exporter 服务信息到 Consul 中,服务地址及端口为 node-exporter 默认提供指标数据的地址。注意:服务注册到Consul的id不能相同。

Consul API注册"id=node-exporter"服务 #用curl的方式注册到Consul中

curl -X PUT -d '{"id": "node-exporter","name": "node-exporter-192.168.100.221","address": "192.168.100.221","port": 9100,"tags": ["node-exporter"],"Meta":{"app": "linux-node","project": "yewu"},"checks": [{"http": "http://192.168.100.221:9100/metrics", "interval": "5s"}]}'  http://192.168.100.221:8500/v1/agent/service/register

Consul Web 控制台 查看注册服务

e7618bb0277ea2a5edec2f438d15a88d

Consul API 注销"id=node-exporter" 服务

curl -X PUT http://192.168.100.221:8500/v1/agent/service/deregister/node-exporter

Prometheus 实现自动服务发现

vim /usr/local/prometheus/prometheus.yml

- job_name: 'consul-node-exporter'
    consul_sd_configs:
      - server: '192.168.100.221:8500'
        services: []
    relabel_configs:
      - source_labels: [__meta_consul_tags]
        regex: .*node*
        action: keep
      - source_labels: ['__meta_consul_service']
        regex: '(.*)'
        target_label: 'job'
        replacement: '$1'
      - source_labels: ['__meta_consul_service_address']
        regex: '(.*)'
        target_label: 'instance'
        replacement: '$1'
      - source_labels: ['__meta_consul_service_metadata_project']
        regex: '(.*)'
        target_label: 'project'
        replacement: '$1'

高级配置 把标签全部打印出来:

  - job_name: 'consul61'
    metrics_path: /metrics
    scheme: http
    consul_sd_configs:
      - server: "10.10.0.61:8500"
        services: []
    relabel_configs:
      - source_labels: [__meta_consul_tags]
        action: keep
        regex: .*node.*
      - source_labels: ['__meta_consul_service']
        regex: '(.*)'
        target_label: 'job'
        replacement: '$1'
      - source_labels: ['__meta_consul_service_address']
        regex: '(.*)'
        target_label: 'instance'
        replacement: '$1'
        #标签全部打印出来配置
      - source_labels: ['__meta_consul_service_metadata']
        regex: __meta_consul_service_metadata_(.+)
        action: labelmap
​

注释 :consul_sd_configs 配置使用 Consul 服务发现类型

server 指定Consul 的服务地址

relabel_configs 用来实现自定义标签及分类

Prometheus的restful接口热加载配置

curl  -X POST http://127.0.0.1:9090/-/reload

ce6a7562a63a3bfcbb3714640b3e09c3

#######################################上面配置意图如下

1.会发现 Prometheus 同时加载出来了默认服务 consul,这个是不需要的。
​
2.默认只显示 job 及 instance 两个标签,其他标签都默认属于 before relabeling 下,有些必要的服务信息,也想要在标签中展示,该如何操作呢?
​
3.如果需要自定义一些标签,例如 team、group、project 等关键分组信息,方便后边 alertmanager 进行告警规则匹配,该如何处理呢?
​
4.所有 Consul 中注册的 Service 都会默认加载到 Prometheus 下配置的 consul_prometheus 组,如果有多种类型的 exporter,如何在 Prometheus 中配置分配给指定类型的组,方便直观的区别它们?
​

以上问题,我们可以通过 Prometheus 配置中的 relabel_configs 参数来解决

6、配置 relabel_configs 实现自定义标签及分类

我们先来普及一下 relabel_configs 的功能, Prometheus 允许用户在采集任务设置中,通过 relabel_configs 来添加自定义的 Relabeling 的额过程,来对标签进行指定规则的重写。 Prometheus 加载 Targets 后,这些 Targets 会自动包含一些默认的标签,Target 以 __ 作为前置的标签是在系统内部使用的,这些标签不会被写入到样本数据中。眼尖的会发现,每次增加 Target 时会自动增加一个 instance 标签,而 instance 标签的内容刚好对应 Target 实例的 address 值,这是因为实际上 Prometheus 内部做了一次标签重写处理,默认 address 标签设置为 : 地址,经过标签重写后,默认会自动将该值设置为 instance 标签,所以我们能够在页面看到该标签。

19507e98bc174f27b88db9cd27d99281

详细 relabel_configs 配置及说明可以参考 relabel_config 官网说明,这里我简单列举一下里面每个 relabel_action 的作用,方便下边演示。

1.replace: 根据 regex 的配置匹配 source_labels标签的值(注意:多个 source_label的值会按照 separator 进行拼接),并且将匹配到的值写入到 target_label当中,如果有多个匹配组,则可以使用 ${1}, ${2} 确定写入的内容。如果没匹配到任何内容则不对 target_label进行重新, 默认为 replace。
2.keep: 丢弃 source_labels 的值中没有匹配到 regex 正则表达式内容的 Target 实例
3.drop: 丢弃 source_labels 的值中匹配到 regex 正则表达式内容的 Target 实例
4.hashmod: 将 target_label 设置为关联的 source_label的哈希模块
5.labelmap: 根据 regex 去匹配 Target 实例所有标签的名称(注意是名称),并且将捕获到的内容作为为新的标签名称,regex 匹配到标签的的值作为新标签的值
6.labeldrop: 对 Target 标签进行过滤,会移除匹配过滤条件的所有标签
7.labelkeep: 对 Target 标签进行过滤,会移除不匹配过滤条件的所有标签

接下来,我们来挨个处理上述问题。

问题一,我们可以配置 relabel_configs 来实现标签过滤,只加载符合规则的服务。以上边为例,可以通过过滤 __meta_consul_tags 标签为 test 的服务,relabel_config 向 Consul 注册服务的时候,只加载匹配 regex 表达式的标签的服务到自己的配置文件。修改 prometheus.yml 配置如下:

...
- job_name: 'consul-prometheus'
  consul_sd_configs:
    - server: '172.30.12.167:8500'
      services: []  
  relabel_configs:
    - source_labels: [__meta_consul_tags]
      regex: .*test.*
      action: keep
​

解释下,这里的 relabel_configs 配置作用为丢弃源标签中 meta_consul_tags 不包含 test 标签的服务,meta_consul_tags 对应到 Consul 服务中的值为 “tags”: [“test”],默认 consul 服务是不带该标签的,从而实现过滤。重启 Prometheus 可以看到现在只获取了 node-exporter-172.30.12.167 这个服务了。

cae085c173c84839a8fb9256e78b2ba9

问题二和问题三可以归为一类,就是将系统默认标签或者用户自定义标签转换成可视化标签,方便查看及后续 Alertmanager 进行告警规则匹配分组。不过要实现给服务添加自定义标签,我们还得做一下修改,就是在注册服务时,将自定义标签信息添加到 Meta Data 数据中,具体可以参考 这里 官网说明,下边来演示一下如何操作。

对于自定义标签的添加,可通过json文件的方式进行操作。

新建 node1.json 如下:

$ vim node1.json
{
  "ID": "node-exporter",
  "Name": "node-exporter-172.30.12.167",
  "Tags": [
    "test"
  ],
  "Address": "172.30.12.167",
  "Port": 9100,
  "Meta": {
    "app": "spring-boot",
    "team": "appgroup",
    "project": "bigdata"
  },
  "EnableTagOverride": false,
  "Check": {
    "HTTP": "http://172.30.12.167:9100/metrics",
    "Interval": "10s"
  }
}

注释:ID 指定实例的唯一ID名称;Name 指定服务名,可以多个实例共用服务名;Tags 指定服务的标签列表//可写可不写,这些标签可用于过滤服务,并通过API进行公开;Address 指定服务的实例地址;Port 指定实例的端口号;Meta 指定服务的元数据,格式为key:value,此处用于保存我们的标签信息;EnableTagOverride 此处禁用服务标签的反熵功能;Check 服务的检查列表,Consul会根据配置信息定时发起检查,确定服务是否正常;

通过json文件注册服务
curl --request PUT --data @node1.json http://192.168.75.160:8500/v1/agent/service/register

示例:

{
    "services": [
        {
            "id": "stfp-test-206",
            "name": "测试sftp",
            "address": "10.75.43.206",
            "port": 9100,
            "meta": {
                "project": "SFTP",
                "env": "test",
                "service": "sftp",
                "user": "刘汉宝",
                "phone": "15721586305"
            },
            "checks": [
                {
                    "tcp": "10.75.13.10:9100",
                    "interval": "10s"
                }
            ]
        },
        {
            "id": "prod-java-node-11",
            "name": "收银平台",
            "address": "10.75.13.11",
            "port": 9100,
            "meta": {
                "project": "收银平台",
                "env": "prod",
                "service": "核心区",
                "user": "钟佳林",
                "phone": "xxx"
            },
            "checks": [
                {
                    "tcp": "10.75.13.11:9100",
                    "interval": "10s"
                }
            ]
        }
    ]
}
5.3 查看Consul

查看Consul,可看到实例的元数据中已包含标签信息

fb9e02b5bb4246f4bb85ea09f3239bfe

5.4 查看Prometheus

元数据在Prometheus自动发现的过程中,会变成以_meta_consul_service_metadata开头的标签,如下图所示。

我们可以通过前面介绍的Relabeling(标签重写)功能,将其转换为我们需要的标签。

将job进行如下修改:

- job_name: 'consul-prom'
    consul_sd_configs:
      - server: '192.168.75.160:8500'
        services: ['node_exporter']
    relabel_configs:
      - regex: __meta_consul_service_metadata_(.+)
        action: labelmap
​
5.5 重新加载配置

重新加载配置后,可看到标签已经生成。

abeb74009d3349e0a7e57d497a744b9d

#生成环境配置
- job_name: consul服务发现
    consul_sd_configs:
      - server: "10.75.41.115:8500"
        refresh_interval: 10s
    relabel_configs:
      - regex: __meta_consul_service_metadata_(.*)
        replacement: $1

Consul 集群部署

Consul角色

  • client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群.

  • server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯. 每个数据中心的 server 数量推荐为 3 个或是 5 个.

此处采用的是 3Server + 1Client 的集群架构

image-20230829172413403

1、安装 Consul 配置主机名,命令示例:(以下操作在所有节点配置)

 hostnamectl set-hostname consul-01
 hostnamectl set-hostname consul-02
 hostnamectl set-hostname consul-03
 hostnamectl set-hostname consul-client

下载consul二进制文件

export VER="1.12.3"
wget https://releases.hashicorp.com/consul/${VER}/consul_${VER}_linux_amd64.zip

解压安装

apt install -y unzip
unzip consul_${VER}_linux_amd64.zip -d /usr/local/bin/

查看版本

 consul version
1

创建consul用户和组

sudo groupadd --system consul
sudo useradd -s /sbin/nologin --system -g consul consul

创建consul安装目录

mkdir -p /data/consul/{config,data,logs}
chown -R consul:consul /data/consul
chmod -R 775 /data/consul

设置 DNS 或编辑/etc/hosts 文件以配置所有服务器的主机名,替换example.com为您的实际域名。

cat >/etc/hosts<<EOF
192.168.94.10 consul-01.example.com consul-01
192.168.94.11 consul-02.example.com consul-02
192.168.94.12 consul-03.example.com consul-03
192.168.94.13 consul-client.example.com consul-client
EOF

2、Server 节点配置

生成Consul secret

consul keygen

为server节点创建json配置文件,其他节点配置修改IP地址及节点名称即可

consul-01节点

cat >/data/consul/config/consul.json<<EOF
{
    "advertise_addr": "192.168.94.10",
    "bind_addr": "192.168.94.10",
    "bootstrap_expect": 3,
    "client_addr": "0.0.0.0",
    "datacenter": "DC1",
    "node_name": "consul-01", 
    "data_dir": "/data/consul/data",
    "domain": "consul",
    "enable_script_checks": true,
    "dns_config": {
        "enable_truncate": true,
        "only_passing": true
    },
    "enable_syslog": true,
    "encrypt": "F0VSq05Die32sMUimbZ2LAzUIc5Ry4jCL2ouwz/Gu8Q=",
    "leave_on_terminate": true,
    "log_level": "INFO",
    "rejoin_after_leave": true,
    "retry_join": [
     "consul-01",
     "consul-02",
     "consul-03"
    ],
    "server": true,
    "start_join": [
        "consul-01",
        "consul-02",
        "consul-03"
    ],
    "ui": true
}
EOF

配置参数说明:

datacenter:此标志控制运行代理的数据中心。如果未提供,则默认为dc1。Consul 对多个数据中心有一流的支持,但它依赖于正确的配置。同一数据中心中的节点应位于单个 LAN 上。

data_dir:该标志为代理提供了一个数据目录来存储状态。

node_name:节点名字,一般为主机名。

server:指定是否为server节点。

bootstrap_expect:在一个datacenter中期望提供的server节点数目,当提供该值的时候,consul一直等到达到指定sever数目才会引导整个集群,该标记不能和bootstrap公用。

bind_addr:该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0。

client_addr:Consul 将绑定客户端接口的地址,包括 HTTP 和 DNS 服务器。默认情况下,这是127.0.0.1,仅允许环回连接。在 Consul 1.0 及更高版本中,这可以设置为要绑定到的以空格分隔的地址列表。

log_json:此标志使代理能够以 JSON 格式输出日志。默认为false。

log_level:在 Consul 代理启动后显示的日志级别。默认为 info 。可用的日志级别是trace、debug、info、warn和err。

retry_join:指定将要置入集群的IP列表,如果失败,会自动重试,知道直到成功加入。

start_join:启动时加入集群的地址

ui_config:此对象允许设置多个子键,用于控制 UI 中可用的显示或功能。

rejoin_after_leave :允许重新加入集群

consul-02节点

cat >/data/consul/config/consul.json<<EOF
{
    "advertise_addr": "192.168.94.11",
    "bind_addr": "192.168.94.11",
    "bootstrap_expect": 3,
    "client_addr": "0.0.0.0",
    "datacenter": "DC1",
    "node_name": "consul-02", 
    "data_dir": "/data/consul/data",
    "domain": "consul",
    "enable_script_checks": true,
    "dns_config": {
        "enable_truncate": true,
        "only_passing": true
    },
    "enable_syslog": true,
    "encrypt": "F0VSq05Die32sMUimbZ2LAzUIc5Ry4jCL2ouwz/Gu8Q=",
    "leave_on_terminate": true,
    "log_level": "INFO",
    "rejoin_after_leave": true,
    "retry_join": [
     "consul-01",
     "consul-02",
     "consul-03"
    ],
    "server": true,
    "start_join": [
        "consul-01",
        "consul-02",
        "consul-03"
    ],
    "ui": true
}
EOF

consul-03节点

cat >/data/consul/config/consul.json<<EOF
{
    "advertise_addr": "192.168.94.12",
    "bind_addr": "192.168.94.12",
    "bootstrap_expect": 3,
    "client_addr": "0.0.0.0",
    "datacenter": "DC1",
    "node_name": "consul-03", 
    "data_dir": "/data/consul/data",
    "domain": "consul",
    "enable_script_checks": true,
    "dns_config": {
        "enable_truncate": true,
        "only_passing": true
    },
    "enable_syslog": true,
    "encrypt": "F0VSq05Die32sMUimbZ2LAzUIc5Ry4jCL2ouwz/Gu8Q=",
    "leave_on_terminate": true,
    "log_level": "INFO",
    "rejoin_after_leave": true,
    "retry_join": [
     "consul-01",
     "consul-02",
     "consul-03"
    ],
    "server": true,
    "start_join": [
        "consul-01",
        "consul-02",
        "consul-03"
    ],
    "ui": true
}
EOF

3、Client 节点配置 client是consul客户端,客户端不保存数据,客户端将接收到的请求转发给Server端。Server之间通过局域网或广域网通信实现数据一致性。每个Server或Client都是一个consul agent。

创建consul-client节点配置文件

cat >/data/consul/config/consul.json<<EOF
{
    "advertise_addr": "192.168.94.13",
    "bind_addr": "192.168.94.13",
    "client_addr": "0.0.0.0",
    "datacenter": "DC1",
    "node_name": "consul-client", 
    "data_dir": "/data/consul/data",
    "domain": "consul",
    "enable_script_checks": true,
    "dns_config": {
        "enable_truncate": true,
        "only_passing": true
    },
    "enable_syslog": true,
    "encrypt": "F0VSq05Die32sMUimbZ2LAzUIc5Ry4jCL2ouwz/Gu8Q=",
    "leave_on_terminate": true,
    "log_level": "INFO",
    "rejoin_after_leave": true,
    "retry_join": [
     "consul-01",
     "consul-02",
     "consul-03"
    ],
    "server": false,
    "start_join": [
        "consul-01",
        "consul-02",
        "consul-03"
    ],
    "ui": true
}
EOF

4、systemd 启动文件

所有节点创建systemd 服务文件/etc/systemd/system/consul.service,所有节点具有相同内容,配置如下:

cat >/etc/systemd/system/consul.service<<EOF
[Unit]
Description=Consul Service Discovery Agent
Documentation=https://www.consul.io/
After=network-online.target
Wants=network-online.target


[Service]
Type=simple
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -config-dir=/data/consul/config/consul.json


ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
TimeoutStopSec=5
Restart=on-failure
SyslogIdentifier=consul


[Install]
WantedBy=multi-user.target
EOF

所有节点启动consul服务

systemctl enable --now consul.service

查看consul服务运行状态

systemctl status consul

5、查看 Consul 集群状态 3 台 Server 和 1 台 Client 上的 Consul 服务都启动后会根据 consul.json 中的配置自动组成一个集群,并且 Server 会通过 Raft 协议在 Server 节点中选举出 leader 节点。

可以用如下命令在任意一台机器上查看集群节点状态:

root@consul-01:~# consul members
Node           Address             Status  Type    Build   Protocol  DC   Partition  Segment
consul-01      192.168.94.10:8301  alive   server  1.12.3  2         dc1  default    <all>
consul-02      192.168.94.11:8301  alive   server  1.12.3  2         dc1  default    <all>
consul-03      192.168.94.12:8301  alive   server  1.12.3  2         dc1  default    <all>
consul-client  192.168.94.13:8301  alive   client  1.12.3  2         dc1  default    <default>

查看server节点成员状态信息:

root@consul-01:~# consul operator raft list-peers
Node       ID                                    Address             State     Voter  RaftProtocol
consul-02  edefb129-e0fd-804d-bbfb-76e259077d03  192.168.94.11:8300  leader    true   3
consul-01  50f9830f-45cd-683c-2382-77e1cf6d6e48  192.168.94.10:8300  follower  true   3
consul-03  5dae09d2-7079-4611-1ab5-7728d0966cbe  192.168.94.12:8300  follower  true   3
访问Consul UI:  http://192.168.94.10:8500/ui

bc4cc98334bcb090c931a9b6dcd73c92_1d89115c17ca41d1bd4ab1415aa6f1cd

集群就搭建完成了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

博学咪AIGC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值